[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.{c,h,cpp,hpp}]\nindent_style = tab\nindent_size = 4\n\n[*.py]\nindent_style = space\nindent_size = 4\n\n[Makefile]\nindent_style = tab\n\n[*.{yml,yaml}]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".github/issue_template.md",
    "content": "<!--\n\nSTOP!  If you are reporting a security issue, which includes *anything* that\n       may cause the broker/client to crash, please do NOT report it here but\n       follow the steps at https://www.eclipse.org/security/\n\nIf your issue is not a bug or a feature request, please use the mailing list at\nhttps://dev.eclipse.org/mailman/listinfo/mosquitto-dev to ask your question.\nThere are many more people available to help there than on this issue tracker.\n\nIf you are reporting a bug PLEASE include the version of Mosquitto you are\nusing and what platform (Windows, Ubuntu/Fedora/... Linux, FreeBSD, ...) you\nare running on.\n\nPlease also note that some systems have old versions of Mosquitto available in\ntheir package repositories. We would be very grateful if you would check to\nsee whether the bug is still present in a newer version before submitting your\nissue.\n\n-->\n\n"
  },
  {
    "path": ".github/labeler.yml",
    "content": "'Status: Available':\n  - '/.*/'\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "Thank you for contributing your time to the Mosquitto project!\n\nBefore you go any further, please note that we cannot accept contributions if\nyou haven't signed the [Eclipse Contributor Agreement](https://www.eclipse.org/legal/ECA.php).\nIf you aren't able to do that, or just don't want to, please describe your bug\nfix/feature change in an issue. For simple bug fixes it is can be just as easy\nfor us to be told about the problem and then go fix it directly.\n\nThen please check the following list of things we ask for in your pull request:\n\n- [ ] Have you signed the [Eclipse Contributor Agreement](https://www.eclipse.org/legal/ECA.php), using the same email address as you used in your commits?\n- [ ] Do each of your commits have a \"Signed-off-by\" line, with the correct email address? Use \"git commit -s\" to generate this line for you.\n- [ ] If you are contributing a new feature, is your work based off the develop branch?\n- [ ] If you are contributing a bugfix, is your work based off the fixes branch?\n- [ ] Have you added an explanation of what your changes do and why you'd like us to include them?\n- [ ] Have you successfully run `make test` with your changes locally?\n\n-----\n"
  },
  {
    "path": ".github/workflows/build-variants.yml",
    "content": "name: Mosquitto - Build variants\n\non:\n  push:\n    branches:\n      - master\n      - develop\n      - fixes\n      - release/*\n  pull_request:\n    branches:\n      - master\n      - develop\n      - fixes\n      - release/*\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Install third party dependencies\n      run: |\n        sudo apt-get update\n        sudo apt-get install -y \\\n          docbook-xsl \\\n          libargon2-dev \\\n          libc-ares-dev \\\n          libcjson-dev \\\n          libcunit1-dev \\\n          libedit-dev \\\n          libgmock-dev \\\n          libmicrohttpd-dev \\\n          libssl-dev \\\n          libsystemd-dev \\\n          libwrap0-dev \\\n          python3-all \\\n          uthash-dev \\\n          xsltproc\n    - uses: actions/checkout@v6\n      with:\n        submodules: 'true'\n    - name: build\n      run: ./buildtest.py\n"
  },
  {
    "path": ".github/workflows/cifuzz.yml",
    "content": "name: CIFuzz\non:\n  workflow_dispatch:\n  pull_request:\n    branches:\n      - master\n      - develop\n    paths:\n      - '**.c'\n      - '**.cpp'\n      - '**.h'\npermissions: {}\njobs:\n  Fuzzing:\n    runs-on: ubuntu-latest\n    permissions:\n      security-events: write\n    steps:\n    - name: Build Fuzzers\n      id: build\n      uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master\n      with:\n        oss-fuzz-project-name: 'mosquitto'\n    - name: Run Fuzzers\n      uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master\n      with:\n        oss-fuzz-project-name: 'mosquitto'\n        fuzz-seconds: 600\n        output-sarif: true\n    - name: Upload Crash\n      uses: actions/upload-artifact@v6\n      if: failure() && steps.build.outcome == 'success'\n      with:\n        name: artifacts\n        path: ./out/artifacts\n    - name: Upload Sarif\n      if: always() && steps.build.outcome == 'success'\n      uses: github/codeql-action/upload-sarif@v3\n      with:\n        # Path to SARIF file relative to the root of the repository\n        sarif_file: cifuzz-sarif/results.sarif\n        checkout_path: cifuzz-sarif\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  workflow_dispatch:\n  push:\n    branches: [ master, fixes, develop ]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ master ]\n  schedule:\n    - cron: '44 18 * * 1'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'cpp', 'python' ]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]\n        # Learn more:\n        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v6\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v3\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n        # queries: ./path/to/local/query, your-org/your-repo/queries@main\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 https://git.io/JvXDl\n\n    - run: sudo apt-get update && sudo apt-get install -y gcc g++ git libssl-dev libargon2-dev libedit-dev libmicrohttpd-dev\n    # Install cJSON\n    - run: git clone https://github.com/DaveGamble/cJSON /tmp/cJSON\n    - run: wget https://github.com/DaveGamble/cJSON/archive/v1.7.19.tar.gz -O /tmp/cjson.tar.gz\n    - run: mkdir -p /tmp/build/cjson\n    - run: tar --strip=1 -xf /tmp/cjson.tar.gz -C /tmp/build/cjson\n    - run: rm /tmp/cjson.tar.gz\n    - run: cd /tmp/build/cjson && cmake .  -DCMAKE_BUILD_TYPE=MinSizeRel -DCJSON_BUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr\n    - run: sudo make -C /tmp/build/cjson install\n\n    # Now build Mosquitto\n    - run: make binary\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v3\n"
  },
  {
    "path": ".github/workflows/coverage.yml",
    "content": "name: Coverage\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n      - develop\n      - fixes\n      - release/*\n  pull_request:\n    branches:\n      - master\n      - develop\n      - fixes\n      - release/*\n\njobs:\n  coverage:\n    runs-on: ubuntu-22.04\n\n    steps:\n    - run: |\n        sudo apt-get update\n        sudo apt-get install -y \\\n            docbook-xsl \\\n            lcov \\\n            libargon2-dev \\\n            libc-ares-dev \\\n            libcjson-dev \\\n            libcjson1 \\\n            libcunit1-dev \\\n            libedit-dev \\\n            libgmock-dev \\\n            libmicrohttpd-dev \\\n            libssl-dev \\\n            libwrap0-dev \\\n            microsocks \\\n            python3-all \\\n            python3-paho-mqtt \\\n            python3-psutil \\\n            uthash-dev \\\n            xsltproc\n\n    - uses: actions/checkout@v6\n\n    - run: |\n        make \\\n          WITH_COVERAGE=yes \\\n          CFLAGS=\"-O0 -Wall -ggdb -fprofile-arcs\" \\\n          -j $(nproc) \\\n          binary\n        make \\\n          WITH_COVERAGE=yes \\\n          CFLAGS=\"-O0 -Wall -ggdb -fprofile-arcs\" \\\n          -j $(nproc) \\\n          test-compile\n\n    - run: |\n        make -C test test\n\n    - run: |\n        lcov --capture --directory . --output-file coverage.info --no-external\n\n    - uses: codecov/codecov-action@v4\n      with:\n        token: ${{ secrets.CODECOV_TOKEN }}\n        fail_ci_if_error: true\n        files: ./coverage.info\n        verbose: true\n"
  },
  {
    "path": ".github/workflows/coverity-scan-develop.yml",
    "content": "name: Coverity Scan develop branch on a weekly basis\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"7 3 * * 0\"\n\njobs:\n  coverity:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        ref: develop\n\n    - name: Dependencies\n      run: |\n        sudo apt-get update\n        sudo apt-get install -y \\\n            docbook-xsl \\\n            google-mock \\\n            googletest \\\n            lcov \\\n            libc-ares-dev \\\n            libcjson-dev \\\n            libcjson1 \\\n            libcunit1-dev \\\n            libedit-dev \\\n            libgmock-dev \\\n            libmicrohttpd-dev \\\n            libssl-dev \\\n            libwrap0-dev \\\n            microsocks \\\n            python3-all \\\n            python3-paho-mqtt \\\n            python3-psutil \\\n            uthash-dev \\\n            xsltproc\n\n    - uses: vapier/coverity-scan-action@v1\n      with:\n        build_language: 'cxx'\n        project: \"eclipse/mosquitto\"\n        token: ${{ secrets.COVERITY_SCAN_TOKEN }}\n        email: ${{ secrets.COVERITY_SCAN_EMAIL }}\n        command: \"make binary-all\"\n"
  },
  {
    "path": ".github/workflows/coverity-scan-fixes.yml",
    "content": "name: Coverity Scan fixes branch on a weekly basis\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"7 3 * * 3\"\n\njobs:\n  coverity:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        ref: fixes\n\n    - name: Dependencies\n      run: |\n        sudo apt-get update\n        sudo apt-get install -y \\\n            docbook-xsl \\\n            lcov \\\n            libc-ares-dev \\\n            libcjson-dev \\\n            libcjson1 \\\n            libcunit1-dev \\\n            libedit-dev \\\n            libmicrohttpd-dev \\\n            libssl-dev \\\n            libwrap0-dev \\\n            microsocks \\\n            python3-all \\\n            python3-paho-mqtt \\\n            python3-psutil \\\n            uthash-dev \\\n            xsltproc\n\n    - uses: vapier/coverity-scan-action@v1\n      with:\n        build_language: 'cxx'\n        project: \"eclipse/mosquitto\"\n        token: ${{ secrets.COVERITY_SCAN_TOKEN }}\n        email: ${{ secrets.COVERITY_SCAN_EMAIL }}\n        command: \"make binary-all\"\n"
  },
  {
    "path": ".github/workflows/delete-old-workflow-runs.yml",
    "content": "name: Delete old workflow runs\non:\n  workflow_dispatch:\n  schedule:\n    - cron: '0 0 1 * *'\n# Run monthly, at 00:00 on the 1st day of month.\n\njobs:\n  del_runs:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Delete workflow runs\n        uses: Mattraks/delete-workflow-runs@v2\n        with:\n          token: ${{ github.token }}\n          repository: ${{ github.repository }}\n          retain_days: 30\n          keep_minimum_runs: 6\n"
  },
  {
    "path": ".github/workflows/issue-labler.yml",
    "content": "name: \"Issue Labeler\"\non:\n  issues:\n    types: [opened]\n\npermissions:\n  issues: write\n  contents: read\n\njobs:\n  triage:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: github/issue-labeler@v3.4\n      with:\n        configuration-path: .github/labeler.yml\n        not-before: 2024-11-03T00:00:00Z\n        enable-versioned-regex: 0\n        repo-token: ${{ github.token }}\n"
  },
  {
    "path": ".github/workflows/lock.yml",
    "content": "name: 'Lock Threads'\n\non:\n  schedule:\n    - cron: '0 0 * * 0'\n  workflow_dispatch:\n\npermissions:\n  issues: write\n  pull-requests: write\n\nconcurrency:\n  group: lock-threads\n\njobs:\n  action:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: dessant/lock-threads@v6\n        with:\n          issue-inactive-days: '90'\n"
  },
  {
    "path": ".github/workflows/macos.yml",
    "content": "name: Mac OS build\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n      - fixes\n      - develop\n      - release/*\n    tags:\n      - 'v[0-9]+.*'\n  pull_request:\n    branches:\n      - master\n      - fixes\n      - develop\n      - release/*\n\nenv:\n  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)\n  BUILD_TYPE: Release\n\njobs:\n  mosquitto:\n    runs-on: macos-latest\n\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: pin cmake to 3.x series\n        uses: jwlawson/actions-setup-cmake@09fd9b0fb3b239b4b68d9256cd65adf8d6b91da0\n        with:\n          cmake-version: '3.31.6'\n\n      - name: Python test dependencies\n        uses: actions/setup-python@v6\n        with:\n          cache: 'pip'\n\n      - name: Install Homebrew dependencies\n        run: |\n          brew update\n          brew list cmake || brew install cmake\n          brew install \\\n            argon2 \\\n            cjson \\\n            cunit \\\n            docbook-xsl \\\n            gcc \\\n            googletest \\\n            libedit \\\n            libmicrohttpd \\\n            make \\\n            openssl \\\n            uthash\n\n      - name: Configure CMake\n        run: |\n          EDITLINE_DIR=$(brew --prefix libedit)\n          HOMEBREW_PREFIX=$(brew --prefix)\n          cmake -B ${{github.workspace}}/build64 \\\n            -G Ninja \\\n            -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \\\n            -DCMAKE_PREFIX_PATH=\"$HOMEBREW_PREFIX\" \\\n            -DWITH_DOCS=OFF \\\n            -DOPENSSL_ROOT_DIR=$(brew --prefix openssl@3)\n      - name: Build\n        run: |\n          cmake --build ${{github.workspace}}/build64 \\\n            --config ${{env.BUILD_TYPE}}\n\n      - name: Test\n        working-directory: build64/\n        run: |\n          python3 -m venv venv\n          source venv/bin/activate\n          python3 -m pip install --upgrade pip\n          python3 -m pip install psutil\n          ctest --output-on-failure --repeat until-pass:5\n"
  },
  {
    "path": ".github/workflows/mosquitto-cmake.yml",
    "content": "name: Mosquitto - CMake\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n      - fixes\n      - develop\n      - release/*\n    tags:\n      - 'v[0-9]+.*'\n  pull_request:\n    branches:\n      - master\n      - fixes\n      - develop\n      - release/*\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Install third party dependencies\n      run: |\n        sudo apt-get update\n        sudo apt-get install -y \\\n          docbook-xsl \\\n          lcov \\\n          libargon2-dev \\\n          libc-ares-dev \\\n          libcjson-dev \\\n          libcjson1 \\\n          libcunit1-dev \\\n          libedit-dev \\\n          libgmock-dev \\\n          libmicrohttpd-dev \\\n          libssl-dev \\\n          libwrap0-dev \\\n          microsocks \\\n          python3-all \\\n          python3-paho-mqtt \\\n          python3-psutil \\\n          uthash-dev \\\n          xsltproc\n\n    - uses: actions/checkout@v6\n\n    - run: cmake -E make_directory build\n\n    - run: |\n        cmake \\\n          -DCMAKE_BUILD_TYPE=Debug \\\n          -S . \\\n          -B build\n\n    - run: cmake --build build --parallel $(nproc)\n\n    - working-directory: build/\n      run: ctest --output-on-failure --repeat until-pass:5\n"
  },
  {
    "path": ".github/workflows/mosquitto-make-asan.yml",
    "content": "name: Mosquitto - Make ASAN\n\non:\n  push:\n    branches:\n      - master\n      - develop\n      - fixes\n      - release/*\n  pull_request:\n    branches:\n      - master\n      - develop\n      - fixes\n      - release/*\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Install third party dependencies\n      run: |\n        sudo apt-get update\n        sudo apt-get install -y \\\n          clang \\\n          docbook-xsl \\\n          lcov \\\n          libargon2-dev \\\n          libc-ares-dev \\\n          libcjson-dev \\\n          libcjson1 \\\n          libcunit1-dev \\\n          libedit-dev \\\n          libgmock-dev \\\n          libmicrohttpd-dev \\\n          libssl-dev \\\n          libwrap0-dev \\\n          microsocks \\\n          python3-all \\\n          python3-paho-mqtt \\\n          python3-psutil \\\n          uthash-dev \\\n          xsltproc\n    -\n      uses: actions/checkout@v6\n      with:\n        submodules: 'true'\n    -\n      name: make\n      run: make WITH_ASAN=yes\n    -\n      name: make test\n      run: |\n        make WITH_ASAN=yes ptest\n"
  },
  {
    "path": ".github/workflows/mosquitto-make.yml",
    "content": "name: Mosquitto - Make\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n      - fixes\n      - develop\n      - release/*\n    tags:\n      - 'v[0-9]+.*'\n  pull_request:\n    branches:\n      - master\n      - fixes\n      - develop\n      - release/*\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Install third party dependencies\n      run: |\n        sudo apt-get update\n        sudo apt-get install -y \\\n          docbook-xsl \\\n          lcov \\\n          libargon2-dev \\\n          libc-ares-dev \\\n          libcjson-dev \\\n          libcjson1 \\\n          libcunit1-dev \\\n          libedit-dev \\\n          libgmock-dev \\\n          libmicrohttpd-dev \\\n          libssl-dev \\\n          libwrap0-dev \\\n          microsocks \\\n          python3-all \\\n          python3-paho-mqtt \\\n          python3-psutil \\\n          uthash-dev \\\n          xsltproc\n    -\n      uses: actions/checkout@v6\n      with:\n        submodules: 'true'\n    -\n      name: make\n      run: make\n    -\n      name: make test\n      run: |\n        make ptest\n"
  },
  {
    "path": ".github/workflows/windows-x86.yml",
    "content": "name: Windows x86 build\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n      - fixes\n      - develop\n      - release/*\n    tags:\n      - 'v[0-9]+.*'\n  pull_request:\n    branches:\n      - master\n      - fixes\n      - develop\n      - release/*\n\nenv:\n  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)\n  BUILD_TYPE: Release\n\njobs:\n  mosquitto:\n    runs-on: windows-2022\n\n    steps:\n      - uses: actions/checkout@v6\n\n\n      - name: vcpkg build\n        uses: johnwason/vcpkg-action@v7\n        id: vcpkg\n        with:\n          manifest-dir: ${{ github.workspace }}\n          triplet: x86-windows\n          token: ${{ github.token }}\n\n      - name: Configure CMake\n        run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DWITH_WEBSOCKETS=ON -DWITH_TESTS=OFF -DCMAKE_GENERATOR_PLATFORM=WIN32 -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x86-windows -DVCPKG_MANIFEST_MODE=ON -DWITH_HTTP_API=ON -DHTTP_API_DIR=\"C:\\\\Program Files (x86)\\\\Mosquitto\\\\dashboard\"\n      - name: Build\n        run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n\n      - uses: suisei-cn/actions-download-file@v1.6.1\n        id: vcredist\n        name: Download VC redistributable\n        with:\n          url: https://aka.ms/vs/17/release/vc_redist.x86.exe\n          target: ${{github.workspace}}/installer/\n\n      - name: Installer\n        uses: joncloud/makensis-action@v5.0\n        with:\n          script-file: ${{github.workspace}}/installer/mosquitto.nsi\n\n      - name: Upload installer to artifacts\n        uses: actions/upload-artifact@v6\n        with:\n          name: installer\n          path: ${{ github.workspace }}/installer/mosquitto*.exe\n"
  },
  {
    "path": ".github/workflows/windows.yml",
    "content": "name: Windows build\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n      - fixes\n      - develop\n      - release/*\n    tags:\n      - 'v[0-9]+.*'\n  pull_request:\n    branches:\n      - master\n      - fixes\n      - develop\n      - release/*\n\nenv:\n  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)\n  BUILD_TYPE: Release\n\njobs:\n  mosquitto:\n    runs-on: windows-2022\n\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: pin cmake to 3.x series\n        uses: jwlawson/actions-setup-cmake@09fd9b0fb3b239b4b68d9256cd65adf8d6b91da0\n        with:\n          cmake-version: '3.31.6'\n\n      - name: vcpkg build\n        uses: johnwason/vcpkg-action@v7\n        id: vcpkg\n        with:\n          manifest-dir: ${{ github.workspace }}\n          triplet: x64-windows-release\n          token: ${{ github.token }}\n\n      - name: Configure CMake\n        run: cmake -B ${{github.workspace}}/build64 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DWITH_WEBSOCKETS=ON -DWITH_TESTS=OFF -DCMAKE_GENERATOR_PLATFORM=x64 -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-release -DVCPKG_MANIFEST_MODE=ON -DWITH_HTTP_API=ON -DHTTP_API_DIR=\"C:\\\\Program Files\\\\Mosquitto\\\\dashboard\"\n      - name: Build\n        run: cmake --build ${{github.workspace}}/build64 --config ${{env.BUILD_TYPE}}\n\n      - uses: suisei-cn/actions-download-file@v1.6.1\n        id: vcredist\n        name: Download VC redistributable\n        with:\n          url: https://aka.ms/vs/17/release/vc_redist.x64.exe\n          target: ${{github.workspace}}/installer/\n\n      - name: Installer\n        uses: joncloud/makensis-action@v5.0\n        with:\n          script-file: ${{github.workspace}}/installer/mosquitto64.nsi\n\n      - name: Upload installer to artifacts\n        uses: actions/upload-artifact@v6\n        with:\n          name: installer\n          path: ${{ github.workspace }}/installer/mosquitto*.exe\n"
  },
  {
    "path": ".gitignore",
    "content": "# .gitignore\n*.a\n*.db\n*.gcda\n*.gcno\n*.exe\n*.o\n*.old\n*.pyc\n*.so\n*.vglog\ncallgrind.out.*\ncoverage.info\ndhat.out.*\nmassif.out.*\nvglog*\n\nc/*.test\ncpp/*.test\n\napps/db_dump/mosquitto_db_dump\napps/db_dump/mosquitto_db_dump.a\napps/mosquitto_ctrl/mosquitto_ctrl\napps/mosquitto_passwd/mosquitto_passwd\napps/mosquitto_passwd/mosquitto_passwd.a\napps/mosquitto_signal/mosquitto_signal\n\nbuild/\nbuild64/\n\nclient/mosquitto_pub\nclient/mosquitto_pub.a\nclient/mosquitto_rr\nclient/mosquitto_rr.a\nclient/mosquitto_sub\nclient/mosquitto_sub.a\nclient/testing\nclient/testing.c\n\ncov-int/\n\ndist/\n\ndocker/local/mosq.tar.gz\n\nexamples/mysql_log/mosquitto_mysql_log\nexamples/temperature_conversion/mqtt_temperature_conversion\nexamples/publish/basic-1\nexamples/publish/basic-websockets-1\n\nfuzzing/apps/db_dump/db_dump_fuzz_load\nfuzzing/apps/db_dump/db_dump_fuzz_load_client_stats\nfuzzing/apps/db_dump/db_dump_fuzz_load_stats\nfuzzing/apps/mosquitto_passwd/mosquitto_passwd_fuzz_load\nfuzzing/broker/broker_fuzz_acl_file\nfuzzing/broker/broker_fuzz_handle_auth\nfuzzing/broker/broker_fuzz_handle_connect\nfuzzing/broker/broker_fuzz_handle_publish\nfuzzing/broker/broker_fuzz_handle_subscribe\nfuzzing/broker/broker_fuzz_handle_unsubscribe\nfuzzing/broker/broker_fuzz_initial_packet\nfuzzing/broker/broker_fuzz_initial_packet_with_init\nfuzzing/broker/broker_fuzz_password_file\nfuzzing/broker/broker_fuzz_proxy_v1\nfuzzing/broker/broker_fuzz_proxy_v2\nfuzzing/broker/broker_fuzz_psk_file\nfuzzing/broker/broker_fuzz_queue_msg\nfuzzing/broker/broker_fuzz_read_handle\nfuzzing/broker/broker_fuzz_second_packet\nfuzzing/broker/broker_fuzz_second_packet_with_init\nfuzzing/broker/broker_fuzz_test_config\nfuzzing/corpora/broker/*\nfuzzing/corpora/broker_packet_seed_corpus.zip\nfuzzing/corpora/client/*\nfuzzing/corpora/client_packet_seed_corpus.zip\nfuzzing/corpora/db_dump_seed_corpus.zip\nfuzzing/lib/lib_fuzz_pub_topic_check2\nfuzzing/lib/lib_fuzz_sub_topic_check2\nfuzzing/lib/lib_fuzz_utf8\nfuzzing/libcommon/libcommon_fuzz_property\nfuzzing/libcommon/libcommon_fuzz_property.pb.cc\nfuzzing/libcommon/libcommon_fuzz_property.pb.h\nfuzzing/libcommon/libcommon_fuzz_pub_topic_check2\nfuzzing/libcommon/libcommon_fuzz_sub_topic_check2\nfuzzing/libcommon/libcommon_fuzz_topic_matching\nfuzzing/libcommon/libcommon_fuzz_topic_matching.pb.cc\nfuzzing/libcommon/libcommon_fuzz_topic_matching.pb.h\nfuzzing/libcommon/libcommon_fuzz_topic_tokenise\nfuzzing/libcommon/libcommon_fuzz_utf8\nfuzzing/plugins/dynamic-security/dynsec_fuzz_load\n\nlib/cpp/libmosquittopp.so*\nlib/cpp/libmosquittopp.a\nlib/libmosquitto.so*\nlib/libmosquitto.a\n\nman/libmosquitto.3\nman/mosquitto-tls.7\nman/mosquitto.7\nman/mosquitto.8\nman/mosquitto.conf.5\nman/mosquitto_ctrl.1\nman/mosquitto_ctrl_dynsec.1\nman/mosquitto_ctrl_shell.1\nman/mosquitto_passwd.1\nman/mosquitto_pub.1\nman/mosquitto_rr.1\nman/mosquitto_signal.1\nman/mosquitto_sub.1\nman/mqtt.7\n\nout/\n\nsrc/mosquitto\nsrc/mosquitto_broker.a\n\ntest/apps/ctrl/ctrl_shell_broker_test\ntest/apps/ctrl/ctrl_shell_completion_test\ntest/apps/ctrl/ctrl_shell_dynsec_test\ntest/apps/ctrl/ctrl_shell_help_test\ntest/apps/ctrl/ctrl_shell_options_test\ntest/apps/ctrl/ctrl_shell_pre_connect_test\ntest/apps/ctrl/ctrl_shell_test\ntest/broker/broker.pid\ntest/test_client\ntest/fake_user\ntest/msgsps_pub\ntest/msgsps_sub\ntest/msgsps_pub.dat\ntest/msgsps_sub.dat\ntest/broker/c/auth_plugin.so\ntest/broker/c/*.test\n\ntest/ssl/*.csr\ntest/ssl/rootCA/\ntest/ssl/signingCA/\n\ntest/lib/c/*.test\ntest/lib/cpp/*.test\n\ntest/unit/broker/bridge_topic_test\ntest/unit/broker/keepalive_test\ntest/unit/broker/persist_read_test\ntest/unit/broker/persist_write_test\ntest/unit/broker/subs_test\ntest/unit/coverage.info\ntest/unit/lib/lib_test\ntest/unit/libcommon/libcommon_test\ntest/unit/tls_test\n\nwww/cache/\n__pycache__\n*.sync-conflict-*\n# Debian generated files\ndebian/.debhelper/\ndebian/debhelper-build-stamp\ndebian/files\ndebian/*.log\ndebian/*.substvars\ndebian/*mosquitto*/\ndebian/*.debhelper\ndebian/tmp/\nobj-*/\n\n# Emacs generated files\n*~\n\n# clangd\n.cache/\n\n# VSCode generated files\n.vscode/\n\n# Others\ntmp/\nfailed-tests.json\n"
  },
  {
    "path": ".uncrustify.cfg",
    "content": "# Uncrustify_d-0.80.1-109-229eb1c05\n\n#\n# General options\n#\n\nset FOR HASH_ITER\nset FOR DL_FOREACH\nset FOR DL_FOREACH_SAFE\nset FOR DL_FOREACH_SAFE2\nset FOR LL_FOREACH\nset FOR LL_FOREACH_SAFE\nset FOR cJSON_ArrayForEach\n\n# The type of line endings.\n#\n# Default: auto\nnewlines                        = auto     # lf/crlf/cr/auto\n\n# The original size of tabs in the input.\n#\n# Default: 8\ninput_tab_size                  = 4        # unsigned number\n\n# The size of tabs in the output (only used if align_with_tabs=true).\n#\n# Default: 8\noutput_tab_size                 = 4        # unsigned number\n\n# The ASCII value of the string escape char, usually 92 (\\) or (Pawn) 94 (^).\n#\n# Default: 92\nstring_escape_char              = 92       # unsigned number\n\n# Alternate string escape char (usually only used for Pawn).\n# Only works right before the quote char.\nstring_escape_char2             = 0        # unsigned number\n\n# Replace tab characters found in string literals with the escape sequence \\t\n# instead.\nstring_replace_tab_chars        = false    # true/false\n\n# Allow interpreting '>=' and '>>=' as part of a template in code like\n# 'void f(list<list<B>>=val);'. If true, 'assert(x<0 && y>=3)' will be broken.\n# Improvements to template detection may make this option obsolete.\ntok_split_gte                   = false    # true/false\n\n# Disable formatting of NL_CONT ('\\\\n') ended lines (e.g. multi-line macros).\ndisable_processing_nl_cont      = false    # true/false\n\n# Specify the marker used in comments to disable processing of part of the\n# file.\n#\n# Default:  *INDENT-OFF*\ndisable_processing_cmt          = \" *INDENT-OFF*\"      # string\n\n# Specify the marker used in comments to (re)enable processing in a file.\n#\n# Default:  *INDENT-ON*\nenable_processing_cmt           = \" *INDENT-ON*\"     # string\n\n# Enable parsing of digraphs.\nenable_digraphs                 = false    # true/false\n\n# Option to allow both disable_processing_cmt and enable_processing_cmt\n# strings, if specified, to be interpreted as ECMAScript regular expressions.\n# If true, a regex search will be performed within comments according to the\n# specified patterns in order to disable/enable processing.\nprocessing_cmt_as_regex         = false    # true/false\n\n# Add or remove the UTF-8 BOM (recommend 'remove').\nutf8_bom                        = remove   # ignore/add/remove/force\n\n# If the file contains bytes with values between 128 and 255, but is not\n# UTF-8, then output as UTF-8.\nutf8_byte                       = false    # true/false\n\n# Force the output encoding to UTF-8.\nutf8_force                      = true    # true/false\n\n#\n# Spacing options\n#\n\n# Add or remove space around non-assignment symbolic operators ('+', '/', '%',\n# '<<', and so forth).\nsp_arith                        = ignore   # ignore/add/remove/force\n\n# Add or remove space around arithmetic operators '+' and '-'.\n#\n# Overrides sp_arith.\nsp_arith_additive               = ignore   # ignore/add/remove/force\n\n# Add or remove space around assignment operator '=', '+=', etc.\nsp_assign                       = ignore   # ignore/add/remove/force\n\n# Add or remove space around '=' in C++11 lambda capture specifications.\n#\n# Overrides sp_assign.\nsp_cpp_lambda_assign            = ignore   # ignore/add/remove/force\n\n# Add or remove space after the capture specification of a C++11 lambda when\n# an argument list is present, as in '[] <here> (int x){ ... }'.\nsp_cpp_lambda_square_paren      = ignore   # ignore/add/remove/force\n\n# Add or remove space after the capture specification of a C++11 lambda with\n# no argument list is present, as in '[] <here> { ... }'.\nsp_cpp_lambda_square_brace      = ignore   # ignore/add/remove/force\n\n# Add or remove space after the opening parenthesis and before the closing\n# parenthesis of a argument list of a C++11 lambda, as in\n# '[]( <here> ){ ... }'\n# with an empty list.\nsp_cpp_lambda_argument_list_empty = ignore   # ignore/add/remove/force\n\n# Add or remove space after the opening parenthesis and before the closing\n# parenthesis of a argument list of a C++11 lambda, as in\n# '[]( <here> int x <here> ){ ... }'.\nsp_cpp_lambda_argument_list     = ignore   # ignore/add/remove/force\n\n# Add or remove space after the argument list of a C++11 lambda, as in\n# '[](int x) <here> { ... }'.\nsp_cpp_lambda_paren_brace       = ignore   # ignore/add/remove/force\n\n# Add or remove space between a lambda body and its call operator of an\n# immediately invoked lambda, as in '[]( ... ){ ... } <here> ( ... )'.\nsp_cpp_lambda_fparen            = ignore   # ignore/add/remove/force\n\n# Add or remove space around assignment operator '=' in a prototype.\n#\n# If set to ignore, use sp_assign.\nsp_assign_default               = ignore   # ignore/add/remove/force\n\n# Add or remove space before assignment operator '=', '+=', etc.\n#\n# Overrides sp_assign.\nsp_before_assign                = ignore   # ignore/add/remove/force\n\n# Add or remove space after assignment operator '=', '+=', etc.\n#\n# Overrides sp_assign.\nsp_after_assign                 = ignore   # ignore/add/remove/force\n\n# Add or remove space in 'enum {'.\n#\n# Default: add\nsp_enum_brace                   = remove      # ignore/add/remove/force\n\n# Add or remove space in 'NS_ENUM ('.\nsp_enum_paren                   = ignore   # ignore/add/remove/force\n\n# Add or remove space around assignment '=' in enum.\nsp_enum_assign                  = ignore   # ignore/add/remove/force\n\n# Add or remove space before assignment '=' in enum.\n#\n# Overrides sp_enum_assign.\nsp_enum_before_assign           = ignore   # ignore/add/remove/force\n\n# Add or remove space after assignment '=' in enum.\n#\n# Overrides sp_enum_assign.\nsp_enum_after_assign            = ignore   # ignore/add/remove/force\n\n# Add or remove space around assignment ':' in enum.\nsp_enum_colon                   = ignore   # ignore/add/remove/force\n\n# Add or remove space around preprocessor '##' concatenation operator.\n#\n# Default: add\nsp_pp_concat                    = add      # ignore/add/remove/force\n\n# Add or remove space after preprocessor '#' stringify operator.\n# Also affects the '#@' charizing operator.\nsp_pp_stringify                 = ignore   # ignore/add/remove/force\n\n# Add or remove space before preprocessor '#' stringify operator\n# as in '#define x(y) L#y'.\nsp_before_pp_stringify          = ignore   # ignore/add/remove/force\n\n# Add or remove space around boolean operators '&&' and '||'.\nsp_bool                         = ignore   # ignore/add/remove/force\n\n# Add or remove space around compare operator '<', '>', '==', etc.\nsp_compare                      = ignore   # ignore/add/remove/force\n\n# Add or remove space inside '(' and ')'.\nsp_inside_paren                 = remove   # ignore/add/remove/force\n\n# Add or remove space between nested parentheses, i.e. '((' vs. ') )'.\nsp_paren_paren                  = remove   # ignore/add/remove/force\n\n# Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('.\nsp_cparen_oparen                = ignore   # ignore/add/remove/force\n\n# Add or remove space between ')' and '{'.\nsp_paren_brace                  = remove   # ignore/add/remove/force\n\n# Add or remove space between nested braces, i.e. '{{' vs. '{ {'.\nsp_brace_brace                  = remove   # ignore/add/remove/force\n\n# Add or remove space before pointer star '*'.\nsp_before_ptr_star              = add   # ignore/add/remove/force\n\n# Add or remove space before pointer star '*' that isn't followed by a\n# variable name. If set to ignore, sp_before_ptr_star is used instead.\nsp_before_unnamed_ptr_star      = add   # ignore/add/remove/force\n\n# Add or remove space before pointer star '*' that is followed by a qualifier.\n# If set to ignore, sp_before_unnamed_ptr_star is used instead.\nsp_before_qualifier_ptr_star    = add   # ignore/add/remove/force\n\n# Add or remove space before pointer star '*' that is followed by 'operator' keyword.\n# If set to ignore, sp_before_unnamed_ptr_star is used instead.\nsp_before_operator_ptr_star     = ignore   # ignore/add/remove/force\n\n# Add or remove space before pointer star '*' that is followed by\n# a class scope (as in 'int *MyClass::method()') or namespace scope\n# (as in 'int *my_ns::func()').\n# If set to ignore, sp_before_unnamed_ptr_star is used instead.\nsp_before_scope_ptr_star        = ignore   # ignore/add/remove/force\n\n# Add or remove space before pointer star '*' that is followed by '::',\n# as in 'int *::func()'.\n# If set to ignore, sp_before_unnamed_ptr_star is used instead.\nsp_before_global_scope_ptr_star = ignore   # ignore/add/remove/force\n\n# Add or remove space between a qualifier and a pointer star '*' that isn't\n# followed by a variable name, as in '(char const *)'. If set to ignore,\n# sp_before_ptr_star is used instead.\nsp_qualifier_unnamed_ptr_star   = ignore   # ignore/add/remove/force\n\n# Add or remove space between pointer stars '*', as in 'int ***a;'.\nsp_between_ptr_star             = remove   # ignore/add/remove/force\n\n# Add or remove space between pointer star '*' and reference '&', as in 'int *& a;'.\nsp_between_ptr_ref              = ignore   # ignore/add/remove/force\n\n# Add or remove space after pointer star '*', if followed by a word.\n#\n# Overrides sp_type_func.\nsp_after_ptr_star               = remove   # ignore/add/remove/force\n\n# Add or remove space after pointer caret '^', if followed by a word.\nsp_after_ptr_block_caret        = ignore   # ignore/add/remove/force\n\n# Add or remove space after pointer star '*', if followed by a qualifier.\nsp_after_ptr_star_qualifier     = ignore   # ignore/add/remove/force\n\n# Add or remove space after a pointer star '*', if followed by a function\n# prototype or function definition.\n#\n# Overrides sp_after_ptr_star and sp_type_func.\nsp_after_ptr_star_func          = remove   # ignore/add/remove/force\n\n# Add or remove space after a pointer star '*' in the trailing return of a\n# function prototype or function definition.\nsp_after_ptr_star_trailing      = ignore   # ignore/add/remove/force\n\n# Add or remove space between the pointer star '*' and the name of the variable\n# in a function pointer definition.\nsp_ptr_star_func_var            = remove   # ignore/add/remove/force\n\n# Add or remove space between the pointer star '*' and the name of the type\n# in a function pointer type definition.\nsp_ptr_star_func_type           = ignore   # ignore/add/remove/force\n\n# Add or remove space after a pointer star '*', if followed by an open\n# parenthesis, as in 'void* (*)()'.\nsp_ptr_star_paren               = ignore   # ignore/add/remove/force\n\n# Add or remove space before a pointer star '*', if followed by a function\n# prototype or function definition. If set to ignore, sp_before_ptr_star is\n# used instead.\nsp_before_ptr_star_func         = ignore   # ignore/add/remove/force\n\n# Add or remove space between a qualifier and a pointer star '*' followed by\n# the name of the function in a function prototype or definition, as in\n# 'char const *foo()`. If set to ignore, sp_before_ptr_star is used instead.\nsp_qualifier_ptr_star_func      = ignore   # ignore/add/remove/force\n\n# Add or remove space before a pointer star '*' in the trailing return of a\n# function prototype or function definition.\nsp_before_ptr_star_trailing     = ignore   # ignore/add/remove/force\n\n# Add or remove space between a qualifier and a pointer star '*' in the\n# trailing return of a function prototype or function definition, as in\n# 'auto foo() -> char const *'.\nsp_qualifier_ptr_star_trailing  = ignore   # ignore/add/remove/force\n\n# Add or remove space before a reference sign '&'.\nsp_before_byref                 = ignore   # ignore/add/remove/force\n\n# Add or remove space before a reference sign '&' that isn't followed by a\n# variable name. If set to ignore, sp_before_byref is used instead.\nsp_before_unnamed_byref         = ignore   # ignore/add/remove/force\n\n# Add or remove space after reference sign '&', if followed by a word.\n#\n# Overrides sp_type_func.\nsp_after_byref                  = ignore   # ignore/add/remove/force\n\n# Add or remove space after a reference sign '&', if followed by a function\n# prototype or function definition.\n#\n# Overrides sp_after_byref and sp_type_func.\nsp_after_byref_func             = ignore   # ignore/add/remove/force\n\n# Add or remove space before a reference sign '&', if followed by a function\n# prototype or function definition.\nsp_before_byref_func            = ignore   # ignore/add/remove/force\n\n# Add or remove space after a reference sign '&', if followed by an open\n# parenthesis, as in 'char& (*)()'.\nsp_byref_paren                  = ignore   # ignore/add/remove/force\n\n# Add or remove space between type and word. In cases where total removal of\n# whitespace would be a syntax error, a value of 'remove' is treated the same\n# as 'force'.\n#\n# This also affects some other instances of space following a type that are\n# not covered by other options; for example, between the return type and\n# parenthesis of a function type template argument, between the type and\n# parenthesis of an array parameter, or between 'decltype(...)' and the\n# following word.\n#\n# Default: force\nsp_after_type                   = force    # ignore/add/remove/force\n\n# Add or remove space between 'decltype(...)' and word,\n# brace or function call.\nsp_after_decltype               = ignore   # ignore/add/remove/force\n\n# (D) Add or remove space before the parenthesis in the D constructs\n# 'template Foo(' and 'class Foo('.\nsp_before_template_paren        = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'template' and '<'.\n# If set to ignore, sp_before_angle is used.\nsp_template_angle               = ignore   # ignore/add/remove/force\n\n# Add or remove space before '<'.\nsp_before_angle                 = ignore   # ignore/add/remove/force\n\n# Add or remove space inside '<' and '>'.\nsp_inside_angle                 = ignore   # ignore/add/remove/force\n\n# Add or remove space inside '<>'.\n# if empty.\nsp_inside_angle_empty           = ignore   # ignore/add/remove/force\n\n# Add or remove space between '>' and ':'.\nsp_angle_colon                  = ignore   # ignore/add/remove/force\n\n# Add or remove space after '>'.\nsp_after_angle                  = ignore   # ignore/add/remove/force\n\n# Add or remove space between '>' and '(' as found in 'new List<byte>(foo);'.\nsp_angle_paren                  = ignore   # ignore/add/remove/force\n\n# Add or remove space between '>' and '()' as found in 'new List<byte>();'.\nsp_angle_paren_empty            = ignore   # ignore/add/remove/force\n\n# Add or remove space between '>' and a word as in 'List<byte> m;' or\n# 'template <typename T> static ...'.\nsp_angle_word                   = ignore   # ignore/add/remove/force\n\n# Add or remove space between '>' and '>' in '>>' (template stuff).\n#\n# Default: add\nsp_angle_shift                  = add      # ignore/add/remove/force\n\n# (C++11) Permit removal of the space between '>>' in 'foo<bar<int> >'. Note\n# that sp_angle_shift cannot remove the space without this option.\nsp_permit_cpp11_shift           = false    # true/false\n\n# Add or remove space before '(' of control statements ('if', 'for', 'switch',\n# 'while', etc.).\nsp_before_sparen                = remove   # ignore/add/remove/force\n\n# Add or remove space inside '(' and ')' of control statements other than\n# 'for'.\nsp_inside_sparen                = remove   # ignore/add/remove/force\n\n# Add or remove space after '(' of control statements other than 'for'.\n#\n# Overrides sp_inside_sparen.\nsp_inside_sparen_open           = ignore   # ignore/add/remove/force\n\n# Add or remove space before ')' of control statements other than 'for'.\n#\n# Overrides sp_inside_sparen.\nsp_inside_sparen_close          = ignore   # ignore/add/remove/force\n\n# Add or remove space inside '(' and ')' of 'for' statements.\nsp_inside_for                   = ignore   # ignore/add/remove/force\n\n# Add or remove space after '(' of 'for' statements.\n#\n# Overrides sp_inside_for.\nsp_inside_for_open              = ignore   # ignore/add/remove/force\n\n# Add or remove space before ')' of 'for' statements.\n#\n# Overrides sp_inside_for.\nsp_inside_for_close             = ignore   # ignore/add/remove/force\n\n# Add or remove space between '((' or '))' of control statements.\nsp_sparen_paren                 = remove   # ignore/add/remove/force\n\n# Add or remove space after ')' of control statements.\nsp_after_sparen                 = ignore   # ignore/add/remove/force\n\n# Add or remove space between ')' and '{' of control statements.\nsp_sparen_brace                 = remove   # ignore/add/remove/force\n\n# Add or remove space between 'do' and '{'.\nsp_do_brace_open                = remove   # ignore/add/remove/force\n\n# Add or remove space between '}' and 'while'.\nsp_brace_close_while            = remove   # ignore/add/remove/force\n\n# Add or remove space between 'while' and '('. Overrides sp_before_sparen.\nsp_while_paren_open             = remove   # ignore/add/remove/force\n\n# (D) Add or remove space between 'invariant' and '('.\nsp_invariant_paren              = ignore   # ignore/add/remove/force\n\n# (D) Add or remove space after the ')' in 'invariant (C) c'.\nsp_after_invariant_paren        = ignore   # ignore/add/remove/force\n\n# Add or remove space before empty statement ';' on 'if', 'for' and 'while'.\n# examples:\n#   if (b) <here> ;\n#   for (a=1; a<10; a++) <here> ;\n#   while (*p++ = ' ') <here> ;\nsp_special_semi                 = ignore   # ignore/add/remove/force\n\n# Add or remove space before ';'.\n#\n# Default: remove\nsp_before_semi                  = remove   # ignore/add/remove/force\n\n# Add or remove space before ';' in non-empty 'for' statements.\nsp_before_semi_for              = ignore   # ignore/add/remove/force\n\n# Add or remove space before a semicolon of an empty left part of a for\n# statement, as in 'for ( <here> ; ; )'.\nsp_before_semi_for_empty        = ignore   # ignore/add/remove/force\n\n# Add or remove space between the semicolons of an empty middle part of a for\n# statement, as in 'for ( ; <here> ; )'.\nsp_between_semi_for_empty       = ignore   # ignore/add/remove/force\n\n# Add or remove space after ';', except when followed by a comment.\n#\n# Default: add\nsp_after_semi                   = add      # ignore/add/remove/force\n\n# Add or remove space after ';' in non-empty 'for' statements.\n#\n# Default: force\nsp_after_semi_for               = force    # ignore/add/remove/force\n\n# Add or remove space after the final semicolon of an empty part of a for\n# statement, as in 'for ( ; ; <here> )'.\nsp_after_semi_for_empty         = ignore   # ignore/add/remove/force\n\n# Add or remove space before '[' (except '[]').\nsp_before_square                = ignore   # ignore/add/remove/force\n\n# Add or remove space before '[' for a variable definition.\n#\n# Default: remove\nsp_before_vardef_square         = remove   # ignore/add/remove/force\n\n# Add or remove space before '[' for asm block.\nsp_before_square_asm_block      = ignore   # ignore/add/remove/force\n\n# Add or remove space before '[]'.\nsp_before_squares               = ignore   # ignore/add/remove/force\n\n# Add or remove space before C++17 structured bindings\n# after byref.\nsp_cpp_before_struct_binding_after_byref = ignore   # ignore/add/remove/force\n\n# Add or remove space before C++17 structured bindings.\nsp_cpp_before_struct_binding    = ignore   # ignore/add/remove/force\n\n# Add or remove space inside a non-empty '[' and ']'.\nsp_inside_square                = ignore   # ignore/add/remove/force\n\n# Add or remove space inside '[]'.\n# if empty.\nsp_inside_square_empty          = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space inside a non-empty Objective-C boxed array '@[' and\n# ']'. If set to ignore, sp_inside_square is used.\nsp_inside_square_oc_array       = ignore   # ignore/add/remove/force\n\n# Add or remove space after ',', i.e. 'a,b' vs. 'a, b'.\nsp_after_comma                  = add   # ignore/add/remove/force\n\n# Add or remove space before ',', i.e. 'a,b' vs. 'a ,b'.\n#\n# Default: remove\nsp_before_comma                 = remove   # ignore/add/remove/force\n\n# (C#, Vala) Add or remove space between ',' and ']' in multidimensional array type\n# like 'int[,,]'.\nsp_after_mdatype_commas         = ignore   # ignore/add/remove/force\n\n# (C#, Vala) Add or remove space between '[' and ',' in multidimensional array type\n# like 'int[,,]'.\nsp_before_mdatype_commas        = ignore   # ignore/add/remove/force\n\n# (C#, Vala) Add or remove space between ',' in multidimensional array type\n# like 'int[,,]'.\nsp_between_mdatype_commas       = ignore   # ignore/add/remove/force\n\n# Add or remove space between an open parenthesis and comma,\n# i.e. '(,' vs. '( ,'.\n#\n# Default: force\nsp_paren_comma                  = force    # ignore/add/remove/force\n\n# Add or remove space between a type and ':'.\nsp_type_colon                   = ignore   # ignore/add/remove/force\n\n# Add or remove space after the variadic '...' when preceded by a\n# non-punctuator.\n# The value REMOVE will be overridden with FORCE\nsp_after_ellipsis               = ignore   # ignore/add/remove/force\n\n# Add or remove space before the variadic '...' when preceded by a\n# non-punctuator.\n# The value REMOVE will be overridden with FORCE\nsp_before_ellipsis              = ignore   # ignore/add/remove/force\n\n# Add or remove space between a type and '...'.\nsp_type_ellipsis                = ignore   # ignore/add/remove/force\n\n# Add or remove space between a '*' and '...'.\nsp_ptr_type_ellipsis            = ignore   # ignore/add/remove/force\n\n# Add or remove space between ')' and '...'.\nsp_paren_ellipsis               = ignore   # ignore/add/remove/force\n\n# Add or remove space between '&&' and '...'.\nsp_byref_ellipsis               = ignore   # ignore/add/remove/force\n\n# Add or remove space between ')' and a qualifier such as 'const'.\nsp_paren_qualifier              = ignore   # ignore/add/remove/force\n\n# Add or remove space between ')' and 'noexcept'.\nsp_paren_noexcept               = ignore   # ignore/add/remove/force\n\n# Add or remove space after class ':'.\nsp_after_class_colon            = ignore   # ignore/add/remove/force\n\n# Add or remove space before class ':'.\nsp_before_class_colon           = ignore   # ignore/add/remove/force\n\n# Add or remove space after class constructor ':'.\n#\n# Default: add\nsp_after_constr_colon           = add      # ignore/add/remove/force\n\n# Add or remove space before class constructor ':'.\n#\n# Default: add\nsp_before_constr_colon          = add      # ignore/add/remove/force\n\n# Add or remove space before case ':'.\n#\n# Default: remove\nsp_before_case_colon            = remove   # ignore/add/remove/force\n\n# Add or remove space between 'operator' and operator sign.\nsp_after_operator               = ignore   # ignore/add/remove/force\n\n# Add or remove space between the operator symbol and the open parenthesis, as\n# in 'operator ++('.\nsp_after_operator_sym           = ignore   # ignore/add/remove/force\n\n# Overrides sp_after_operator_sym when the operator has no arguments, as in\n# 'operator *()'.\nsp_after_operator_sym_empty     = ignore   # ignore/add/remove/force\n\n# Add or remove space after C/D cast, i.e. 'cast(int)a' vs. 'cast(int) a' or\n# '(int)a' vs. '(int) a'.\nsp_after_cast                   = remove   # ignore/add/remove/force\n\n# Add or remove spaces inside cast parentheses.\nsp_inside_paren_cast            = ignore   # ignore/add/remove/force\n\n# Add or remove space between the type and open parenthesis in a C++ cast,\n# i.e. 'int(exp)' vs. 'int (exp)'.\nsp_cpp_cast_paren               = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'sizeof' and '('.\nsp_sizeof_paren                 = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'sizeof' and '...'.\nsp_sizeof_ellipsis              = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'sizeof...' and '('.\nsp_sizeof_ellipsis_paren        = ignore   # ignore/add/remove/force\n\n# Add or remove space between '...' and a parameter pack.\nsp_ellipsis_parameter_pack      = ignore   # ignore/add/remove/force\n\n# Add or remove space between a parameter pack and '...'.\nsp_parameter_pack_ellipsis      = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'decltype' and '('.\nsp_decltype_paren               = ignore   # ignore/add/remove/force\n\n# (Pawn) Add or remove space after the tag keyword.\nsp_after_tag                    = ignore   # ignore/add/remove/force\n\n# Add or remove space inside enum '{' and '}'.\nsp_inside_braces_enum           = ignore   # ignore/add/remove/force\n\n# Add or remove space inside struct/union '{' and '}'.\nsp_inside_braces_struct         = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space inside Objective-C boxed dictionary '{' and '}'\nsp_inside_braces_oc_dict        = ignore   # ignore/add/remove/force\n\n# Add or remove space after open brace in an unnamed temporary\n# direct-list-initialization\n# if statement is a brace_init_lst\n# works only if sp_brace_brace is set to ignore.\nsp_after_type_brace_init_lst_open = ignore   # ignore/add/remove/force\n\n# Add or remove space before close brace in an unnamed temporary\n# direct-list-initialization\n# if statement is a brace_init_lst\n# works only if sp_brace_brace is set to ignore.\nsp_before_type_brace_init_lst_close = ignore   # ignore/add/remove/force\n\n# Add or remove space inside an unnamed temporary direct-list-initialization\n# if statement is a brace_init_lst\n# works only if sp_brace_brace is set to ignore\n# works only if sp_before_type_brace_init_lst_close is set to ignore.\nsp_inside_type_brace_init_lst   = ignore   # ignore/add/remove/force\n\n# Add or remove space inside '{' and '}'.\nsp_inside_braces                = ignore   # ignore/add/remove/force\n\n# Add or remove space inside '{}'.\n# if empty.\nsp_inside_braces_empty          = remove   # ignore/add/remove/force\n\n# Add or remove space around trailing return operator '->'.\nsp_trailing_return              = ignore   # ignore/add/remove/force\n\n# Add or remove space between return type and function name. A minimum of 1\n# is forced except for pointer return types.\nsp_type_func                    = ignore   # ignore/add/remove/force\n\n# Add or remove space between type and open brace of an unnamed temporary\n# direct-list-initialization.\nsp_type_brace_init_lst          = ignore   # ignore/add/remove/force\n\n# Add or remove space between function name and '(' on function declaration.\nsp_func_proto_paren             = remove   # ignore/add/remove/force\n\n# Add or remove space between function name and '()' on function declaration\n# if empty.\nsp_func_proto_paren_empty       = remove   # ignore/add/remove/force\n\n# Add or remove space between function name and '(' with a typedef specifier.\nsp_func_type_paren              = ignore   # ignore/add/remove/force\n\n# Add or remove space between alias name and '(' of a non-pointer function type typedef.\nsp_func_def_paren               = ignore   # ignore/add/remove/force\n\n# Add or remove space between function name and '()' on function definition\n# if empty.\nsp_func_def_paren_empty         = ignore   # ignore/add/remove/force\n\n# Add or remove space inside empty function '()'.\n# Overrides sp_after_angle unless use_sp_after_angle_always is set to true.\nsp_inside_fparens               = remove   # ignore/add/remove/force\n\n# Add or remove space inside function '(' and ')'.\nsp_inside_fparen                = remove   # ignore/add/remove/force\n\n# Add or remove space inside user functor '(' and ')'.\nsp_func_call_user_inside_rparen = ignore   # ignore/add/remove/force\n\n# Add or remove space inside empty functor '()'.\n# Overrides sp_after_angle unless use_sp_after_angle_always is set to true.\nsp_inside_rparens               = ignore   # ignore/add/remove/force\n\n# Add or remove space inside functor '(' and ')'.\nsp_inside_rparen                = ignore   # ignore/add/remove/force\n\n# Add or remove space inside the first parentheses in a function type, as in\n# 'void (*x)(...)'.\nsp_inside_tparen                = ignore   # ignore/add/remove/force\n\n# Add or remove space between the ')' and '(' in a function type, as in\n# 'void (*x)(...)'.\nsp_after_tparen_close           = ignore   # ignore/add/remove/force\n\n# Add or remove space between ']' and '(' when part of a function call.\nsp_square_fparen                = ignore   # ignore/add/remove/force\n\n# Add or remove space between ')' and '{' of function.\nsp_fparen_brace                 = remove   # ignore/add/remove/force\n\n# Add or remove space between ')' and '{' of a function call in object\n# initialization.\n#\n# Overrides sp_fparen_brace.\nsp_fparen_brace_initializer     = remove   # ignore/add/remove/force\n\n# (Java) Add or remove space between ')' and '{{' of double brace initializer.\nsp_fparen_dbrace                = ignore   # ignore/add/remove/force\n\n# Add or remove space between function name and '(' on function calls.\nsp_func_call_paren              = remove   # ignore/add/remove/force\n\n# Add or remove space between function name and '()' on function calls without\n# parameters. If set to ignore (the default), sp_func_call_paren is used.\nsp_func_call_paren_empty        = ignore   # ignore/add/remove/force\n\n# Add or remove space between the user function name and '(' on function\n# calls. You need to set a keyword to be a user function in the config file,\n# like:\n#   set func_call_user tr _ i18n\nsp_func_call_user_paren         = ignore   # ignore/add/remove/force\n\n# Add or remove space inside user function '(' and ')'.\nsp_func_call_user_inside_fparen = ignore   # ignore/add/remove/force\n\n# Add or remove space between nested parentheses with user functions,\n# i.e. '((' vs. '( ('.\nsp_func_call_user_paren_paren   = ignore   # ignore/add/remove/force\n\n# Add or remove space between a constructor/destructor and the open\n# parenthesis.\nsp_func_class_paren             = ignore   # ignore/add/remove/force\n\n# Add or remove space between a constructor without parameters or destructor\n# and '()'.\nsp_func_class_paren_empty       = ignore   # ignore/add/remove/force\n\n# Add or remove space after 'return'.\n#\n# Default: force\nsp_return                       = force    # ignore/add/remove/force\n\n# Add or remove space between 'return' and '('.\nsp_return_paren                 = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'return' and '{'.\nsp_return_brace                 = ignore   # ignore/add/remove/force\n\n# Add or remove space between '__attribute__' and '('.\nsp_attribute_paren              = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'defined' and '(' in '#if defined (FOO)'.\nsp_defined_paren                = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'throw' and '(' in 'throw (something)'.\nsp_throw_paren                  = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'throw' and anything other than '(' as in\n# '@throw [...];'.\nsp_after_throw                  = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'catch' and '(' in 'catch (something) { }'.\n# If set to ignore, sp_before_sparen is used.\nsp_catch_paren                  = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space between '@catch' and '('\n# in '@catch (something) { }'. If set to ignore, sp_catch_paren is used.\nsp_oc_catch_paren               = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space before Objective-C protocol list\n# as in '@protocol Protocol<here><Protocol_A>' or '@interface MyClass : NSObject<here><MyProtocol>'.\nsp_before_oc_proto_list         = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space between class name and '('\n# in '@interface className(categoryName)<ProtocolName>:BaseClass'\nsp_oc_classname_paren           = ignore   # ignore/add/remove/force\n\n# (D) Add or remove space between 'version' and '('\n# in 'version (something) { }'. If set to ignore, sp_before_sparen is used.\nsp_version_paren                = ignore   # ignore/add/remove/force\n\n# (D) Add or remove space between 'scope' and '('\n# in 'scope (something) { }'. If set to ignore, sp_before_sparen is used.\nsp_scope_paren                  = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'super' and '(' in 'super (something)'.\n#\n# Default: remove\nsp_super_paren                  = remove   # ignore/add/remove/force\n\n# Add or remove space between 'this' and '(' in 'this (something)'.\n#\n# Default: remove\nsp_this_paren                   = remove   # ignore/add/remove/force\n\n# Add or remove space between a macro name and its definition.\nsp_macro                        = ignore   # ignore/add/remove/force\n\n# Add or remove space between a macro function ')' and its definition.\nsp_macro_func                   = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'else' and '{' if on the same line.\nsp_else_brace                   = remove   # ignore/add/remove/force\n\n# Add or remove space between '}' and 'else' if on the same line.\nsp_brace_else                   = remove   # ignore/add/remove/force\n\n# Add or remove space between '}' and the name of a typedef on the same line.\nsp_brace_typedef                = ignore   # ignore/add/remove/force\n\n# Add or remove space before the '{' of a 'catch' statement, if the '{' and\n# 'catch' are on the same line, as in 'catch (decl) <here> {'.\nsp_catch_brace                  = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space before the '{' of a '@catch' statement, if the '{'\n# and '@catch' are on the same line, as in '@catch (decl) <here> {'.\n# If set to ignore, sp_catch_brace is used.\nsp_oc_catch_brace               = ignore   # ignore/add/remove/force\n\n# Add or remove space between '}' and 'catch' if on the same line.\nsp_brace_catch                  = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space between '}' and '@catch' if on the same line.\n# If set to ignore, sp_brace_catch is used.\nsp_oc_brace_catch               = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'finally' and '{' if on the same line.\nsp_finally_brace                = ignore   # ignore/add/remove/force\n\n# Add or remove space between '}' and 'finally' if on the same line.\nsp_brace_finally                = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'try' and '{' if on the same line.\nsp_try_brace                    = ignore   # ignore/add/remove/force\n\n# Add or remove space between get/set and '{' if on the same line.\nsp_getset_brace                 = ignore   # ignore/add/remove/force\n\n# Add or remove space between a variable and '{' for C++ uniform\n# initialization.\nsp_word_brace_init_lst          = ignore   # ignore/add/remove/force\n\n# Add or remove space between a variable and '{' for a namespace.\n#\n# Default: add\nsp_word_brace_ns                = remove      # ignore/add/remove/force\n\n# Add or remove space before the '::' operator.\nsp_before_dc                    = ignore   # ignore/add/remove/force\n\n# Add or remove space after the '::' operator.\nsp_after_dc                     = ignore   # ignore/add/remove/force\n\n# (D) Add or remove around the D named array initializer ':' operator.\nsp_d_array_colon                = ignore   # ignore/add/remove/force\n\n# Add or remove space after the '!' (not) unary operator.\n#\n# Default: remove\nsp_not                          = remove   # ignore/add/remove/force\n\n# Add or remove space between two '!' (not) unary operators.\n# If set to ignore, sp_not will be used.\nsp_not_not                      = ignore   # ignore/add/remove/force\n\n# Add or remove space after the '~' (invert) unary operator.\n#\n# Default: remove\nsp_inv                          = remove   # ignore/add/remove/force\n\n# Add or remove space after the '&' (address-of) unary operator. This does not\n# affect the spacing after a '&' that is part of a type.\n#\n# Default: remove\nsp_addr                         = remove   # ignore/add/remove/force\n\n# Add or remove space around the '.' or '->' operators.\n# also the c-sharp null-conditional operator '?.'\n#\n# Default: remove\nsp_member                       = remove   # ignore/add/remove/force\n\n# Add or remove space after the '*' (dereference) unary operator. This does\n# not affect the spacing after a '*' that is part of a type.\n#\n# Default: remove\nsp_deref                        = remove   # ignore/add/remove/force\n\n# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'.\n#\n# Default: remove\nsp_sign                         = remove   # ignore/add/remove/force\n\n# Add or remove space between '++' and '--' the word to which it is being\n# applied, as in '(--x)' or 'y++;'.\n#\n# Default: remove\nsp_incdec                       = remove   # ignore/add/remove/force\n\n# Add or remove space before a backslash-newline at the end of a line.\n#\n# Default: add\nsp_before_nl_cont               = add      # ignore/add/remove/force\n\n# (OC) Add or remove space after the scope '+' or '-', as in '-(void) foo;'\n# or '+(int) bar;'.\nsp_after_oc_scope               = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space after the colon in message specs,\n# i.e. '-(int) f:(int) x;' vs. '-(int) f: (int) x;'.\nsp_after_oc_colon               = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space before the colon in message specs,\n# i.e. '-(int) f: (int) x;' vs. '-(int) f : (int) x;'.\nsp_before_oc_colon              = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space after the colon in immutable dictionary expression\n# 'NSDictionary *test = @{@\"foo\" :@\"bar\"};'.\nsp_after_oc_dict_colon          = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space before the colon in immutable dictionary expression\n# 'NSDictionary *test = @{@\"foo\" :@\"bar\"};'.\nsp_before_oc_dict_colon         = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space after the colon in message specs,\n# i.e. '[object setValue:1];' vs. '[object setValue: 1];'.\nsp_after_send_oc_colon          = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space before the colon in message specs,\n# i.e. '[object setValue:1];' vs. '[object setValue :1];'.\nsp_before_send_oc_colon         = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space after the (type) in message specs,\n# i.e. '-(int)f: (int) x;' vs. '-(int)f: (int)x;'.\nsp_after_oc_type                = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space after the first (type) in message specs,\n# i.e. '-(int) f:(int)x;' vs. '-(int)f:(int)x;'.\nsp_after_oc_return_type         = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space between '@selector' and '(',\n# i.e. '@selector(msgName)' vs. '@selector (msgName)'.\n# Also applies to '@protocol()' constructs.\nsp_after_oc_at_sel              = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space between '@selector(x)' and the following word,\n# i.e. '@selector(foo) a:' vs. '@selector(foo)a:'.\nsp_after_oc_at_sel_parens       = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space inside '@selector' parentheses,\n# i.e. '@selector(foo)' vs. '@selector( foo )'.\n# Also applies to '@protocol()' constructs.\nsp_inside_oc_at_sel_parens      = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space before a block pointer caret,\n# i.e. '^int (int arg){...}' vs. ' ^int (int arg){...}'.\nsp_before_oc_block_caret        = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space after a block pointer caret,\n# i.e. '^int (int arg){...}' vs. '^ int (int arg){...}'.\nsp_after_oc_block_caret         = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space between the receiver and selector in a message,\n# as in '[receiver selector ...]'.\nsp_after_oc_msg_receiver        = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space after '@property'.\nsp_after_oc_property            = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove space between '@synchronized' and the open parenthesis,\n# i.e. '@synchronized(foo)' vs. '@synchronized (foo)'.\nsp_after_oc_synchronized        = ignore   # ignore/add/remove/force\n\n# Add or remove space around the ':' in 'b ? t : f'.\nsp_cond_colon                   = ignore   # ignore/add/remove/force\n\n# Add or remove space before the ':' in 'b ? t : f'.\n#\n# Overrides sp_cond_colon.\nsp_cond_colon_before            = ignore   # ignore/add/remove/force\n\n# Add or remove space after the ':' in 'b ? t : f'.\n#\n# Overrides sp_cond_colon.\nsp_cond_colon_after             = ignore   # ignore/add/remove/force\n\n# Add or remove space around the '?' in 'b ? t : f'.\nsp_cond_question                = ignore   # ignore/add/remove/force\n\n# Add or remove space before the '?' in 'b ? t : f'.\n#\n# Overrides sp_cond_question.\nsp_cond_question_before         = ignore   # ignore/add/remove/force\n\n# Add or remove space after the '?' in 'b ? t : f'.\n#\n# Overrides sp_cond_question.\nsp_cond_question_after          = ignore   # ignore/add/remove/force\n\n# In the abbreviated ternary form '(a ?: b)', add or remove space between '?'\n# and ':'.\n#\n# Overrides all other sp_cond_* options.\nsp_cond_ternary_short           = ignore   # ignore/add/remove/force\n\n# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make\n# sense here.\nsp_case_label                   = ignore   # ignore/add/remove/force\n\n# (D) Add or remove space around the D '..' operator.\nsp_range                        = ignore   # ignore/add/remove/force\n\n# Add or remove space after ':' in a Java/C++11 range-based 'for',\n# as in 'for (Type var : <here> expr)'.\nsp_after_for_colon              = ignore   # ignore/add/remove/force\n\n# Add or remove space before ':' in a Java/C++11 range-based 'for',\n# as in 'for (Type var <here> : expr)'.\nsp_before_for_colon             = ignore   # ignore/add/remove/force\n\n# (D) Add or remove space between 'extern' and '(' as in 'extern <here> (C)'.\nsp_extern_paren                 = ignore   # ignore/add/remove/force\n\n# Add or remove space after the opening of a C++ comment, as in '// <here> A'.\nsp_cmt_cpp_start                = ignore   # ignore/add/remove/force\n\n# remove space after the '//' and the pvs command '-V1234',\n# only works with sp_cmt_cpp_start set to add or force.\nsp_cmt_cpp_pvs                  = false    # true/false\n\n# remove space after the '//' and the command 'lint',\n# only works with sp_cmt_cpp_start set to add or force.\nsp_cmt_cpp_lint                 = false    # true/false\n\n# Add or remove space in a C++ region marker comment, as in '// <here> BEGIN'.\n# A region marker is defined as a comment which is not preceded by other text\n# (i.e. the comment is the first non-whitespace on the line), and which starts\n# with either 'BEGIN' or 'END'.\n#\n# Overrides sp_cmt_cpp_start.\nsp_cmt_cpp_region               = ignore   # ignore/add/remove/force\n\n# If true, space added with sp_cmt_cpp_start will be added after Doxygen\n# sequences like '///', '///<', '//!' and '//!<'.\nsp_cmt_cpp_doxygen              = false    # true/false\n\n# If true, space added with sp_cmt_cpp_start will be added after Qt translator\n# or meta-data comments like '//:', '//=', and '//~'.\nsp_cmt_cpp_qttr                 = false    # true/false\n\n# Add or remove space between #else or #endif and a trailing comment.\nsp_endif_cmt                    = ignore   # ignore/add/remove/force\n\n# Add or remove space after 'new', 'delete' and 'delete[]'.\nsp_after_new                    = ignore   # ignore/add/remove/force\n\n# Add or remove space between 'new' and '(' in 'new()'.\nsp_between_new_paren            = ignore   # ignore/add/remove/force\n\n# Add or remove space between ')' and type in 'new(foo) BAR'.\nsp_after_newop_paren            = ignore   # ignore/add/remove/force\n\n# Add or remove space inside parentheses of the new operator\n# as in 'new(foo) BAR'.\nsp_inside_newop_paren           = ignore   # ignore/add/remove/force\n\n# Add or remove space after the open parenthesis of the new operator,\n# as in 'new(foo) BAR'.\n#\n# Overrides sp_inside_newop_paren.\nsp_inside_newop_paren_open      = ignore   # ignore/add/remove/force\n\n# Add or remove space before the close parenthesis of the new operator,\n# as in 'new(foo) BAR'.\n#\n# Overrides sp_inside_newop_paren.\nsp_inside_newop_paren_close     = ignore   # ignore/add/remove/force\n\n# Add or remove space before a trailing comment.\nsp_before_tr_cmt                = ignore   # ignore/add/remove/force\n\n# Number of spaces before a trailing comment.\nsp_num_before_tr_cmt            = 0        # unsigned number\n\n# Add or remove space before an embedded comment.\n#\n# Default: force\nsp_before_emb_cmt               = force    # ignore/add/remove/force\n\n# Number of spaces before an embedded comment.\n#\n# Default: 1\nsp_num_before_emb_cmt           = 1        # unsigned number\n\n# Add or remove space after an embedded comment.\n#\n# Default: force\nsp_after_emb_cmt                = force    # ignore/add/remove/force\n\n# Number of spaces after an embedded comment.\n#\n# Default: 1\nsp_num_after_emb_cmt            = 1        # unsigned number\n\n# Embedded comment spacing options have higher priority (== override)\n# than other spacing options (comma, parenthesis, braces, ...)\nsp_emb_cmt_priority             = false    # true/false\n\n# (Java) Add or remove space between an annotation and the open parenthesis.\nsp_annotation_paren             = ignore   # ignore/add/remove/force\n\n# If true, vbrace tokens are dropped to the previous token and skipped.\nsp_skip_vbrace_tokens           = false    # true/false\n\n# Add or remove space after 'noexcept'.\nsp_after_noexcept               = ignore   # ignore/add/remove/force\n\n# Add or remove space after '_'.\nsp_vala_after_translation       = ignore   # ignore/add/remove/force\n\n# Add or remove space before a bit colon ':'.\nsp_before_bit_colon             = ignore   # ignore/add/remove/force\n\n# Add or remove space after a bit colon ':'.\nsp_after_bit_colon              = ignore   # ignore/add/remove/force\n\n# If true, a <TAB> is inserted after #define.\nforce_tab_after_define          = false    # true/false\n\n# Add or remove space between two strings.\nsp_string_string                = ignore   # ignore/add/remove/force\n\n# Add or remove space 'struct' and a type.\nsp_struct_type                  = ignore   # ignore/add/remove/force\n\n#\n# Indenting options\n#\n\n# The number of columns to indent per level. Usually 2, 3, 4, or 8.\n#\n# Default: 8\nindent_columns                  = 4        # unsigned number\n\n# Whether to ignore indent for the first continuation line. Subsequent\n# continuation lines will still be indented to match the first.\nindent_ignore_first_continue    = false    # true/false\n\n# The continuation indent. If non-zero, this overrides the indent of '(', '['\n# and '=' continuation indents. Negative values are OK; negative value is\n# absolute and not increased for each '(' or '[' level.\n#\n# For FreeBSD, this is set to 4.\n# Requires indent_ignore_first_continue=false.\nindent_continue                 = 8        # number\n\n# The continuation indent, only for class header line(s). If non-zero, this\n# overrides the indent of 'class' continuation indents.\n# Requires indent_ignore_first_continue=false.\nindent_continue_class_head      = 0        # unsigned number\n\n# Whether to indent empty lines (i.e. lines which contain only spaces before\n# the newline character).\nindent_single_newlines          = false    # true/false\n\n# The continuation indent for func_*_param if they are true. If non-zero, this\n# overrides the indent.\nindent_param                    = 0        # unsigned number\n\n# How to use tabs when indenting code.\n#\n# 0: Spaces only\n# 1: Indent with tabs to brace level, align with spaces (default)\n# 2: Indent and align with tabs, using spaces when not on a tabstop\n#\n# Default: 1\nindent_with_tabs                = 2        # unsigned number\n\n# Whether to indent comments that are not at a brace level with tabs on a\n# tabstop. Requires indent_with_tabs=2. If false, will use spaces.\nindent_cmt_with_tabs            = false    # true/false\n\n# Whether to indent strings broken by '\\' so that they line up.\nindent_align_string             = false    # true/false\n\n# The number of spaces to indent multi-line XML strings.\n# Requires indent_align_string=true.\nindent_xml_string               = 0        # unsigned number\n\n# Spaces to indent '{' from level.\nindent_brace                    = 0        # unsigned number\n\n# Whether braces are indented to the body level.\nindent_braces                   = false    # true/false\n\n# Whether to disable indenting function braces if indent_braces=true.\nindent_braces_no_func           = false    # true/false\n\n# Whether to disable indenting class braces if indent_braces=true.\nindent_braces_no_class          = false    # true/false\n\n# Whether to disable indenting struct braces if indent_braces=true.\nindent_braces_no_struct         = false    # true/false\n\n# Whether to indent based on the size of the brace parent,\n# i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc.\nindent_brace_parent             = false    # true/false\n\n# Whether to indent based on the open parenthesis instead of the open brace\n# in '({\\n'.\nindent_paren_open_brace         = false    # true/false\n\n# (C#) Whether to indent the brace of a C# delegate by another level.\nindent_cs_delegate_brace        = false    # true/false\n\n# (C#) Whether to indent a C# delegate (to handle delegates with no brace) by\n# another level.\nindent_cs_delegate_body         = false    # true/false\n\n# Whether to indent the body of a 'namespace'.\nindent_namespace                = false    # true/false\n\n# Whether to indent only the first namespace, and not any nested namespaces.\n# Requires indent_namespace=true.\nindent_namespace_single_indent  = false    # true/false\n\n# The number of spaces to indent a namespace block.\n# If set to zero, use the value indent_columns\nindent_namespace_level          = 0        # unsigned number\n\n# If the body of the namespace is longer than this number, it won't be\n# indented. Requires indent_namespace=true. 0 means no limit.\nindent_namespace_limit          = 0        # unsigned number\n\n# Whether to indent only in inner namespaces (nested in other namespaces).\n# Requires indent_namespace=true.\nindent_namespace_inner_only     = false    # true/false\n\n# Whether the 'extern \"C\"' body is indented.\nindent_extern                   = false    # true/false\n\n# Whether the 'class' body is indented.\nindent_class                    = true    # true/false\n\n# Whether to ignore indent for the leading base class colon.\nindent_ignore_before_class_colon = false    # true/false\n\n# Additional indent before the leading base class colon.\n# Negative values decrease indent down to the first column.\n# Requires indent_ignore_before_class_colon=false and a newline break before\n# the colon (see pos_class_colon and nl_class_colon)\nindent_before_class_colon       = 0        # number\n\n# Whether to indent the stuff after a leading base class colon.\nindent_class_colon              = false    # true/false\n\n# Whether to indent based on a class colon instead of the stuff after the\n# colon. Requires indent_class_colon=true.\nindent_class_on_colon           = false    # true/false\n\n# Whether to ignore indent for a leading class initializer colon.\nindent_ignore_before_constr_colon = false    # true/false\n\n# Whether to indent the stuff after a leading class initializer colon.\nindent_constr_colon             = false    # true/false\n\n# Virtual indent from the ':' for leading member initializers.\n#\n# Default: 2\nindent_ctor_init_leading        = 2        # unsigned number\n\n# Virtual indent from the ':' for following member initializers.\n#\n# Default: 2\nindent_ctor_init_following      = 2        # unsigned number\n\n# Additional indent for constructor initializer list.\n# Negative values decrease indent down to the first column.\nindent_ctor_init                = 0        # number\n\n# Whether to indent 'if' following 'else' as a new block under the 'else'.\n# If false, 'else\\nif' is treated as 'else if' for indenting purposes.\nindent_else_if                  = false    # true/false\n\n# Amount to indent variable declarations after a open brace.\n#\n#  <0: Relative\n# >=0: Absolute\nindent_var_def_blk              = 0        # number\n\n# Whether to indent continued variable declarations instead of aligning.\nindent_var_def_cont             = true    # true/false\n\n# How to indent continued shift expressions ('<<' and '>>').\n# Set align_left_shift=false when using this.\n#  0: Align shift operators instead of indenting them (default)\n#  1: Indent by one level\n# -1: Preserve original indentation\nindent_shift                    = 0        # number\n\n# Whether to force indentation of function definitions to start in column 1.\nindent_func_def_force_col1      = false    # true/false\n\n# Whether to indent continued function call parameters one indent level,\n# rather than aligning parameters under the open parenthesis.\nindent_func_call_param          = true    # true/false\n\n# Whether to indent continued function definition parameters one indent level,\n# rather than aligning parameters under the open parenthesis.\nindent_func_def_param           = true    # true/false\n\n# for function definitions, only if indent_func_def_param is false\n# Allows to align params when appropriate and indent them when not\n# behave as if it was true if paren position is more than this value\n# if paren position is more than the option value\nindent_func_def_param_paren_pos_threshold = 0        # unsigned number\n\n# Whether to indent continued function call prototype one indent level,\n# rather than aligning parameters under the open parenthesis.\nindent_func_proto_param         = true    # true/false\n\n# Whether to indent continued function call declaration one indent level,\n# rather than aligning parameters under the open parenthesis.\nindent_func_class_param         = true    # true/false\n\n# Whether to indent continued class variable constructors one indent level,\n# rather than aligning parameters under the open parenthesis.\nindent_func_ctor_var_param      = true    # true/false\n\n# Whether to indent continued template parameter list one indent level,\n# rather than aligning parameters under the open parenthesis.\nindent_template_param           = true    # true/false\n\n# Double the indent for indent_func_xxx_param options.\n# Use both values of the options indent_columns and indent_param.\nindent_func_param_double        = true    # true/false\n\n# Indentation column for standalone 'const' qualifier on a function\n# prototype.\nindent_func_const               = 0        # unsigned number\n\n# Indentation column for standalone 'throw' qualifier on a function\n# prototype.\nindent_func_throw               = 0        # unsigned number\n\n# How to indent within a macro followed by a brace on the same line\n# This allows reducing the indent in macros that have (for example)\n# `do { ... } while (0)` blocks bracketing them.\n#\n# true:  add an indent for the brace on the same line as the macro\n# false: do not add an indent for the brace on the same line as the macro\n#\n# Default: true\nindent_macro_brace              = true     # true/false\n\n# The number of spaces to indent a continued '->' or '.'.\n# Usually set to 0, 1, or indent_columns.\nindent_member                   = indent_columns        # unsigned number\n\n# Whether lines broken at '.' or '->' should be indented by a single indent.\n# The indent_member option will not be effective if this is set to true.\nindent_member_single            = false    # true/false\n\n# Spaces to indent single line ('//') comments on lines before code.\nindent_single_line_comments_before = 0        # unsigned number\n\n# Spaces to indent single line ('//') comments on lines after code.\nindent_single_line_comments_after = 0        # unsigned number\n\n# When opening a paren for a control statement (if, for, while, etc), increase\n# the indent level by this value. Negative values decrease the indent level.\nindent_sparen_extra             = 0        # number\n\n# Whether to indent trailing single line ('//') comments relative to the code\n# instead of trying to keep the same absolute column.\nindent_relative_single_line_comments = false    # true/false\n\n# Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns.\n# It might be wise to choose the same value for the option indent_case_brace.\nindent_switch_case              = indent_columns        # unsigned number\n\n# Spaces to indent the body of a 'switch' before any 'case'.\n# Usually the same as indent_columns or indent_switch_case.\nindent_switch_body              = 0        # unsigned number\n\n# Whether to ignore indent for '{' following 'case'.\nindent_ignore_case_brace        = false    # true/false\n\n# Spaces to indent '{' from 'case'. By default, the brace will appear under\n# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK.\n# It might be wise to choose the same value for the option indent_switch_case.\nindent_case_brace               = indent_columns        # number\n\n# indent 'break' with 'case' from 'switch'.\nindent_switch_break_with_case   = false    # true/false\n\n# Whether to indent preprocessor statements inside of switch statements.\n#\n# Default: true\nindent_switch_pp                = true     # true/false\n\n# Spaces to shift the 'case' line, without affecting any other lines.\n# Usually 0.\nindent_case_shift               = 0        # unsigned number\n\n# Whether to align comments before 'case' with the 'case'.\n#\n# Default: true\nindent_case_comment             = true     # true/false\n\n# Whether to indent comments not found in first column.\n#\n# Default: true\nindent_comment                  = true     # true/false\n\n# Whether to indent comments found in first column.\nindent_col1_comment             = false    # true/false\n\n# Whether to indent multi string literal in first column.\nindent_col1_multi_string_literal = false    # true/false\n\n# Align comments on adjacent lines that are this many columns apart or less.\n#\n# Default: 3\nindent_comment_align_thresh     = 3        # unsigned number\n\n# Whether to ignore indent for goto labels.\nindent_ignore_label             = false    # true/false\n\n# How to indent goto labels. Requires indent_ignore_label=false.\n#\n#  >0: Absolute column where 1 is the leftmost column\n# <=0: Subtract from brace indent\n#\n# Default: 1\nindent_label                    = 1        # number\n\n# How to indent access specifiers that are followed by a\n# colon.\n#\n#  >0: Absolute column where 1 is the leftmost column\n# <=0: Subtract from brace indent\n#\n# Default: 1\nindent_access_spec              = 1        # number\n\n# Whether to indent the code after an access specifier by one level.\n# If true, this option forces 'indent_access_spec=0'.\nindent_access_spec_body         = false    # true/false\n\n# If an open parenthesis is followed by a newline, whether to indent the next\n# line so that it lines up after the open parenthesis (not recommended).\nindent_paren_nl                 = false    # true/false\n\n# How to indent a close parenthesis after a newline.\n#\n#  0: Indent to body level (default)\n#  1: Align under the open parenthesis\n#  2: Indent to the brace level\n# -1: Preserve original indentation\nindent_paren_close              = 0        # number\n\n# Whether to indent the open parenthesis of a function definition,\n# if the parenthesis is on its own line.\nindent_paren_after_func_def     = false    # true/false\n\n# Whether to indent the open parenthesis of a function declaration,\n# if the parenthesis is on its own line.\nindent_paren_after_func_decl    = false    # true/false\n\n# Whether to indent the open parenthesis of a function call,\n# if the parenthesis is on its own line.\nindent_paren_after_func_call    = false    # true/false\n\n# How to indent a comma when inside braces.\n#  0: Indent by one level (default)\n#  1: Align under the open brace\n# -1: Preserve original indentation\nindent_comma_brace              = 0        # number\n\n# How to indent a comma when inside parentheses.\n#  0: Indent by one level (default)\n#  1: Align under the open parenthesis\n# -1: Preserve original indentation\nindent_comma_paren              = 0        # number\n\n# How to indent a Boolean operator when inside parentheses.\n#  0: Indent by one level (default)\n#  1: Align under the open parenthesis\n# -1: Preserve original indentation\nindent_bool_paren               = 0        # number\n\n# Whether to ignore the indentation of a Boolean operator when outside\n# parentheses.\nindent_ignore_bool              = false    # true/false\n\n# Whether to indent lines that are nested in boolean expression one more level for each nesting\nindent_bool_nested_all          = false    # true/false\n\n# Whether to ignore the indentation of an arithmetic operator.\nindent_ignore_arith             = false    # true/false\n\n# Whether to indent a semicolon when inside a for parenthesis.\n# If true, aligns under the open for parenthesis.\nindent_semicolon_for_paren      = false    # true/false\n\n# Whether to ignore the indentation of a semicolon outside of a 'for'\n# statement.\nindent_ignore_semicolon         = false    # true/false\n\n# Whether to align the first expression to following ones\n# if indent_bool_paren=1.\nindent_first_bool_expr          = false    # true/false\n\n# Whether to align the first expression to following ones\n# if indent_semicolon_for_paren=true.\nindent_first_for_expr           = false    # true/false\n\n# If an open square is followed by a newline, whether to indent the next line\n# so that it lines up after the open square (not recommended).\nindent_square_nl                = false    # true/false\n\n# (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies.\nindent_preserve_sql             = false    # true/false\n\n# Whether to ignore the indentation of an assignment operator.\nindent_ignore_assign            = false    # true/false\n\n# Whether to align continued statements at the '='. If false or if the '=' is\n# followed by a newline, the next line is indent one tab.\n#\n# Default: true\nindent_align_assign             = true     # true/false\n\n# If true, the indentation of the chunks after a '=' sequence will be set at\n# LHS token indentation column before '='.\nindent_off_after_assign         = false    # true/false\n\n# Whether to align continued statements at the '('. If false or the '(' is\n# followed by a newline, the next line indent is one tab.\n#\n# Default: true\nindent_align_paren              = true     # true/false\n\n# (OC) Whether to indent Objective-C code inside message selectors.\nindent_oc_inside_msg_sel        = false    # true/false\n\n# (OC) Whether to indent Objective-C blocks at brace level instead of usual\n# rules.\nindent_oc_block                 = false    # true/false\n\n# (OC) Indent for Objective-C blocks in a message relative to the parameter\n# name.\n#\n# =0: Use indent_oc_block rules\n# >0: Use specified number of spaces to indent\nindent_oc_block_msg             = 0        # unsigned number\n\n# (OC) Minimum indent for subsequent parameters\nindent_oc_msg_colon             = 0        # unsigned number\n\n# (OC) Whether to prioritize aligning with initial colon (and stripping spaces\n# from lines, if necessary).\n#\n# Default: true\nindent_oc_msg_prioritize_first_colon = true     # true/false\n\n# (OC) Whether to indent blocks the way that Xcode does by default\n# (from the keyword if the parameter is on its own line; otherwise, from the\n# previous indentation level). Requires indent_oc_block_msg=true.\nindent_oc_block_msg_xcode_style = false    # true/false\n\n# (OC) Whether to indent blocks from where the brace is, relative to a\n# message keyword. Requires indent_oc_block_msg=true.\nindent_oc_block_msg_from_keyword = false    # true/false\n\n# (OC) Whether to indent blocks from where the brace is, relative to a message\n# colon. Requires indent_oc_block_msg=true.\nindent_oc_block_msg_from_colon  = false    # true/false\n\n# (OC) Whether to indent blocks from where the block caret is.\n# Requires indent_oc_block_msg=true.\nindent_oc_block_msg_from_caret  = false    # true/false\n\n# (OC) Whether to indent blocks from where the brace caret is.\n# Requires indent_oc_block_msg=true.\nindent_oc_block_msg_from_brace  = false    # true/false\n\n# When indenting after virtual brace open and newline add further spaces to\n# reach this minimum indent.\nindent_min_vbrace_open          = 0        # unsigned number\n\n# Whether to add further spaces after regular indent to reach next tabstop\n# when indenting after virtual brace open and newline.\nindent_vbrace_open_on_tabstop   = false    # true/false\n\n# How to indent after a brace followed by another token (not a newline).\n# true:  indent all contained lines to match the token\n# false: indent all contained lines to match the brace\n#\n# Default: true\nindent_token_after_brace        = true     # true/false\n\n# Whether to indent the body of a C++11 lambda.\nindent_cpp_lambda_body          = false    # true/false\n\n# How to indent compound literals that are being returned.\n# true: add both the indent from return & the compound literal open brace\n#       (i.e. 2 indent levels)\n# false: only indent 1 level, don't add the indent for the open brace, only\n#        add the indent for the return.\n#\n# Default: true\nindent_compound_literal_return  = true     # true/false\n\n# (C#) Whether to indent a 'using' block if no braces are used.\n#\n# Default: true\nindent_using_block              = true     # true/false\n\n# How to indent the continuation of ternary operator.\n#\n# 0: Off (default)\n# 1: When the `if_false` is a continuation, indent it under the `if_true` branch\n# 2: When the `:` is a continuation, indent it under `?`\nindent_ternary_operator         = 0        # unsigned number\n\n# Whether to indent the statements inside ternary operator.\nindent_inside_ternary_operator  = false    # true/false\n\n# If true, the indentation of the chunks after a `return` sequence will be set at return indentation column.\nindent_off_after_return         = false    # true/false\n\n# If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column.\nindent_off_after_return_new     = false    # true/false\n\n# If true, the tokens after return are indented with regular single indentation. By default (false) the indentation is after the return token.\nindent_single_after_return      = false    # true/false\n\n# Whether to ignore indent and alignment for 'asm' blocks (i.e. assume they\n# have their own indentation).\nindent_ignore_asm_block         = false    # true/false\n\n# Don't indent the close parenthesis of a function definition,\n# if the parenthesis is on its own line.\ndonot_indent_func_def_close_paren = false    # true/false\n\n#\n# Newline adding and removing options\n#\n\n# Whether to collapse empty blocks between '{' and '}' except for functions.\n# Use nl_collapse_empty_body_functions to specify how empty function braces\n# should be formatted.\nnl_collapse_empty_body          = false    # true/false\n\n# Whether to collapse empty blocks between '{' and '}' for functions only.\n# If true, overrides nl_inside_empty_func.\nnl_collapse_empty_body_functions = false    # true/false\n\n# Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'.\nnl_assign_leave_one_liners      = true    # true/false\n\n# Don't split one-line braced statements inside a 'class xx { }' body.\nnl_class_leave_one_liners       = true    # true/false\n\n# Don't split one-line enums, as in 'enum foo { BAR = 15 };'\nnl_enum_leave_one_liners        = true    # true/false\n\n# Don't split one-line get or set functions.\nnl_getset_leave_one_liners      = true    # true/false\n\n# (C#) Don't split one-line property get or set functions.\nnl_cs_property_leave_one_liners = false    # true/false\n\n# Don't split one-line function definitions, as in 'int foo() { return 0; }'.\n# might modify nl_func_type_name\nnl_func_leave_one_liners        = false    # true/false\n\n# Don't split one-line C++11 lambdas, as in '[]() { return 0; }'.\nnl_cpp_lambda_leave_one_liners  = false    # true/false\n\n# Don't split one-line if/else statements, as in 'if(...) b++;'.\nnl_if_leave_one_liners          = false    # true/false\n\n# Don't split one-line while statements, as in 'while(...) b++;'.\nnl_while_leave_one_liners       = false    # true/false\n\n# Don't split one-line do statements, as in 'do { b++; } while(...);'.\nnl_do_leave_one_liners          = false    # true/false\n\n# Don't split one-line for statements, as in 'for(...) b++;'.\nnl_for_leave_one_liners         = false    # true/false\n\n# (OC) Don't split one-line Objective-C messages.\nnl_oc_msg_leave_one_liner       = false    # true/false\n\n# (OC) Add or remove newline between method declaration and '{'.\nnl_oc_mdef_brace                = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove newline between Objective-C block signature and '{'.\nnl_oc_block_brace               = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove blank line before '@interface' statement.\nnl_oc_before_interface          = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove blank line before '@implementation' statement.\nnl_oc_before_implementation     = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove blank line before '@end' statement.\nnl_oc_before_end                = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove newline between '@interface' and '{'.\nnl_oc_interface_brace           = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove newline between '@implementation' and '{'.\nnl_oc_implementation_brace      = ignore   # ignore/add/remove/force\n\n# Add or remove newlines at the start of the file.\nnl_start_of_file                = ignore   # ignore/add/remove/force\n\n# The minimum number of newlines at the start of the file (only used if\n# nl_start_of_file is 'add' or 'force').\nnl_start_of_file_min            = 0        # unsigned number\n\n# Add or remove newline at the end of the file.\nnl_end_of_file                  = ignore   # ignore/add/remove/force\n\n# The minimum number of newlines at the end of the file (only used if\n# nl_end_of_file is 'add' or 'force').\nnl_end_of_file_min              = 0        # unsigned number\n\n# Add or remove newline between '=' and '{'.\nnl_assign_brace                 = remove   # ignore/add/remove/force\n\n# (D) Add or remove newline between '=' and '['.\nnl_assign_square                = ignore   # ignore/add/remove/force\n\n# Add or remove newline between '[]' and '{'.\nnl_tsquare_brace                = ignore   # ignore/add/remove/force\n\n# (D) Add or remove newline after '= ['. Will also affect the newline before\n# the ']'.\nnl_after_square_assign          = ignore   # ignore/add/remove/force\n\n# Add or remove newline between a function call's ')' and '{', as in\n# 'list_for_each(item, &list) { }'.\nnl_fcall_brace                  = add   # ignore/add/remove/force\n\n# Add or remove newline between 'enum' and '{'.\nnl_enum_brace                   = remove   # ignore/add/remove/force\n\n# Add or remove newline between 'enum' and 'class'.\nnl_enum_class                   = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'enum class' and the identifier.\nnl_enum_class_identifier        = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'enum class' type and ':'.\nnl_enum_identifier_colon        = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'enum class identifier :' and type.\nnl_enum_colon_type              = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'struct and '{'.\nnl_struct_brace                 = remove   # ignore/add/remove/force\n\n# Add or remove newline between 'union' and '{'.\nnl_union_brace                  = remove   # ignore/add/remove/force\n\n# Add or remove newline between 'if' and '{'.\nnl_if_brace                     = remove   # ignore/add/remove/force\n\n# Add or remove newline between '}' and 'else'.\nnl_brace_else                   = remove   # ignore/add/remove/force\n\n# Add or remove newline between 'else if' and '{'. If set to ignore,\n# nl_if_brace is used instead.\nnl_elseif_brace                 = remove   # ignore/add/remove/force\n\n# Add or remove newline between 'else' and '{'.\nnl_else_brace                   = remove   # ignore/add/remove/force\n\n# Add or remove newline between 'else' and 'if'.\nnl_else_if                      = remove   # ignore/add/remove/force\n\n# Add or remove newline before '{' opening brace\nnl_before_opening_brace_func_class_def = ignore   # ignore/add/remove/force\n\n# Add or remove newline before 'if'/'else if' closing parenthesis.\nnl_before_if_closing_paren      = ignore   # ignore/add/remove/force\n\n# Add or remove newline between '}' and 'finally'.\nnl_brace_finally                = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'finally' and '{'.\nnl_finally_brace                = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'try' and '{'.\nnl_try_brace                    = ignore   # ignore/add/remove/force\n\n# Add or remove newline between get/set and '{'.\nnl_getset_brace                 = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'for' and '{'.\nnl_for_brace                    = remove   # ignore/add/remove/force\n\n# Add or remove newline before the '{' of a 'catch' statement, as in\n# 'catch (decl) <here> {'.\nnl_catch_brace                  = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove newline before the '{' of a '@catch' statement, as in\n# '@catch (decl) <here> {'. If set to ignore, nl_catch_brace is used.\nnl_oc_catch_brace               = ignore   # ignore/add/remove/force\n\n# Add or remove newline between '}' and 'catch'.\nnl_brace_catch                  = ignore   # ignore/add/remove/force\n\n# (OC) Add or remove newline between '}' and '@catch'. If set to ignore,\n# nl_brace_catch is used.\nnl_oc_brace_catch               = ignore   # ignore/add/remove/force\n\n# Add or remove newline between '}' and ']'.\nnl_brace_square                 = ignore   # ignore/add/remove/force\n\n# Add or remove newline between '}' and ')' in a function invocation.\nnl_brace_fparen                 = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'while' and '{'.\nnl_while_brace                  = remove   # ignore/add/remove/force\n\n# (D) Add or remove newline between 'scope (x)' and '{'.\nnl_scope_brace                  = ignore   # ignore/add/remove/force\n\n# (D) Add or remove newline between 'unittest' and '{'.\nnl_unittest_brace               = ignore   # ignore/add/remove/force\n\n# (D) Add or remove newline between 'version (x)' and '{'.\nnl_version_brace                = ignore   # ignore/add/remove/force\n\n# (C#) Add or remove newline between 'using' and '{'.\nnl_using_brace                  = ignore   # ignore/add/remove/force\n\n# Add or remove newline between two open or close braces. Due to general\n# newline/brace handling, REMOVE may not work.\nnl_brace_brace                  = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'do' and '{'.\nnl_do_brace                     = remove   # ignore/add/remove/force\n\n# Add or remove newline between '}' and 'while' of 'do' statement.\nnl_brace_while                  = remove   # ignore/add/remove/force\n\n# Add or remove newline between 'switch' and '{'.\nnl_switch_brace                 = remove   # ignore/add/remove/force\n\n# Add or remove newline between 'synchronized' and '{'.\nnl_synchronized_brace           = remove   # ignore/add/remove/force\n\n# Add a newline between ')' and '{' if the ')' is on a different line than the\n# if/for/etc.\n#\n# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and\n# nl_catch_brace.\nnl_multi_line_cond              = false    # true/false\n\n# Add a newline after '(' if an if/for/while/switch condition spans multiple\n# lines\nnl_multi_line_sparen_open       = ignore   # ignore/add/remove/force\n\n# Add a newline before ')' if an if/for/while/switch condition spans multiple\n# lines. Overrides nl_before_if_closing_paren if both are specified.\nnl_multi_line_sparen_close      = ignore   # ignore/add/remove/force\n\n# Force a newline in a define after the macro name for multi-line defines.\nnl_multi_line_define            = false    # true/false\n\n# Whether to add a newline before 'case', and a blank line before a 'case'\n# statement that follows a ';' or '}'.\nnl_before_case                  = false    # true/false\n\n# Whether to add a newline after a 'case' statement.\nnl_after_case                   = false    # true/false\n\n# Add or remove newline between a case ':' and '{'.\n#\n# Overrides nl_after_case.\nnl_case_colon_brace             = ignore   # ignore/add/remove/force\n\n# Add or remove newline between ')' and 'throw'.\nnl_before_throw                 = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'namespace' and '{'.\nnl_namespace_brace              = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<...>' of a template class.\nnl_template_class               = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<...>' of a template class declaration.\n#\n# Overrides nl_template_class.\nnl_template_class_decl          = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<>' of a specialized class declaration.\n#\n# Overrides nl_template_class_decl.\nnl_template_class_decl_special  = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<...>' of a template class definition.\n#\n# Overrides nl_template_class.\nnl_template_class_def           = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<>' of a specialized class definition.\n#\n# Overrides nl_template_class_def.\nnl_template_class_def_special   = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<...>' of a template function.\nnl_template_func                = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<...>' of a template function\n# declaration.\n#\n# Overrides nl_template_func.\nnl_template_func_decl           = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<>' of a specialized function\n# declaration.\n#\n# Overrides nl_template_func_decl.\nnl_template_func_decl_special   = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<...>' of a template function\n# definition.\n#\n# Overrides nl_template_func.\nnl_template_func_def            = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<>' of a specialized function\n# definition.\n#\n# Overrides nl_template_func_def.\nnl_template_func_def_special    = ignore   # ignore/add/remove/force\n\n# Add or remove newline after 'template<...>' of a template variable.\nnl_template_var                 = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'template<...>' and 'using' of a templated\n# type alias.\nnl_template_using               = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'class' and '{'.\nnl_class_brace                  = ignore   # ignore/add/remove/force\n\n# Add or remove newline before or after (depending on pos_class_comma,\n# may not be IGNORE) each',' in the base class list.\nnl_class_init_args              = ignore   # ignore/add/remove/force\n\n# Add or remove newline after each ',' in the constructor member\n# initialization. Related to nl_constr_colon, pos_constr_colon and\n# pos_constr_comma.\nnl_constr_init_args             = ignore   # ignore/add/remove/force\n\n# Add or remove newline before first element, after comma, and after last\n# element, in 'enum'.\nnl_enum_own_lines               = ignore   # ignore/add/remove/force\n\n# Add or remove newline between return type and function name in a function\n# definition.\n# might be modified by nl_func_leave_one_liners\nnl_func_type_name               = ignore   # ignore/add/remove/force\n\n# Add or remove newline between return type and function name inside a class\n# definition. If set to ignore, nl_func_type_name or nl_func_proto_type_name\n# is used instead.\nnl_func_type_name_class         = ignore   # ignore/add/remove/force\n\n# Add or remove newline between class specification and '::'\n# in 'void A::f() { }'. Only appears in separate member implementation (does\n# not appear with in-line implementation).\nnl_func_class_scope             = ignore   # ignore/add/remove/force\n\n# Add or remove newline between function scope and name, as in\n# 'void A :: <here> f() { }'.\nnl_func_scope_name              = ignore   # ignore/add/remove/force\n\n# Add or remove newline between return type and function name in a prototype.\nnl_func_proto_type_name         = ignore   # ignore/add/remove/force\n\n# Add or remove newline between a function name and the opening '(' in the\n# declaration.\nnl_func_paren                   = remove   # ignore/add/remove/force\n\n# Overrides nl_func_paren for functions with no parameters.\nnl_func_paren_empty             = remove   # ignore/add/remove/force\n\n# Add or remove newline between a function name and the opening '(' in the\n# definition.\nnl_func_def_paren               = remove   # ignore/add/remove/force\n\n# Overrides nl_func_def_paren for functions with no parameters.\nnl_func_def_paren_empty         = remove   # ignore/add/remove/force\n\n# Add or remove newline between a function name and the opening '(' in the\n# call.\nnl_func_call_paren              = remove   # ignore/add/remove/force\n\n# Overrides nl_func_call_paren for functions with no parameters.\nnl_func_call_paren_empty        = remove   # ignore/add/remove/force\n\n# Add or remove newline after '(' in a function declaration.\nnl_func_decl_start              = ignore   # ignore/add/remove/force\n\n# Add or remove newline after '(' in a function definition.\nnl_func_def_start               = ignore   # ignore/add/remove/force\n\n# Overrides nl_func_decl_start when there is only one parameter.\nnl_func_decl_start_single       = ignore   # ignore/add/remove/force\n\n# Overrides nl_func_def_start when there is only one parameter.\nnl_func_def_start_single        = ignore   # ignore/add/remove/force\n\n# Whether to add a newline after '(' in a function declaration if '(' and ')'\n# are in different lines. If false, nl_func_decl_start is used instead.\nnl_func_decl_start_multi_line   = false    # true/false\n\n# Whether to add a newline after '(' in a function definition if '(' and ')'\n# are in different lines. If false, nl_func_def_start is used instead.\nnl_func_def_start_multi_line    = false    # true/false\n\n# Add or remove newline after each ',' in a function declaration.\nnl_func_decl_args               = ignore   # ignore/add/remove/force\n\n# Add or remove newline after each ',' in a function definition.\nnl_func_def_args                = ignore   # ignore/add/remove/force\n\n# Add or remove newline after each ',' in a function call.\nnl_func_call_args               = ignore   # ignore/add/remove/force\n\n# Whether to add a newline after each ',' in a function declaration if '('\n# and ')' are in different lines. If false, nl_func_decl_args is used instead.\nnl_func_decl_args_multi_line    = false    # true/false\n\n# Whether to add a newline after each ',' in a function definition if '('\n# and ')' are in different lines. If false, nl_func_def_args is used instead.\nnl_func_def_args_multi_line     = false    # true/false\n\n# Add or remove newline before the ')' in a function declaration.\nnl_func_decl_end                = ignore   # ignore/add/remove/force\n\n# Add or remove newline before the ')' in a function definition.\nnl_func_def_end                 = ignore   # ignore/add/remove/force\n\n# Overrides nl_func_decl_end when there is only one parameter.\nnl_func_decl_end_single         = ignore   # ignore/add/remove/force\n\n# Overrides nl_func_def_end when there is only one parameter.\nnl_func_def_end_single          = ignore   # ignore/add/remove/force\n\n# Whether to add a newline before ')' in a function declaration if '(' and ')'\n# are in different lines. If false, nl_func_decl_end is used instead.\nnl_func_decl_end_multi_line     = false    # true/false\n\n# Whether to add a newline before ')' in a function definition if '(' and ')'\n# are in different lines. If false, nl_func_def_end is used instead.\nnl_func_def_end_multi_line      = false    # true/false\n\n# Add or remove newline between '()' in a function declaration.\nnl_func_decl_empty              = ignore   # ignore/add/remove/force\n\n# Add or remove newline between '()' in a function definition.\nnl_func_def_empty               = ignore   # ignore/add/remove/force\n\n# Add or remove newline between '()' in a function call.\nnl_func_call_empty              = ignore   # ignore/add/remove/force\n\n# Whether to add a newline after '(' in a function call,\n# has preference over nl_func_call_start_multi_line.\nnl_func_call_start              = ignore   # ignore/add/remove/force\n\n# Whether to add a newline before ')' in a function call.\nnl_func_call_end                = ignore   # ignore/add/remove/force\n\n# Whether to add a newline after '(' in a function call if '(' and ')' are in\n# different lines.\nnl_func_call_start_multi_line   = false    # true/false\n\n# Whether to add a newline after each ',' in a function call if '(' and ')'\n# are in different lines.\nnl_func_call_args_multi_line    = false    # true/false\n\n# Whether to add a newline before ')' in a function call if '(' and ')' are in\n# different lines.\nnl_func_call_end_multi_line     = false    # true/false\n\n# Whether to respect nl_func_call_XXX option in case of closure args.\nnl_func_call_args_multi_line_ignore_closures = false    # true/false\n\n# Whether to add a newline after '<' of a template parameter list.\nnl_template_start               = false    # true/false\n\n# Whether to add a newline after each ',' in a template parameter list.\nnl_template_args                = false    # true/false\n\n# Whether to add a newline before '>' of a template parameter list.\nnl_template_end                 = false    # true/false\n\n# (OC) Whether to put each Objective-C message parameter on a separate line.\n# See nl_oc_msg_leave_one_liner.\nnl_oc_msg_args                  = false    # true/false\n\n# (OC) Minimum number of Objective-C message parameters before applying nl_oc_msg_args.\nnl_oc_msg_args_min_params       = 0        # unsigned number\n\n# (OC) Max code width of Objective-C message before applying nl_oc_msg_args.\nnl_oc_msg_args_max_code_width   = 0        # unsigned number\n\n# (OC) Whether to apply nl_oc_msg_args if some of the parameters are already\n# on new lines. Overrides nl_oc_msg_args_min_params and nl_oc_msg_args_max_code_width.\nnl_oc_msg_args_finish_multi_line = false    # true/false\n\n# Add or remove newline between function signature and '{'.\nnl_fdef_brace                   = add   # ignore/add/remove/force\n\n# Add or remove newline between function signature and '{',\n# if signature ends with ')'. Overrides nl_fdef_brace.\nnl_fdef_brace_cond              = ignore   # ignore/add/remove/force\n\n# Add or remove newline between C++11 lambda signature and '{'.\nnl_cpp_ldef_brace               = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'return' and the return expression.\nnl_return_expr                  = ignore   # ignore/add/remove/force\n\n# Add or remove newline between 'throw' and the throw expression.\nnl_throw_expr                   = ignore   # ignore/add/remove/force\n\n# Whether to add a newline after semicolons, except in 'for' statements.\nnl_after_semicolon              = false    # true/false\n\n# (Java) Add or remove newline between the ')' and '{{' of the double brace\n# initializer.\nnl_paren_dbrace_open            = ignore   # ignore/add/remove/force\n\n# Whether to add a newline after the type in an unnamed temporary\n# direct-list-initialization, better:\n# before a direct-list-initialization.\nnl_type_brace_init_lst          = ignore   # ignore/add/remove/force\n\n# Whether to add a newline after the open brace in an unnamed temporary\n# direct-list-initialization.\nnl_type_brace_init_lst_open     = ignore   # ignore/add/remove/force\n\n# Whether to add a newline before the close brace in an unnamed temporary\n# direct-list-initialization.\nnl_type_brace_init_lst_close    = ignore   # ignore/add/remove/force\n\n# Whether to add a newline before '{'.\nnl_before_brace_open            = false    # true/false\n\n# Whether to add a newline after '{'.\nnl_after_brace_open             = false    # true/false\n\n# Whether to add a newline between the open brace and a trailing single-line\n# comment. Requires nl_after_brace_open=true.\nnl_after_brace_open_cmt         = false    # true/false\n\n# Whether to add a newline after a virtual brace open with a non-empty body.\n# These occur in un-braced if/while/do/for statement bodies.\nnl_after_vbrace_open            = false    # true/false\n\n# Whether to add a newline after a virtual brace open with an empty body.\n# These occur in un-braced if/while/do/for statement bodies.\nnl_after_vbrace_open_empty      = false    # true/false\n\n# Whether to add a newline after '}'. Does not apply if followed by a\n# necessary ';'.\nnl_after_brace_close            = false    # true/false\n\n# Whether to add a newline after a virtual brace close,\n# as in 'if (foo) a++; <here> return;'.\nnl_after_vbrace_close           = false    # true/false\n\n# Add or remove newline between the close brace and identifier,\n# as in 'struct { int a; } <here> b;'. Affects enumerations, unions and\n# structures. If set to ignore, uses nl_after_brace_close.\nnl_brace_struct_var             = ignore   # ignore/add/remove/force\n\n# Whether to add a newline before/after each '&&' or `||` on the same\n# nesting level in a boolean expression if boolean expression will not\n# fit on a line. code_width needs to be positive and pos_bool needs\n# to be 'lead' or 'trail' for this option to have any effect.\nnl_bool_expr_hierarchical       = false    # true/false\n\n# Whether to alter newlines in '#define' macros.\nnl_define_macro                 = false    # true/false\n\n# Whether to alter newlines between consecutive parenthesis closes. The number\n# of closing parentheses in a line will depend on respective open parenthesis\n# lines.\nnl_squeeze_paren_close          = false    # true/false\n\n# Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and\n# '#endif'. Does not affect top-level #ifdefs.\nnl_squeeze_ifdef                = false    # true/false\n\n# Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well.\nnl_squeeze_ifdef_top_level      = false    # true/false\n\n# Add or remove blank line before 'if'.\nnl_before_if                    = ignore   # ignore/add/remove/force\n\n# Add or remove blank line after 'if' statement. Add/Force work only if the\n# next token is not a closing brace.\nnl_after_if                     = ignore   # ignore/add/remove/force\n\n# Add or remove blank line before 'for'.\nnl_before_for                   = ignore   # ignore/add/remove/force\n\n# Add or remove blank line after 'for' statement.\nnl_after_for                    = ignore   # ignore/add/remove/force\n\n# Add or remove blank line before 'while'.\nnl_before_while                 = ignore   # ignore/add/remove/force\n\n# Add or remove blank line after 'while' statement.\nnl_after_while                  = ignore   # ignore/add/remove/force\n\n# Add or remove blank line before 'switch'.\nnl_before_switch                = ignore   # ignore/add/remove/force\n\n# Add or remove blank line after 'switch' statement.\nnl_after_switch                 = ignore   # ignore/add/remove/force\n\n# Add or remove blank line before 'synchronized'.\nnl_before_synchronized          = ignore   # ignore/add/remove/force\n\n# Add or remove blank line after 'synchronized' statement.\nnl_after_synchronized           = ignore   # ignore/add/remove/force\n\n# Add or remove blank line before 'do'.\nnl_before_do                    = ignore   # ignore/add/remove/force\n\n# Add or remove blank line after 'do/while' statement.\nnl_after_do                     = ignore   # ignore/add/remove/force\n\n# Ignore nl_before_{if,for,switch,do,synchronized} if the control\n# statement is immediately after a case statement.\n# if nl_before_{if,for,switch,do} is set to remove, this option\n# does nothing.\nnl_before_ignore_after_case     = false    # true/false\n\n# Whether to put a blank line before 'return' statements, unless after an open\n# brace.\nnl_before_return                = false    # true/false\n\n# Whether to put a blank line after 'return' statements, unless followed by a\n# close brace.\nnl_after_return                 = false    # true/false\n\n# Whether to put a blank line before a member '.' or '->' operators.\nnl_before_member                = ignore   # ignore/add/remove/force\n\n# (Java) Whether to put a blank line after a member '.' or '->' operators.\nnl_after_member                 = ignore   # ignore/add/remove/force\n\n# Whether to double-space commented-entries in 'struct'/'union'/'enum'.\nnl_ds_struct_enum_cmt           = false    # true/false\n\n# Whether to force a newline before '}' of a 'struct'/'union'/'enum'.\n# (Lower priority than eat_blanks_before_close_brace.)\nnl_ds_struct_enum_close_brace   = false    # true/false\n\n# Add or remove newline before or after (depending on pos_class_colon) a class\n# colon, as in 'class Foo <here> : <or here> public Bar'.\nnl_class_colon                  = ignore   # ignore/add/remove/force\n\n# Add or remove newline around a class constructor colon. The exact position\n# depends on nl_constr_init_args, pos_constr_colon and pos_constr_comma.\nnl_constr_colon                 = ignore   # ignore/add/remove/force\n\n# Whether to collapse a two-line namespace, like 'namespace foo\\n{ decl; }'\n# into a single line. If true, prevents other brace newline rules from turning\n# such code into four lines. If true, it also preserves one-liner namespaces.\nnl_namespace_two_to_one_liner   = false    # true/false\n\n# Whether to remove a newline in simple unbraced if statements, turning them\n# into one-liners, as in 'if(b)\\n i++;' => 'if(b) i++;'.\nnl_create_if_one_liner          = false    # true/false\n\n# Whether to remove a newline in simple unbraced for statements, turning them\n# into one-liners, as in 'for (...)\\n stmt;' => 'for (...) stmt;'.\nnl_create_for_one_liner         = false    # true/false\n\n# Whether to remove a newline in simple unbraced while statements, turning\n# them into one-liners, as in 'while (expr)\\n stmt;' => 'while (expr) stmt;'.\nnl_create_while_one_liner       = false    # true/false\n\n# Whether to collapse a function definition whose body (not counting braces)\n# is only one line so that the entire definition (prototype, braces, body) is\n# a single line.\nnl_create_func_def_one_liner    = false    # true/false\n\n# Whether to split one-line simple list definitions into three lines by\n# adding newlines, as in 'int a[12] = { <here> 0 <here> };'.\nnl_create_list_one_liner        = false    # true/false\n\n# Whether to split one-line simple unbraced if statements into two lines by\n# adding a newline, as in 'if(b) <here> i++;'.\nnl_split_if_one_liner           = true    # true/false\n\n# Whether to split one-line simple unbraced for statements into two lines by\n# adding a newline, as in 'for (...) <here> stmt;'.\nnl_split_for_one_liner          = true    # true/false\n\n# Whether to split one-line simple unbraced while statements into two lines by\n# adding a newline, as in 'while (expr) <here> stmt;'.\nnl_split_while_one_liner        = true    # true/false\n\n# Don't add a newline before a cpp-comment in a parameter list of a function\n# call.\ndonot_add_nl_before_cpp_comment = false    # true/false\n\n#\n# Blank line options\n#\n\n# The maximum number of consecutive newlines (3 = 2 blank lines).\nnl_max                          = 0        # unsigned number\n\n# The maximum number of consecutive newlines in a function.\nnl_max_blank_in_func            = 0        # unsigned number\n\n# The number of newlines inside an empty function body.\n# This option overrides eat_blanks_after_open_brace and\n# eat_blanks_before_close_brace, but is ignored when\n# nl_collapse_empty_body_functions=true\nnl_inside_empty_func            = 0        # unsigned number\n\n# The number of newlines before a function prototype.\nnl_before_func_body_proto       = 0        # unsigned number\n\n# The number of newlines before a multi-line function definition. Where\n# applicable, this option is overridden with eat_blanks_after_open_brace=true\nnl_before_func_body_def         = 3        # unsigned number\n\n# The number of newlines before a class constructor/destructor prototype.\nnl_before_func_class_proto      = 0        # unsigned number\n\n# The number of newlines before a class constructor/destructor definition.\nnl_before_func_class_def        = 0        # unsigned number\n\n# The number of newlines after a function prototype.\nnl_after_func_proto             = 0        # unsigned number\n\n# The number of newlines after a function prototype, if not followed by\n# another function prototype.\nnl_after_func_proto_group       = 0        # unsigned number\n\n# The number of newlines after a class constructor/destructor prototype.\nnl_after_func_class_proto       = 0        # unsigned number\n\n# The number of newlines after a class constructor/destructor prototype,\n# if not followed by another constructor/destructor prototype.\nnl_after_func_class_proto_group = 0        # unsigned number\n\n# Whether one-line method definitions inside a class body should be treated\n# as if they were prototypes for the purposes of adding newlines.\n#\n# Requires nl_class_leave_one_liners=true. Overrides nl_before_func_body_def\n# and nl_before_func_class_def for one-liners.\nnl_class_leave_one_liner_groups = false    # true/false\n\n# The number of newlines after '}' of a multi-line function body.\n#\n# Overrides nl_min_after_func_body and nl_max_after_func_body.\nnl_after_func_body              = 0        # unsigned number\n\n# The minimum number of newlines after '}' of a multi-line function body.\n#\n# Only works when nl_after_func_body is 0.\nnl_min_after_func_body          = 0        # unsigned number\n\n# The maximum number of newlines after '}' of a multi-line function body.\n#\n# Only works when nl_after_func_body is 0.\n# Takes precedence over nl_min_after_func_body.\nnl_max_after_func_body          = 0        # unsigned number\n\n# The number of newlines after '}' of a multi-line function body in a class\n# declaration. Also affects class constructors/destructors.\n#\n# Overrides nl_after_func_body.\nnl_after_func_body_class        = 0        # unsigned number\n\n# The number of newlines after '}' of a single line function body. Also\n# affects class constructors/destructors.\n#\n# Overrides nl_after_func_body and nl_after_func_body_class.\nnl_after_func_body_one_liner    = 0        # unsigned number\n\n# The number of newlines before a block of typedefs. If nl_after_access_spec\n# is non-zero, that option takes precedence.\n#\n# 0: No change (default).\nnl_typedef_blk_start            = 0        # unsigned number\n\n# The number of newlines after a block of typedefs.\n#\n# 0: No change (default).\nnl_typedef_blk_end              = 0        # unsigned number\n\n# The maximum number of consecutive newlines within a block of typedefs.\n#\n# 0: No change (default).\nnl_typedef_blk_in               = 0        # unsigned number\n\n# The minimum number of blank lines after a block of variable definitions\n# at the top of a function body. If any preprocessor directives appear\n# between the opening brace of the function and the variable block, then\n# it is considered as not at the top of the function.Newlines are added\n# before trailing preprocessor directives, if any exist.\n#\n# 0: No change (default).\nnl_var_def_blk_end_func_top     = 0        # unsigned number\n\n# The minimum number of empty newlines before a block of variable definitions\n# not at the top of a function body. If nl_after_access_spec is non-zero,\n# that option takes precedence. Newlines are not added at the top of the\n# file or just after an opening brace. Newlines are added above any\n# preprocessor directives before the block.\n#\n# 0: No change (default).\nnl_var_def_blk_start            = 0        # unsigned number\n\n# The minimum number of empty newlines after a block of variable definitions\n# not at the top of a function body. Newlines are not added if the block\n# is at the bottom of the file or just before a preprocessor directive.\n#\n# 0: No change (default).\nnl_var_def_blk_end              = 0        # unsigned number\n\n# The maximum number of consecutive newlines within a block of variable\n# definitions.\n#\n# 0: No change (default).\nnl_var_def_blk_in               = 0        # unsigned number\n\n# The minimum number of newlines before a multi-line comment.\n# Doesn't apply if after a brace open or another multi-line comment.\nnl_before_block_comment         = 0        # unsigned number\n\n# The minimum number of newlines before a single-line C comment.\n# Doesn't apply if after a brace open or other single-line C comments.\nnl_before_c_comment             = 0        # unsigned number\n\n# The minimum number of newlines before a CPP comment.\n# Doesn't apply if after a brace open or other CPP comments.\nnl_before_cpp_comment           = 0        # unsigned number\n\n# Whether to force a newline after a multi-line comment.\nnl_after_multiline_comment      = false    # true/false\n\n# Whether to force a newline after a label's colon.\nnl_after_label_colon            = false    # true/false\n\n# The number of newlines before a struct definition.\nnl_before_struct                = 0        # unsigned number\n\n# The number of newlines after '}' or ';' of a struct/enum/union definition.\nnl_after_struct                 = 0        # unsigned number\n\n# The number of newlines before a class definition.\nnl_before_class                 = 0        # unsigned number\n\n# The number of newlines after '}' or ';' of a class definition.\nnl_after_class                  = 0        # unsigned number\n\n# The number of newlines before a namespace.\nnl_before_namespace             = 0        # unsigned number\n\n# The number of newlines after '{' of a namespace. This also adds newlines\n# before the matching '}'.\n#\n# 0: Apply eat_blanks_after_open_brace or eat_blanks_before_close_brace if\n#     applicable, otherwise no change.\n#\n# Overrides eat_blanks_after_open_brace and eat_blanks_before_close_brace.\nnl_inside_namespace             = 0        # unsigned number\n\n# The number of newlines after '}' of a namespace.\nnl_after_namespace              = 0        # unsigned number\n\n# The number of newlines before an access specifier label. This also includes\n# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count\n# if after a brace open.\n#\n# 0: No change (default).\nnl_before_access_spec           = 0        # unsigned number\n\n# The number of newlines after an access specifier label. This also includes\n# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count\n# if after a brace open.\n#\n# 0: No change (default).\n#\n# Overrides nl_typedef_blk_start and nl_var_def_blk_start.\nnl_after_access_spec            = 0        # unsigned number\n\n# The number of newlines between a function definition and the function\n# comment, as in '// comment\\n <here> void foo() {...}'.\n#\n# 0: No change (default).\nnl_comment_func_def             = 0        # unsigned number\n\n# The number of newlines after a try-catch-finally block that isn't followed\n# by a brace close.\n#\n# 0: No change (default).\nnl_after_try_catch_finally      = 0        # unsigned number\n\n# (C#) The number of newlines before and after a property, indexer or event\n# declaration.\n#\n# 0: No change (default).\nnl_around_cs_property           = 0        # unsigned number\n\n# (C#) The number of newlines between the get/set/add/remove handlers.\n#\n# 0: No change (default).\nnl_between_get_set              = 0        # unsigned number\n\n# (C#) Add or remove newline between property and the '{'.\nnl_property_brace               = ignore   # ignore/add/remove/force\n\n# Whether to remove blank lines after '{'.\neat_blanks_after_open_brace     = false    # true/false\n\n# Whether to remove blank lines before '}'.\neat_blanks_before_close_brace   = false    # true/false\n\n# How aggressively to remove extra newlines not in preprocessor.\n#\n# 0: No change (default)\n# 1: Remove most newlines not handled by other config\n# 2: Remove all newlines and reformat completely by config\nnl_remove_extra_newlines        = 0        # unsigned number\n\n# (Java) Add or remove newline after an annotation statement. Only affects\n# annotations that are after a newline.\nnl_after_annotation             = ignore   # ignore/add/remove/force\n\n# (Java) Add or remove newline between two annotations.\nnl_between_annotation           = ignore   # ignore/add/remove/force\n\n# The number of newlines before a whole-file #ifdef.\n#\n# 0: No change (default).\nnl_before_whole_file_ifdef      = 0        # unsigned number\n\n# The number of newlines after a whole-file #ifdef.\n#\n# 0: No change (default).\nnl_after_whole_file_ifdef       = 0        # unsigned number\n\n# The number of newlines before a whole-file #endif.\n#\n# 0: No change (default).\nnl_before_whole_file_endif      = 0        # unsigned number\n\n# The number of newlines after a whole-file #endif.\n#\n# 0: No change (default).\nnl_after_whole_file_endif       = 0        # unsigned number\n\n#\n# Positioning options\n#\n\n# The position of arithmetic operators in wrapped expressions.\npos_arith                       = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of assignment in wrapped expressions. Do not affect '='\n# followed by '{'.\npos_assign                      = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of Boolean operators in wrapped expressions.\npos_bool                        = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of comparison operators in wrapped expressions.\npos_compare                     = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of conditional operators, as in the '?' and ':' of\n# 'expr ? stmt : stmt', in wrapped expressions.\npos_conditional                 = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of the comma in wrapped expressions.\npos_comma                       = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of the comma in enum entries.\npos_enum_comma                  = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of the comma in the base class list if there is more than one\n# line. Affects nl_class_init_args.\npos_class_comma                 = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of the comma in the constructor initialization list.\n# Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon.\npos_constr_comma                = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of trailing/leading class colon, between class and base class\n# list. Affects nl_class_colon.\npos_class_colon                 = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of colons between constructor and member initialization.\n# Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma.\npos_constr_colon                = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n# The position of shift operators in wrapped expressions.\npos_shift                       = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force\n\n#\n# Line splitting options\n#\n\n# Try to limit code width to N columns.\ncode_width                      = 0        # unsigned number\n\n# Whether to fully split long 'for' statements at semi-colons.\nls_for_split_full               = false    # true/false\n\n# Whether to fully split long function prototypes/calls at commas.\n# The option ls_code_width has priority over the option ls_func_split_full.\nls_func_split_full              = false    # true/false\n\n# Whether to split lines as close to code_width as possible and ignore some\n# groupings.\n# The option ls_code_width has priority over the option ls_func_split_full.\nls_code_width                   = false    # true/false\n\n#\n# Code alignment options (not left column spaces/tabs)\n#\n\n# Whether to keep non-indenting tabs.\nalign_keep_tabs                 = false    # true/false\n\n# Whether to use tabs for aligning.\nalign_with_tabs                 = false    # true/false\n\n# Whether to bump out to the next tab when aligning.\nalign_on_tabstop                = false    # true/false\n\n# Whether to right-align numbers.\nalign_number_right              = false    # true/false\n\n# Whether to keep whitespace not required for alignment.\nalign_keep_extra_space          = false    # true/false\n\n# Whether to align variable definitions in prototypes and functions.\nalign_func_params               = false    # true/false\n\n# The span for aligning parameter definitions in function on parameter name.\n#\n# 0: Don't align (default).\nalign_func_params_span          = 0        # unsigned number\n\n# The threshold for aligning function parameter definitions.\n# Use a negative number for absolute thresholds.\n#\n# 0: No limit (default).\nalign_func_params_thresh        = 0        # number\n\n# The gap for aligning function parameter definitions.\nalign_func_params_gap           = 0        # unsigned number\n\n# The span for aligning constructor value.\n#\n# 0: Don't align (default).\nalign_constr_value_span         = 0        # unsigned number\n\n# The threshold for aligning constructor value.\n# Use a negative number for absolute thresholds.\n#\n# 0: No limit (default).\nalign_constr_value_thresh       = 0        # number\n\n# The gap for aligning constructor value.\nalign_constr_value_gap          = 0        # unsigned number\n\n# Whether to align parameters in single-line functions that have the same\n# name. The function names must already be aligned with each other.\nalign_same_func_call_params     = false    # true/false\n\n# The span for aligning function-call parameters for single line functions.\n#\n# 0: Don't align (default).\nalign_same_func_call_params_span = 0        # unsigned number\n\n# The threshold for aligning function-call parameters for single line\n# functions.\n# Use a negative number for absolute thresholds.\n#\n# 0: No limit (default).\nalign_same_func_call_params_thresh = 0        # number\n\n# The span for aligning variable definitions.\n#\n# 0: Don't align (default).\nalign_var_def_span              = 0        # unsigned number\n\n# How to consider (or treat) the '*' in the alignment of variable definitions.\n#\n# 0: Part of the type     'void *   foo;' (default)\n# 1: Part of the variable 'void     *foo;'\n# 2: Dangling             'void    *foo;'\n# Dangling: the '*' will not be taken into account when aligning.\nalign_var_def_star_style        = 0        # unsigned number\n\n# How to consider (or treat) the '&' in the alignment of variable definitions.\n#\n# 0: Part of the type     'long &   foo;' (default)\n# 1: Part of the variable 'long     &foo;'\n# 2: Dangling             'long    &foo;'\n# Dangling: the '&' will not be taken into account when aligning.\nalign_var_def_amp_style         = 0        # unsigned number\n\n# The threshold for aligning variable definitions.\n# Use a negative number for absolute thresholds.\n#\n# 0: No limit (default).\nalign_var_def_thresh            = 0        # number\n\n# The gap for aligning variable definitions.\nalign_var_def_gap               = 0        # unsigned number\n\n# Whether to align the colon in struct bit fields.\nalign_var_def_colon             = false    # true/false\n\n# The gap for aligning the colon in struct bit fields.\nalign_var_def_colon_gap         = 0        # unsigned number\n\n# Whether to align any attribute after the variable name.\nalign_var_def_attribute         = false    # true/false\n\n# Whether to align inline struct/enum/union variable definitions.\nalign_var_def_inline            = false    # true/false\n\n# The span for aligning on '=' in assignments.\n#\n# 0: Don't align (default).\nalign_assign_span               = 0        # unsigned number\n\n# The span for aligning on '=' in function prototype modifier.\n#\n# 0: Don't align (default).\nalign_assign_func_proto_span    = 0        # unsigned number\n\n# The threshold for aligning on '=' in assignments.\n# Use a negative number for absolute thresholds.\n#\n# 0: No limit (default).\nalign_assign_thresh             = 0        # number\n\n# Whether to align on the left most assignment when multiple\n# definitions are found on the same line.\n# Depends on 'align_assign_span' and 'align_assign_thresh' settings.\nalign_assign_on_multi_var_defs  = false    # true/false\n\n# The span for aligning on '{' in braced init list.\n#\n# 0: Don't align (default).\nalign_braced_init_list_span     = 0        # unsigned number\n\n# The threshold for aligning on '{' in braced init list.\n# Use a negative number for absolute thresholds.\n#\n# 0: No limit (default).\nalign_braced_init_list_thresh   = 0        # number\n\n# How to apply align_assign_span to function declaration \"assignments\", i.e.\n# 'virtual void foo() = 0' or '~foo() = {default|delete}'.\n#\n# 0: Align with other assignments (default)\n# 1: Align with each other, ignoring regular assignments\n# 2: Don't align\nalign_assign_decl_func          = 0        # unsigned number\n\n# The span for aligning on '=' in enums.\n#\n# 0: Don't align (default).\nalign_enum_equ_span             = 0        # unsigned number\n\n# The threshold for aligning on '=' in enums.\n# Use a negative number for absolute thresholds.\n#\n# 0: no limit (default).\nalign_enum_equ_thresh           = 0        # number\n\n# The span for aligning class member definitions.\n#\n# 0: Don't align (default).\nalign_var_class_span            = 0        # unsigned number\n\n# The threshold for aligning class member definitions.\n# Use a negative number for absolute thresholds.\n#\n# 0: No limit (default).\nalign_var_class_thresh          = 0        # number\n\n# The gap for aligning class member definitions.\nalign_var_class_gap             = 0        # unsigned number\n\n# The span for aligning struct/union member definitions.\n#\n# 0: Don't align (default).\nalign_var_struct_span           = 0        # unsigned number\n\n# The threshold for aligning struct/union member definitions.\n# Use a negative number for absolute thresholds.\n#\n# 0: No limit (default).\nalign_var_struct_thresh         = 0        # number\n\n# The gap for aligning struct/union member definitions.\nalign_var_struct_gap            = 0        # unsigned number\n\n# The span for aligning struct initializer values.\n#\n# 0: Don't align (default).\nalign_struct_init_span          = 0        # unsigned number\n\n# The span for aligning single-line typedefs.\n#\n# 0: Don't align (default).\nalign_typedef_span              = 0        # unsigned number\n\n# The minimum space between the type and the synonym of a typedef.\nalign_typedef_gap               = 0        # unsigned number\n\n# How to align typedef'd functions with other typedefs.\n#\n# 0: Don't mix them at all (default)\n# 1: Align the open parenthesis with the types\n# 2: Align the function type name with the other type names\nalign_typedef_func              = 0        # unsigned number\n\n# How to consider (or treat) the '*' in the alignment of typedefs.\n#\n# 0: Part of the typedef type, 'typedef int * pint;' (default)\n# 1: Part of type name:        'typedef int   *pint;'\n# 2: Dangling:                 'typedef int  *pint;'\n# Dangling: the '*' will not be taken into account when aligning.\nalign_typedef_star_style        = 0        # unsigned number\n\n# How to consider (or treat) the '&' in the alignment of typedefs.\n#\n# 0: Part of the typedef type, 'typedef int & intref;' (default)\n# 1: Part of type name:        'typedef int   &intref;'\n# 2: Dangling:                 'typedef int  &intref;'\n# Dangling: the '&' will not be taken into account when aligning.\nalign_typedef_amp_style         = 0        # unsigned number\n\n# The span for aligning comments that end lines.\n#\n# 0: Don't align (default).\nalign_right_cmt_span            = 0        # unsigned number\n\n# Minimum number of columns between preceding text and a trailing comment in\n# order for the comment to qualify for being aligned. Must be non-zero to have\n# an effect.\nalign_right_cmt_gap             = 0        # unsigned number\n\n# If aligning comments, whether to mix with comments after '}' and #endif with\n# less than three spaces before the comment.\nalign_right_cmt_mix             = false    # true/false\n\n# Whether to only align trailing comments that are at the same brace level.\nalign_right_cmt_same_level      = false    # true/false\n\n# Minimum column at which to align trailing comments. Comments which are\n# aligned beyond this column, but which can be aligned in a lesser column,\n# may be \"pulled in\".\n#\n# 0: Ignore (default).\nalign_right_cmt_at_col          = 0        # unsigned number\n\n# The span for aligning function prototypes.\n#\n# 0: Don't align (default).\nalign_func_proto_span           = 0        # unsigned number\n\n# Whether to ignore continuation lines when evaluating the number of\n# new lines for the function prototype alignment's span.\n#\n# false: continuation lines are part of the newlines count\n# true:  continuation lines are not counted\nalign_func_proto_span_ignore_cont_lines = false    # true/false\n\n# How to consider (or treat) the '*' in the alignment of function prototypes.\n#\n# 0: Part of the type     'void *   foo();' (default)\n# 1: Part of the function 'void     *foo();'\n# 2: Dangling             'void    *foo();'\n# Dangling: the '*' will not be taken into account when aligning.\nalign_func_proto_star_style     = 0        # unsigned number\n\n# How to consider (or treat) the '&' in the alignment of function prototypes.\n#\n# 0: Part of the type     'long &   foo();' (default)\n# 1: Part of the function 'long     &foo();'\n# 2: Dangling             'long    &foo();'\n# Dangling: the '&' will not be taken into account when aligning.\nalign_func_proto_amp_style      = 0        # unsigned number\n\n# The threshold for aligning function prototypes.\n# Use a negative number for absolute thresholds.\n#\n# 0: No limit (default).\nalign_func_proto_thresh         = 0        # number\n\n# Minimum gap between the return type and the function name.\nalign_func_proto_gap            = 0        # unsigned number\n\n# Whether to align function prototypes on the 'operator' keyword instead of\n# what follows.\nalign_on_operator               = false    # true/false\n\n# Whether to mix aligning prototype and variable declarations. If true,\n# align_var_def_XXX options are used instead of align_func_proto_XXX options.\nalign_mix_var_proto             = false    # true/false\n\n# Whether to align single-line functions with function prototypes.\n# Uses align_func_proto_span.\nalign_single_line_func          = false    # true/false\n\n# Whether to align the open brace of single-line functions.\n# Requires align_single_line_func=true. Uses align_func_proto_span.\nalign_single_line_brace         = false    # true/false\n\n# Gap for align_single_line_brace.\nalign_single_line_brace_gap     = 0        # unsigned number\n\n# (OC) The span for aligning Objective-C message specifications.\n#\n# 0: Don't align (default).\nalign_oc_msg_spec_span          = 0        # unsigned number\n\n# Whether and how to align backslashes that split a macro onto multiple lines.\n# This will not work right if the macro contains a multi-line comment.\n#\n# 0: Do nothing (default)\n# 1: Align the backslashes in the column at the end of the longest line\n# 2: Align with the backslash that is farthest to the left, or, if that\n#    backslash is farther left than the end of the longest line, at the end of\n#    the longest line\n# 3: Align with the backslash that is farthest to the right\nalign_nl_cont                   = 0        # unsigned number\n\n# The minimum number of spaces between the end of a line and its continuation\n# backslash. Requires align_nl_cont.\n#\n# Default: 1\nalign_nl_cont_spaces            = 1        # unsigned number\n\n# Whether to align macro functions and variables together.\nalign_pp_define_together        = false    # true/false\n\n# The span for aligning on '#define' bodies.\n#\n# =0: Don't align (default)\n# >0: Number of lines (including comments) between blocks\nalign_pp_define_span            = 0        # unsigned number\n\n# The minimum space between label and value of a preprocessor define.\nalign_pp_define_gap             = 0        # unsigned number\n\n# Whether to align lines that start with '<<' with previous '<<'.\n#\n# Default: true\nalign_left_shift                = true     # true/false\n\n# Whether to align comma-separated statements following '<<' (as used to\n# initialize Eigen matrices).\nalign_eigen_comma_init          = false    # true/false\n\n# Whether to align text after 'asm volatile ()' colons.\nalign_asm_colon                 = false    # true/false\n\n# (OC) Span for aligning parameters in an Objective-C message call\n# on the ':'.\n#\n# 0: Don't align.\nalign_oc_msg_colon_span         = 0        # unsigned number\n\n# (OC) Whether to always align with the first parameter, even if it is too\n# short.\nalign_oc_msg_colon_first        = false    # true/false\n\n# (OC) Whether to align parameters in an Objective-C '+' or '-' declaration\n# on the ':'.\nalign_oc_decl_colon             = false    # true/false\n\n# (OC) Whether to not align parameters in an Objectve-C message call if first\n# colon is not on next line of the message call (the same way Xcode does\n# alignment)\nalign_oc_msg_colon_xcode_like   = false    # true/false\n\n#\n# Comment modification options\n#\n\n# Try to wrap comments at N columns.\ncmt_width                       = 0        # unsigned number\n\n# How to reflow comments.\n#\n# 0: No reflowing (apart from the line wrapping due to cmt_width) (default)\n# 1: No touching at all\n# 2: Full reflow (enable cmt_indent_multi for indent with line wrapping due to cmt_width)\ncmt_reflow_mode                 = 0        # unsigned number\n\n# Path to a file that contains regular expressions describing patterns for\n# which the end of one line and the beginning of the next will be folded into\n# the same sentence or paragraph during full comment reflow. The regular\n# expressions are described using ECMAScript syntax. The syntax for this\n# specification is as follows, where \"...\" indicates the custom regular\n# expression and \"n\" indicates the nth end_of_prev_line_regex and\n# beg_of_next_line_regex regular expression pair:\n#\n# end_of_prev_line_regex[1] = \"...$\"\n# beg_of_next_line_regex[1] = \"^...\"\n# end_of_prev_line_regex[2] = \"...$\"\n# beg_of_next_line_regex[2] = \"^...\"\n#             .\n#             .\n#             .\n# end_of_prev_line_regex[n] = \"...$\"\n# beg_of_next_line_regex[n] = \"^...\"\n#\n# Note that use of this option overrides the default reflow fold regular\n# expressions, which are internally defined as follows:\n#\n# end_of_prev_line_regex[1] = \"[\\w,\\]\\)]$\"\n# beg_of_next_line_regex[1] = \"^[\\w,\\[\\(]\"\n# end_of_prev_line_regex[2] = \"\\.$\"\n# beg_of_next_line_regex[2] = \"^[A-Z]\"\ncmt_reflow_fold_regex_file      = \"\"         # string\n\n# Whether to indent wrapped lines to the start of the encompassing paragraph\n# during full comment reflow (cmt_reflow_mode = 2). Overrides the value\n# specified by cmt_sp_after_star_cont.\n#\n# Note that cmt_align_doxygen_javadoc_tags overrides this option for\n# paragraphs associated with javadoc tags\ncmt_reflow_indent_to_paragraph_start = false    # true/false\n\n# Whether to convert all tabs to spaces in comments. If false, tabs in\n# comments are left alone, unless used for indenting.\ncmt_convert_tab_to_spaces       = false    # true/false\n\n# Whether to apply changes to multi-line comments, including cmt_width,\n# keyword substitution and leading chars.\n#\n# Default: true\ncmt_indent_multi                = false     # true/false\n\n# Whether to align doxygen javadoc-style tags ('@param', '@return', etc.)\n# and corresponding fields such that groups of consecutive block tags,\n# parameter names, and descriptions align with one another. Overrides that\n# which is specified by the cmt_sp_after_star_cont. If cmt_width > 0, it may\n# be necessary to enable cmt_indent_multi and set cmt_reflow_mode = 2\n# in order to achieve the desired alignment for line-wrapping.\ncmt_align_doxygen_javadoc_tags  = false    # true/false\n\n# The number of spaces to insert after the star and before doxygen\n# javadoc-style tags (@param, @return, etc). Requires enabling\n# cmt_align_doxygen_javadoc_tags. Overrides that which is specified by the\n# cmt_sp_after_star_cont.\n#\n# Default: 1\ncmt_sp_before_doxygen_javadoc_tags = 1        # unsigned number\n\n# Whether to change trailing, single-line c-comments into cpp-comments.\ncmt_trailing_single_line_c_to_cpp = false    # true/false\n\n# Whether to group c-comments that look like they are in a block.\ncmt_c_group                     = false    # true/false\n\n# Whether to put an empty '/*' on the first line of the combined c-comment.\ncmt_c_nl_start                  = false    # true/false\n\n# Whether to add a newline before the closing '*/' of the combined c-comment.\ncmt_c_nl_end                    = false    # true/false\n\n# Whether to change cpp-comments into c-comments.\ncmt_cpp_to_c                    = false    # true/false\n\n# Whether to group cpp-comments that look like they are in a block. Only\n# meaningful if cmt_cpp_to_c=true.\ncmt_cpp_group                   = false    # true/false\n\n# Whether to put an empty '/*' on the first line of the combined cpp-comment\n# when converting to a c-comment.\n#\n# Requires cmt_cpp_to_c=true and cmt_cpp_group=true.\ncmt_cpp_nl_start                = false    # true/false\n\n# Whether to add a newline before the closing '*/' of the combined cpp-comment\n# when converting to a c-comment.\n#\n# Requires cmt_cpp_to_c=true and cmt_cpp_group=true.\ncmt_cpp_nl_end                  = false    # true/false\n\n# Whether to put a star on subsequent comment lines.\ncmt_star_cont                   = false    # true/false\n\n# The number of spaces to insert at the start of subsequent comment lines.\ncmt_sp_before_star_cont         = 0        # unsigned number\n\n# The number of spaces to insert after the star on subsequent comment lines.\ncmt_sp_after_star_cont          = 0        # unsigned number\n\n# For multi-line comments with a '*' lead, remove leading spaces if the first\n# and last lines of the comment are the same length.\n#\n# Default: true\ncmt_multi_check_last            = true     # true/false\n\n# For multi-line comments with a '*' lead, remove leading spaces if the first\n# and last lines of the comment are the same length AND if the length is\n# bigger as the first_len minimum.\n#\n# Default: 4\ncmt_multi_first_len_minimum     = 4        # unsigned number\n\n# Path to a file that contains text to insert at the beginning of a file if\n# the file doesn't start with a C/C++ comment. If the inserted text contains\n# '$(filename)', that will be replaced with the current file's name.\ncmt_insert_file_header          = \"\"         # string\n\n# Path to a file that contains text to insert at the end of a file if the\n# file doesn't end with a C/C++ comment. If the inserted text contains\n# '$(filename)', that will be replaced with the current file's name.\ncmt_insert_file_footer          = \"\"         # string\n\n# Path to a file that contains text to insert before a function definition if\n# the function isn't preceded by a C/C++ comment. If the inserted text\n# contains '$(function)', '$(javaparam)' or '$(fclass)', these will be\n# replaced with, respectively, the name of the function, the javadoc '@param'\n# and '@return' stuff, or the name of the class to which the member function\n# belongs.\ncmt_insert_func_header          = \"\"         # string\n\n# Path to a file that contains text to insert before a class if the class\n# isn't preceded by a C/C++ comment. If the inserted text contains '$(class)',\n# that will be replaced with the class name.\ncmt_insert_class_header         = \"\"         # string\n\n# Path to a file that contains text to insert before an Objective-C message\n# specification, if the method isn't preceded by a C/C++ comment. If the\n# inserted text contains '$(message)' or '$(javaparam)', these will be\n# replaced with, respectively, the name of the function, or the javadoc\n# '@param' and '@return' stuff.\ncmt_insert_oc_msg_header        = \"\"         # string\n\n# Whether a comment should be inserted if a preprocessor is encountered when\n# stepping backwards from a function name.\n#\n# Applies to cmt_insert_oc_msg_header, cmt_insert_func_header and\n# cmt_insert_class_header.\ncmt_insert_before_preproc       = false    # true/false\n\n# Whether a comment should be inserted if a function is declared inline to a\n# class definition.\n#\n# Applies to cmt_insert_func_header.\n#\n# Default: true\ncmt_insert_before_inlines       = true     # true/false\n\n# Whether a comment should be inserted if the function is a class constructor\n# or destructor.\n#\n# Applies to cmt_insert_func_header.\ncmt_insert_before_ctor_dtor     = false    # true/false\n\n#\n# Code modifying options (non-whitespace)\n#\n\n# Add or remove braces on a single-line 'do' statement.\nmod_full_brace_do               = add   # ignore/add/remove/force\n\n# Add or remove braces on a single-line 'for' statement.\nmod_full_brace_for              = add   # ignore/add/remove/force\n\n# (Pawn) Add or remove braces on a single-line function definition.\nmod_full_brace_function         = ignore   # ignore/add/remove/force\n\n# Add or remove braces on a single-line 'if' statement. Braces will not be\n# removed if the braced statement contains an 'else'.\nmod_full_brace_if               = add   # ignore/add/remove/force\n\n# Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either\n# have, or do not have, braces. Overrides mod_full_brace_if.\n#\n# 0: Don't override mod_full_brace_if\n# 1: Add braces to all blocks if any block needs braces and remove braces if\n#    they can be removed from all blocks\n# 2: Add braces to all blocks if any block already has braces, regardless of\n#    whether it needs them\n# 3: Add braces to all blocks if any block needs braces and remove braces if\n#    they can be removed from all blocks, except if all blocks have braces\n#    despite none needing them\nmod_full_brace_if_chain         = 0        # unsigned number\n\n# Whether to add braces to all blocks of an 'if'/'else if'/'else' chain.\n# If true, mod_full_brace_if_chain will only remove braces from an 'if' that\n# does not have an 'else if' or 'else'.\nmod_full_brace_if_chain_only    = false    # true/false\n\n# Add or remove braces on single-line 'while' statement.\nmod_full_brace_while            = add   # ignore/add/remove/force\n\n# Add or remove braces on single-line 'using ()' statement.\nmod_full_brace_using            = ignore   # ignore/add/remove/force\n\n# Don't remove braces around statements that span N newlines\nmod_full_brace_nl               = 0        # unsigned number\n\n# Whether to prevent removal of braces from 'if'/'for'/'while'/etc. blocks\n# which span multiple lines.\n#\n# Affects:\n#   mod_full_brace_for\n#   mod_full_brace_if\n#   mod_full_brace_if_chain\n#   mod_full_brace_if_chain_only\n#   mod_full_brace_while\n#   mod_full_brace_using\n#\n# Does not affect:\n#   mod_full_brace_do\n#   mod_full_brace_function\nmod_full_brace_nl_block_rem_mlcond = false    # true/false\n\n# Add or remove unnecessary parentheses on 'return' statement.\nmod_paren_on_return             = ignore   # ignore/add/remove/force\n\n# Add or remove unnecessary parentheses on 'throw' statement.\nmod_paren_on_throw              = ignore   # ignore/add/remove/force\n\n# (Pawn) Whether to change optional semicolons to real semicolons.\nmod_pawn_semicolon              = false    # true/false\n\n# Whether to fully parenthesize Boolean expressions in 'while' and 'if'\n# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'.\nmod_full_paren_if_bool          = false    # true/false\n\n# Whether to fully parenthesize Boolean expressions after '='\n# statement, as in 'x = a && b > c;' => 'x = (a && (b > c));'.\nmod_full_paren_assign_bool      = false    # true/false\n\n# Whether to fully parenthesize Boolean expressions after '='\n# statement, as in 'return  a && b > c;' => 'return (a && (b > c));'.\nmod_full_paren_return_bool      = false    # true/false\n\n# Whether to remove superfluous semicolons.\nmod_remove_extra_semicolon      = false    # true/false\n\n# Whether to remove duplicate include.\nmod_remove_duplicate_include    = false    # true/false\n\n# the following options (mod_XX_closebrace_comment) use different comment,\n# depending of the setting of the next option.\n# false: Use the c comment (default)\n# true : Use the cpp comment\nmod_add_force_c_closebrace_comment = false    # true/false\n\n# If a function body exceeds the specified number of newlines and doesn't have\n# a comment after the close brace, a comment will be added.\nmod_add_long_function_closebrace_comment = 0        # unsigned number\n\n# If a namespace body exceeds the specified number of newlines and doesn't\n# have a comment after the close brace, a comment will be added.\nmod_add_long_namespace_closebrace_comment = 0        # unsigned number\n\n# If a class body exceeds the specified number of newlines and doesn't have a\n# comment after the close brace, a comment will be added.\nmod_add_long_class_closebrace_comment = 0        # unsigned number\n\n# If a switch body exceeds the specified number of newlines and doesn't have a\n# comment after the close brace, a comment will be added.\nmod_add_long_switch_closebrace_comment = 0        # unsigned number\n\n# If an #ifdef body exceeds the specified number of newlines and doesn't have\n# a comment after the #endif, a comment will be added.\nmod_add_long_ifdef_endif_comment = 0        # unsigned number\n\n# If an #ifdef or #else body exceeds the specified number of newlines and\n# doesn't have a comment after the #else, a comment will be added.\nmod_add_long_ifdef_else_comment = 0        # unsigned number\n\n# Whether to take care of the case by the mod_sort_xx options.\nmod_sort_case_sensitive         = false    # true/false\n\n# Whether to sort consecutive single-line 'import' statements.\nmod_sort_import                 = false    # true/false\n\n# (C#) Whether to sort consecutive single-line 'using' statements.\nmod_sort_using                  = false    # true/false\n\n# Whether to sort consecutive single-line '#include' statements (C/C++) and\n# '#import' statements (Objective-C). Be aware that this has the potential to\n# break your code if your includes/imports have ordering dependencies.\nmod_sort_include                = false    # true/false\n\n# Whether to prioritize '#include' and '#import' statements that contain\n# filename without extension when sorting is enabled.\nmod_sort_incl_import_prioritize_filename = false    # true/false\n\n# Whether to prioritize '#include' and '#import' statements that does not\n# contain extensions when sorting is enabled.\nmod_sort_incl_import_prioritize_extensionless = false    # true/false\n\n# Whether to prioritize '#include' and '#import' statements that contain\n# angle over quotes when sorting is enabled.\nmod_sort_incl_import_prioritize_angle_over_quotes = false    # true/false\n\n# Whether to ignore file extension in '#include' and '#import' statements\n# for sorting comparison.\nmod_sort_incl_import_ignore_extension = false    # true/false\n\n# Whether to group '#include' and '#import' statements when sorting is enabled.\nmod_sort_incl_import_grouping_enabled = false    # true/false\n\n# Whether to move a 'break' that appears after a fully braced 'case' before\n# the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'.\nmod_move_case_break             = false    # true/false\n\n# Whether to move a 'return' that appears after a fully braced 'case' before\n# the close brace, as in 'case X: { ... } return;' => 'case X: { ... return; }'.\nmod_move_case_return            = false    # true/false\n\n# Add or remove braces around a fully braced case statement. Will only remove\n# braces if there are no variable declarations in the block.\nmod_case_brace                  = ignore   # ignore/add/remove/force\n\n# Whether to remove a void 'return;' that appears as the last statement in a\n# function.\nmod_remove_empty_return         = false    # true/false\n\n# Add or remove the comma after the last value of an enumeration.\nmod_enum_last_comma             = add   # ignore/add/remove/force\n\n# Syntax to use for infinite loops.\n#\n# 0: Leave syntax alone (default)\n# 1: Rewrite as `for(;;)`\n# 2: Rewrite as `while(true)`\n# 3: Rewrite as `do`...`while(true);`\n# 4: Rewrite as `while(1)`\n# 5: Rewrite as `do`...`while(1);`\n#\n# Infinite loops that do not already match one of these syntaxes are ignored.\n# Other options that affect loop formatting will be applied after transforming\n# the syntax.\nmod_infinite_loop               = 0        # unsigned number\n\n# Add or remove the 'int' keyword in 'int short'.\nmod_int_short                   = ignore   # ignore/add/remove/force\n\n# Add or remove the 'int' keyword in 'short int'.\nmod_short_int                   = ignore   # ignore/add/remove/force\n\n# Add or remove the 'int' keyword in 'int long'.\nmod_int_long                    = ignore   # ignore/add/remove/force\n\n# Add or remove the 'int' keyword in 'long int'.\nmod_long_int                    = ignore   # ignore/add/remove/force\n\n# Add or remove the 'int' keyword in 'int signed'.\nmod_int_signed                  = ignore   # ignore/add/remove/force\n\n# Add or remove the 'int' keyword in 'signed int'.\nmod_signed_int                  = ignore   # ignore/add/remove/force\n\n# Add or remove the 'int' keyword in 'int unsigned'.\nmod_int_unsigned                = ignore   # ignore/add/remove/force\n\n# Add or remove the 'int' keyword in 'unsigned int'.\nmod_unsigned_int                = ignore   # ignore/add/remove/force\n\n# If there is a situation where mod_int_* and mod_*_int would result in\n# multiple int keywords, whether to keep the rightmost int (the default) or the\n# leftmost int.\nmod_int_prefer_int_on_left      = false    # true/false\n\n# (OC) Whether to organize the properties. If true, properties will be\n# rearranged according to the mod_sort_oc_property_*_weight factors.\nmod_sort_oc_properties          = false    # true/false\n\n# (OC) Weight of a class property modifier.\nmod_sort_oc_property_class_weight = 0        # number\n\n# (OC) Weight of 'atomic' and 'nonatomic'.\nmod_sort_oc_property_thread_safe_weight = 0        # number\n\n# (OC) Weight of 'readwrite' when organizing properties.\nmod_sort_oc_property_readwrite_weight = 0        # number\n\n# (OC) Weight of a reference type specifier ('retain', 'copy', 'assign',\n# 'weak', 'strong') when organizing properties.\nmod_sort_oc_property_reference_weight = 0        # number\n\n# (OC) Weight of getter type ('getter=') when organizing properties.\nmod_sort_oc_property_getter_weight = 0        # number\n\n# (OC) Weight of setter type ('setter=') when organizing properties.\nmod_sort_oc_property_setter_weight = 0        # number\n\n# (OC) Weight of nullability type ('nullable', 'nonnull', 'null_unspecified',\n# 'null_resettable') when organizing properties.\nmod_sort_oc_property_nullability_weight = 0        # number\n\n#\n# Preprocessor options\n#\n\n# How to use tabs when indenting preprocessor code.\n#\n# -1: Use 'indent_with_tabs' setting (default)\n#  0: Spaces only\n#  1: Indent with tabs to brace level, align with spaces\n#  2: Indent and align with tabs, using spaces when not on a tabstop\n#\n# Default: -1\npp_indent_with_tabs             = -1       # number\n\n# Add or remove indentation of preprocessor directives inside #if blocks\n# at brace level 0 (file-level).\npp_indent                       = remove   # ignore/add/remove/force\n\n# Whether to indent #if/#else/#endif at the brace level. If false, these are\n# indented from column 1.\npp_indent_at_level              = false    # true/false\n\n# Whether to indent #if/#else/#endif at the parenthesis level if the brace\n# level is 0. If false, these are indented from column 1.\npp_indent_at_level0             = false    # true/false\n\n# Specifies the number of columns to indent preprocessors per level\n# at brace level 0 (file-level). If pp_indent_at_level=false, also specifies\n# the number of columns to indent preprocessors per level\n# at brace level > 0 (function-level).\n#\n# Default: 1\npp_indent_count                 = 1        # unsigned number\n\n# Add or remove space after # based on pp level of #if blocks.\npp_space_after                  = ignore   # ignore/add/remove/force\n\n# Sets the number of spaces per level added with pp_space_after.\npp_space_count                  = 0        # unsigned number\n\n# The indent for '#region' and '#endregion' in C# and '#pragma region' in\n# C/C++. Negative values decrease indent down to the first column.\npp_indent_region                = 0        # number\n\n# Whether to indent the code between #region and #endregion.\npp_region_indent_code           = false    # true/false\n\n# If pp_indent_at_level=true, sets the indent for #if, #else and #endif when\n# not at file-level. Negative values decrease indent down to the first column.\n#\n# =0: Indent preprocessors using output_tab_size\n# >0: Column at which all preprocessors will be indented\npp_indent_if                    = 0        # number\n\n# Whether to indent the code between #if, #else and #endif.\npp_if_indent_code               = false    # true/false\n\n# Whether to indent the body of an #if that encompasses all the code in the file.\npp_indent_in_guard              = false    # true/false\n\n# Whether to indent '#define' at the brace level. If false, these are\n# indented from column 1.\npp_define_at_level              = false    # true/false\n\n# Whether to indent '#include' at the brace level.\npp_include_at_level             = false    # true/false\n\n# Whether to ignore the '#define' body while formatting.\npp_ignore_define_body           = false    # true/false\n\n# An offset value that controls the indentation of the body of a multiline #define.\n# 'body' refers to all the lines of a multiline #define except the first line.\n# Requires 'pp_ignore_define_body = false'.\n#\n#  <0: Absolute column: the body indentation starts off at the specified column\n#      (ex. -3 ==> the body is indented starting from column 3)\n# >=0: Relative to the column of the '#' of '#define'\n#      (ex.  3 ==> the body is indented starting 3 columns at the right of '#')\n#\n# Default: 8\npp_multiline_define_body_indent = 8        # number\n\n# Whether to indent case statements between #if, #else, and #endif.\n# Only applies to the indent of the preprocessor that the case statements\n# directly inside of.\n#\n# Default: true\npp_indent_case                  = true     # true/false\n\n# Whether to indent whole function definitions between #if, #else, and #endif.\n# Only applies to the indent of the preprocessor that the function definition\n# is directly inside of.\n#\n# Default: true\npp_indent_func_def              = true     # true/false\n\n# Whether to indent extern C blocks between #if, #else, and #endif.\n# Only applies to the indent of the preprocessor that the extern block is\n# directly inside of.\n#\n# Default: true\npp_indent_extern                = true     # true/false\n\n# How to indent braces directly inside #if, #else, and #endif.\n# Requires pp_if_indent_code=true and only applies to the indent of the\n# preprocessor that the braces are directly inside of.\n#  0: No extra indent\n#  1: Indent by one level\n# -1: Preserve original indentation\n#\n# Default: 1\npp_indent_brace                 = 1        # number\n\n# Action to perform when unbalanced #if and #else blocks are found.\n# 0: do nothing\n# 1: print a warning message\n# 2: terminate the program with an error (EX_SOFTWARE)\n#\n# The action will be triggered in the following cases:\n# - if an #ifdef block ends on a different indent level than\n#   where it started from. Example:\n#\n#    #ifdef TEST\n#      int i;\n#      {\n#        int j;\n#    #endif\n#\n# - an #elif/#else block ends on a different indent level than\n#   the corresponding #ifdef block. Example:\n#\n#    #ifdef TEST\n#        int i;\n#    #else\n#        }\n#      int j;\n#    #endif\npp_unbalanced_if_action         = 0        # unsigned number\n\n#\n# Sort includes options\n#\n\n# The regex for include category with priority 0.\ninclude_category_0              = \"\"         # string\n\n# The regex for include category with priority 1.\ninclude_category_1              = \"\"         # string\n\n# The regex for include category with priority 2.\ninclude_category_2              = \"\"         # string\n\n#\n# Use or Do not Use options\n#\n\n# true:  indent_func_call_param will be used (default)\n# false: indent_func_call_param will NOT be used\n#\n# Default: true\nuse_indent_func_call_param      = true     # true/false\n\n# The value of the indentation for a continuation line is calculated\n# differently if the statement is:\n# - a declaration: your case with QString fileName ...\n# - an assignment: your case with pSettings = new QSettings( ...\n#\n# At the second case the indentation value might be used twice:\n# - at the assignment\n# - at the function call (if present)\n#\n# To prevent the double use of the indentation value, use this option with the\n# value 'true'.\n#\n# true:  indent_continue will be used only once\n# false: indent_continue will be used every time (default)\n#\n# Requires indent_ignore_first_continue=false.\nuse_indent_continue_only_once   = false    # true/false\n\n# The indentation can be:\n# - after the assignment, at the '[' character\n# - at the beginning of the lambda body\n#\n# true:  indentation will be at the beginning of the lambda body\n# false: indentation will be after the assignment (default)\nindent_cpp_lambda_only_once     = false    # true/false\n\n# Whether sp_after_angle takes precedence over sp_inside_fparen. This was the\n# historic behavior, but is probably not the desired behavior, so this is off\n# by default.\nuse_sp_after_angle_always       = false    # true/false\n\n# Whether to apply special formatting for Qt SIGNAL/SLOT macros. Essentially,\n# this tries to format these so that they match Qt's normalized form (i.e. the\n# result of QMetaObject::normalizedSignature), which can slightly improve the\n# performance of the QObject::connect call, rather than how they would\n# otherwise be formatted.\n#\n# See options_for_QT.cpp for details.\n#\n# Default: true\nuse_options_overriding_for_qt_macros = true     # true/false\n\n# If true: the form feed character is removed from the list of whitespace\n# characters. See https://en.cppreference.com/w/cpp/string/byte/isspace.\nuse_form_feed_no_more_as_whitespace_character = false    # true/false\n\n#\n# Warn levels - 1: error, 2: warning (default), 3: note\n#\n\n# (C#) Warning is given if doing tab-to-\\t replacement and we have found one\n# in a C# verbatim string literal.\n#\n# Default: 2\nwarn_level_tabs_found_in_verbatim_string_literals = 2        # unsigned number\n\n# Limit the number of loops.\n# Used by uncrustify.cpp to exit from infinite loop.\n# 0: no limit.\ndebug_max_number_of_loops       = 0        # number\n\n# Set the number of the line to protocol;\n# Used in the function prot_the_line if the 2. parameter is zero.\n# 0: nothing protocol.\ndebug_line_number_to_protocol   = 0        # number\n\n# Set the number of second(s) before terminating formatting the current file,\n# 0: no timeout.\n# only for linux\ndebug_timeout                   = 0        # number\n\n# Set the number of characters to be printed if the text is too long,\n# 0: do not truncate.\ndebug_truncate                  = 0        # unsigned number\n\n# sort (or not) the tracking info.\n#\n# Default: true\ndebug_sort_the_tracks           = true     # true/false\n\n# decode (or not) the flags as a new line.\n# only if the -p option is set.\ndebug_decode_the_flags          = false    # true/false\n\n# use (or not) the exit(EX_SOFTWARE) function.\n#\n# Default: true\ndebug_use_the_exit_function_pop = true     # true/false\n\n# print (or not) the version in the file defined at the command option -o.\ndebug_print_version             = false    # true/false\n\n# insert the number of the line at the beginning of each line\nset_numbering_for_html_output   = false    # true/false\n\n# Meaning of the settings:\n#   Ignore - do not do any changes\n#   Add    - makes sure there is 1 or more space/brace/newline/etc\n#   Force  - makes sure there is exactly 1 space/brace/newline/etc,\n#            behaves like Add in some contexts\n#   Remove - removes space/brace/newline/etc\n#\n#\n# - Token(s) can be treated as specific type(s) with the 'set' option:\n#     `set tokenType tokenString [tokenString...]`\n#\n#     Example:\n#       `set BOOL __AND__ __OR__`\n#\n#     tokenTypes are defined in src/token_enum.h, use them without the\n#     'CT_' prefix: 'CT_BOOL' => 'BOOL'\n#\n#\n# - Token(s) can be treated as type(s) with the 'type' option.\n#     `type tokenString [tokenString...]`\n#\n#     Example:\n#       `type int c_uint_8 Rectangle`\n#\n#     This can also be achieved with `set TYPE int c_uint_8 Rectangle`\n#\n#\n# To embed whitespace in tokenStrings use the '\\' escape character, or quote\n# the tokenStrings. These quotes are supported: \"'`\n#\n#\n# - Support for the auto detection of languages through the file ending can be\n#   added using the 'file_ext' command.\n#     `file_ext langType langString [langString..]`\n#\n#     Example:\n#       `file_ext CPP .ch .cxx .cpp.in`\n#\n#     langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use\n#     them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP'\n#\n#\n# - Custom macro-based indentation can be set up using 'macro-open',\n#   'macro-else' and 'macro-close'.\n#     `(macro-open | macro-else | macro-close) tokenString`\n#\n#     Example:\n#       `macro-open  BEGIN_TEMPLATE_MESSAGE_MAP`\n#       `macro-open  BEGIN_MESSAGE_MAP`\n#       `macro-close END_MESSAGE_MAP`\n#\n#\n# option(s) with 'not default' value: 0\n#\n"
  },
  {
    "path": "CITATION.cff",
    "content": "cff-version: 1.2.0\nmessage: \"If you use this software, please cite it as below.\"\nauthors:\n- family-names: \"Light\"\n  given-names: \"Roger A.\"\n  orcid: \"https://orcid.org/0000-0001-9218-7797\"\ntitle: \"My Research Software\"\nversion: 2.0.21\ndoi: 10.21105/joss.00265\nurl: \"https://github.com/eclise-mosquitto/mosquitto\"\npreferred-citation:\n  type: article\n  authors:\n  - family-names: \"Light\"\n    given-names: \"Roger A.\"\n    orcid: \"https://orcid.org/0000-0001-9218-7797\"\n  doi: \"10.21105/joss.00265\"\n  journal: \"Journal of Open Source Software\"\n  start: 265\n  title: \"Mosquitto: server and client implementation of the MQTT protocol\"\n  issue: 13\n  volume: 2\n  year: 2017\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "# This is a cmake script. Process it with the CMake gui or command line utility\n# to produce makefiles / Visual Studio project files on Mac OS X and Windows.\n#\n# To configure the build options either use the CMake gui, or run the command\n# line utility including the \"-i\" option.\n\ncmake_minimum_required(VERSION 3.18)\n\nset (VERSION 2.1.2)\nproject(mosquitto\n\tVERSION ${VERSION}\n\tDESCRIPTION \"Eclipse Mosquitto\"\n\tLANGUAGES C\n)\n\nset(CMAKE_C_STANDARD 99)\nset(CMAKE_C_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_EXTENSIONS OFF)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\nlist(APPEND CMAKE_MODULE_PATH \"${PROJECT_SOURCE_DIR}/cmake/\")\n\nadd_definitions (-DCMAKE -DVERSION=\\\"${VERSION}\\\")\n\nif(WIN32)\n\tadd_definitions(\"-D_CRT_SECURE_NO_WARNINGS\")\n\tadd_definitions(\"-D_CRT_NONSTDC_NO_DEPRECATE\")\nendif()\n\nif(APPLE)\n\tset(CMAKE_MODULE_LINKER_FLAGS \"${CMAKE_MODULE_LINKER_FLAGS} -undefined dynamic_lookup\")\nendif()\n\nadd_library(common-options INTERFACE)\nif(NOT WIN32)\n\ttarget_compile_options(common-options INTERFACE -Wall -Wextra -Wconversion)\nendif()\n\ninclude(CMakePushCheckState)\ninclude(CheckIncludeFiles)\ninclude(CheckLibraryExists)\ninclude(CheckSourceCompiles)\ninclude(GNUInstallDirs)\ninclude(CMakePushCheckState)\ninclude(CheckSourceCompiles)\n\noption(WITH_BUNDLED_DEPS \"Build with bundled dependencies?\" ON)\noption(WITH_LIB_CPP \"Build C++ library?\" ON)\noption(WITH_TLS \"Include SSL/TLS support?\" ON)\noption(WITH_TLS_PSK \"Include TLS-PSK support (requires WITH_TLS)?\" ON)\noption(WITH_TESTS \"Enable tests\" ON)\noption(INC_MEMTRACK \"Include memory tracking support?\" ON)\n\nif (WITH_LIB_CPP OR WITH_TESTS)\n\tENABLE_LANGUAGE(CXX)\nendif()\n\nif (WITH_TLS)\n\tfind_package(OpenSSL REQUIRED)\n\tadd_definitions(\"-DWITH_TLS\")\n\n\t# mosquitto uses OpenSSL 1.1 API, so set OPENSSL_API_COMPAT accordingly:\n\t# https://www.openssl.org/docs/manmaster/man7/OPENSSL_API_COMPAT.html\n\t# TODO: migrate off ENGINE API (deprecated since OpenSSL 3.0), see:\n\t#       https://www.openssl.org/docs/manmaster/man7/migration_guide.html#Engines-and-METHOD-APIs\n\tadd_definitions(\"-DOPENSSL_API_COMPAT=0x10100000L\")\n\n\tif (WITH_TLS_PSK)\n\t\tadd_definitions(\"-DWITH_TLS_PSK\")\n\tendif (WITH_TLS_PSK)\nelse()\n\tset (OPENSSL_INCLUDE_DIR \"\")\nendif()\n\noption(WITH_UNIX_SOCKETS \"Include Unix Domain Socket support?\" ON)\nif(WITH_UNIX_SOCKETS)\n\tadd_definitions(\"-DWITH_UNIX_SOCKETS\")\nendif()\n\noption(WITH_SOCKS \"Include SOCKS5 support?\" ON)\nif(WITH_SOCKS)\n\tadd_definitions(\"-DWITH_SOCKS\")\nendif()\n\noption(WITH_WEBSOCKETS \"Include websockets support?\" ON)\noption(WITH_WEBSOCKETS_BUILTIN \"Websockets support uses builtin library? Set OFF to use libwebsockets\" ON)\n\nif(WITH_WEBSOCKETS AND NOT WITH_TLS)\n\tmessage(FATAL_ERROR \"WITH_WEBSOCKETS support requires WITH_TLS.\")\nendif()\noption(WITH_SRV \"Include SRV lookup support?\" OFF)\noption(WITH_STATIC_LIBRARIES \"Build static versions of the libmosquitto/pp libraries?\" OFF)\noption(WITH_PIC \"Build the static library with PIC (Position Independent Code) enabled archives?\" OFF)\n\noption(WITH_THREADING \"Include threading support?\" ON)\nif(WITH_THREADING)\n\tadd_definitions(\"-DWITH_THREADING\")\n\tif(WIN32)\n\t\tfind_package(PThreads4W REQUIRED)\n\tendif()\nendif()\n\noption(WITH_DLT \"Include DLT support?\" OFF)\nmessage(STATUS \"WITH_DLT = ${WITH_DLT}\")\nif(WITH_DLT)\n\tfind_package(PkgConfig)\n\tpkg_check_modules(DLT \"automotive-dlt >= 2.11\" REQUIRED)\nendif()\n\nfind_package(cJSON REQUIRED)\nfind_package(argon2)\n\noption(WITH_CTRL_SHELL \"Include mosquitto_ctrl interactive shell support?\" ON)\nif(WITH_CTRL_SHELL)\n\tfind_package(LineEditing)\nendif()\nif(ARGON2_FOUND)\n\t# Disable until separate password handling thread is implemented\n\t#add_definitions(\"-DWITH_ARGON2\")\nendif()\n\noption(WITH_LTO \"Build with link time optimizations (IPO) / interprocedural optimization (IPO) enabled.\" ON)\nif(WITH_LTO)\n\tinclude(CheckIPOSupported)\n\tcheck_ipo_supported(RESULT is_ipo_supported OUTPUT output)\n\tif(is_ipo_supported)\n\t\tset_property(TARGET common-options PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)\n\telse()\n\t\tmessage(WARNING \"LTO/IPO is not supported: ${output}\")\n\tendif()\nendif()\n\n# ========================================\n# Include projects\n# ========================================\n\noption(WITH_CLIENTS \"Build clients?\" ON)\noption(WITH_BROKER \"Build broker?\" ON)\noption(WITH_APPS \"Build apps?\" ON)\noption(WITH_PLUGINS \"Build plugins?\" ON)\noption(WITH_DOCS \"Build documentation?\" ON)\n\ntarget_sources(common-options INTERFACE config.h)\ntarget_include_directories(common-options\n\tINTERFACE\n\t\t${mosquitto_SOURCE_DIR}\n\t\t${mosquitto_SOURCE_DIR}/include\n)\n\nif(WITH_TLS)\n\ttarget_include_directories(common-options\n\t\tINTERFACE\n\t\t\t\"${OPENSSL_INCLUDE_DIR}\"\n\t)\nendif()\n\nif(WITH_BUNDLED_DEPS)\n\ttarget_include_directories(common-options\n\t\tINTERFACE\n\t\t\t\"${mosquitto_SOURCE_DIR}/deps\"\n\t)\nendif()\n\n\nadd_subdirectory(libcommon)\nadd_subdirectory(lib)\nif(WITH_CLIENTS)\n\tadd_subdirectory(client)\nendif()\n\nif(WITH_BROKER)\n\tadd_subdirectory(src)\nendif()\n\nif(WITH_APPS)\n\tadd_subdirectory(apps)\nendif()\n\nif(WITH_PLUGINS)\n\tadd_subdirectory(plugins)\nendif()\n\nif(WITH_DOCS)\n\tadd_subdirectory(man)\nendif()\n\n# ========================================\n# Install config file\n# ========================================\n\nif(WITH_BROKER)\n\tinstall(FILES mosquitto.conf RENAME mosquitto.conf.example DESTINATION \"${CMAKE_INSTALL_SYSCONFDIR}/mosquitto\")\n\tinstall(FILES aclfile.example pskfile.example pwfile.example DESTINATION \"${CMAKE_INSTALL_SYSCONFDIR}/mosquitto\")\nendif()\n\n# ========================================\n# Install pkg-config files\n# ========================================\n\nconfigure_file(libmosquitto.pc.in libmosquitto.pc @ONLY)\ninstall(FILES \"${CMAKE_CURRENT_BINARY_DIR}/libmosquitto.pc\" DESTINATION \"${CMAKE_INSTALL_LIBDIR}/pkgconfig\")\nconfigure_file(libmosquittopp.pc.in libmosquittopp.pc @ONLY)\ninstall(FILES \"${CMAKE_CURRENT_BINARY_DIR}/libmosquittopp.pc\" DESTINATION \"${CMAKE_INSTALL_LIBDIR}/pkgconfig\")\n\n# ========================================\n# Install headers\n# ========================================\ninstall(\n\tFILES\n\t${mosquitto_SOURCE_DIR}/include/mosquitto.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto_broker.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto_plugin.h\n\t${mosquitto_SOURCE_DIR}/include/mosquittopp.h\n\t${mosquitto_SOURCE_DIR}/include/mqtt_protocol.h\n\tDESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}\"\n)\ninstall(\n\tFILES\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/broker.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/broker_control.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/broker_plugin.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/defs.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_base64.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_cjson.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_file.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_memory.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_password.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_properties.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_random.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_string.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_time.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_topic.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_utf8.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_auth.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_callbacks.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_connect.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_create_delete.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_helpers.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_loop.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_message.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_options.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_publish.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_socks.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_subscribe.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_tls.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_unsubscribe.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquitto_will.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/libmosquittopp.h\n\t${mosquitto_SOURCE_DIR}/include/mosquitto/mqtt_protocol.h\n\tDESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}/mosquitto\"\n)\n\n# ========================================\n# Testing\n# ========================================\nif(WITH_TESTS)\n\tfind_package(GTest REQUIRED)\n\tinclude(GoogleTest)\n\tenable_testing()\n\tadd_subdirectory(test)\nendif()\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Contributing to Mosquitto\n=========================\n\nThank you for your interest in this project.\n\nProject description:\n--------------------\n\nThe Mosquitto project has been created to provide a light weight, open-source\nimplementation, of an MQTT broker to allow new, existing, and emerging\napplications for Machine-to-Machine (M2M) and Internet of Things (IoT).\n\n- <https://mosquitto.org/>\n- <https://projects.eclipse.org/projects/iot.mosquitto>\n\n\nSource\n------\n\nThe Mosquitto code is stored in a git repository.\n\n- https://github.com/eclipse/mosquitto\n\nYou can contribute bugfixes and new features by sending pull requests through GitHub.\n\n\n## Legal\n\nIn order for your contribution to be accepted, it must comply with the Eclipse\nFoundation IP policy.\n\nPlease read the [Eclipse Foundation policy on accepting contributions via Git](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git).\n\n1. Sign the [Eclipse ECA](http://www.eclipse.org/legal/ECA.php)\n    1. Register for an Eclipse Foundation User ID. You can register [here](https://accounts.eclipse.org/user/register).\n    2. Log into the [Accounts Portal](https://accounts.eclipse.org/), and click on the '[Eclipse Contributor Agreement](https://accounts.eclipse.org/user/eca)' link.\n2. Go to your [account settings](https://accounts.eclipse.org/user/edit) and add your GitHub username to your account.\n3. Make sure that you _sign-off_ your Git commits in the following format:\n  ``` Signed-off-by: John Smith <johnsmith@nowhere.com> ``` This is usually at the bottom of the commit message. You can automate this by adding the '-s' flag when you make the commits. e.g.   ```git commit -s -m \"Adding a cool feature\"```\n4. Ensure that the email address that you make your commits with is the same one you used to sign up to the Eclipse Foundation website with.\n\n## Contributing a change\n\n1. [Fork the repository on GitHub](https://github.com/eclipse/mosquitto/fork)\n2. Clone the forked repository onto your computer: ``` git clone\n   https://github.com/<your username>/mosquitto.git ```\n3. If you are adding a new feature, then create a new branch from the latest\n   ```develop``` branch with ```git checkout -b YOUR_BRANCH_NAME\n   origin/develop```\n4. If you are fixing a bug, then create a new branch from the latest\n   ```fixes``` branch with ```git checkout -b YOUR_BRANCH_NAME origin/fixes```\n5. Make your changes\n6. Ensure that all new and existing tests pass.\n7. Commit the changes into the branch: ``` git commit -s ``` Make sure that\n   your commit message is meaningful and describes your changes correctly.\n8. If you have a lot of commits for the change, squash them into a single / few\n   commits.\n9. Push the changes in your branch to your forked repository.\n10. Finally, go to\n\t[https://github.com/eclipse/mosquitto](https://github.com/eclipse/mosquitto)\n\tand create a pull request from your \"YOUR_BRANCH_NAME\" branch to the\n\t```develop``` or ```fixes``` branch as appropriate to request review and\n\tmerge of the commits in your pushed branch.\n\n\nWhat happens next depends on the content of the patch. If it is 100% authored\nby the contributor and is less than 1000 lines (and meets the needs of the\nproject), then it can be pulled into the main repository. If not, more steps\nare required. These are detailed in the\n[legal process poster](http://www.eclipse.org/legal/EclipseLegalProcessPoster.pdf).\n\n\n\nContact:\n--------\n\nContact the project developers via the project's development\n[mailing list](https://dev.eclipse.org/mailman/listinfo/mosquitto-dev).\n\nSearch for bugs:\n----------------\n\nThis project uses [Github](https://github.com/eclipse/mosquitto/issues)\nto track ongoing development and issues.\n\nCreate a new bug:\n-----------------\n\nBe sure to search for existing bugs before you create another one. Remember\nthat contributions are always welcome!\n\n- [Create new Mosquitto bug](https://github.com/eclipse/mosquitto/issues)\n"
  },
  {
    "path": "ChangeLog.txt",
    "content": "2.1.3 - 2026-02-xx\n==================\n\n# Broker\n- Fix MOSQ_EVT_DISCONNECT being called before MOSQ_EVT_ACL_CHECK for the will\n  of that client. Closes #3487.\n- Fix password length not being passed to MOSQ_EVT_BASIC_AUTH events.\n  Closes #3490.\n- Fix incorrect maximum-packet-size restriction for outgoing packets.\n  Closes #3503.\n- Fix incorrect maximum-packet-size restriction for incoming packets.\n  Closes #3515.\n- Fix will messages being incorrectly delayed if a client set\n  session-expiry-interval=0 when using will-delay-interval>0. Closes #3505.\n\n# Common lib:\n- Fix potential crash if reading a file in restricted mode and the group id\n  does not have an entry in /etc/groups. Closes #3498.\n- Fix missing SONAME. Closes #3483.\n\n# Lib\n- Fix mosquitto_loop_start() leaving the mosq struct in an invalid state if\n  thread creation fails. Closes #3496.\n\n# Plugins\n- Fix migrate_to_persist_sqlite.py not base64 decoding message payloads when\n  migrating. Closes #3492.\n\n# Build:\n- Fix test when nbuilding with WITH_EDITLINE=no. Closes #3484.\n- Fix tests when building with WITH_WEBSOCKETS=no. Closes #3502.\n- Fix libmosquitto_static cmake build.\n- Enable WITH_UNIX_SOCKETS on Windows.\n\n\n2.1.2 - 2026-02-09\n==================\n\n# Broker\n- Forbid running with `persistence true` and with a persistence plugin at the\n  same time.\n\n# Build\n- Build fixes for OpenBSD. Closes #3474.\n- Add missing libedit to docker builds. Closes #3476.\n- Fix static/shared linking of libwebsockets under cmake.\n\n\n2.1.1 - 2026-02-04\n==================\n\n# Broker\n\n- Fix PUID/PGID checking for docker\n- Add MOSQUITTO_UNSAFE_ALLOW_SYMLINKS environment variable to allow the\n  restrictions on reading files through symlinks to be lifted in safe\n  environments like kubernetes. Closes #3461.\n- Fix inconsistent disconnect log message format, and add address:port.\n- Fix `plugin`/`global_plugin` option not allowing space characters.\n- Fix $SYS load values not being published initially. Closes #3459.\n- Fix max_connections not being honoured on libwebsockets listeners. This does\n  not affect the built-in websockets support. Closes #3455.\n- Don't enforce receive-maximum, just log a warning. This allows badly\n  behaving clients to be fixed. Closes #3471.\n\n# Plugins\n- Fix incorrect linking of libmosquitto_common.so for the acl and password\n  file plugins. Closes #3460.\n\n# Build\n- Fix building with WITH_TLS=no\n\n\n2.1.0 - 2026-01-29\n==================\n\n# Broker\n\n## Deprecations\n- The acl_file option is deprecated in favour of the acl-file plugin, which is\n  the same code but moved into a plugin. The acl_file option will be removed\n  in 3.0.\n- The password_file option is deprecated in favour of the password-file plugin,\n  which is the same code but moved into a plugin. The password_file option will\n  be removed in 3.0.\n- The per_listener_settings option is deprecated in favour of the new listener\n  specific options. The per_listener_settings option will be removed in 3.0.\n\n## Behaviour changes\n\n- max_packet_size now defaults to 2,000,000 bytes instead of the 256MB MQTT\n  limit. If you are using payloads that will result in a packet larger than\n  this, you need to manually set the option to a value that suits your\n  application.\n- acl_file and password_file will produce an error on invalid input when\n  reloading the config, causing the broker to quit.\n\n## Protocol related\n- Add support for broker created topic aliases. Topics are allocated on a\n  first come first serve basis.\n- Add support for bridges to allow remote brokers to create topic aliases when\n  running in MQTT v5 mode.\n- Enforce receive maximum on MQTT v5.\n- Return protocol error if a client attemps to subscribe to a shared\n  subscription and also sets no-local.\n- Protocol version numbers reported in the log when a client connects now\n  match the MQTT protocol version numbers, not internal Mosquitto values.\n- Send DISCONNECT With session-takeover return code to MQTT v5 clients when a\n  client connects with the same client id. Closes #2340.\n- The `allow_duplicate_messages` now defaults to `true`.\n- Add `accept_protocol_versions` option to allow limiting which MQTT protocol\n  versions are allowed for a particular listener.\n\n## TLS related\n- Add `--tls-keylog` option which can be used to generate a file that can be\n  used by wireshark to decrypt TLS traffic for debugging purposes. Closes #1818.\n- Add `disable_client_cert_date_checks` option to allow expired client\n  certificate to be considered valid.\n- Add `bridge_tls_use_os_certs` option to allow bridges to be easily configured\n  to trust default CA certificates. Closes #2473.\n- Remove support for TLS v1.1 (clients only - it remains available in the\n  broker but is now undocumented)\n- Use openssl provided function for x509 certificate hostname verification,\n  rather than own function.\n\n## Bridge related\n- Add `bridge_receive_maximum` option for MQTT v5.0 bridges.\n- Add `bridge_session_expiry_interval` option for MQTT v5.0 bridges.\n- Bridge reconnection backoff improvements.\n\n## Transport related\n- Add the `websockets_origin` option to allow optional enforcement of origin\n  when a connection attempts an upgrade to WebSockets.\n- Add built-in websockets support that doesn't use libwebsockets. This is the\n  preferred websockets implementation.\n- Add support for X-Forwarded-For header for built in websockets.\n- Add suport for PROXY protocol v1 and v2.\n\n## Platform specific\n- Increase maximum connection count on Windows from 2048 to 8192 where\n  supported. Closes #2122.\n- Allow multiple instances of mosquitto to run as services on Windows. See\n  README-windows.txt.\n- Add kqueue support.\n- Add support for systemd watchdog.\n\n## General\n- Report on what compile time options are enabled. Closes #2193.\n- Performance: reduce memory allocations when sending packets.\n- Log protocol version and ciphers that a client negotiates when connecting.\n- Password salts are now 64 bytes long.\n- Add the `global_plugin` option, which gives global plugin loaded regardless\n  of `per_listener_settings`.\n- Add `global_max_clients` option to allow limiting client sessions globally\n  on the broker.\n- Add `global_max_connections` option to allow limiting client connections globally\n  on the broker.\n- Improve idle performance. The broker now calculates when the next event of\n  interest is, and uses that as the timeout for e.g. `epoll_wait()`. This can\n  reduce the number of process wakeups by 100x on an idle broker.\n- Add more efficient keepalive check.\n- Add support for sending the SIGRTMIN signal to trigger log rotation.\n  Closes #2337.\n- Add `--test-config` option which can be used to test a configuration file\n  before trying to use it in a live broker. Closes #2521.\n- Add support for PUID/PGID environment variables for setting the user/group\n  to drop privileges to. Closes #2441.\n- Report persistence stats when starting.\n- $SYS updates are now aligned to `sys_interval` seconds, meaning that if set\n  to 10, for example, updates will be sent at times matching x0 seconds.\n  Previously update intervals were aligned to the time the broker was started.\n- Add `log_dest android` for logging to the Android logd daemon.\n- Fix some retained topic memory not being cleared immediately after used.\n- Add -q option to allow logging to be disabled at the command line.\n- Log message if a client attempts to connect with TLS to a non-TLS listener.\n- Add `listener_allow_anonymous` option.\n- Add `listener_auto_id_prefix` option.\n- Allow seconds when defining `persistent_client_expiration`.\n\n## Plugin interface\n- Add `mosquitto_topic_matches_sub_with_pattern()`, which can match against\n  subscriptions with `%c` and `%u` patterns for client id / username\n  substitution.\n- Add support for modifying outgoing messages using `MOSQ_EVT_MESSAGE_OUT`.\n- Add `mosquitto_client()` function for retrieving a client struct if that\n  client is connected.\n- Add `MOSQ_ERR_PLUGIN_IGNORE` to allow plugins to register basic auth or acl\n  check callbacks, but still act as though they are not registered. A plugin\n  that wanted to act as a blocklist for certain usernames, but wasn't carrying\n  out authentication could return `MOSQ_ERR_PLUGIN_IGNORE` for usernames not on\n  its blocklist. If no other plugins were configured, the client would be\n  authenticated. Using `MOSQ_ERR_PLUGIN_DEFER` instead would mean the clients\n  would be denied if no other plugins were configured.\n- Add `mosquitto_client_port()` function for plugins.\n- Add `MOSQ_EVT_CONNECT`, to allow plugins to know when a client has\n  successfully authenticated to the broker.\n- Add connection-state example plugin to demonstrate `MOSQ_EVT_CONNECT`.\n- Add `MOSQ_EVT_CLIENT_OFFLINE`, to allow plugins to know when a client with a\n  non-zero session expiry interval has gone offline.\n- Plugins on non-Windows platforms now no longer make their symbols globally\n  available, which means they are self contained.\n- Add support for delayed basic authentication in plugins.\n- Plugins using the `MOSQ_EVT_MESSAGE_WRITE` callback can now return\n  `MOSQ_ERR_QUOTA_EXCEEDED` to have the message be rejected. MQTT v5 clients\n  using QoS 1 or 2 will receive the quota-exceeded reason code in the\n  corresponding PUBACK/PUBREC.\n- `MOSQ_EVT_TICK` is now passed to plugins when `per_listener_settings` is true.\n- Add `mosquitto_sub_matches_acl()`, which can match one topic filter (a\n  subscription) against another topic filter (an ACL).\n- Registration of the `MOSQ_EVT_CONTROL` plugin event is now handled globally\n  across the broker, so only a single plugin can register for a given $CONTROL\n  topic.\n- Add `mosquitto_plugin_set_info()` to allow plugins to tell the broker their\n  name and version.\n- Add builtin $CONTROL/broker/v1 control topic with the `listPlugins`\n  command. This is disabled by default, but can be enabled with the\n  `enable_control_api` option.\n- Plugins no longer need to define `mosquitto_plugin_cleanup()` if they do not\n  need to do any of their own cleanup. Callbacks will be unregistered\n  automatically.\n- Add `mosquitto_set_clientid()` to allow plugins to force a client id for a\n  client.\n- Add `MOSQ_EVT_SUBSCRIBE` and `MOSQ_EVT_UNSUBSCRIBE` events that are called when\n  subscribe/unsubscribes actually succeed. Allow modifying topic and qos.\n- Add `mosquitto_persistence_location()` for plugins to use to find a valid\n  location for storing persistent data.\n- Plugins can now use the `next_s` and `next_ms` members of the tick event data\n  struct to set a minimum interval that the broker will wait before calling the\n  tick callback again.\n- MOSQ_EVT_ACL_CHECK event is now passed message properties where possible.\n\n# Plugins\n- Add acl-file plugin.\n- Add password-file plugin.\n- Add persist-sqlite plugin.\n- Add sparkplug-aware plugin.\n\n# Dynamic security plugin\n- Add ability to deny wildcard subscriptions for a role to the dynsec plugin.\n- The dynamic security plugin now only kicks clients at the start of the next\n  network loop, to give chance for PUBACK/PUBREC to be sent. Closes #2474.\n- The dynamic security plugin now reports client connections in getClient and\n  listClients.\n- The dynamic security plugin now generates an initial configuration if none\n  is present, including a set of default roles.\n- The dynamic security plugin now supports `%c` and `%u` patterns for\n  substituting client id and username respectively, in all ACLs except for\n  subscribeLiteral and unsubscribeLiteral.\n- The dynamic security plugin now supports multiple ways to initialise the\n  first configuration file.\n\n# Client library\n- Add `MOSQ_OPT_DISABLE_SOCKETPAIR` to allow the disabling of the socketpair\n  feature that allows the network thread to be woken from select() by another\n  thread when e.g.  `mosquitto_publish()` is called. This reduces the number of\n  sockets used by each client by two.\n- Add `on_pre_connect()` callback to allow clients to update\n  username/password/TLS parameters before an automatic reconnection.\n- Callbacks no longer block other callbacks, and can be set from within a\n  callback. Closes #2127.\n- Add support for MQTT v5 broker to client topic aliases.\n- Add `mosquitto_topic_matches_sub_with_pattern()`, which can match against\n  subscriptions with `%c` and `%u` patterns for client id / username\n  substitution.\n- Add `mosquitto_sub_matches_acl()`, which can match one topic filter (a\n  subscription) against another topic filter (an ACL).\n- Add `mosquitto_sub_matches_acl_with_pattern()`, which can match one topic\n  filter (a subscription) against another topic filter (an ACL), with `%c` and\n  `%u` patterns for client id / username substitution.\n- Performance: reduce memory allocations when sending packets.\n- Reintroduce threading support for Windows. Closes #1509.\n- `mosquitto_subscribe*()` now returns `MOSQ_ERR_INVAL` if an empty string is\n  passed as a topic filter.\n- `mosquitto_unsubscribe*()` now returns `MOSQ_ERR_INVAL` if an empty string is\n  passed as a topic filter.\n- Add websockets support.\n- `mosquitto_property_read_binary/string/string_pair` will now set the\n  name/value parameter to NULL if the binary/string is empty. This aligns the\n  behaviour with other property functions. Closes #2648.\n- Add `mosquitto_unsubscribe2_v5_callback_set`, which provides a callback that\n  gives access to reason codes for each of the unsubscription requests.\n- Add `mosquitto_property_remove`, for removing properties from property\n  lists.\n- Add `on_ext_auth()` callback to allow handling MQTT v5 extended authentication.\n- Add `mosquitto_ext_auth_continue()` function to continue an MQTT v5 extended\n  authentication.\n- Remove support for TLS v1.1.\n- Use openssl provided function for x509 certificate hostname verification,\n  rather than own function.\n\n# Clients\n\n## General\n- Add `-W` timeout support to Windows.\n- The `--insecure` option now disables all server certificate verification.\n- Add websockets support.\n- Using `-x` now sets the clients to use MQTT v5.0.\n- Fix parsing of IPv6 addresses in socks proxy urls.\n- Add `--tls-keylog` option which can be used to generate a file that can be\n  used by wireshark to decrypt TLS traffic for debugging purposes.\n- Remove support for TLS v1.1.\n- Add `-o` option for all clients loading options from a specific file.\n- Add `--no-tls` option for all clients which disables all TLS options for\n  that instance. This is useful for negating TLS options provided in a config\n  file, or to disable the automatic use of TLS when using port 8883.\n  Closes #2180.\n\n## mosquitto_rr\n- Fix `-f` and `-s` options in mosquitto_rr.\n- Add `--latency` option to mosquitto_rr, for printing the request/response\n  latency.\n- Add `--retain-handling` option.\n\n## mosquitto_sub\n- Fix incorrect output formatting in mosquitto_sub when using field widths\n  with `%x` and `%X` for printing the payload in hex.\n- Add float printing option to mosquitto_sub.\n- mosquitto_sub payload hex output can now be split by fixed field length.\n- Add `--message-rate` option to mosquitto_sub, for printing the count of\n  messages received each second.\n- Add `--retain-handling` option.\n- Add `-w`/`--watch` to mosquitto_sub which means messages will be printed on a\n  fixed line number based on the topic and order in which messages were\n  received. Requires ANSI escape code support in the terminal.\n- mosquitto_sub now only needs `-t` or `-U` to run - this means that `-t` is\n  not required in all situations.\n\n# Apps\n\n## mosquitto_signal\n- Add `mosquitto_signal` for helping send signals to mosquitto on Windows.\n\n## mosquitto_ctrl\n- Add interactive shell mode to mosquitto_ctrl.\n- Add support for `listPlugins` to mosquitto_ctrl.\n- Allow mosquitto_ctrl dynsec module to update passwords in files rather than\n  having to connect to a broker.\n\n## mosquitto_passwd\n- Print messages in mosquitto_passwd when adding/updating passwords.\n  Closes #2544.\n- When creating a new file with `-c`, setting the output filename to a dash `-`\n  will output the result to stdout.\n\n## mosquitto_db_dump\n- Add `--json` output mode do mosquitto_db_dump.\n\n# Build\n- Increased CMake minimal required version to 3.14, which is required for the\n  preinstalled SQLite3 find module.\n- Add an CMake option `WITH_LTO` to enable/disable link time optimization.\n- Set C99 as the explicit, rather than implicit, build standard.\n- cJSON is now a required dependency.\n- Refactored headers for easier discovery.\n- Support for openssl < 3.0 removed.\n\n\n2.0.23 - 2026-01-14\n===================\n\nBroker:\n- Fix handling of disconnected sessions for `per_listener_settings true`\n- Check return values of openssl *_get_ex_data() and *_set_ex_data() to\n  prevent possible crash. This could occur only in extremely unlikely\n  situations. See https://github.com/eclipse-mosquitto/mosquitto/issues/3389\n  Closes #3389.\n- Check return value of openssl ASN1_string_[get0_]data() functions for NULL.\n  This prevents a crash in case of incorrect certificate handling in openssl.\n  Closes #3390.\n- Fix potential crash on startup if a malicious/corrupt persistence file from\n  mosquitto 1.5 or earlier is loaded. Closes #3439.\n- Limit auto_id_prefix to 50 characters. Closes #3440.\n\n\n2.0.22 - 2025-07-11\n===================\n\n# Broker\n- Windows: Fix broker crash on startup if using `log_dest stdout`\n- Bridge: Fix idle_timeout never occurring for lazy bridges.\n- Fix case where max_queued_messages = 0 was not treated as unlimited.\n  Closes #3244.\n- Fix `--version` exit code and output. Closes #3267.\n- Fix crash on receiving a $CONTROL message over a bridge, if\n  per_listener_settings is set true and the bridge is carrying out topic\n  remapping. Closes #3261.\n- Fix incorrect reference clock being selected on startup on Linux.\n  Closes #3238.\n- Fix reporting of client disconnections being incorrectly attributed to \"out\n  of memory\". Closes #3253.\n- Fix compilation when using `WITH_OLD_KEEPALIVE`. Closes #3250.\n- Add Windows linker file for the broker to the installer. Closes #3269.\n- Fix Websockets PING not being sent on Windows. Closes #3272.\n- Fix problems with secure websockets. Closes #1211.\n- Fix crash on exit when using WITH_EPOLL=no. Closes #3302.\n- Fix clients being incorrectly expired when they have keepalive ==\n  max_keepalive. Closes #3226, #3286.\n\n# Dynamic security plugin\n- Fix mismatch memory free when saving config which caused memory tracking to\n  be incorrect.\n\n# Client library\n- Fix C++ symbols being removed when compiled with link time optimisation.\n  Closes #3259.\n- TLS error handling was incorrectly setting a protocol error for non-TLS\n  errors.  This would cause the mosquitto_loop_start() thread to exit if no\n  broker was available on the first connection attempt. This has been fixed.\n  Closes #3258.\n- Fix linker errors on some architectures using cmake. Closes #3167.\n\n\n# Tests\n- Fix 08-ssl-connect-cert-auth-expired and 08-ssl-connect-cert-auth-revoked\n  tests when running on a single CPU system. Closes #3230.\n\n\n2.0.21 - 2025-03-06\n===================\n\n# Security\n- Fix leak on malicious SUBSCRIBE by authenticated client.\n  Closes eclipse #248.\n- Further fix for CVE-2023-28366.\n\n# Broker\n- Fix clients sending a RESERVED packet not being quickly disconnected.\n  Closes #2325.\n- Fix `bind_interface` producing an error when used with an interface that has\n  an IPv6 link-local address and no other IPv6 addresses. Closes #2696.\n- Fix mismatched wrapped/unwrapped memory alloc/free in properties. Closes #3192.\n- Fix `allow_anonymous false` not being applied in local only mode. Closes #3198.\n- Add `retain_expiry_interval` option to fix expired retained message not\n  being removed from memory if they are not subscribed to. Closes #3221.\n- Produce an error if invalid combinations of cafile/capath/certfile/keyfile\n  are used. Closes #1836. Closes #3130.\n- Backport keepalive checking from develop to fix problems in current\n  implementation. Closes #3138.\n\n# Client library\n- Fix potential deadlock in mosquitto_sub if `-W` is used. Closes #3175.\n\n# Apps\n- mosquitto_ctrl dynsec now also allows `-i` to specify a clientid as well as\n  `-c`. This matches the documentation which states `-i`. Closes #3219.\n# Client library\n- Fix threads linking on Windows for static libmosquitto library\n  Closes #3143\n\n# Build\n- Fix Windows builds not having websockets enabled.\n- Add tzdata to docker images\n\n# Tests\n- Fix 08-ssl-connect-cert-auth-expired and 08-ssl-connect-cert-auth-revoked\n  tests when under load. Closes #3208.\n\n\n2.0.20 - 2024-10-16\n===================\n\n# Broker\n- Fix QoS 1 / QoS 2 publish incorrectly returning \"no subscribers\".\n  Closes #3128.\n- Open files with appropriate access on Windows. Closes #3119.\n- Don't allow invalid response topic values.\n- Fix some strict protocol compliance issues. Closes #3052.\n\n# Client library\n- Fix cmake build on OS X. Closes #3125.\n\n# Build\n- Fix build on NetBSD\n\n\n2.0.19 - 2024-10-02\n===================\n\n# Security\n- Fix mismatched subscribe/unsubscribe with normal/shared topics.\n- Fix crash on bridge using remapped topic being sent a crafted packet.\n\n# Broker\n- Fix assert failure when loading a persistence file that contains\n  subscriptions with no client id.\n- Fix local bridges being incorrectly expired when\n  persistent_client_expiration is in use.\n- Fix use of CLOCK_BOOTTIME for getting time. Closes #3089.\n- Fix mismatched subscribe/unsubscribe with normal/shared topics.\n- Fix crash on bridge using remapped topic being sent a crafted packet.\n\n# Client library\n- Fix some error codes being converted to string as \"unknown\". Closes #2579.\n- Clear SSL error state to avoid spurious error reporting. Closes #3054.\n- Fix \"payload format invalid\" not being allowed as a PUBREC reason code.\n- Don't allow SUBACK with missing reason codes.\n\n# Build\n- Thread support is re-enabled on Windows.\n\n\n2.0.18 - 2023-09-18\n===================\n\n# Broker\n- Fix crash on subscribe under certain unlikely conditions. Closes #2885.\n  Closes #2881.\n\n# Clients\n- Fix mosquitto_rr not honouring `-R`. Closes #2893.\n\n# Windows\n- Installer will start/stop the mosquitto service when installing and\n  uninstalling, to prevent problems with not being able to overwrite or remove\n  mosquitto.exe.\n\n\n2.0.17 - 2023-08-22\n===================\n\n# Broker\n- Fix `max_queued_messages 0` stopping clients from receiving messages.\n  Closes #2879.\n- Fix `max_inflight_messages` not being set correctly. Closes #2876.\n\n# Apps\n- Fix `mosquitto_passwd -U` backup file creation. Closes #2873.\n\n\n2.0.16 - 2023-08-16\n===================\n\n# Security\n- CVE-2023-28366: Fix memory leak in broker when clients send multiple QoS 2\n  messages with the same message ID, but then never respond to the PUBREC\n  commands.\n- CVE-2023-0809: Fix excessive memory being allocated based on malicious\n  initial packets that are not CONNECT packets.\n- CVE-2023-3592: Fix memory leak when clients send v5 CONNECT packets with a\n  will message that contains invalid property types.\n- Broker will now reject Will messages that attempt to publish to $CONTROL/.\n- Broker now validates usernames provided in a TLS certificate or TLS-PSK\n  identity are valid UTF-8.\n- Fix potential crash when loading invalid persistence file.\n- Library will no longer allow single level wildcard certificates, e.g. *.com\n\n# Broker\n- Fix $SYS messages being expired after 60 seconds and hence unchanged values\n  disappearing.\n- Fix some retained topic memory not being cleared immediately after used.\n- Fix error handling related to the `bind_interface` option.\n- Fix std* files not being redirected when daemonising, when built with\n  assertions removed. Closes #2708.\n- Fix default settings incorrectly allowing TLS v1.1. Closes #2722.\n- Use line buffered mode for stdout. Closes #2354. Closes #2749.\n- Fix bridges with non-matching cleansession/local_cleansession being expired\n  on start after restoring from persistence. Closes #2634.\n- Fix connections being limited to 2048 on Windows. The limit is now 8192,\n  where supported. Closes #2732.\n- Broker will log warnings if sensitive files are world readable/writable, or\n  if the owner/group is not the same as the user/group the broker is running\n  as. In future versions the broker will refuse to open these files.\n- mosquitto_memcmp_const is now more constant time.\n- Only register with DLT if DLT logging is enabled.\n- Fix any possible case where a json string might be incorrectly loaded. This\n  could have caused a crash if a textname or textdescription field of a role was\n  not a string, when loading the dynsec config from file only.\n- Dynsec plugin will not allow duplicate clients/groups/roles when loading\n  config from file, which matches the behaviour for when creating them.\n- Fix heap overflow when reading corrupt config with \"log_dest file\".\n\n# Client library\n- Use CLOCK_BOOTTIME when available, to keep track of time. This solves the\n  problem of the client OS sleeping and the client hence not being able to\n  calculate the actual time for keepalive purposes. Closes #2760.\n- Fix default settings incorrectly allowing TLS v1.1. Closes #2722.\n- Fix high CPU use on slow TLS connect. Closes #2794.\n\n# Clients\n- Fix incorrect topic-alias property value in mosquitto_sub json output.\n- Fix confusing message on TLS certificate verification. Closes #2746.\n\n# Apps\n- mosquitto_passwd uses mkstemp() for backup files.\n- `mosquitto_ctrl dynsec init` will refuse to overwrite an existing file,\n  without a race-condition.\n\n\n2.0.15 - 2022-08-16\n===================\n\n# Security\n- Deleting the group configured as the anonymous group in the Dynamic Security\n  plugin, would leave a dangling pointer that could lead to a single crash.\n  This is considered a minor issue - only administrative users should have\n  access to dynsec, the impact on availability is one-off, and there is no\n  associated loss of data. It is now forbidden to delete the group configured\n  as the anonymous group.\n\n# Broker\n- Fix memory leak when a plugin modifies the topic of a message in\n  MOSQ_EVT_MESSAGE.\n- Fix bridge `restart_timeout` not being honoured.\n- Fix potential memory leaks if a plugin modifies the message in the\n  MOSQ_EVT_MESSAGE event.\n- Fix unused flags in CONNECT command being forced to be 0, which is not\n  required for MQTT v3.1. Closes #2522.\n- Improve documentation of `persistent_client_expiration` option.\n  Closes #2404.\n- Add clients to session expiry check list when restarting and reloading from\n  persistence. Closes #2546.\n- Fix bridges not sending failure notification messages to the local broker if\n  the remote bridge connection fails. Closes #2467. Closes #1488.\n- Fix some PUBLISH messages not being counted in $SYS stats. Closes #2448.\n- Fix incorrect return code being sent in DISCONNECT when a client session is\n  taken over. Closes #2607.\n- Fix confusing \"out of memory\" error when a client is kicked in the dynamic\n  security plugin. Closes #2525.\n- Fix confusing error message when dynamic security config file was a\n  directory. Closes #2520.\n- Fix bridge queued messages not being persisted when local_cleansession is\n  set to false and cleansession is set to true. Closes #2604.\n- Dynamic security: Fix modifyClient and modifyGroup commands to not modify\n  the client/group if a new group/client being added is not valid.\n  Closes #2598.\n- Dynamic security: Fix the plugin being able to be loaded twice. Currently\n  only a single plugin can interact with a unique $CONTROL topic. Using\n  multiple instances of the plugin would produce duplicate entries in the\n  config file. Closes #2601. Closes #2470.\n- Fix case where expired messages were causing queued messages not to be\n  delivered. Closes #2609.\n- Fix websockets not passing on the X-Forwarded-For header.\n\n# Client library\n- Fix threads library detection on Windows under cmake. Bumps the minimum\n  cmake version to 3.1, which is still ancient.\n- Fix use of `MOSQ_OPT_TLS_ENGINE` being unable to be used due to the openssl\n  ctx not being initialised until starting to connect. Closes #2537.\n- Fix incorrect use of SSL_connect. Closes #2594.\n- Don't set SIGPIPE to ignore, use MSG_NOSIGNAL instead. Closes #2564.\n- Add documentation of struct mosquitto_message to header. Closes #2561.\n- Fix documentation omission around mosquitto_reinitialise. Closes #2489.\n- Fix use of MOSQ_OPT_SSL_CTX when used in conjunction with\n  MOSQ_OPT_SSL_CTX_DEFAULTS. Closes #2463.\n- Fix failure to close thread in some situations. Closes #2545.\n\n# Clients\n- Fix mosquitto_pub incorrectly reusing topic aliases when reconnecting.\n  Closes #2494.\n\n# Apps\n- Fix `-o` not working in `mosquitto_ctrl`, and typo in related documentation.\n  Closes #2471.\n\n\n2.0.14 - 2021-11-17\n===================\n\n# Broker\n- Fix bridge not respecting receive-maximum when reconnecting with MQTT v5.\n\n# Client library\n- Fix mosquitto_topic_matches_sub2() not using the length parameters.\n  Closes #2364.\n- Fix incorrect subscribe_callback in mosquittopp.h. Closes #2367.\n\n\n2.0.13 - 2021-10-27\n===================\n\n# Broker\n- Fix `max_keepalive` option not being able to be set to 0.\n- Fix LWT messages not being delivered if `per_listener_settings` was set to\n  true. Closes #2314.\n- Various fixes around inflight quota management. Closes #2306.\n- Fix problem parsing config files with Windows line endings. Closes #2297.\n- Don't send retained messages when a shared subscription is made.\n- Fix log being truncated in Windows.\n- Fix client id not showing in log on failed connections, where possible.\n- Fix broker sending duplicate CONNACK on failed MQTT v5 reauthentication.\n  Closes #2339.\n- Fix mosquitto_plugin.h not including mosquitto_broker.h. Closes #2350.\n- Fix unlimited message quota not being properly checked for incoming\n  messages. Closes #2593.\n- Fixed build for openssl compiled with OPENSSL_NO_ENGINE. Closes #2589.\n\n# Client library\n- Initialise sockpairR/W to invalid in `mosquitto_reinitialise()` to avoid\n  closing invalid sockets in `mosquitto_destroy()` on error. Closes #2326.\n\n# Clients\n- Fix date format in mosquitto_sub output. Closes #2353.\n\n\n2.0.12 - 2021-08-31\n===================\n\n# Security\n- An MQTT v5 client connecting with a large number of user-property properties\n  could cause excessive CPU usage, leading to a loss of performance and\n  possible denial of service. This has been fixed.\n- Fix `max_keepalive` not applying to MQTT v3.1.1 and v3.1 connections.\n  These clients are now rejected if their keepalive value exceeds\n  max_keepalive. This option allows CVE-2020-13849, which is for the MQTT\n  v3.1.1 protocol itself rather than an implementation, to be addressed.\n- Using certain listener related configuration options e.g. `cafile`, that\n  apply to the default listener without defining any listener would cause a\n  remotely accessible listener to be opened that was not confined to the local\n  machine but did have anonymous access enabled, contrary to the\n  documentation. This has been fixed. Closes #2283.\n- CVE-2021-34434: If a plugin had granted ACL subscription access to a\n  durable/non-clean-session client, then removed that access, the client would\n  keep its existing subscription. This has been fixed.\n- Incoming QoS 2 messages that had not completed the QoS flow were not being\n  checked for ACL access when a clean session=False client was reconnecting.\n  This has been fixed.\n\n# Broker\n- Fix possible out of bounds memory reads when reading a corrupt/crafted\n  configuration file. Unless your configuration file is writable by untrusted\n  users this is not a risk. Closes #567213.\n- Fix `max_connections` option not being correctly counted.\n- Fix TLS certificates and TLS-PSK not being able to be configured at the same\n  time.\n- Disable TLS v1.3 when using TLS-PSK, because it isn't correctly configured.\n- Fix `max_keepalive` not applying to MQTT v3.1.1 and v3.1 connections.\n  These clients are now rejected if their keepalive value exceeds\n  max_keepalive. This option allows CVE-2020-13849, which is for the MQTT\n  v3.1.1 protocol itself rather than an implementation, to be addressed.\n- Fix broker not quitting if e.g. the `password_file` is specified as a\n  directory. Closes #2241.\n- Fix listener mount_point not being removed on outgoing messages.\n  Closes #2244.\n- Strict protocol compliance fixes, plus test suite.\n- Fix $share subscriptions not being recovered for durable clients that\n  reconnect.\n- Update plugin configuration documentation. Closes #2286.\n\n# Client library\n- If a client uses TLS-PSK then force the default cipher list to use \"PSK\"\n  ciphers only. This means that a client connecting to a broker configured\n  with x509 certificates only will now fail. Prior to this, the client would\n  connect successfully without verifying certificates, because they were not\n  configured.\n- Disable TLS v1.3 when using TLS-PSK, because it isn't correctly configured.\n- Threaded mode is deconfigured when the mosquitto_loop_start() thread ends,\n  which allows mosquitto_loop_start() to be called again. Closes #2242.\n- Fix MOSQ_OPT_SSL_CTX not being able to be set to NULL. Closes #2289.\n- Fix reconnecting failing when MOSQ_OPT_TLS_USE_OS_CERTS was in use, but none\n  of capath, cafile, psk, nor MOSQ_OPT_SSL_CTX were set, and\n  MOSQ_OPT_SSL_CTX_WITH_DEFAULTS was set to the default value of true.\n  Closes #2288.\n\n# Apps\n- Fix `mosquitto_ctrl dynsec setDefaultACLAccess` command not working.\n\n# Clients\n- mosquitto_sub and mosquitto_rr now open stdout in binary mode on Windows\n  so binary payloads are not modified when printing.\n- Document TLS certificate behaviour when using `-p 8883`.\n\n# Build\n- Fix installation using WITH_TLS=no. Closes #2281.\n- Fix builds with libressl 3.4.0. Closes #2198.\n- Remove some unnecessary code guards related to libressl.\n- Fix printf format build warning on MIPS. Closes #2271.\n\n\n\n2.0.11 - 2021-06-08\n===================\n\n# Security\n- If a MQTT v5 client connects with a crafted CONNECT packet a memory leak\n  will occur. This has been fixed.\n\n# Broker\n- Fix possible crash having just upgraded from 1.6 if `per_listener_settings\n  true` is set, and a SIGHUP is sent to the broker before a client has\n  reconnected to the broker. Closes #2167.\n- Fix bridge not reconnectng if the first reconnection attempt fails.\n  Closes #2207.\n- Improve QoS 0 outgoing packet queueing.\n- Fix non-reachable bridge blocking the broker on Windows. Closes #2172.\n- Fix possible corruption of pollfd array on Windows when bridges were\n  reconnecting. Closes #2173.\n- Fix QoS 0 messages not being queued when `queue_qos0_messages` was enabled.\n  Closes #2224.\n- Fix openssl not being linked to dynamic security plugin. Closes #2277.\n\n# Clients\n- If sending mosquitto_sub output to a pipe, mosquitto_sub will now detect\n  that the pipe has closed and disconnect. Closes #2164.\n- Fix `mosquitto_pub -l` quitting if a message publication is attempted when\n  the broker is temporarily unavailable. Closes #2187.\n\n\n2.0.10 - 2021-04-03\n==================\n\n# Security\n- CVE-2021-28166: If an authenticated client connected with MQTT v5 sent a\n  malformed CONNACK message to the broker a NULL pointer dereference occurred,\n  most likely resulting in a segfault.\n  Affects versions 2.0.0 to 2.0.9 inclusive.\n\n# Broker\n- Don't over write new receive-maximum if a v5 client connects and takes over\n  an old session. Closes #2134.\n- Fix CVE-2021-28166. Closes #2163.\n\n# Clients\n- Set `receive-maximum` to not exceed the `-C` message count in mosquitto_sub\n  and mosquitto_rr, to avoid potentially lost messages. Closes #2134.\n- Fix TLS-PSK mode not working with port 8883. Closes #2152.\n\n# Client library\n- Fix possible socket leak. This would occur if a client was using\n  `mosquitto_loop_start()`, then if the connection failed due to the remote\n  server being inaccessible they called `mosquitto_loop_stop(, true)` and\n  recreated the mosquitto object.\n\n# Build\n- A variety of minor build related fixes, like functions not having previous\n  declarations.\n- Fix CMake cross compile builds not finding opensslconf.h. Closes #2160.\n- Fix build on Solaris non-sparc. Closes #2136.\n\n\n2.0.9 - 2021-03-11\n==================\n\n# Security\n- If an empty or invalid CA file was provided to the client library for\n  verifying the remote broker, then the initial connection would fail but\n  subsequent connections would succeed without verifying the remote broker\n  certificate. Closes #2130.\n- If an empty or invalid CA file was provided to the broker for verifying the\n  remote broker for an outgoing bridge connection then the initial connection\n  would fail but subsequent connections would succeed without verifying the\n  remote broker certificate. Closes #2130.\n\n# Broker\n- Fix encrypted bridge connections incorrectly connecting when `bridge_cafile`\n  is empty or invalid. Closes #2130.\n- Fix `tls_version` behaviour not matching documentation. It was setting the\n  exact TLS version to use, not the minimum TLS version to use. Closes #2110.\n- Fix messages to `$` prefixed topics being rejected. Closes #2111.\n- Fix QoS 0 messages not being delivered when max_queued_bytes was configured.\n  Closes #2123.\n- Fix bridge increasing backoff calculation.\n- Improve handling of invalid combinations of listener address and bind\n  interface configurations. Closes #2081.\n- Fix `max_keepalive` option not applying to clients connecting with keepalive\n  set to 0. Closes #2117.\n\n# Client library\n- Fix encrypted connections incorrectly connecting when the CA file passed to\n  `mosquitto_tls_set()` is empty or invalid. Closes #2130.\n- Fix connections retrying very rapidly in some situations.\n\n# Build\n- Fix cmake epoll detection.\n\n\n2.0.8 - 2021-02-25\n==================\n\n# Broker\n- Fix incorrect datatypes in `struct mosquitto_evt_tick`. This changes the\n  size and offset of two of the members of this struct, and changes the size\n  of the struct. This is an ABI break, but is considered to be acceptable\n  because plugins should never be allocating their own instance of this\n  struct, and currently none of the struct members are used for anything, so a\n  plugin should not be accessing them. It would also be safe to read/write\n  from the existing struct parameters.\n- Give compile time warning if libwebsockets compiled without external poll\n  support. Closes #2060.\n- Fix memory tracking not being available on FreeBSD or macOS. Closes #2096.\n\n# Client library\n- Fix mosquitto_{pub|sub}_topic_check() functions not returning MOSQ_ERR_INVAL\n  on topic == NULL.\n\n# Clients\n- Fix possible loss of data in `mosquitto_pub -l` when sending multiple long\n  lines. Closes #2078.\n\n# Build\n- Provide a mechanism for Docker users to run a broker that doesn't use\n  authentication, without having to provide their own configuration file.\n  Closes #2040.\n\n\n2.0.7 - 2021-02-04\n==================\n\n# Broker\n- Fix exporting of executable symbols on BSD when building via makefile.\n- Fix some minor memory leaks on exit only.\n- Fix possible memory leak on connect. Closes #2057.\n- Fix openssl engine not being able to load private key. Closes #2066.\n\n# Clients\n- Fix config files truncating options after the first space. Closes #2059.\n\n# Build\n- Fix man page building to not absolutely require xsltproc when using CMake.\n  This now handles the case where we are building from the released tar, or\n  building from git if xsltproc is available, or building from git if xsltproc\n  is not available.\n\n\n1.6.13 - 2021-02-04\n===================\n\n# Broker\n- Fix crash on Windows if loading a plugin fails. Closes #1866.\n- Fix DH group not being set for TLS connections, which meant ciphers using\n  DHE couldn't be used. Closes #1925. Closes #1476.\n- Fix local bridges being disconnected on SIGHUP. Closes #1942.\n- Fix $SYS/broker/publish/messages/+ counters not being updated for QoS 1, 2\n  messages. Closes #1968.\n- Fix listener not being reassociated with client when reloading a persistence\n  file and `per_listener_settings true` is set and the client did not set a\n  username. Closes #1891.\n- Fix file logging on Windows. Closes #1880.\n- Fix bridge sock not being removed from sock hash on error. Closes #1897.\n\n# Client library\n- Fix build on Mac Big Sur. Closes #1905.\n- Fix DH group not being set for TLS connections, which meant ciphers using\n  DHE couldn't be used. Closes #1925. Closes #1476.\n\n# Clients\n- mosquitto_sub will now quit with an error if the %U option is used on\n  Windows, rather than just quitting. Closes #1908.\n- Fix config files truncating options after the first space. Closes #2059.\n\n# Apps\n- Perform stricter parsing of input username in mosquitto_passwd. Closes\n  #570126 (Eclipse bugzilla).\n\n# Build\n- Enable epoll support in CMake builds.\n\n\n2.0.6 - 2021-01-28\n\n# Broker\n- Fix calculation of remaining length parameter for websockets clients that\n  send fragmented packets. Closes #1974.\n# Broker\n- Fix potential duplicate Will messages being sent when a will delay interval\n  has been set.\n- Fix message expiry interval property not being honoured in\n  `mosquitto_broker_publish` and `mosquitto_broker_publish_copy`.\n- Fix websockets listeners with TLS not responding. Closes #2020.\n- Add notes that libsystemd-dev or similar is needed if building with systemd\n  support. Closes #2019.\n- Improve logging in obscure cases when a client disconnects. Closes #2017.\n- Fix reloading of listeners where multiple listeners have been defined with\n  the same port but different bind addresses. Closes #2029.\n- Fix `message_size_limit` not applying to the Will payload. Closes #2022.\n- The error topic-alias-invalid was being sent if an MQTT v5 client published\n  a message with empty topic and topic alias set, but the topic alias hadn't\n  already been configured on the broker. This has been fixed to send a\n  protocol error, as per section 3.3.4 of the specification.\n- Note in the man pages that SIGHUP reloads TLS certificates. Closes #2037.\n- Fix bridges not always connecting on Windows. Closes #2043.\n\n# Apps\n- Allow command line arguments to override config file options in\n  mosquitto_ctrl. Closes #2010.\n- mosquitto_ctrl: produce an error when requesting a new password if both\n  attempts do not match. Closes #2011.\n\n# Build\n- Fix cmake builds using `WITH_CJSON=no` not working if cJSON not found.\n  Closes #2026.\n\n# Other\n- The SPDX identifiers for EDL-1.0 have been changed to BSD-3-Clause as per\n  The Eclipse legal documentation generator. The licenses are identical.\n\n\n2.0.5 - 2021-01-11\n==================\n\n# Broker\n- Fix `auth_method` not being provided to the extended auth plugin event.\n  Closes #1975.\n- Fix large packets not being completely published to slow clients.\n  Closes #1977.\n- Fix bridge connection not relinquishing POLLOUT after messages are sent.\n  Closes #1979.\n- Fix apparmor incorrectly denying access to\n  /var/lib/mosquitto/mosquitto.db.new. Closes #1978.\n- Fix potential intermittent initial bridge connections when using poll().\n- Fix `bind_interface` option. Closes #1999.\n- Fix invalid behaviour in dynsec plugin if a group or client is deleted\n  before a role that was attached to the group or client is deleted.\n  Closes #1998.\n- Improve logging in dynsec addGroupRole command. Closes #2005.\n- Improve logging in dynsec addGroupClient command. Closes #2008.\n\n# Client library\n- Improve documentation around the `_v5()` and non-v5 functions, e.g.\n  `mosquitto_publish()` and `mosquitto_publish_v5().\n\n# Build\n- `install` Makefile target should depend on `all`, not `mosquitto`, to ensure\n  that man pages are always built. Closes #1989.\n- Fixes for lots of minor build warnings highlighted by Visual Studio.\n\n# Apps\n- Disallow control characters in mosquitto_passwd usernames.\n- Fix incorrect description in mosquitto_ctrl man page. Closes #1995.\n- Fix `mosquitto_ctrl dynsec getGroup` not showing roles. Closes #1997.\n\n\n2.0.4 - 2020-12-22\n==================\n\n# Broker\n- Fix $SYS/broker/publish/messages/+ counters not being updated for QoS 1, 2\n  messages. Closes #1968.\n- mosquitto_connect_bind_async() and mosquitto_connect_bind_v5() should not\n  reset the bind address option if called with bind_address == NULL.\n- Fix dynamic security configuration possibly not being reloaded on Windows\n  only. Closes #1962.\n- Add more log messages for dynsec load/save error conditions.\n- Fix websockets connections blocking non-websockets connections on Windows.\n  Closes #1934.\n\n# Build\n- Fix man pages not being built when using CMake. Closes #1969.\n\n\n2.0.3 - 2020-12-17\n==================\n\n# Security\n- Running mosquitto_passwd with the following arguments only\n  `mosquitto_passwd -b password_file username password` would cause the\n  username to be used as the password.\n\n# Broker\n- Fix excessive CPU use on non-Linux systems when the open file limit is set\n  high. Closes #1947.\n- Fix LWT not being sent on client takeover when the existing session wasn't\n  being continued. Closes #1946.\n- Fix bridges possibly not completing connections when WITH_ADNS is in use.\n  Closes #1960.\n- Fix QoS 0 messages not being delivered if max_queued_messages was set to 0.\n  Closes #1956.\n- Fix local bridges being disconnected on SIGHUP. Closes #1942.\n- Fix slow initial bridge connections for WITH_ADNS=no.\n- Fix persistence_location not appending a '/'.\n\n# Clients\n- Fix mosquitto_sub being unable to terminate with Ctrl-C if a successful\n  connection is not made. Closes #1957.\n\n# Apps\n- Fix `mosquitto_passwd -b` using username as password (not if `-c` is also\n  used). Closes #1949.\n\n# Build\n- Fix `install` target when using WITH_CJSON=no. Closes #1938.\n- Fix `generic` docker build. Closes #1945.\n\n\n2.0.2 - 2020-12-10\n==================\n\n# Broker\n- Fix build regression for WITH_WEBSOCKETS=yes on non-Linux systems.\n\n\n2.0.1 - 2020-12-10\n==================\n\n# Broker\n- Fix websockets connections on Windows blocking subsequent connections.\n  Closes #1934.\n- Fix DH group not being set for TLS connections, which meant ciphers using\n  DHE couldn't be used. Closes #1925. Closes #1476.\n- Fix websockets listeners not causing the main loop not to wake up.\n  Closes #1936.\n\n# Client library\n- Fix DH group not being set for TLS connections, which meant ciphers using\n  DHE couldn't be used. Closes #1925. Closes #1476.\n\n# Apps\n- Fix `mosquitto_passwd -U`\n\n# Build\n- Fix cjson include paths.\n- Fix build using WITH_TLS=no when the openssl headers aren't available.\n- Distribute cmake/ and snap/ directories in tar.\n\n\n2.0.0 - 2020-12-03\n==================\n\n# Breaking changes\n- When the Mosquitto broker is run without configuring any listeners it will\n  now bind to the loopback interfaces 127.0.0.1 and/or ::1. This means that\n  only connections from the local host will be possible.\n\n  Running the broker as `mosquitto` or `mosquitto -p 1883` will bind to the\n  loopback interface.\n\n  Running the broker with a configuration file with no listeners configured\n  will bind to the loopback interface with port 1883.\n\n  Running the broker with a listener defined will bind by default to `0.0.0.0`\n  / `::` and so will be accessible from any interface. It is still possible to\n  bind to a specific address/interface.\n\n  If the broker is run as `mosquitto -c mosquitto.conf -p 1884`, and a\n  listener is defined in the configuration file, then the port defined on the\n  command line will be IGNORED, and no listener configured for it.\n- All listeners now default to `allow_anonymous false` unless explicitly set\n  to true in the configuration file. This means that when configuring a\n  listener the user must either configure an authentication and access control\n  method, or set `allow_anonymous true`. When the broker is run without a\n  configured listener, and so binds to the loopback interface, anonymous\n  connections are allowed.\n- If Mosquitto is run on as root on a unix like system, it will attempt to\n  drop privileges as soon as the configuration file has been read. This is in\n  contrast to the previous behaviour where elevated privileges were only\n  dropped after listeners had been started (and hence TLS certificates loaded)\n  and logging had been started. The change means that clients will never be\n  able to connect to the broker when it is running as root, unless the user\n  explicitly sets it to run as root, which is not advised. It also means that\n  all locations that the broker needs to access must be available to the\n  unprivileged user. In particular those people using TLS certificates from\n  Lets Encrypt will need to do something to allow Mosquitto to access\n  those certificates. An example deploy renewal hook script to help with this\n  is at `misc/letsencrypt/mosquitto-copy.sh`.\n  The user that Mosquitto will change to are the one provided in the\n  configuration, `mosquitto`, or `nobody`, in order of availability.\n- The `pid_file` option will now always attempt to write a pid file,\n  regardless of whether the `-d` argument is used when running the broker.\n- The `tls_version` option now defines the *minimum* TLS protocol version to\n  be used, rather than the exact version. Closes #1258.\n- The `max_queued_messages` option has been increased from 100 to 1000 by\n  default, and now also applies to QoS 0 messages, when a client is connected.\n- The mosquitto_sub, mosquitto_pub, and mosquitto_rr clients will now load\n  OS provided CA certificates by default if `-L mqtts://...` is used, or if\n  the port is set to 8883 and no other CA certificates are loaded.\n- Minimum support libwebsockets version is now 2.4.0\n- The license has changed from \"EPL-1.0 OR EDL-1.0\" to \"EPL-2.0 OR EDL-1.0\".\n\n# Broker features\n- New plugin interface which is more flexible, easier to develop for and\n  easier to extend.\n- New dynamic security plugin, which allows clients, groups, and roles to be\n  defined and updated as the broker is running.\n- Performance improvements, particularly for higher numbers of clients.\n- When running as root, if dropping privileges to the \"mosquitto\" user fails,\n  then try \"nobody\" instead. This reduces the burden on users installing\n  Mosquitto themselves.\n- Add support for Unix domain socket listeners.\n- Add `bridge_outgoing_retain` option, to allow outgoing messages from a\n  bridge to have the retain bit completely disabled, which is useful when\n  bridging to e.g. Amazon or Google.\n- Add support for MQTT v5 bridges to handle the \"retain-available\" property\n  being false.\n- Allow MQTT v5.0 outgoing bridges to fall back to MQTT v3.1.1 if connecting\n  to a v3.x only broker.\n- DLT logging is now configurable at runtime with `log_dest dlt`.\n  Closes #1735.\n- Add `mosquitto_broker_publish()` and `mosquitto_broker_publish_copy()`\n  functions, which can be used by plugins to publish messages.\n- Add `mosquitto_client_protocol_version()` function which can be used by\n  plugins to determine which version of MQTT a client has connected with.\n- Add `mosquitto_kick_client_by_clientid()` and `mosquitto_kick_client_by_username()`\n  functions, which can be used by plugins to disconnect clients.\n- Add support for handling $CONTROL/ topics in plugins.\n- Add support for PBKDF2-SHA512 password hashing.\n- Enabling certificate based TLS encryption is now through certfile and\n  keyfile, not capath or cafile.\n- Added support for controlling UNSUBSCRIBE calls in v5 plugin ACL checks.\n- Add \"deny\" acl type. Closes #1611.\n- The broker now sends the receive-maximum property for MQTT v5 CONNACKs.\n- Add the `bridge_max_packet_size` option. Closes #265.\n- Add the `bridge_bind_address` option. Closes #1311.\n- TLS certificates for the server are now reloaded on SIGHUP.\n- Default for max_queued_messages has been changed to 1000.\n- Add `ciphers_tls1.3` option, to allow setting TLS v1.3 ciphersuites.\n  Closes #1825.\n- Bridges now obey MQTT v5 server-keepalive.\n- Add bridge support for the MQTT v5 maximum-qos property.\n- Log client port on new connections. Closes #1911.\n\n# Broker fixes\n- Send DISCONNECT with `malformed-packet` reason code on invalid PUBLISH,\n  SUBSCRIBE, and UNSUBSCRIBE packets.\n- Document that X509_free() must be called after using\n  mosquitto_client_certificate(). Closes #1842.\n- Fix listener not being reassociated with client when reloading a persistence\n  file and `per_listener_settings true` is set and the client did not set a\n  username. Closes #1891.\n- Fix bridge sock not being removed from sock hash on error. Closes #1897.\n- mosquitto_password now forbids the : character. Closes #1833.\n- Fix `log_timestamp_format` not applying to `log_dest topic`. Closes #1862.\n- Fix crash on Windows if loading a plugin fails. Closes #1866.\n- Fix file logging on Windows. Closes #1880.\n- Report an error if the config file is set to a directory. Closes #1814.\n- Fix bridges incorrectly setting Wills to manage remote notifications when\n  `notifications_local_only` was set true. Closes #1902.\n\n# Client library features\n- Client no longer generates random client ids for v3.1.1 clients, these are\n  now expected to be generated on the broker. This matches the behaviour for\n  v5 clients. Closes #291.\n- Add support for connecting to brokers through Unix domain sockets.\n- Add `mosquitto_property_identifier()`, for retrieving the identifier integer\n  for a property.\n- Add `mosquitto_property_identifier_to_string()` for converting a property\n  identifier integer to the corresponding property name string.\n- Add `mosquitto_property_next()` to retrieve the next property in a list, for\n  iterating over property lists.\n- mosquitto_pub now handles the MQTT v5 retain-available property by never\n  setting the retain bit.\n- Added MOSQ_OPT_TCP_NODELAY, to allow disabling Nagle's algorithm on client\n  sockets. Closes #1526.\n- Add `mosquitto_ssl_get()` to allow clients to access their SSL structure and\n  perform additional verification.\n- Add MOSQ_OPT_BIND_ADDRESS to allow setting of a bind address independently\n  of the `mosquitto_connect*()` call.\n- Add `MOSQ_OPT_TLS_USE_OS_CERTS` option, to instruct the client to load and\n  trust OS provided CA certificates for use with TLS connections.\n\n# Client library fixes\n- Fix send quota being incorrectly reset on reconnect. Closes #1822.\n- Don't use logging until log mutex is initialised. Closes #1819.\n- Fix missing mach/mach_time.h header on OS X. Closes #1831.\n- Fix connect properties not being sent when the client automatically\n  reconnects. Closes #1846.\n\n# Client features\n- Add timeout return code (27) for `mosquitto_sub -W <secs>` and\n  `mosquitto_rr -W <secs>`. Closes #275.\n- Add support for connecting to brokers through Unix domain sockets with the\n  `--unix` argument.\n- Use cJSON library for producing JSON output, where available. Closes #1222.\n- Add support for outputting MQTT v5 property information to mosquitto_sub/rr\n  JSON output. Closes #1416.\n- Add `--pretty` option to mosquitto_sub/rr for formatted/unformatted JSON\n  output.\n- Add support for v5 property printing to mosquitto_sub/rr in non-JSON mode.\n  Closes #1416.\n- Add `--nodelay` to all clients to allow them to use the MOSQ_OPT_TCP_NODELAY\n  option.\n- Add `-x` to all clients to all the session-expiry-interval property to be\n  easily set for MQTT v5 clients.\n- Add `--random-filter` to mosquitto_sub, to allow only a certain proportion\n  of received messages to be printed.\n- mosquitto_sub %j and %J timestamps are now in a ISO 8601 compatible format.\n- mosquitto_sub now supports extra format specifiers for field width and\n  precision for some parameters.\n- Add `--version` for all clients.\n- All clients now load OS provided CA certificates if used with `-L\n  mqtts://...`, or if port is set to 8883 and no other CA certificates are\n  used. Closes #1824.\n- Add the `--tls-use-os-certs` option to all clients.\n\n# Client fixes\n- mosquitto_sub will now exit if all subscriptions were denied.\n- mosquitto_pub now sends 0 length files without an error when using `-f`.\n- Fix description of `-e` and `-t` arguments in mosquitto_rr. Closes #1881.\n- mosquitto_sub will now quit with an error if the %U option is used on\n  Windows, rather than just quitting. Closes #1908.\n\n\n1.6.12 - 2020-08-19\n===================\n\n# Security\n- In some circumstances, Mosquitto could leak memory when handling PUBLISH\n  messages. This is limited to incoming QoS 2 messages, and is related\n  to the combination of the broker having persistence enabled, a clean\n  session=false client, which was connected prior to the broker restarting,\n  then has reconnected and has now sent messages at a sufficiently high rate\n  that the incoming queue at the broker has filled up and hence messages are\n  being dropped. This is more likely to have an effect where\n  max_queued_messages is a small value. This has now been fixed. Closes #1793.\n\n# Broker\n- Build warning fixes when building with WITH_BRIDGE=no and WITH_TLS=no.\n\n# Clients\n- All clients exit with an error exit code on CONNACK failure. Closes #1778.\n- Don't busy loop with `mosquitto_pub -l` on a slow connection.\n\n\n1.5.10 - 2020-08-19\n===================\n\n# Security\n- In some circumstances, Mosquitto could leak memory when handling PUBLISH\n  messages. This is limited to incoming QoS 2 messages, and is related\n  to the combination of the broker having persistence enabled, a clean\n  session=false client, which was connected prior to the broker restarting,\n  then has reconnected and has now sent messages at a sufficiently high rate\n  that the incoming queue at the broker has filled up and hence messages are\n  being dropped. This is more likely to have an effect where\n  max_queued_messages is a small value. This has now been fixed. Closes #1793.\n\n\n1.6.11 - 2020-08-11\n===================\n\n# Security\n- On Windows the Mosquitto service was being installed without appropriate\n  path quoting, this has been fixed.\n\n# Broker\n- Fix usage message only mentioning v3.1.1. Closes #1713.\n- Fix broker refusing to start if only websockets listeners were defined.\n  Closes #1740.\n- Change systemd unit files to create /var/log/mosquitto before starting.\n  Closes #821.\n- Don't quit with an error if opening the log file isn't possible.\n  Closes #821.\n- Fix bridge topic remapping when using \"\" as the topic. Closes #1749.\n- Fix messages being queued for disconnected bridges when clean start was\n  set to true. Closes #1729.\n- Fix `autosave_interval` not being triggered by messages being delivered.\n  Closes #1726.\n- Fix websockets clients sometimes not being disconnected promptly.\n  Closes #1718.\n- Fix \"slow\" file based logging by switching to line based buffering.\n  Closes #1689. Closes #1741.\n- Log protocol error message where appropriate from a bad UNSUBSCRIBE, rather\n  than the generic \"socket error\".\n- Don't try to start DLT logging if DLT unavailable, to avoid a long delay\n  when shutting down the broker. Closes #1735.\n- Fix potential memory leaks. Closes #1773. Closes #1774.\n- Fix clients not receiving messages after a previous client with the same\n  client ID and positive will delay interval quit. Closes #1752.\n- Fix overly broad HAVE_PTHREAD_CANCEL compile guard. Closes #1547.\n\n# Client library\n- Improved documentation around connect callback return codes. Close #1730.\n- Fix `mosquitto_publish*()` no longer returning `MOSQ_ERR_NO_CONN` when not\n  connected. Closes #1725.\n- `mosquitto_loop_start()` now sets a thread name on Linux, FreeBSD, NetBSD,\n  and OpenBSD. Closes #1777.\n- Fix `mosquitto_loop_stop()` not stopping on Windows. Closes #1748. Closes #117.\n\n\n1.6.10 - 2020-05-25\n===================\n\n# Broker\n- Report invalid bridge prefix+pattern combinations at config parsing time\n  rather than letting the bridge fail later. Issue #1635.\n- Fix `mosquitto_passwd -b` not updating passwords for existing users\n  correctly. Creating a new user with `-b` worked without problem.\n  Closes #1664.\n- Fix memory leak when connecting clients rejected.\n- Don't disconnect clients that are already disconnected. This prevents the\n  session expiry being extended on SIGHUP. Closes #1521.\n- Fix support for openssl 3.0.\n- Fix check when loading persistence file of a different version than the\n  native version. Closes #1684.\n- Fix possible assert crash associated with bridge reconnecting when compiled\n  without epoll support. Closes #1700.\n\n# Client library\n- Don't treat an unexpected PUBACK, PUBREL, or PUBCOMP as a fatal error.\n  Issue #1629.\n- Fix support for openssl 3.0.\n- Fix memory leaks from multiple calls to\n  `mosquitto_lib_init()`/`mosquitto_lib_cleanup()`. Closes #1691.\n- Fix documentation on return code of `mosquitto_lib_init()` for Windows.\n  Closes #1690.\n\n# Clients\n- Fix mosquitto_sub %j or %J not working on Windows. Closes #1674.\n\n# Build\n- Various fixes for building with <C99 support. Closes #1622.\n- Fix use of sed on BSD. Closes #1614.\n\n\n1.6.9 - 20200227\n================\n\n# Broker\n- Fix session expiry with very large expiry intervals. Closes #1525.\n- Check ACL patterns for validity when loading. Closes #1539.\n- Use presence of password file as indicator for whether username checks\n  should take place, not whether usernames are defined in the password file.\n  Closes #1545.\n- Strip whitespace from end of config file string options. Closes #1566.\n- Satisfy valgrind when exiting on error due to not being able to open a\n  listening socket, by calling freeaddrinfo. Closes #1565.\n- Fix config->user not being freed on exit. Closes #1564.\n- Fix trailing whitespace not being trimmed on acl users. Closes #1539.\n- Fix `bind_interface` not working for the default listener. Closes #1533.\n- Improve password file parsing in the broker and mosqitto_passwd. Closes #1584.\n- Print OpenSSL errors in more situations, like when loading certificates\n  fails. Closes #1552.\n- Fix `mosquitto_client_protocol() returning incorrect values.\n\n# Client library\n- Set minimum keepalive argument to `mosquitto_connect*()` to be 5 seconds.\n  Closes #1550.\n- Fix `mosquitto_topic_matches_sub()` not returning MOSQ_ERR_INVAL if the\n  topic contains a wildcard. Closes #1589.\n\n# Clients\n- Fix `--remove-retained` not obeying the `-T` option for filtering out\n  topics. Closes #1585.\n- Default behaviour for v5 clients using `-c` is now to use infinite length\n  sessions, as with v3 clients. Closes #1546.\n\n\n1.6.8 - 20191128\n================\n\n# Broker\n- Various fixes for `allow_zero_length_clientid` config, where this option was\n  not being set correctly. Closes #1429.\n- Fix incorrect memory tracking causing problems with memory_limit option.\n  Closes #1437.\n- Fix subscription topics being limited to 200 characters instead of 200\n  hierarchy levels. Closes #1441.\n- Only a single CRL could be loaded at once. This has been fixed.\n  Closes #1442.\n- Fix problems with reloading config when `per_listener_settings` was true.\n  Closes #1459.\n- Fix retained messages with an expiry interval not being expired after being\n  restored from persistence. Closes #1464.\n- Fix messages with an expiry interval being sent without an expiry interval\n  property just before they were expired. Closes #1464.\n- Fix TLS Websockets clients not receiving messages after taking over a\n  previous connection. Closes #1489.\n- Fix MQTT 3.1.1 clients using clean session false, or MQTT 5.0 clients using\n  session-expiry-interval set to infinity never expiring, even when the global\n  `persistent_client_expiration` option was set. Closes #1494.\n\n# Client library\n- Fix publish properties not being passed to on_message_v5 callback for QoS 2\n  messages. Closes #1432.\n- Fix documentation issues in mosquitto.h. Closes #1478.\n- Document `mosquitto_connect_srv()`. Closes #1499.\n\n# Clients\n- Fix duplicate cfg definition in rr_client. Closes #1453.\n- Fix `mosquitto_pub -l` hang when stdin stream ends. Closes #1448.\n- Fix `mosquitto_pub -l` not sending the final line of stdin if it does not\n  end with a new line. Closes #1473.\n- Make documentation for `mosquitto_pub -l` match reality - blank lines are\n  sent as empty messages. Closes #1474.\n- Free memory in `mosquitto_sub` when quitting without having made a successful\n  connection. Closes #1513.\n\n# Build\n- Added `CLIENT_STATIC_LDADD` to makefile builds to allow more libraries to be\n  linked when compiling the clients with a static libmosquitto, as required\n  for e.g. openssl on some systems.\n\n# Installer\n- Fix mosquitto_rr.exe not being included in Windows installers. Closes #1463.\n\n\n1.6.7 - 20190925\n================\n\n# Broker\n- Add workaround for working with libwebsockets 3.2.0.\n- Fix potential crash when reloading config. Closes #1424, #1425.\n\n# Client library\n- Don't use `/` in autogenerated client ids, to avoid confusing with topics.\n- Fix `mosquitto_max_inflight_messages_set()` and `mosquitto_int_option(...,\n  MOSQ_OPT_*_MAX, ...)` behaviour. Closes #1417.\n- Fix regression on use of `mosquitto_connect_async()` not working.\n  Closes #1415 and #1422.\n\n# Clients\n- mosquitto_sub: Fix `-E` incorrectly not working unless `-d` was also\n  specified. Closes #1418.\n- Updated documentation around automatic client ids.\n\n\n1.6.6 - 20190917\n================\n\n# Security\n- Restrict topic hierarchy to 200 levels to prevent possible stack overflow.\n  Closes #1412.\n\n# Broker\n- Restrict topic hierarchy to 200 levels to prevent possible stack overflow.\n  Closes #1412.\n- mosquitto_passwd now returns 1 when attempting to update a user that does\n  not exist. Closes #1414.\n\n\n1.6.5 - 20190912\n================\n\n# Broker\n- Fix v5 DISCONNECT packets with remaining length == 2 being treated as a\n  protocol error. Closes #1367.\n- Fix support for libwebsockets 3.x.\n- Fix slow websockets performance when sending large messages. Closes #1390.\n- Fix bridges potentially not connecting on Windows. Closes #478.\n- Fix clients authorised using `use_identity_as_username` or\n  `use_subject_as_username` being disconnected on SIGHUP. Closes #1402.\n- Improve error messages in some situations when clients disconnect. Reduces\n  the number of \"Socket error on client X, disconnecting\" messages.\n- Fix Will for v5 clients not being sent if will delay interval was greater\n  than the session expiry interval. Closes #1401.\n- Fix CRL file not being reloaded on HUP. Closes #35.\n- Fix repeated \"Error in poll\" messages on Windows when only websockets\n  listeners are defined. Closes #1391.\n\n# Client library\n- Fix reconnect backoff for the situation where connections are dropped rather\n  than refused. Closes #737.\n- Fix missing locks on `mosq->state`. Closes #1374.\n\n# Documentation\n- Improve details on global/per listener options in the mosquitto.conf man page.\n  Closes #274.\n- Clarify behaviour when clients exceed the `message_size_limit`. Closes #448.\n- Improve documentation for `max_inflight_bytes`, `max_inflight_messages`,\n  and `max_queued_messages`.\n\n# Build\n- Fix missing function warnings on NetBSD.\n- Fix WITH_STATIC_LIBRARIES using CMake on Windows. Closes #1369.\n- Guard ssize_t definition on Windows. Closes #522.\n\n\n1.6.4 - 20190801\n================\n\n# Broker\n- Fix persistent clients being incorrectly expired on Raspberry Pis.\n  Closes #1272.\n- Windows: Allow other applications access to the log file when running.\n  Closes #515.\n- Fix incoming QoS 2 messages being blocked when `max_inflight_messages` was\n  set to 1. Closes #1332.\n- Fix incoming messages not being removed for a client if the topic being\n  published to does not have any subscribers. Closes #1322.\n\n# Client library\n- Fix MQTT v5 subscription options being incorrectly set for MQTT v3\n  subscriptions. Closes #1353.\n- Make behaviour of `mosquitto_connect_async()` consistent with\n  `mosquitto_connect()` when connecting to a non-existent server.\n  Closes #1345.\n- `mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, ...)` was incorrectly\n  returning `MOSQ_ERR_INVAL` with valid input. This has been fixed.\n  Closes #1360.\n- on_connect callback is now called with the correct v5 reason code if a v5\n  client connects to a v3.x broker and is sent a CONNACK with the\n  \"unacceptable protocol version\" connack reason code.\n- Fix memory leak when setting v5 properties in mosquitto_connect_v5().\n- Fix properties not being sent on QoS>0 PUBLISH messages.\n\n# Clients\n- mosquitto_pub: fix error codes not being returned when mosquitto_pub exits.\n  Closes #1354.\n- All clients: improve error messages when connecting to a v3.x broker when in\n  v5 mode. Closes #1344.\n\n# Other\n- Various documentation fixes.\n\n\n1.6.3 - 20190618\n================\n\n# Broker\n- Fix detection of incoming v3.1/v3.1.1 bridges. Closes #1263.\n- Fix default max_topic_alias listener config not being copied to the in-use\n  listener when compiled without TLS support.\n- Fix random number generation if compiling using `WITH_TLS=no` and on Linux\n  with glibc >= 2.25. Without this fix, no random numbers would be generated\n  for e.g. on broker client id generation, and so clients connecting expecting\n  this feature would be unable to connect.\n- Fix compilation problem related to `getrandom()` on non-glibc systems.\n- Fix Will message for a persistent client incorrectly being sent when the\n  client reconnects after a clean disconnect. Closes #1273.\n- Fix Will message for a persistent client not being sent on disconnect.\n  Closes #1273.\n- Improve documentation around the upgrading of persistence files. Closes\n  #1276.\n- Add 'extern \"C\"' on mosquitto_broker.h and mosquitto_plugin.h for C++ plugin\n  writing. Closes #1290.\n- Fix persistent Websockets clients not receiving messages after they\n  reconnect, having sent DISCONNECT on a previous session. Closes #1227.\n- Disable TLS renegotiation. Client initiated renegotiation is considered to\n  be a potential attack vector against servers. Closes #1257.\n- Fix incorrect shared subscription topic '$shared'.\n- Fix zero length client ids being rejected for MQTT v5 clients with clean\n  start set to true.\n- Fix MQTT v5 overlapping subscription behaviour. Clients now receive message\n  from all matching subscriptions rather than the first one encountered, which\n  ensures the maximum QoS requirement is met.\n- Fix incoming/outgoing quota problems for QoS>0.\n- Remove obsolete `store_clean_interval` from documentation.\n- Fix v4 authentication plugin never calling psk_key_get.\n\n# Client library\n- Fix typo causing build error on Windows when building without TLS support.\n  Closes #1264.\n\n# Clients\n- Fix -L url parsing when `/topic` part is missing.\n- Stop some error messages being printed even when `--quiet` was used.\n  Closes #1284.\n- Fix mosquitto_pub exiting with error code 0 when an error occurred.\n  Closes #1285.\n- Fix mosquitto_pub not using the `-c` option. Closes #1273.\n- Fix MQTT v5 clients not being able to specify a password without a username.\n  Closes #1274.\n- Fix `mosquitto_pub -l` not handling network failures. Closes #1152.\n- Fix `mosquitto_pub -l` not handling zero length input. Closes #1302.\n- Fix double free on exit in mosquitto_pub. Closes #1280.\n\n# Documentation\n- Remove references to Python binding and C++ wrapper in libmosquitto man\n  page. Closes #1266.\n\n# Build\n- CLIENT_LDFLAGS now uses LDFLAGS. Closes #1294.\n\n\n1.6.2 - 20190430\n================\n\n# Broker\n- Fix memory access after free, leading to possible crash, when v5 client with\n  Will message disconnects, where the Will message has as its first property\n  one of `content-type`, `correlation-data`, `payload-format-indicator`, or\n  `response-topic`.  Closes #1244.\n- Fix build for WITH_TLS=no. Closes #1250.\n- Fix Will message not allowing user-property properties.\n- Fix broker originated messages (e.g. $SYS/broker/version) not being\n  published when `check_retain_source` set to true. Closes #1245.\n- Fix $SYS/broker/version being incorrectly expired after 60 seconds.\n  Closes #1245.\n\n# Library\n- Fix crash after client has been unable to connect to a broker. This occurs\n  when the client is exiting and is part of the final library cleanup routine.\n  Closes #1246.\n\n# Clients\n- Fix -L url parsing. Closes #1248.\n\n\n1.6.1 - 20190426\n================\n\n# Broker\n- Document `memory_limit` option.\n\n# Clients\n- Fix compilation on non glibc systems due to missing sys/time.h header.\n\n# Build\n- Add `make check` target and document testing procedure. Closes #1230.\n- Document bundled dependencies and how to disable. Closes #1231.\n- Split CFLAGS and CPPFLAGS, and LDFLAGS and LDADD/LIBADD.\n- test/unit now respects CPPFLAGS and LDFLAGS. Closes #1232.\n- Don't call ldconfig in CMake scripts. Closes #1048.\n- Use CMAKE_INSTALL_* variables when installing in CMake. Closes #1049.\n\n\n1.6 - 20190417\n==============\n\n# Broker features\n- Add support for MQTT v5\n- Add support for OCSP stapling.\n- Add support for ALPN on bridge TLS connections. Closes #924.\n- Add support for Automotive DLT logging.\n- Add TLS Engine support.\n- Persistence file read/write performance improvements.\n- General performance improvements.\n- Add max_keepalive option, to allow a maximum keepalive value to be set for\n  MQTT v5 clients only.\n- Add `bind_interface` option which allows a listener to be bound to a\n  specific network interface, in a similar fashion to the `bind_address` option.\n  Linux only.\n- Add improved bridge restart interval based on Decorrelated Jitter.\n- Add `dhparamfile` option, to allow DH parameters to be loaded for Ephemeral\n  DH support\n- Disallow writing to $ topics where appropriate.\n- Fix mosquitto_passwd crashing on corrupt password file. Closes #1207.\n- Add explicit support for TLS v1.3.\n- Drop support for TLS v1.0.\n- Improved general support for broker generated client ids. Removed libuuid\n  dependency.\n- auto_id_prefix now defaults to 'auto-'.\n- QoS 1 and 2 flow control improvements.\n\n# Client library features\n- Add support for MQTT v5\n- Add mosquitto_subscribe_multiple() for sending subscriptions to multiple\n  topics in one command.\n- Add TLS Engine support.\n- Add explicit support for TLS v1.3.\n- Drop support for TLS v1.0.\n- QoS 1 and 2 flow control improvements.\n\n# Client features\n- Add support for MQTT v5\n- Add mosquitto_rr client, which can be used for \"request-response\" messaging,\n  by sending a request message and awaiting a response.\n- Add TLS Engine support.\n- Add support for ALPN on TLS connections. Closes #924.\n- Add -D option for all clients to specify MQTT v5 properties.\n- Add -E to mosquitto_sub, which causes it to exit immediately after having\n  its subscriptions acknowledged. Use with -c to create a durable client\n  session without requiring a message to be received.\n- Add --remove-retained to mosquitto_sub, which can be used to clear retained\n  messages on a broker.\n- Add --repeat and --repeat-delay to mosquitto_pub, which can be used to\n  repeat single message publishes at a regular interval.\n- -V now accepts `5, `311`, `31`, as well as `mqttv5` etc.\n- Add explicit support for TLS v1.3.\n- Drop support for TLS v1.0.\n\n# Broker fixes\n- Improve error reporting when creating listeners.\n- Fix build on SmartOS due to missing IPV6_V6ONLY. Closes #1212.\n\nClient library fixes\n- Add missing `mosquitto_userdata()` function.\n\n# Client fixes\n- mosquitto_pub wouldn't always publish all messages when using `-l` and\n  QoS>0. This has been fixed.\n- mosquitto_sub was incorrectly encoding special characters when using %j\n  output format. Closes #1220.\n\n\n1.5.8 - 20190228\n================\n\n# Broker\n- Fix clients being disconnected when ACLs are in use. This only affects the\n  case where a client connects using a username, and the anonymous ACL list is\n  defined but specific user ACLs are not defined. Closes #1162.\n- Make error messages for missing config file clearer.\n- Fix some Coverity Scan reported errors that could occur when the broker was\n  already failing to start.\n- Fix broken mosquitto_passwd on FreeBSD. Closes #1032.\n- Fix delayed bridge local subscriptions causing missing messages.\n  Closes #1174.\n\n# Library\n- Use higher resolution timer for random initialisation of client id\n  generation. Closes #1177.\n- Fix some Coverity Scan reported errors that could occur when the library was\n  already quitting.\n\n\n1.5.7 - 20190213\n================\n\n# Broker\n- Fix build failure when using WITH_ADNS=yes\n- Ensure that an error occurs if `per_listener_settings true` is given after\n  other security options. Closes #1149.\n- Fix include_dir not sorting config files before loading. This was partially\n  fixed in 1.5 previously.\n- Improve documentation around the `include_dir` option. Closes #1154.\n- Fix case where old unreferenced msg_store messages were being saved to the\n  persistence file, bloating its size unnecessarily. Closes #389.\n\n# Library\n- Fix `mosquitto_topic_matches_sub()` not returning MOSQ_ERR_INVAL for\n  invalid subscriptions like `topic/#abc`. This only affects the return value,\n  not the match/no match result, which was already correct.\n\n# Build\n- Don't require C99 compiler.\n- Add rewritten build test script and remove some build warnings.\n\n\n1.5.6 - 20190206\n================\n\n# Security\n- CVE-2018-12551: If Mosquitto is configured to use a password file for\n  authentication, any malformed data in the password file will be treated as\n  valid. This typically means that the malformed data becomes a username and no\n  password. If this occurs, clients can circumvent authentication and get access\n  to the broker by using the malformed username. In particular, a blank line\n  will be treated as a valid empty username. Other security measures are\n  unaffected. Users who have only used the mosquitto_passwd utility to create\n  and modify their password files are unaffected by this vulnerability.\n  Affects version 1.0 to 1.5.5 inclusive.\n- CVE-2018-12550: If an ACL file is empty, or has only blank lines or\n  comments, then mosquitto treats the ACL file as not being defined, which\n  means that no topic access is denied. Although denying access to all topics\n  is not a useful configuration, this behaviour is unexpected and could lead\n  to access being incorrectly granted in some circumstances. This is now\n  fixed. Affects versions 1.0 to 1.5.5 inclusive.\n- CVE-2018-12546. If a client publishes a retained message to a topic that\n  they have access to, and then their access to that topic is revoked, the\n  retained message will still be delivered to future subscribers. This\n  behaviour may be undesirable in some applications, so a configuration option\n  `check_retain_source` has been introduced to enforce checking of the\n  retained message source on publish.\n\n# Broker\n- Fixed comment handling for config options that have optional arguments.\n- Improved documentation around bridge topic remapping.\n- Handle mismatched handshakes (e.g. QoS1 PUBLISH with QoS2 reply) properly.\n- Fix spaces not being allowed in the bridge remote_username option. Closes\n  #1131.\n- Allow broker to always restart on Windows when using `log_dest file`. Closes\n  #1080.\n- Fix Will not being sent for Websockets clients. Closes #1143.\n- Windows: Fix possible crash when client disconnects. Closes #1137.\n- Fixed durable clients being unable to receive messages when offline, when\n  per_listener_settings was set to true. Closes #1081.\n- Add log message for the case where a client is disconnected for sending a\n  topic with invalid UTF-8. Closes #1144.\n\n# Library\n- Fix TLS connections not working over SOCKS.\n- Don't clear SSL context when TLS connection is closed, meaning if a user\n  provided an external SSL_CTX they have less chance of leaking references.\n\n# Build\n- Fix comparison of boolean values in CMake build. Closes #1101.\n- Fix compilation when openssl deprecated APIs are not available.\n  Closes #1094.\n- Man pages can now be built on any system. Closes #1139.\n\n\n1.5.5 - 20181211\n================\n\n# Security\n- If `per_listener_settings` is set to true, then the `acl_file` setting was\n  ignored for the \"default listener\" only. This has been fixed. This does not\n  affect any listeners defined with the `listener` option. Closes #1073.\n  This is now tracked as CVE-2018-20145.\n\n# Broker\n- Add `socket_domain` option to allow listeners to disable IPv6 support.\n  This is required to work around a problem in libwebsockets that means\n  sockets only listen on IPv6 by default if IPv6 support is compiled in.\n  Closes #1004.\n- When using ADNS, don't ask for all network protocols when connecting,\n  because this can lead to confusing \"Protocol not supported\" errors if the\n  network is down. Closes #1062.\n- Fix outgoing retained messages not being sent by bridges on initial\n  connection. Closes #1040.\n- Don't reload auth_opt_ options on reload, to match the behaviour of the\n  other plugin options. Closes #1068.\n- Print message on error when installing/uninstalling as a Windows service.\n- All non-error connect/disconnect messages are controlled by the\n  `connection_messages` option. Closes #772. Closes #613. Closes #537.\n\n# Library\n- Fix reconnect delay backoff behaviour. Closes #1027.\n- Don't call on_disconnect() twice if keepalive tests fail. Closes #1067.\n\n# Client\n- Always print leading zeros in mosquitto_sub when output format is hex.\n  Closes #1066.\n\n# Build\n- Fix building where TLS-PSK is not available. Closes #68.\n\n\n1.5.4 - 20181108\n================\n\n# Security\n- When using a TLS enabled websockets listener with \"require_certificate\"\n  enabled, the mosquitto broker does not correctly verify client certificates.\n  This is now fixed. All other security measures operate as expected, and in\n  particular non-websockets listeners are not affected by this. Closes #996.\n\n# Broker\n- Process all pending messages even when a client has disconnected. This means\n  a client that send a PUBLISH then DISCONNECT quickly, then disconnects will\n  have its DISCONNECT message processed properly and so no Will will be sent.\n  Closes #7.\n- $SYS/broker/clients/disconnected should never be negative. Closes #287.\n- Give better error message if a client sends a password without a username.\n  Closes #1015.\n- Fix bridge not honoring restart_timeout. Closes #1019.\n- Don't disconnect a client if an auth plugin denies access to SUBSCRIBE.\n  Closes #1016.\n\n# Library\n- Fix memory leak that occurred if mosquitto_reconnect() was used when TLS\n  errors were present. Closes #592.\n- Fix TLS connections when using an external event loop with\n  mosquitto_loop_read() and mosquitto_write(). Closes #990.\n\n# Build\n- Fix clients not being compiled with threading support when using CMake.\n  Closes #983.\n- Header fixes for FreeBSD. Closes #977.\n- Use _GNU_SOURCE to fix build errors in websockets and getaddrinfo usage.\n  Closes #862 and #933.\n- Fix builds on QNX 7.0.0. Closes #1018.\n\n\n1.5.3 - 20180925\n================\n\n# Security\n- Fix CVE-2018-12543. If a message is sent to Mosquitto with a topic that\n  begins with $, but is not $SYS, then an assert that should be unreachable is\n  triggered and Mosquitto will exit.\n\n# Broker\n- Elevate log level to warning for situation when socket limit is hit.\n- Remove requirement to use `user root` in snap package config files.\n- Fix retained messages not sent by bridges on outgoing topics at the first\n  connection. Closes #701.\n- Documentation fixes. Closes #520, #600.\n- Fix duplicate clients being added to by_id hash before the old client was\n  removed. Closes #645.\n- Fix Windows version not starting if include_dir did not contain any files.\n  Closes #566.\n- When an authentication plugin denied access to a SUBSCRIBE, the client would\n  be disconnected incorrectly. This has been fixed. Closes #1016.\n\n# Build\n- Various fixes to ease building.\n\n\n1.5.2 - 20180919\n================\n\n# Broker\n- Fix build when using WITH_ADNS=yes.\n- Fix incorrect call to setsockopt() for TCP_NODELAY. Closes #941.\n- Fix excessive CPU usage when the number of sockets exceeds the system limit.\n  Closes #948.\n- Fix for bridge connections when using WITH_ADNS=yes.\n- Fix round_robin false behaviour. Closes #481.\n- Fix segfault on HUP when bridges and security options are configured.\n  Closes #965.\n\n# Library\n- Fix situation where username and password is used with SOCKS5 proxy. Closes\n  #927.\n- Fix SOCKS5 behaviour when passing IP addresses. Closes #927.\n\n# Build\n- Make it easier to build without bundled uthash.h using \"WITH_BUNDLED_DEPS=no\".\n- Fix build with OPENSSL_NO_ENGINE. Closes #932.\n\n\n1.5.1 - 20180816\n================\n\n# Broker\n- Fix plugin cleanup function not being called on exit of the broker.\n  Closes #900.\n- Print more OpenSSL errors when loading certificates/keys fail.\n- Use AF_UNSPEC etc. instead of PF_UNSPEC to comply with POSIX. Closes #863.\n- Remove use of AI_ADDRCONFIG, which means the broker can be used on systems\n  where only the loopback interface is defined. Closes #869, Closes #901.\n- Fix IPv6 addresses not being able to be used as bridge addresses.\n  Closes #886.\n- All clients now time out if they exceed their keepalive*1.5, rather than\n  just reach it. This was inconsistent in two places.\n- Fix segfault on startup if bridge CA certificates could not be read.\n  Closes #851.\n- Fix problem opening listeners on Pi caused by unsigned char being default.\n  Found via #849.\n- ACL patterns that do not contain either %c or %u now produce a warning in\n  the log. Closes #209.\n- Fix bridge publishing failing when per_listener_settings was true. Closes\n  #860.\n- Fix `use_identity_as_username true` not working. Closes #833.\n- Fix UNSUBACK messages not being logged. Closes #903.\n- Fix possible endian issue when reading the `memory_limit` option.\n- Fix building for libwebsockets < 1.6.\n- Fix accessor functions for username and client id when used in plugin auth\n  check.\n\n# Library\n- Fix some places where return codes were incorrect, including to the\n  on_disconnect() callback. This has resulted in two new error codes,\n  MOSQ_ERR_KEEPALIVE and MOSQ_ERR_LOOKUP.\n- Fix connection problems when mosquitto_loop_start() was called before\n  mosquitto_connect_async(). Closes #848.\n\n# Clients\n- When compiled using WITH_TLS=no, the default port was incorrectly being set\n  to -1. This has been fixed.\n- Fix compiling on Mac OS X <10.12. Closes #813 and #240.\n\n# Build\n- Fixes for building on NetBSD. Closes #258.\n- Fixes for building on FreeBSD.\n- Add support for compiling with static libwebsockets library.\n\n\n1.5 - 20180502\n==============\n\n# Security\n- Fix memory leak that could be caused by a malicious CONNECT packet.\n  CVE-2017-7654. Closes #533493 (on Eclipse bugtracker)\n\n# Broker features\n- Add per_listener_settings to allow authentication and access control to be\n  per listener.\n- Add limited support for reloading listener settings. This allows settings\n  for an already defined listener to be reloaded, but port numbers must not be\n  changed.\n- Add ability to deny access to SUBSCRIBE messages as well as the current\n  read/write accesses. Currently for auth plugins only.\n- Reduce calls to malloc through the use of UHPA.\n- Outgoing messages with QoS>1 are no longer retried after a timeout period.\n  Messages will be retried when a client reconnects.  This change in behaviour\n  can be justified by considering when the timeout may have occurred.\n  * If a connection is unreliable and has dropped, but without one end\n    noticing, the messages will be retried on reconnection. Sending\n    additional PUBLISH or PUBREL would not have changed anything.\n  * If a client is overloaded/unable to respond/has a slow connection then\n    sending additional PUBLISH or PUBREL would not help the client catch\n    up. Once the backlog has cleared the client will respond. If it is not\n    able to catch up, sending additional duplicates would not help either.\n- Add use_subject_as_username option for certificate based client\n  authentication to use the entire certificate subject as a username, rather\n  than just the CN. Closes #469467.\n- Change sys tree printing output. This format shouldn't be relied upon and\n  may change at any time. Closes #470246.\n- Minimum supported libwebsockets version is now 1.3.\n- Add systemd startup notification and services. Closes #471053.\n- Reduce unnecessary malloc and memcpy when receiving a message and storing\n  it. Closes #470258.\n- Support for Windows XP has been dropped.\n- Bridge connections now default to using MQTT v3.1.1.\n- mosquitto_db_dump tool can now output some stats on clients.\n- Perform utf-8 validation on incoming will, subscription and unsubscription\n  topics.\n- new $SYS/broker/store/messages/count (deprecates $SYS/broker/messages/stored)\n- new $SYS/broker/store/messages/bytes\n- max_queued_bytes feature to limit queues by real size rather than\n  than just message count. Closes Eclipse #452919 or Github #100\n- Add support for bridges to be configured to only send notifications to the\n  local broker.\n- Add set_tcp_nodelay option to allow Nagle's algorithm to be disabled on\n  client sockets. Closes #433.\n- The behaviour of allow_anonymous has changed. In the old behaviour, the\n  default if not set was to allow anonymous access. The new behaviour is to\n  default is to allow anonymous access unless another security option is set.\n  For example, if password_file is set and allow_anonymous is not set, then\n  anonymous access will be denied. It is still possible to allow anonymous\n  access by setting it explicitly.\n\n# Broker fixes\n- Fix UNSUBSCRIBE with no topic is accepted on MQTT 3.1.1. Closes #665.\n- Produce an error if two bridges share the same local_clientid.\n- Miscellaneous fixes on Windows.\n- queue_qos0_messages was not observing max_queued_** limits\n- When using the include_dir configuration option sort the files\n  alphabetically before loading them.  Closes #17.\n- IPv6 is no longer disabled for websockets listeners.\n- Remove all build timestamp information including $SYS/broker/timestamp.\n  Close #651.\n- Correctly handle incoming strings that contain a NULL byte. Closes #693.\n- Use constant time memcmp for password comparisons.\n- Fix incorrect PSK key being used if it had leading zeroes.\n- Fix memory leak if a client provided a username/password for a listener with\n  use_identity_as_username configured.\n- Fix use_identity_as_username not working on websockets clients.\n- Don't crash if an auth plugin returns MOSQ_ERR_AUTH for a username check on\n  a websockets client. Closes #490.\n- Fix 08-ssl-bridge.py test when using async dns lookups. Closes #507.\n- Lines in the config file are no longer limited to 1024 characters long.\n  Closes #652.\n- Fix $SYS counters of messages and bytes sent when message is sent over\n  a Websockets. Closes #250.\n- Fix upgrade_outgoing_qos for retained message. Closes #534.\n- Fix CONNACK message not being sent for unauthorised connect on websockets.\n  Closes #8.\n- Maximum connections on Windows increased to 2048.\n- When a client with an in-use client-id connects, if the old client has a\n  will, send the will message. Closes #26.\n- Fix parsing of configuration options that end with a space. Closes #804.\n\n# Client library features\n- Outgoing messages with QoS>1 are no longer retried after a timeout period.\n  Messages will be retried when a client reconnects.\n- DNS-SRV support is now disabled by default.\n- Add mosquitto_subscribe_simple() This is a helper function to make\n  retrieving messages from a broker very straightforward. Examples of its use\n  are in examples/subscribe_simple.\n- Add mosquitto_subscribe_callback() This is a helper function to make\n  processing messages from a broker very straightforward. An example of its use\n  is in examples/subscribe_simple.\n- Connections now default to using MQTT v3.1.1.\n- Add mosquitto_validate_utf8() to check whether a string is valid UTF-8\n  according to the UTF-8 spec and to the additional restrictions imposed by\n  the MQTT spec.\n- Topic inputs are checked for UTF-8 validity.\n- Add mosquitto_userdata function to allow retrieving the client userdata\n  member variable. Closes #111.\n- Add mosquitto_pub_topic_check2(), mosquitto_sub_topic_check2(), and\n  mosquitto_topic_matches_sub2() which are identical to the similarly named\n  functions but also take length arguments.\n- Add mosquitto_connect_with_flags_callback_set(), which allows a second\n  connect callback to be used which also exposes the connect flags parameter.\n  Closes #738 and #128.\n- Add MOSQ_OPT_SSL_CTX option to allow a user specified SSL_CTX to be used\n  instead of the one generated by libmosquitto. This allows greater control\n  over what options can be set. Closes #715.\n- Add MOSQ_OPT_SSL_CTX_WITH_DEFAULTS to work with MOSQ_OPT_SSL_CTX and have\n  the default libmosquitto SSL_CTX configuration applied to the user provided\n  SSL_CTX. Closes #567.\n\n# Client library fixes\n- Fix incorrect PSK key being used if it had leading zeroes.\n- Initialise \"result\" variable as soon as possible in\n  mosquitto_topic_matches_sub. Closes #654.\n- No need to close socket again if setting non-blocking failed. Closes #649.\n- Fix mosquitto_topic_matches_sub() not correctly matching foo/bar against\n  foo/+/#. Closes #670.\n- SNI host support added.\n\n# Client features\n- Add -F to mosquitto_sub to allow the user to choose the output format.\n- Add -U to mosquitto_sub for unsubscribing from topics.\n- Add -c (clean session) to mosquitto_pub.\n- Add --retained-only to mosquitto_sub to exit after receiving all retained\n  messages.\n- Add -W to allow mosquitto_sub to stop processing incoming messages after a\n  timeout.\n- Connections now default to using MQTT v3.1.1.\n- Default to using port 8883 when using TLS.\n- mosquitto_sub doesn't continue to keep connecting if CONNACK tells it the\n  connection was refused.\n\n# Client fixes\n- Correctly handle empty files with \"mosquitto_pub -l\". Closes #676.\n\n# Build\n- Add WITH_STRIP option (defaulting to \"no\") that when set to \"yes\" will strip\n  executables and shared libraries when installing.\n- Add WITH_STATIC_LIBRARIES (defaulting to \"no\") that when set to \"yes\" will\n  build and install static versions of the client libraries.\n- Don't run TLS-PSK tests if TLS-PSK disabled at compile time. Closes #636.\n- Support for openssl versions 1.0.0 and 1.0.1 has been removed as these are\n  no longer supported by openssl.\n\n# Documentation\n- Replace mentions of deprecated 'c_rehash' with 'openssl rehash'.\n\n1.4.15 - 20180228\n=================\n\n# Security\n- Fix CVE-2017-7652. If a SIGHUP is sent to the broker when there are no more\n  file descriptors, then opening the configuration file will fail and security\n  settings will be set back to their default values.\n- Fix CVE-2017-7651. Unauthenticated clients can cause excessive memory use by\n  setting \"remaining length\" to be a large value. This is now mitigated by\n  limiting the size of remaining length to valid values. A \"memory_limit\"\n  configuration option has also been added to allow the overall memory used by\n  the broker to be limited.\n\n# Broker\n- Use constant time memcmp for password comparisons.\n- Fix incorrect PSK key being used if it had leading zeroes.\n- Fix memory leak if a client provided a username/password for a listener with\n  use_identity_as_username configured.\n- Fix use_identity_as_username not working on websockets clients.\n- Don't crash if an auth plugin returns MOSQ_ERR_AUTH for a username check on\n  a websockets client. Closes #490.\n- Fix 08-ssl-bridge.py test when using async dns lookups. Closes #507.\n- Lines in the config file are no longer limited to 1024 characters long.\n  Closes #652.\n- Fix $SYS counters of messages and bytes sent when message is sent over\n  a Websockets. Closes #250.\n- Fix upgrade_outgoing_qos for retained message. Closes #534.\n- Fix CONNACK message not being sent for unauthorised connect on websockets.\n  Closes #8.\n\n# Client library\n- Fix incorrect PSK key being used if it had leading zeroes.\n- Initialise \"result\" variable as soon as possible in\n  mosquitto_topic_matches_sub. Closes #654.\n- No need to close socket again if setting non-blocking failed. Closes #649.\n- Fix mosquitto_topic_matches_sub() not correctly matching foo/bar against\n  foo/+/#. Closes #670.\n\n# Clients\n- Correctly handle empty files with \"mosquitto_pub -l\". Closes #676.\n\n# Build\n- Don't run TLS-PSK tests if TLS-PSK disabled at compile time. Closes #636.\n\n\n1.4.14 - 20170710\n=================\n\n# Broker\n- Fix regression from 1.4.13 where persistence data was not being saved.\n\n\n1.4.13 - 20170627\n=================\n\n# Security\n- Fix CVE-2017-9868. The persistence file was readable by all local users,\n  potentially allowing sensitive information to be leaked.\n  This can also be fixed administratively, by restricting access to the\n  directory in which the persistence file is stored.\n\n# Broker\n- Fix for poor websockets performance.\n- Fix lazy bridges not timing out for idle_timeout. Closes #417.\n- Fix problems with large retained messages over websockets. Closes #427.\n- Set persistence file to only be readable by owner, except on Windows. Closes\n  #468.\n- Fix CONNECT check for reserved=0, as per MQTT v3.1.1 check MQTT-3.1.2-3.\n- When the broker stop, wills for any connected clients are now \"sent\". Closes\n  #477.\n- Auth plugins can be configured to disable the check for +# in\n  usernames/client ids with the auth_plugin_deny_special_chars option.\n  Partially closes #462.\n- Restrictions for CVE-2017-7650 have been relaxed - '/' is allowed in\n  usernames/client ids. Remainder of fix for #462.\n\n# Clients\n- Don't use / in auto-generated client ids.\n\n\n1.4.12 - 20170528\n=================\n\n# Security\n- Fix CVE-2017-7650, which allows clients with username or client id set to\n  '#' or '+' to bypass pattern based ACLs or third party plugins. The fix\n  denies message sending or receiving of messages for clients with a '#' or\n  '+' in their username or client id and if the message is subject to a\n  pattern ACL check or plugin check.\n  Patches for other versions are available at\n  https://mosquitto.org/files/cve/2017-7650/\n\n# Broker\n- Fix mosquitto.db from becoming corrupted due to client messages being\n  persisted with no stored message. Closes #424.\n- Fix bridge not restarting properly. Closes #428.\n- Fix uninitialised memory in gets_quiet on Windows. Closes #426.\n- Fix building with WITH_ADNS=no for systems that don't use glibc. Closes\n  #415.\n- Fixes to readme.md.\n- Fix deprecation warning for OpenSSL 1.1. PR #416.\n- Don't segfault on duplicate bridge names. Closes #446.\n- Fix CVE-2017-7650.\n\n\n1.4.11 - 20170220\n=================\n\n# Broker\n- Fix crash when \"lazy\" type bridge attempts to reconnect. Closes #259.\n- maximum_connections now applies to websockets listeners. Closes #271.\n- Allow bridges to use TLS with IPv6.\n- Don't error on zero length persistence files. Closes #316.\n- For http only websockets clients, close files served over http in all cases\n  when the client disconnects. Closes #354.\n- Fix error message when websockets http_dir directory does not exist.\n- Improve password utility error message. Closes #379.\n\n# Clients\n- Use of --ciphers no longer requires you to also pass --tls-version.\n  Closes #380.\n\n# Client library\n- Clients can now use TLS with IPv6.\n- Fix potential socket leakage when reconnecting. Closes #304.\n- Fix potential negative timeout being passed to pselect. Closes #329.\n\n\n1.4.10 - 20160816\n=================\n\n# Broker\n- Fix TLS operation with websockets listeners and libwebsockts 2.x. Closes\n  #186.\n- Don't disconnect client on HUP before reading the pending data. Closes #7.\n- Fix some $SYS messages being incorrectly persisted. Closes #191.\n- Support OpenSSL 1.1.0.\n- Call fsync after persisting data to ensure it is correctly written. Closes\n  #189.\n- Fix persistence saving of subscription QoS on big-endian machines.\n- Fix will retained flag handling on Windows. Closes #222.\n- Broker now displays an error if it is unable to open the log file. Closes\n  #234.\n\n# Client library\n- Support OpenSSL 1.1.0.\n- Fixed the C++ library not allowing SOCKS support to be used. Closes #198.\n- Fix memory leak when verifying a server certificate with a subjectAltName\n  section. Closes #237.\n\n# Build\n- Don't attempt to install docs when WITH_DOCS=no. Closes #184.\n\n\n1.4.9 - 20160603\n================\n\n# Broker\n- Ensure websockets clients that previously connected with clean session set\n  to false have their queued messages delivered immediately on reconnecting.\n  Closes #476314.\n- Reconnecting client with clean session set to false doesn't start with mid=1\n  again.\n- Will topic isn't truncated by one byte when using a mount_point any more.\n- Network errors are printed correctly on Windows.\n- Fix incorrect $SYS heap memory reporting when using ACLs.\n- Bridge config parameters couldn't contain a space, this has been fixed.\n  Closes #150.\n- Fix saving of persistence messages that start with a '/'. Closes #151.\n- Fix reconnecting for bridges that use TLS on Windows. Closes #154.\n- Broker and bridges can now cope with unknown incoming PUBACK, PUBREC,\n  PUBREL, PUBCOMP without disconnecting. Closes #57.\n- Fix websockets listeners not being able to bind to an IP address. Closes\n  #170.\n- mosquitto_passwd utility now correctly deals with unknown command line\n  arguments in all cases. Closes #169.\n- Fix publishing of $SYS/broker/clients/maximum\n- Fix order of #includes in lib/send_mosq.c to ensure struct mosquitto doesn't\n  differ between source files when websockets is being used. Closes #180.\n- Fix possible rare crash when writing out persistence file and a client has\n  incomplete messages inflight that it has been denied the right to publish.\n\n# Client library\n- Fix the case where a message received just before the keepalive timer\n  expired would cause the client to miss the keepalive timer.\n- Return value of pthread_create is now checked.\n- _mosquitto_destroy should not cancel threads that weren't created by\n  libmosquitto. Closes #166.\n- Clients can now cope with unknown incoming PUBACK, PUBREC, PUBREL, PUBCOMP\n  without disconnecting. Closes #57.\n- Fix mosquitto_topic_matches_sub() reporting matches on some invalid\n   subscriptions.\n\n# Clients\n- Handle some unchecked malloc() calls. Closes #1.\n\n# Build\n- Fix string quoting in CMakeLists.txt. Closes #4.\n- Fix building on Visual Studio 2015. Closes #136.\n\n\n1.4.8 - 20160214\n================\n\n# Broker\n- Wills published by clients connected to a listener with mount_point defined\n  now correctly obey the mount point. This was a potential security risk\n  because it allowed clients to publish messages outside of their restricted\n  mount point. This is only affects brokers where the mount_point option is in\n  use. Closes #487178.\n- Fix detection of broken connections on Windows. Closes #485143.\n- Close stdin etc. when daemonised. Closes #485589.\n- Fix incorrect detection of FreeBSD and OpenBSD. Closes #485131.\n\n# Client library\n- mosq->want_write should be cleared immediately before a call to SSL_write,\n  to allow clients using mosquitto_want_write() to get accurate results.\n\n\n1.4.7 - 20151221\n================\n\n# Broker\n- Fix support for libwebsockets 1.22.\n\n\n1.4.6 - 20151220\n================\n\n# Broker\n- Add support for libwebsockets 1.6.\n\n# Client library\n- Fix _mosquitto_socketpair() on Windows, reducing the chance of delays when\n  publishing. Closes #483979.\n\n# Clients\n- Fix \"mosquitto_pub -l\" stripping the final character on a line. Closes\n  #483981.\n\n\n1.4.5 - 20151108\n================\n\n# Broker\n- Fix possible memory leak if bridge using SSL attempts to connect to a\n  host that is not up.\n- Free unused topic tree elements (fix in 1.4.3 was incomplete). Closes\n  #468987.\n\n# Clients\n- \"mosquitto_pub -l\" now no longer limited to 1024 byte lines. Closes #478917.\n\n\n1.4.4 - 20150916\n================\n\n# Broker\n- Don't leak sockets when outgoing bridge with multiple addresses cannot\n  connect. Closes #477571.\n- Fix cross compiling of websockets. Closes #475807.\n- Fix memory free related crashes on openwrt. Closes #475707.\n- Fix excessive calls to message retry check.\n\n\n1.4.3 - 20150818\n================\n\n# Broker\n- Fix incorrect bridge notification on initial connection. Closes #467096.\n- Build fixes for OpenBSD.\n- Fix incorrect behaviour for autosave_interval, most noticeable for\n  autosave_interval=1. Closes #465438.\n- Fix handling of outgoing QoS>0 messages for bridges that could not be sent\n  because the bridge connection was down.\n- Free unused topic tree elements. Closes #468987.\n- Fix some potential memory leaks. Closes #470253.\n- Fix potential crash on libwebsockets error.\n\n# Client library\n- Add missing error strings to mosquitto_strerror.\n- Handle fragmented TLS packets without a delay. Closes #470660.\n- Fix incorrect loop timeout being chosen when using threaded interface and\n  keepalive = 0. Closes #471334.\n- Increment inflight messages count correctly. Closes #474935.\n\n# Clients\n- Report error string on connection failure rather than error code.\n\n\n1.4.2 - 20150507\n================\n\n# Broker\n- Fix bridge prefixes only working for the first outgoing message. Closes\n  #464437.\n- Fix incorrect bridge connection notifications on local broker.\n- Fix persistent db writing on Windows. Closes #464779.\n- ACLs are now checked before sending a will message.\n- Fix possible crash when using bridges on Windows. Closes #465384.\n- Fix parsing of auth_opt_ arguments with extra spaces/tabs.\n- Broker will return CONNACK rc=5 when a username/password is not authorised.\n  This was being incorrectly set as rc=4.\n- Fix handling of payload lengths>4096 with websockets.\n\n# Client library\n- Inflight message count wasn't being decreased for outgoing messages using\n  QoS 2, meaning that only up to 20 QoS 2 messages could be sent. This has\n  been fixed. Closes #464436.\n- Fix CMake dependencies for C++ wrapper building. Closes #463884.\n- Fix possibility of select() being called with a socket that is >FD_SETSIZE.\n  This is a fix for #464632 that will be followed up by removing the select()\n  call in a future version.\n- Fix calls to mosquitto_connect*_async() not completing.\n\n\n1.4.1 - 20150403\n================\n\n# Broker\n- Fix possible crash under heavy network load. Closes #463241.\n- Fix possible crash when using pattern ACLs.\n- Fix problems parsing config strings with multiple leading spaces. Closes\n  #462154.\n- Websockets clients are now periodically disconnected if they have not\n  maintained their keepalive timer. Closes #461619.\n- Fix possible minor memory leak on acl parsing.\n\n# Client library\n- Inflight limits should only apply to outgoing messages. Closes #461620.\n- Fix reconnect bug on Windows. Closes #463000.\n- Return -1 on error from mosquitto_socket(). Closes #461705.\n- Fix crash on multiple calls to mosquitto_lib_init/mosquitto_lib_cleanup.\n  Closes #462780.\n- Allow longer paths on Windows. Closes #462781.\n- Make _mosquitto_mid_generate() thread safe. Closes #463479.\n\n\n1.4 - 20150218\n==============\n\n# Important changes\n- Websockets support in the broker.\n- Bridge behaviour on the local broker has changed due to the introduction of\n  the local_* options. This may affect you if you are using authentication\n  and/or ACLs with bridges.\n- The default TLS behaviour has changed to accept all of TLS v1.2, v1.1 and\n  v1.0, rather than only only one version of the protocol. It is still\n  possible to restrict a listener to a single version of TLS.\n- The Python client has been removed now that the Eclipse Paho Python client\n  has had a release.\n- When a durable client reconnects, its queued messages are now checked\n  against ACLs in case of a change in username/ACL state since it last\n  connected.\n- New use_username_as_clientid option on the broker, for preventing hijacking\n  of a client id.\n- The client library and clients now have experimental SOCKS5 support.\n- Wildcard TLS certificates are now supported for bridges and clients.\n- The clients have support for config files with default options.\n- Client and client libraries have support for MQTT v3.1.1.\n- Bridge support for MQTT v3.1.1.\n\n\n# Broker\n- Websockets support in the broker.\n- Add local_clientid, local_username, local_password for bridge connections to\n  authenticate to the local broker.\n- Default TLS mode now accepts TLS v1.2, v1.1 and v1.0.\n- Support for ECDHE-ECDSA family ciphers.\n- Fix bug #1324411, which could have had unexpected consequences for delayed\n  messages in rare circumstances.\n- Add support for \"session present\" in CONNACK messages for MQTT v3.1.1.\n- Remove strict protocol #ifdefs.\n- Change $SYS/broker/clients/active -> $SYS/broker/clients/connected\n- Change $SYS/broker/clients/inactive -> $SYS/broker/clients/disconnected\n- When a durable client reconnects, its queued messages are now checked\n  against ACLs in case of a change in username/ACL state since it last\n  connected.\n- libuuid is used to generate client ids, where it is available, when an MQTT\n  v3.1.1 client connects with a zero length client id.\n- Anonymous clients are no longer accidently disconnected from the broker\n  after a SIGHUP.\n- mosquitto_passwd now supports -b (batch mode) to allow the password to be\n  provided at the command line.\n- Removed $SYS/broker/changeset. This was intended for use with debugging, but\n  in practice is of no use.\n- Add support for use_username_as_clientid which can be used with\n  authentication to restrict ownership of client ids and hence prevent one\n  client disconnecting another by using the same client id.\n- When \"require_certificate\" was false, the broker was incorrectly asking for\n  a certificate (but not checking it). This caused problems with some clients\n  and has been fixed so the broker no longer asks.\n- When using syslog logging on non-Windows OSs, it is now possible to specify\n  the logging facility to one of local0-7 instead of the default \"daemon\".\n- The bridge_attempt_unsubscribe option has been added, to allow the sending\n  of UNSUBSCRIBE requests to be disabled for topics with \"out\" direction.\n  Closes bug #456899.\n- Wildcard TLS certificates are now supported for bridges.\n- Support for \"hour\" client expiration lengths for the\n  persistent_client_expiration option. Closes bug #425835.\n- Bridge support for MQTT v3.1.1.\n- Root privileges are now dropped after starting listeners and loading\n  certificates/private keys, to allow private keys to have their permissions\n  restricted to the root user only. Closes bug #452914.\n- Usernames and topics given in ACL files can now include a space. Closes bug\n  #431780.\n- Fix hang if pattern acl contains a %u but an anonymous client connect.\n  Closes bug #455402.\n- Fix man page installation with cmake. Closes bug #458843.\n- When using \"log_dest file\" the output file is now flushed periodically.\n\n# Clients\n- Both clients can now load default configuration options from a file.\n- Add -C option to mosquitto_sub to allow the client to quit after receiving a\n  certain count of messages. Closes bug #453850.\n- Add --proxy SOCKS5 support for both clients.\n- Pub client supports setting its keepalive. Closes bug #454852.\n- Add support for config files with default options.\n- Add support for MQTT v3.1.1.\n\n# Client library\n- Add experimental SOCKS5 support.\n- mosquitto_loop_forever now quits after a fatal error, rather than blindly\n  retrying.\n- SRV support is now not compiled in by default.\n- Wildcard TLS certificates are now supported.\n- mosquittopp now has a virtual destructor. Closes bug #452915.\n- Add support for MQTT v3.1.1.\n- Don't quit mosquitto_loop_forever() if broker not available on first\n  connect. Closes bug #453293, but requires more work.\n- Don't reset queued messages state on CONNACK. Fixes bug with duplicate\n  messages on connection.\n\n\n1.3.5 - 20141008\n================\n\n# Broker\n- Fix possible memory leak when using a topic that has a leading slash. Fixes\n  bug #1360985.\n- Fix saving persistent database on Windows.\n- Temporarily disable ACL checks on subscriptions when using MQTT v3.1.1. This\n  is due to the complexity of checking wildcard ACLs against wildcard\n  subscriptions. This does not have a negative impact on security because\n  checks are still made before a message is sent to a client.\n  Fixes bug #1374291.\n- When using -v and the broker receives a SIGHUP, verbose logging was being\n  disabled. This has been fixed.\n\n# Client library\n- Fix mutex being incorrectly passed by value. Fixes bug #1373785.\n\n\n1.3.4 - 20140806\n================\n\n# Broker\n- Don't ask client for certificate when require_certificate is false.\n- Backout incomplete functionality that was incorrectly included in 1.3.2.\n\n\n1.3.3 - 20140801\n================\n\n# Broker\n- Fix incorrect handling of anonymous bridges on the local broker.\n\n\n1.3.2 - 20140713\n================\n\n# Broker\n- Don't allow access to clients when authenticating if a security plugin\n  returns an application error. Fixes bug #1340782.\n- Ensure that bridges verify certificates by default when using TLS.\n- Fix possible crash when using pattern ACLs that do not include a %u and\n  clients that connect without a username.\n- Fix subscriptions being deleted when clients subscribed to a topic beginning\n  with a $ but that is not $SYS.\n- When a durable client reconnects, its queued messages are now checked\n  against ACLs in case of a change in username/ACL state since it last\n  connected.\n- Fix bug #1324411, which could have had unexpected consequences for delayed\n  messages in rare circumstances.\n- Anonymous clients are no longer accidently disconnected from the broker\n  after a SIGHUP.\n\n# Client library\n- Fix topic matching edge case.\n- Fix callback deadlocks after calling mosquitto_disconnect(), when using the\n  threaded interfaces. Closes bug #1313725.\n- Fix SRV support when building with CMake.\n- Remove strict protocol #ifdefs.\n\n# General\n- Use $(STRIP) for stripping binaries when installing, to allow easier cross\n  compilation.\n\n\n1.3.1 - 20140324\n================\n\n# Broker\n- Prevent possible crash on client reconnect. Closes bug #1294108.\n- Don't accept zero length unsubscription strings (MQTT v3.1.1 fix)\n- Don't accept QoS 3 (MQTT v3.1.1 fix)\n- Don't disconnect clients immediately on HUP to give chance for all data to\n  be read.\n- Reject invalid un/subscriptions e.g. foo/+bar #/bar.\n- Take more care not to disconnect clients that are sending large messages.\n\n# Client library\n- Fix socketpair code on the Mac.\n- Fix compilation for WITH_THREADING=no.\n- Break out of select() when calling mosquitto_loop_stop().\n- Reject invalid un/subscriptions e.g. foo/+bar #/bar.\n- Add mosquitto_threaded_set().\n\n# Clients\n- Fix keepalive value on mosquitto_pub.\n- Fix possibility of mosquitto_pub not exiting after sending messages when\n  using -l.\n\n\n1.3 - 20140316\n==============\n\n# Broker\n- The broker no longer ignores the auth_plugin_init() return value.\n- Accept SSLv2/SSLv3 HELLOs when using TLSv1, whilst keeping SSLv2 and SSLv3\n  disabled. This increases client compatibility without sacrificing security.\n- The $SYS tree can now be disabled at runtime as well as at compile time.\n- When remapping bridged topics, only check for matches when the message\n  direction is correct. This allows two identical topics to be remapped\n  differently for both in and out.\n- Change \"$SYS/broker/heap/current size\" to \"$SYS/broker/heap/current\" for\n  easier parsing.\n- Change \"$SYS/broker/heap/maximum size\" to \"$SYS/broker/heap/maximum\" for\n  easier parsing.\n- Topics are no longer normalised from e.g a///topic to a/topic. This matches\n  the behaviour as clarified by the Oasis MQTT spec. This will lead to\n  unexpected behaviour if you were using topics of this form.\n- Log when outgoing messages for a client begin to drop off the end of the\n  queue.\n- Bridge clients are recognised as bridges even after reloading from\n  persistence.\n- Basic support for MQTT v3.1.1. This does not include being able to bridge to\n  an MQTT v3.1.1 broker.\n- Username is displayed in log if present when a client connects.\n- Support for 0 length client ids (v3.1.1 only) that result in automatically\n  generated client ids on the broker (see option allow_zero_length_clientid).\n- Ability to set the prefix of automatically generated client ids (see option\n  auto_id_prefix).\n- Add support for TLS session resumption.\n- When using TLS, the server now chooses the cipher to use when negotiating\n  with the client.\n- Weak TLS ciphers are now disabled by default.\n\n# Client library\n- Fix support for Python 2.6, 3.0, 3.1.\n- Add support for un/subscribing to multiple topics at once in un/subscribe().\n- Clients now close their socket after sending DISCONNECT.\n- Python client now contains its version number.\n- C library mosquitto_want_write() now supports TLS clients.\n- Fix possible memory leak in C/C++ library when communicating with\n  a broker that doesn't follow the spec.\n- Return strerror() through mosquitto_strerror() to make error printing\n  easier.\n- Topics are no longer normalised from e.g a///topic to a/topic. This matches\n  the behaviour as clarified by the Oasis MQTT spec. This will lead to\n  unexpected behaviour if you were using topics of this form.\n- Add support for SRV lookups.\n- Break out of select() on publish(), subscribe() etc. when using the threaded\n  interface. Fixes bug #1270062.\n- Handle incoming and outgoing messages separately. Fixes bug #1263172.\n- Don't terminate threads on mosquitto_destroy() when a client is not using\n  the threaded interface but does use their own thread. Fixes bug #1291473.\n\n# Clients\n- Add --ciphers to allow specifying which TLS ciphers to support.\n- Add support for SRV lookups.\n- Add -N to sub client to suppress printing of EOL after the payload.\n- Add -T to sub client to suppress printing of a topic hierarchy.\n\n\n1.2.3 - 20131202\n================\n\n# Broker\n- Don't always attempt to call read() for SSL clients, irrespective of whether\n  they were ready to read or not. Reduces syscalls significantly.\n- Possible memory leak fixes.\n- Further fix for bug #1226040: multiple retained messages being delivered for\n  subscriptions ending in #.\n- Fix bridge reconnections when using multiple bridge addresses.\n\n# Client library\n- Fix possible memory leak in C/C++ library when communicating with\n  a broker that doesn't follow the spec.\n- Block in Python loop_stop() until all messages are sent, as the\n  documentation states should happen.\n- Fix for asynchronous connections on Windows. Closes bug #1249202.\n- Module version is now available in mosquitto.py.\n\n# Clients\n- mosquitto_sub now uses fwrite() instead of printf() to output messages, so\n  messages with NULL characters aren't truncated.\n\n\n1.2.2 - 20131021\n================\n\n# Broker\n- Fix compliance with max_inflight_messages when a non-clean session client\n  reconnects. Closes one of the issues on bug #1237389.\n\n# Client library\n- Fix incorrect inflight message accounting, which caused messages to go\n  unsent. Partial fix for bug #1237351.\n- Fix potential memory corruption when sending QoS>0 messages at a high rate\n  using the threaded interface. Further fix for #1237351.\n- Fix incorrect delay scaling when exponential_backoff=true in\n  mosquitto_reconnect_delay_set().\n- Some pep8 fixes for Python.\n\n\n1.2.1 - 20130918\n================\n\n# Broker\n- The broker no longer ignores the auth_plugin_init() return value. Closes\n  bug #1215084.\n- Use RTLD_GLOBAL when opening authentication plugins on posix systems. Fixes\n  resolving of symbols in libraries used by authentication plugins.\n- Add/fix some config documentation.\n- Fix ACLs for topics with $SYS.\n- Clients loaded from the persistence file on startup were not being added to\n  the client hash, causing subtle problems when the client reconnected,\n  including ACLs failing. This has been fixed.\n- Add note to mosquitto-tls man page stating that certificates need to be\n  unique. Closes bug #1221285.\n- Fix incorrect retained message delivery when using wildcard subs in some\n  circumstances. Fixes bug #1226040.\n\n# Client library\n- Fix support for Python 2.6, 3.0, 3.1.\n- Fix TLS subjectAltName verification and segfaults.\n- Handle EAGAIN in Python on Windows. Closes bug #1220004.\n- Fix compilation when using WITH_TLS=no.\n- Don't fail reconnecting in Python when broker is temporarily unavailable.\n\n\n1.2 - 20130708\n==============\n\n# Broker\n- Replace O(n) username lookup on CONNECT with a roughly O(1) hashtable version.\n- It is now possible to disable $SYS at compile time.\n- Add dropped publish messages to load tree in $SYS. Closes bug #1183318.\n- Add support for logging SUBSCRIBE/UNSUBSCRIBE events.\n- Add \"log_dest file\" logging support.\n- Auth plugin ACL check function now passes the client id as well as username\n  and password.\n- The queue_qos0_messages option wasn't working correctly, this has now been\n  fixed. Closes bug #1125200.\n- Don't drop all messages for disconnected durable clients when\n  max_queued_messages=0.\n- Add support for \"log_type all\".\n- Add support for \"-v\" option on the command line to provide the equivalent of\n  \"log_type all\" without needing a config file.\n- Add the \"upgrade_outgoing_qos\" option, a non-standard feature.\n- Persistence data is now written to a temporary file which is atomically\n  renamed on completion, so a crash during writing will not produce a corrupt\n  file.\n- mosquitto.conf is now installed as mosquitto.conf.example\n- Configuration file errors are now reported with filename and line number.\n- The broker now uses a monotonic clock if available, to avoid changes in time\n  causing client disconnections or message retries.\n- Clean session and keepalive status are now display the log when a client\n  connects.\n- Add support for TLSv1.2 and TLSv1.1.\n- Clients that connect with zero length will topics are now rejected.\n- Add the ability to set a maximum allowed PUBLISH payload size.\n- Fix an ACL with topic \"#\" incorrectly granting access to $SYS.\n- Fix retained messages incorrectly being set on wildcard topics, leading to\n  duplicate retained messages being sent on subscription. Closes bug #1116233.\n- Don't discard listener values when no \"port\" option given. Closes bug\n  #1131406.\n- Client password check was always failing when security was being reapplied\n  after a config reload. This meant that all clients were being disconnected.\n  This has been fixed.\n- Fix build when WITH_TLS=no. Closes bug #1174971.\n- Fix single outgoing packets not being sent in a timely fashion if they were\n  not sent in one call to write(). Closes bug #1176796.\n- Fix remapping of messages for clients connected to a listener with\n  mount_point set. Closes bug #1180765.\n- Fix duplicate retained messages being sent for some wildcard patterns.\n- If a client connects with a will topic to which they do not have write\n  access, they are now disconnected with CONNACK \"not authorised\".\n- Fix retained messages on topic foo being incorrectly delivered to\n  subscriptions of /#\n- Fix handling of SSL errors on SSL_accept().\n- Fix handling of QoS 2 messages on client reconnect.\n- Drop privileges now sets supplementary groups correctly.\n- Fix load reporting interval (is now 60s).\n- Be strict with malformed PUBLISH packets - clients are now disconnected\n  rather than the packet discarded. This goes inline with future OASIS spec\n  changes and makes other changes more straightforward.\n- Process incoming messages denied by ACL properly so that clients don't keep\n  resending them.\n\n- Add support for round_robin bridge option.\n- Add bridge support for verifying remote server certificate subject against\n  the remote hostname.\n- Fix problem with out of order calls to free() when restarting a lazy bridge.\n- The broker now attempts to resolve bind_address and bridge addresses\n  immediately when parsing the config file in order to detect invalid hosts.\n- Bridges now set their notification state before attempting to connect, so if\n  they fail to connect the state can still be seen.\n- Fix bridge notification payload length - no need to send a null byte.\n\n- mosquitto_passwd utility now reports errors more clearly.\n- Fix \"mosquitto_passwd -U\".\n\n# Client library\n- Add support for TLSv1.2 and TLSv1.1, except for on the Python module.\n- Add support for verifying remote server certificate subject against the\n  remote hostname.\n- Add mosquitto_reconnect_async() support and make asynchronous connections\n  truly asynchronous rather than simply deferred. DNS lookups are still\n  blocking, so asynchronous connections require an IP address instead of\n  hostname.\n- Allow control of reconnection timeouts in mosquitto_loop_forever() and after\n  mosquitto_loop_start() by using mosquitto_reconnect_delay_set().\n- Fix building on Android NDK.\n- Re-raise unhandled errors in Python so as not to provide confusing error\n  messages later on.\n- Python module supports IPv6 connections.\n- mosquitto_sub_topic_tokenise() was behaving incorrectly if the last topic\n  hierarchy had only a single character. This has been fixed. Closes bug\n  #1163348.\n- Fix possible crash after disconnects when using the threaded interface with\n  TLS.\n- Allow build/install without Python. Closes bug #1174972.\n- Add support for binding connection to a local interface.\n- Implement maximum inflight messages handling.\n- Fix Python client not handling will_payload==None.\n- Fix potential memory leak when setting username/password.\n- Fix handling of QoS 2 messages on reconnect.\n- Improve handling of mosquitto_disconnect() with threaded mode.\n\n\n# Clients\n- Add support for TLSv1.2 and TLSv1.1.\n- Sub client can now suppress printing of messages with the retain bit set.\n- Add support for binding connection to a local interface.\n- Implement maximum inflight messages handling for the pub client.\n\n\n1.1.3 - 20130211\n================\n\n# Broker\n- mosquitto_passwd utility now uses tmpfile() to generate its temporary data\n  storage file. It also creates a backup file that can be used to recover data\n  if an errors occur.\n\n# Other\n- Build script fixes to help packaging on Debian.\n\n\n1.1.2 - 20130130\n================\n\n# Client library\n- Fix tls_cert_reqs not being set to SSL_VERIFY_PEER by default. This meant\n  that clients were not verifying the server certificate when connecting over\n  TLS. This affects the C, C++ and Python libraries.\n\n\n1.1.1 - 20130116\n================\n\n# Broker\n- Fix crash on reload if using acl patterns.\n\n# Client library\n- Fix static C++ functions not being exported on Windows. Fixes bug #1098256.\n\n\n1.1 - 20121219\n==============\n\n# Broker\n- Add $SYS/broker/messages/dropped\n- Add $SYS/broker/clients/expired\n- Replace $SYS/broker/+/per second/+ with moving average versions published at\n  $SYS/broker/load/#\n- Add $SYS/broker/load/sockets/+ and $SYS/broker/load/connections/+\n- Documentation on password file format has been fixed.\n- Disable SSL compression. This reduces memory usage significantly and removes\n  the possibility of CRIME type attacks.\n- Enable SSL_MODE_RELEASE_BUFFERS mode to reduce SSL memory usage further.\n- Add allow_duplicate_messages option.\n- ACL files can now have comment lines with # as the first character.\n- Display message on startup about which config is being loaded.\n- Fix max_inflight_messages and max_queued_messages not being applied.\n- Fix documentation error in mosquitto.conf.\n- Ensure that QoS 2 queued messages are sent out in a timely manner.\n- Local bridges now act on clean_session correctly.\n- Local bridges with clean_session==false now remove unused subscriptions on\n  broker restart.\n- The $SYS/broker/heap/# messages now no longer include \"bytes\" as part of the\n  string for ease of use.\n\n# Client library\n- Free memory used by OpenSSL in mosquitto_lib_cleanup() where possible.\n- Change WebSocket subprotocol name to mqttv3.1 to make future changes easier\n  and for compatibility with other implementations.\n- mosquitto_loop_read() and mosquitto_loop_write() now handle errors\n  themselves rather than having mosquitto_loop() handle their errors. This\n  makes using them in a separate event loop more straightforward.\n- Add mosquitto_loop_forever() / loop_forever() function call to make simple\n  clients easier.\n- Disable SSL compression. This reduces memory usage significantly and removes\n  the possibility of CRIME type attacks.\n- Enable SSL_MODE_RELEASE_BUFFERS mode to reduce SSL memory usage further.\n- mosquitto_tls_set() will now return an error or raise an exception\n  immediately if the CA certificate or client certificate/key cannot be\n  accessed.\n- Fix potential memory leaks on connection failures.\n- Don't produce return error from mosquitto_loop() if a system call is\n  interrupted. This prevents disconnects/reconnects in threaded mode and\n  simplifies non-threaded client handling.\n- Ignore SIGPIPE to prevent unnecessary client quits in threaded mode.\n- Fix document error for mosquitto_message_retry_set().\n- Fix mosquitto_topic_matches_sub() for subscriptions with + as the final\n  character. Fixes bug #1085797.\n- Rename all \"obj\" parameters to \"userdata\" for consistency with other\n  libraries.\n- Reset errno before network read/write to ensure EAGAIN isn't mistakenly\n  returned.\n- The message queue length is now tracked and used to determine the maximum\n  number of packets to process at once. This removes the need for the\n  max_packets parameter which is now unused.\n- Fix incorrect error value in Python error_string() function. Fixes bug\n  #1086777.\n- Reset last message in/out timer in Python module when we send a PINGREQ.\n  Fixes too-early disconnects.\n\n# Clients\n- Clients now display their own version number and library version number in\n  their help messages.\n- Fix \"mosquitto_pub -l -q 2\" disconnecting before all messages were\n  transmitted.\n- Fix potential out-of-bounds array access with client ids. Fixes bug\n  #1083182.\n\n# Other\n- mosquitto_passwd can now convert password files with plain text files to\n  hashed versions.\n\n\n1.0.5 - 20121103\n================\n\n# Broker\n- Fix crash when the broker has use_identity_as_username set to true but a\n  client connects without a certificate.\n- mosquitto_passwd should only be installed if WITH_TLS=yes.\n\n# Library\n- Use symbolic errno values rather than numbers in Python module to avoid\n  cross platform issues (incorrect errno on Mac OS).\n\n# Other\n- Build script fixes for FreeBSD.\n\n\n1.0.4 - 20121017\n================\n\n# Broker\n- Deal with poll() POLLIN/POLLOUT before POLL[RD]HUP to correctly handle the\n  case where a client sends data and immediately closes its socket.\n\n# Library\n- Fix memory leak with messages of QoS=2. Fixes bug #1064981.\n- Fix potential thread synchronisation problem with outgoing packets in the\n  Python module. Fixes bug #1064977.\n\n# Clients\n- Fix \"mosquitto_sub -l\" incorrectly only sending one message per second.\n\n\n1.0.3 - 20120927\n================\n\n# Broker\n- Fix loading of psk files.\n- Don't return an error when reloading config if an ACL file isn't defined.\n  This was preventing psk files being reloaded.\n- Clarify meaning of $SYS/broker/clients/total in mosquitto(8) man page.\n- Clarify meaning of $SYS/broker/messages/stored in mosquitto(8) man page.\n- Fix non-retained message delivery when subscribing to #.\n- Fix retained message delivery for subs to foo/# with retained messages at\n  foo.\n- Include the filename in password/acl file loading errors.\n\n# Library\n- Fix possible AttributeError when self._sock == None in Python module.\n- Fix reconnecting after a timeout in Python module.\n- Fix reconnecting when there were outgoing packets in the queue in the Python\n  module.\n- Fix problem with mutex initialisation causing crashes on some Windows\n  installations.\n\n\n1.0.2 - 20120919\n================\n\n# Broker\n- If the broker was configured for persistence, a durable client had a\n  subscription to topics in $SYS/# and had messages in its queue when the\n  broker restarted, then the persistent database would have messages missing\n  and so the broker would not restart properly. This has been fixed.\n\n# Library\n- Fix threading problem on some systems.\n\n# Tests\n- Close socket after 08-ssl-connect-no-auth-wrong-ca.py test to prevent\n  subsequent tests having problems.\n\n# Build scripts\n- Install pskfile.example in CMake. Fixes bug #1037504.\n\n# Other\n- Fix db_dump parameter printing message store and sub chunks.\n\n\n1.0.1 - 20120815\n================\n\n# Broker\n- Fix default log_dest when running as a Windows service.\n\n# Client library\n- Fix incorrect parameters in Python on_log() callback call. Fixes bug\n  #1036818.\n\n# Clients\n- Clients now don't display TLS/TLS-PSK usage help if they don't support it.\n\n# Build scripts\n- Fix TLS-PSK support in the CMake build files.\n- Fix man page installation in the CMake build files.\n- Fix SYSCONFDIR in cmake on *nix when installing to /usr. Fixes bug #1036908.\n\n# Documentation\n- Fix mqtt/MQTT capitalisation in man pages.\n- Update compiling.txt.\n- Fix incorrect callback docs in mosquitto.py. Fixes bug #1036607.\n- Fix various doc typos and remove obsolete script. Fixes bug #1037088.\n\n\n1.0 - 20120814\n==============\n\n# Broker\n\n- Add SSL/TLS support.\n- Add TLS-PSK support, providing a simpler encryption method for constrained\n  devices.\n- Passwords are now salted+hashed if compiled with WITH_TLS (recommended).\n- Add mosquitto_passwd for handling password files.\n- Add $SYS/broker/publish/messages/{sent|received} to show the number of\n  PUBLISH messages sent/received.\n- Add $SYS/broker/publish/bytes/{sent|received} to show the number of\n  PUBLISH bytes sent/received.\n- Add reload parameter for security init/cleanup functions.\n- Add option for expiring disconnected persistent clients.\n- Add option for queueing of QoS 0 messages when persistent clients are\n  disconnected.\n- Enforce client id limits in the broker (only when WITH_STRICT_PROTOCOL is\n  defined).\n- Fix reloading of log configuration.\n- Add support for try_private config option for bridge connections.\n- Add support for autosave_on_changes config option.\n- Add support for include_dir config option.\n- Add support for topic remapping.\n- Usernames were being lost when a non clean-session client reconnected,\n  potentially causing problems with ACLs. This has been fixed.\n- Significant improvement to memory handling on Windows.\n- Bridges with outgoing topics will now set the retain flag correctly so that\n  messages will be retained on the remote broker.\n- Incoming bridge connections are now detected by checking if bit 8 of the\n  protocol version number is set. This requires support from the remote broker.\n- Add support for notification_topic option.\n- Add $SYS/broker/subscriptions/count and $SYS/broker/retained messages/count.\n- Add restart_timeout to control the amount of time an automatic bridge will\n  wait before reconnecting.\n- Overlapping subscriptions are now handled properly. Fixes bug #928538.\n- Fix reloading of persistence_file and persistence_location.\n- Fix broker crash on incorrect protocol number.\n- Fix missing COMPAT_ECONNRESET define on Windows.\n- Clients that had disconnected were not always being detected immediately on\n  Linux. This has been fixed.\n- Don't save $SYS messages to the on-disk persistent db. All $SYS messages\n  should be reconstructed on a restart. This means bridge connection\n  notifications will now be correct on a restart.\n- Fix reloading of bridge clients from the persistent db. This means that\n  outgoing bridged topics should always work.\n- Local bridges are now no longer restricted by local ACLs.\n- Discard publish messages with zero length topics.\n- Drop to \"mosquitto\" user even if no config file specified.\n- Don't incorrectly allow topic access if ACL patterns but no normal ACL rules\n  are defined.\n\n# Client library\n\n- Add SSL/TLS support.\n- Add TLS-PSK support, providing a simpler encryption method for constrained\n  devices.\n- Add javascript/websockets client library.\n- Add \"struct mosquitto *mosq\" parameter for all callbacks in the client\n  library. This is a binary incompatible change so the soversion of the\n  libraries has been incremented. The new parameter should make it easier to\n  use callbacks in practice.\n- Add mosquitto_want_write() for use when using own select() loop with\n  mosquitto_socket().\n- Add mosquitto_connect_async() to provide a non-blocking connect client call.\n- Add mosquitto_user_data_set() to allow user data pointer to be updated.\n- Add \"int rc\" parameter to disconnect callback to indicate whether disconnect\n  was unexpected or the result of calling mosquitto_disconnect().\n- Add mosquitto_strerror() for obtaining a string description of error numbers.\n- Add mosquitto_connack_string() for obtaining a string description of MQTT\n  connection results.\n- Add mosquitto_will_clear() and change mosquitto_will_set() to only set the\n  will.\n- Add mosquitto_sub_topic_tokenise() and mosquitto_sub_topic_tokens_free()\n  utility functions to tokenise a subscription/topic string into a string\n  array.\n- Add mosquitto_topic_matches_sub() to check whether a topic matches a\n  subscription.\n- Replaced mosquitto_log_init() with mosquitto_log_callback_set() to allow\n  clients to decide what to do with log messages.\n- Client will now disconnect itself from the broker if it doesn't receive a\n  PINGRESP in the keepalive period after sending a PINGREQ.\n- Client will now send a PINGREQ if it has not received a message from the\n  broker in keepalive seconds.\n- mosquitto_new() will now generate a random client id if the id parameter is\n  NULL.\n- Added max_packets to mosquitto_loop(), mosquitto_loop_read() and\n  mosquitto_loop_write() to control the maximum number of packets that are\n  handled per call.\n- Payload parameters are now void * instead of uint8_t *.\n- The clean_session parameter has been moved from mosquitto_connect() to\n  mosquitto_new() because it is a client parameter rather than a connection\n  parameter.\n- Functions now use int instead of uint*_t where possible.\n- mosquitto_new() now sets errno to indicate failure type.\n- Return MOSQ_ERR_INVAL on zero length topic.\n- Fix automatic client id generation on Windows.\n- mosquitto_loop_misq() can now return MOSQ_ERR_NO_CONN.\n- Compile static library as well as dynamic library with default makefiles.\n- Rename C++ namespace from mosquittopp to mosqpp to remove ambiguity.\n- C++ lib_init(), lib_version() and lib_cleanup() are now in the mosqpp\n  namespace directly, not mosquittopp class members.\n- The Python library is now written in pure Python and so no longer depends on\n  libmosquitto.\n- The Python library includes SSL/TLS support.\n- The Python library should now be compatible with Python 3.\n\n# Other\n\n- Fix db_dump reading of retained messages.\n- Add example of logging all messages to mysql.\n- Add C++ client example.\n- Fix potential buffer overflow in pub/sub clients.\n- Add \"make binary\" target that doesn't make documents.\n- Add \"--help\" arguments to pub/sub clients.\n- Fix building on Solaris.\n\n\n0.15 - 20120205\n===============\n\n- Add support for $SYS/broker/clients/maximum and $SYS/broker/clients/active\n  topics.\n- Add support for $SYS messages/byte per second received/sent topics.\n- Updated mosquitto man page - $SYS hierarchy and signal support were out of\n  date.\n- Auto generated pub/sub client ids now include the hostname.\n- Tool for dumping persistent DB contents is available in src/db_dump. It isn't\n  installed by default.\n- Enforce topic length checks in client library.\n- Implement \"once\" and \"lazy\" bridge start types.\n- Add new return type MOSQ_ERR_ERRNO to indicate that the errno variable should\n  be checked for the real error code.\n- Add support for connection_messages config option.\n- mosquitto_sub will now refuse to run if the -c option (disable clean session)\n  is given and no client id is provided.\n- mosquitto_pub now gives more useful error messages on invalid input or other\n  error conditions.\n- Fix Python will_set() true/True typo.\n- Fix messages to topic \"a/b\" incorrectly matching on a subscription \"a\" if\n  another subscription \"a/#\" exists.\n\n\n0.14.4 - 20120106\n=================\n\n- Fix local bridge notification messages.\n- Fix return values for more internal library calls.\n- Fix incorrect out of memory checks in library and broker.\n- Never time out local bridge connections.\n\n\n0.14.3 - 20111210\n=================\n\n- Fix potential crash when client connects with an invalid CONNECT packet.\n- Fix incorrect invalid socket comparison on Windows.\n- Server shouldn't crash when a message is published to foo/ when a\n  subscription to foo/# exists (bug #901697).\n- SO_REUSEADDR doesn't work the same on Windows, so don't use it.\n- Cygwin builds now support Windows service features.\n- Fix $SYS/broker/bytes/sent reporting.\n\n\n0.14.2 - 20111123\n=================\n\n- Add uninstall target for libs.\n- Don't try to write packet whilst in a callback.\n\n\n0.14.1 - 20111117\n=================\n\n- Fix Python syntax errors (bug #891673).\n\n\n0.14 - 20111116\n===============\n\n- Add support for matching ACLs based on client id and username.\n- Add a Windows installer file (NSIS based).\n- Add native support for running the broker as a Windows service. This is the\n  default when installed using the new installer.\n- Fix client count for listeners. When clients disconnect, decrement the\n  count. Allow max_connections to work again.\n- Attempt to send all packets immediately upon being queued. This will result\n  in more immediate network communication in many cases.\n- Log IP address when reporting CONNACK packets if the client id isn't yet\n  known.\n- Fix payload length calculation in python will_set function.\n- Fix Python publish and will_set functions for payload=None.\n- Fix keepalive value being lost when reconnecting a client (bug #880863).\n- Persistence file writing now uses portable file functions, so the Cygwin\n  broker build should no longer be necessary.\n- Duplicate code between the client and broker side has been reduced.\n- Queued messages for clients reconnecting with clean_session=false set were\n  not being sent until the next message for that client was received. This has\n  been fixed (bug #890724).\n- Fix subscriptions to # incorrectly matching against topics beginning with /\n\n\n0.13 - 20110920\n===============\n\n- Implement bridge state notification messages.\n- Save client last used mid in persistent database (DB version number bumped).\n- Expose message id in Python MosquittoMessage.\n- It is now possible to set the topic QoS level for bridges.\n- Python MosquittoMessage payload parameter is now a Python string, not a\n  ctypes object which makes it much easier to use.\n- Fix queueing of messages for disconnected clients. The max_queued_messages\n  option is now obeyed.\n- C++ library is now in its own namespace, mosquittopp.\n- Add support for adding log message timestamps in the broker.\n- Fix missing mosquitto_username_pw_set() python binding.\n- Fix keepalive timeout for reconnecting non clean-session clients. Prevents\n  immediate disconnection on reconnection.\n- Fix subscription wildcard matching - a subscription of +/+ will now match\n  against /foo\n- Fix subscription wildcard matching - a subscription of foo/# will now match\n  against foo\n- When restoring persistent database, clients should be set to non\n  clean-session or their subscriptions will be immediately removed.\n- Fix SUBACK payload for multiple topic subscriptions.\n- Don't send retained messages when a client subscribes to a topic it is\n  already subscribed to.\n\n\n0.12 - 20110725\n===============\n\n- Reload (most) configuration on SIGHUP.\n- Memory tracking is no longer compiled in the client library.\n- Add --help option to mosquitto to display usage.\n- Add --id-prefix option to clients to allow easier use with brokers that are\n  using the clientid_prefix option.\n- Fix compilation on QNX.\n- Add -P as a synonym argument for --pw in the clients.\n- Fix python MosquittoMessage payload parameter. This is now returned as a\n  pointer to an array of c_uint8 values so binary data is handled correctly.\n  If a string is needed, use msg.payload_str\n- Fix memory leaks on client authentication.\n- If password_file is not defined then clients can now connect even if they\n  use a username/password.\n- Add mosquitto_reconnect() to the client library.\n- Add option for compiling with liberal protocol compliance support (enabled\n  by default).\n- Fix problems with clients reconnecting and old messages remaining in the\n  message store.\n- Display both ip and client id in the log message when a client connects.\n  Change the socket connection message to make it more obvious that it is just\n  a socket connection being made (bug #801135).\n- Fix retained message delivery where a subscription contains a +.\n- Be more lenient when reloading persistent database to reduce errors with\n  empty retained messages.\n\n\n0.11.3 - 20110707\n=================\n\n- Don't complain and quit if persistence_file option is given (bug #802423).\n- Initialise listeners correctly when clients with duplicate client ids\n  connect. Bug #801678.\n- Memory tracking is now disabled for Symbian builds due to lack of malloc.h.\n- Fix memory tracking compilation for kFreeBSD.\n- Python callbacks can now be used with class member functions.\n- Fix persistent database writing of client message chunks which caused\n  errors when restoring (bug #798164).\n\n\n0.11.2 - 20110626\n=================\n\n- Don't free contexts in mqtt3_context_disconnect() (bug #799688 / #801678).\n- Only free will if present when freeing a client context.\n\n\n0.11.1 - 20110620\n=================\n\n- Fix buffer overrun when checking for + and # in topics (bug #799688).\n- Pub client now quits if publish fails.\n\n\n0.11 - 20110619\n===============\n\n- Removed all old sqlite code.\n- Remove client id limit in clients.\n- Implemented $SYS/broker/heap/maximum size\n- Implemented $SYS/broker/clients/inactive to show the number of disconnected\n  non-clean session clients.\n- $SYS/broker/heap/current size and maximum size messages now include \"bytes\"\n  to match rsmb message format.\n- Implemented the retained_persistence config file option - a synonym of the\n  \"persistence\" option.\n- Added security_external.c to broker source to make it easier for third\n  parties to add support for their existing username/password and ACL database\n  for security checks. See external_security_checks.txt.\n- $SYS messages are now only republished when their value changes.\n- Windows native broker now responds to command line arguments.\n- Simplify client disconnecting so wills gets sent in all cases (bug #792468).\n- Clients now have a --quiet option.\n- The on_disconnect() callback will always be called now, even if the client\n  has disconnected unexpectedly.\n- Always close persistent DB file after restoring.\n- Return error code when exiting the clients.\n- mosquitto_publish() now returns MOSQ_ERR_INVAL if the topic contains + or #\n- mosquitto now silently rejects published messages with + or # in the topic.\n- max_connections is now a per-listener setting instead of global.\n- Connection count is now reduced when clients disconnect (bug #797983).\n\n\n0.10.2 - 20110106\n=================\n\n- Don't abort when connecting if the first connection fails. This is important\n  on e.g. Windows 7, where IPV6 is offered as the first choice but may not be\n  available.\n- Deal with long logging messages properly (bug #785882).\n- Fix library compilation on Symbian - no pselect() available.\n- Don't stop processing subscriptions on received messages after a\n  subscription with # matches. (bug #791206).\n\n\n0.10.1 - 20110512\n=================\n\n- Fix Windows compilation.\n- Fix mosquitto.py on Windows - call lib init/cleanup.\n- Don't abort when connecting if given an unknown address type (assuming an\n  IPv4 or IPv6 address is given).\n\n\n0.10 - 20110429\n===============\n\n- Implement support for the password_file option and accompanying\n  authentication requirements in the broker.\n- Implement topic Access Control Lists.\n- mosquitto_will_set() and mosquitto_publish() now return\n  MOSQ_ERR_PAYLOAD_SIZE if the payload is too large (>268,435,455 bytes).\n- Bridge support can now be disabled at compile time.\n- Group together network writes for outgoing packets - don't send single byte\n  writes!\n- Add support for clientid_prefixes variable.\n- Add support for the clientid config variable for controlling bridge client\n  ids.\n- Remove 32-bit database ID support because htobe64() no longer used.\n- Multiple client subscriptions to the same topic result in only a single\n  subscription. Bug #744077.\n\n\n0.9.3 - 20110310\n================\n\n- Set retained message status for QoS 2 messages (bug #726535).\n- Only abort with an error when opening listening sockets if no address family\n  is available, rather than aborting when any address family is not available.\n- Don't clean queued messages when a non clean session client reconnects.\n- Make mosquitto.py compatible with Python <2.6.\n- Fix mosquitto.h header includes for Windows.\n\n\n0.9.2 - 20110208\n================\n\n- Only send a single DISCONNECT command when using -l in the pub client.\n- Set QoS=1 on PUBREL commands to meet protocol spec.\n- Don't leak sockets on connection failure in the library.\n- Install man pages when building under cmake.\n- Fix crash bug on malformed CONNECT message.\n- Clients are now rejected if their socket peer name cannot be obtained on\n  connection.\n- Fix a number of potential problems caused when a client with a duplicate id\n  connects.\n- Install mosquitto.conf under cmake.\n\n\n0.9.1 - 20101203\n================\n\n- Add missing code for parsing the \"bind_address\" configuration option.\n- Fix missing include when compiling with tcp-wrappers support.\n- Add linker version script for C library to control exported functions.\n\n\n0.9 - 20101114\n==============\n\n- Client and message data is now stored in memory with custom routines rather\n  than a sqlite database. This removes the dependencies on sqlite, pcre and\n  sqlite3-pcre. It also means that the persistent database format has had to\n  be reimplemented in a custom format. Optional support for importing old\n  sqlite databases is provided.\n- Added IPv6 support for mosquitto and the clients.\n- Provide username and password support for the clients and client libraries.\n  This is part of the new MQTT v3.1 spec.\n- The broker supports the username and password connection flags, but will not\n  do anything with the username and password.\n- Python callback functions now optionally take an extra argument which will\n  return the user object passed to the Mosquitto() constructor, or the calling\n  python object itself if nothing was given to Mosquitto().\n- Remove the mosquitto command line option \"-i interface\".\n- Remove the mosquitto.conf \"interface\" variable.\n- Add support for the listener config variable (replaces the interface\n  variable)\n- Add support for the bind_address config variable.\n- Change the port config variable behaviour to match that of rsmb (applies to\n  the default listener only, can be given just once).\n- Fix QoS 2 protocol compliance - stop sending duplicate messages and handle\n  timeouts correctly. Fixes bug #598290.\n- Set retain flag correctly for outgoing messages. It should only be set for\n  messages sent in response to a subscribe command (ie. stale data).\n- Fix bug in returning correct CONNACK result to on_connect client callback.\n- Don't send client will if it is disconnected for exceeding its keepalive\n  timer.\n- Fix client library unsubscribe function incorrectly sending a SUBSCRIBE\n  command when it should be UNSUBSCRIBE.\n- Fix max_inflight_messages and max_queued_messages operation. These\n  parameters now apply only to QoS 1 and 2 messages and are used regardless of\n  the client connection state.\n- mosquitto.conf now installed to /etc/mosquitto/mosquitto.conf instead of\n  /etc/mosquitto.conf. The /etc/mosquitto/ directory will be used for password\n  and access control files in the future.\n- Give the compile time option of using 32-bit integers for the database IDs\n  instead of 64-bit integers. This is useful where htobe64()/be64toh() are not\n  available or for embedded systems for example.\n- The DUP bit is now set correctly when resending PUBREL messages.\n- A port to Windows native has been partially completed. This currently drops a\n  number of features, including the ability to change configuration parameters\n  and persistent storage.\n\n\n0.8.3 - 20101004\n================\n\n- Fix QoS 2 protocol compliance - stop sending duplicate messages and handle\n  timeouts correctly. Fixes bug #598290. (backported from future 0.9 code)\n\n\n0.8.2 - 20100815\n================\n\n- Fix default loop() timeout value in mosquitto.py. Previous value was 0,\n  causing high cpu load.\n- Fix message handling problem in client library when more than one message was\n  in the client queue.\n- Fix the logic used to determine whether a QoS>0 message needs to be retried.\n- Fix the Python sub.py example so that it quits on error.\n\n\n0.8.1 - 20100812\n================\n\n- Improve python interface\n- Fix incorrect return value from message delete function\n- Use logging function to print error messages in clients.\n- Fix python installation script DESTDIR.\n- Fix library destination path for 64-bit machines.\n\n\n0.8 - 20100807\n==============\n\n- Topics starting with a / are treated as distinct to those not starting with\n  a /. For example, /topic/path is different to topic/path. This matches the\n  behaviour of rsmb.\n- Correctly calculate the will QoS on a new client connection (bug #597451).\n- Add \"addresses\" configuration file variable as an alias of \"address\", for\n  better rsmb compatibility.\n- Bridge clean_session setting is now false, to give more sensible behaviour\n  and be more compatible with rsmb.\n- Add cleansession variable for configuring bridges.\n- Add keepalive_interval variable for bridges.\n- Remove default topic subscription for mosquitto_sub because the old\n  behaviour was too confusing.\n- Added a C client library, which the pub and sub clients now use.\n- Added a C++ client library (bound to the C library).\n- Added a Python client library (bound to the C library).\n- Added CMake build scripts to allow the library and clients (not the broker)\n  to be compiled natively on Windows.\n\n\n0.7 - 20100615\n==============\n\n- mosquitto_pub can now send null (zero length) messages.\n- Don't store QoS=0 messages for disconnected clients with subscriptions of\n  QoS>0.\n- accept() all available sockets when new clients are connecting, rather than\n  just one.\n- Add option to print debug messages in pub and sub clients.\n- hg revision is now exported via $SYS/broker/changeset\n- Send Will when client exceeds keepalive timer and is disconnected.\n- Check to see if a client has a will before sending it.\n- Correctly deal with clients connecting with the same id multiple times.\n- Add compile time option to disable heap memory tracking.\n- Use poll() instead of select() to allow >1024 clients.\n- Implement max_connections.\n- Run VACUUM on in-memory database on receiving SIGUSR2.\n- Fix bridge keepalive timeouts and reconnects.\n- Don't attempt to drop root privileges when running on Windows as this isn't\n  well supported (bug #586231).\n\n\n0.6.1 - 20100506\n================\n\n- Fix DB auto upgrade for messages table.\n\n\n0.6 - 20100505\n==============\n\n- Basic support for connecting multiple MQTT brokers together (bridging).\n- mosquitto_sub can now subscribe to multiple topics (limited to a global QoS).\n- mosquitto_pub can now send a file as a message.\n- mosquitto_pub can now read all of stdin and send it as a message.\n- mosquitto_pub can now read stdin and send each line as a message.\n- mosquitto will now correctly run VACUUM on the persistent database on exit.\n- Implement a more efficient database design, so that only one copy of each\n  message is held in the database, rather than one per subscribed client.\n- Add the store_cleanup_interval config option for dealing with the internal\n  message store.\n- Add support for disabling \"clean session\" for the sub client.\n- Add support for automatic upgrading of the mosquitto DB from v1 to v2.\n- Add persistence_file config option to allow changing the filename of the\n  persistence database. This allows multiple mosquitto DBs to be stored in the\n  same location whilst keeping persistence_location compatible with rsmb.\n- Don't store QoS=0 messages for disconnected clients. Fixes bug #572608. This\n  wasn't correctly fixed in version 0.5.\n- Don't disconnect clients if they send a PUBLISH with zero length payload\n  (bug #573610).\n- If a retained message is received with a zero length payload, the retained\n  message for that topic is deleted.\n- Send through zero length messages.\n- Produce a warning on unsupported rsmb options instead of quitting.\n- Describe clean session flag in the mqtt man page.\n- Implement the max_inflight_messages and max_queued_messages features in the\n  broker.\n\n\n0.5.4 - 20100311\n================\n\n- Fix memory allocation in mqtt3_fix_sub_topic() (bug #531861).\n- Remove accidental limit of 100 client connections.\n- Fix mosquitto_pub handling of messages with QoS>0 (bug #537061).\n\n\n0.5.3 - 20100303\n================\n\n- Will messages are now only sent when a client disconnects unexpectedly.\n- Fix all incoming topics/subscriptions that start with a / or contain\n  multiple / in a row (//).\n- Do actually disconnect client when it sends an empty subscription/topic string.\n- Add missing $SYS/broker/clients/total to man page.\n\n\n0.5.2 - 20100302\n================\n\n- Always update last backup time, so that the backup doesn't run every time\n  through the main loop once autosave_interval has been reached.\n- Report $SYS/broker/uptime in the same format as rsmb.\n- Make mandatory options obvious in usage output and man page of mosquitto_pub.\n  Fixes bug #529990.\n- Treat subscriptions with a trailing slash correctly. This should fix bugs\n  #530369 and #530099.\n\n\n0.5.1 - 20100227\n================\n\n- Must daemonise before pid file is written.\n\n\n0.5 - 20100227\n==============\n\n- No longer store QoS=0 messages for disconnected clients that do not have\n  clean start set.\n- Rename msg_timeout option to retry_interval for better rsmb compatibility.\n- Change persistence behaviour. The database is now stored in memory even if\n  persistence is enabled. It is written to disk when mosquitto exits and also at\n  periodic intervals as defined by the new autosave_interval option.\n- The writing of the persistence database may be forced by sending mosquitto\n  the SIGUSR1 signal.\n- Clients that do not send CONNECT as their first command are now\n  disconnected.\n- Boolean configuration values may now be specified with true/false as well as\n  1/0.\n- Log message on CONNECT with invalid protocol or protocol version.\n- Default sqlite3-pcre path on Linux is now /usr/lib/sqlite3/pcre.so to match\n  future sqlite3-pcre packages.\n- Add mosquitto_sub and mosquitto_pub, simple clients for subscribe/publish.\n- Add man pages for clients.\n- Add general man page on mqtt.\n- Root privileges are now dropped only after attempting to write a pid file\n  (if configured). This means that the pid file can be written to /var/run/\n  directly and should fix bug #523183.\n\n\n0.4.2 - 20100203\n================\n\n- Fix segfault on client connect with invalid protocol name/version.\n\n\n0.4.1 - 20100112\n===============\n\n- Fix regex used for finding retained messages to send on new subscription.\n\n\n0.4 - 20100105\n==============\n\n- Added support for wildcard subscriptions using + and #.\n- All network operations are now non-blocking and can cope with partial\n  packets, meaning that networking should be a lot more reliable.\n- Total messsages/bytes sent/received are now available in $SYS.\n- Improved logging information - use client ip address and id instead of\n  socket number.\n- Broker build timestamp is available in $SYS.\n- Keepalive==0 is now correctly treated as \"never disconnect\".\n- Fixed manpage installation.\n- Fixed incorrect $SYS hierarchy locations in documentation and code.\n- Debug type log messages are no longer sent to \"topics\".\n- Default logging destination no longer includes \"topics\" to prevent possible\n  error logging to the db before it is initialised.\n- Periodic $SYS messages can now be disabled.\n- stdout and stderr are flushed when logging to them to give more timely\n  updates.\n- dup is now set correctly when resending messages.\n- Database format bumped due to topic column naming fix.\n\n\n0.3 - 20091217\n==============\n\n- The port option in the configuration file and --port command line argument\n  may now be given any number of times to make mosquitto listen on multiple\n  sockets.\n- Add new config file and command line option \"interface\" to specify an\n  interface to listen on, rather than all interfaces.\n- Added host access control through tcp-wrappers support.\n- Set SO_REUSEADDR on the listening socket so restart is much quicker.\n- Added support for tracking current heap memory usage - this is published on\n  the topic \"$SYS/broker/heap/current size\"\n- Added code for logging to stderr, stdout, syslog and topics.\n- Added logging to numerous places - still plenty of scope for more.\n\n\n0.2 - 20091204\n==============\n\n- Replaced the command line option --foreground with --daemon, swapping the\n  default behaviour.\n- Added the command line option --config-file, to specify a config file to\n  load.  If this is not given, no config file is load and the default options\n  are used.\n- Added the command line option --port for specifying the port to listen on.\n  This overrides values in the config file.\n- Don't use persistence by default.\n- Default behaviour is now more sane when run by a normal user with no command\n  line options (combination of above changes).\n- Added option user to config file, defaulting to a value of mosquitto. If\n  this value isn't blank and mosquitto is started by root, then it will drop\n  privileges by changing to the user and its primary group. This replaces the\n  current behaviour of refusing to start if run by root.\n- Fix non-persistent mode, which would never work in the previous release.\n- Added information on default values of msg_timeout and sys_interval to the\n  mosquitto.conf man page. (closes bug #492045).\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "This project is dual licensed under the Eclipse Public License 2.0 OR the\nEclipse Distribution License 1.0 as described in the epl-v20 and edl-v10 files.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n"
  },
  {
    "path": "Makefile",
    "content": "include config.mk\n\nDIRS=libcommon lib apps client plugins src\nDOCDIRS=man\nDISTDIRS=man\nDISTFILES= \\\n\tapps/ \\\n\tclient/ \\\n\tcmake/ \\\n\tcommon/ \\\n\tdashboard/ \\\n\tdeps/ \\\n\tdoc/ \\\n\tdocker/ \\\n\texamples/ \\\n\tfuzzing/ \\\n\tinclude/ \\\n\tinstaller/ \\\n\tlibcommon/ \\\n\tlib/ \\\n\tlogo/ \\\n\tmake/ \\\n\tman/ \\\n\tmisc/ \\\n\tplugins/ \\\n\tsecurity/ \\\n\tservice/ \\\n\tsnap/ \\\n\tsrc/ \\\n\ttest/ \\\n\twww/ \\\n\t.github \\\n\t\\\n\t.editorconfig \\\n\t.gitignore \\\n\t.uncrustify.cfg \\\n\tbuildtest.py \\\n\tcodecov.yml \\\n\tCMakeLists.txt \\\n\tCITATION.cff \\\n\tCONTRIBUTING.md \\\n\tChangeLog.txt \\\n\tformat.sh \\\n\tLICENSE.txt \\\n\tMakefile \\\n\tabout.html \\\n\taclfile.example \\\n\tconfig.h \\\n\tconfig.mk \\\n\tedl-v10 \\\n\tepl-v20 \\\n\tlibmosquitto.pc.in \\\n\tlibmosquittopp.pc.in \\\n\tmosquitto.conf \\\n\tNOTICE.md \\\n\tpskfile.example \\\n\tpwfile.example \\\n\tREADME-compiling.md \\\n\tREADME-letsencrypt.md \\\n\tREADME-tests.md \\\n\tREADME-windows.txt \\\n\tREADME.md \\\n\trun_tests.py \\\n\tset-version.sh \\\n\tSECURITY.md \\\n\tTHANKS.txt \\\n\tvcpkg.json\n\n.PHONY : all mosquitto api docs binary check clean reallyclean test test-compile install uninstall dist sign copy localdocker\n\nall : $(MAKE_ALL)\n\napi :\n\tmkdir -p api p\n\tnaturaldocs -o HTML api -i include -p p\n\trm -rf p\n\ndocs :\n\tset -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d}; done\n\nbinary : mosquitto\n\nbinary-all : mosquitto test-compile\n\nmosquitto :\nifeq ($(UNAME),Darwin)\n\t$(error Please compile using CMake on Mac OS X)\nendif\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d}; done\n\nfuzzing : mosquitto\n\t$(MAKE) -C fuzzing\n\nclean :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} clean; done\n\tset -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d} clean; done\n\t$(MAKE) -C test clean\n\t$(MAKE) -C fuzzing clean\n\nreallyclean :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} reallyclean; done\n\tset -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d} reallyclean; done\n\t$(MAKE) -C test reallyclean\n\t-rm -f *.orig\n\ncheck : test\n\ntest-compile: mosquitto lib\n\t$(MAKE) -C test test-compile\n\t$(MAKE) -C plugins test-compile\n\ntest : mosquitto lib apps test-compile\n\t$(MAKE) -C test test\n\t$(MAKE) -C plugins test\n\nptest : mosquitto\n\t$(MAKE) -C test ptest\n\nutest : mosquitto\n\t$(MAKE) -C test utest\n\ninstall : all\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} install; done\nifeq ($(WITH_DOCS),yes)\n\tset -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d} install; done\nendif\n\t$(INSTALL) -d \"${DESTDIR}/etc/mosquitto\"\n\t$(INSTALL) -m 644 mosquitto.conf \"${DESTDIR}/etc/mosquitto/mosquitto.conf.example\"\n\t$(INSTALL) -m 644 aclfile.example \"${DESTDIR}/etc/mosquitto/aclfile.example\"\n\t$(INSTALL) -m 644 pwfile.example \"${DESTDIR}/etc/mosquitto/pwfile.example\"\n\t$(INSTALL) -m 644 pskfile.example \"${DESTDIR}/etc/mosquitto/pskfile.example\"\n\t$(INSTALL) -d \"${DESTDIR}$(prefix)/include/mosquitto\"\n\t$(INSTALL) include/mosquitto/*.h \"${DESTDIR}${prefix}/include/mosquitto/\"\n\t$(INSTALL) include/mosquitto.h \"${DESTDIR}${prefix}/include/mosquitto.h\"\n\t$(INSTALL) include/mosquitto_broker.h \"${DESTDIR}${prefix}/include/mosquitto_broker.h\"\n\t$(INSTALL) include/mosquitto_plugin.h \"${DESTDIR}${prefix}/include/mosquitto_plugin.h\"\n\t$(INSTALL) include/mosquittopp.h \"${DESTDIR}${prefix}/include/mosquittopp.h\"\n\t$(INSTALL) include/mqtt_protocol.h \"${DESTDIR}${prefix}/include/mqtt_protocol.h\"\n\nuninstall :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} uninstall; done\n\trm -f \"${DESTDIR}/etc/mosquitto/mosquitto.conf.example\"\n\trm -f \"${DESTDIR}/etc/mosquitto/aclfile.example\"\n\trm -f \"${DESTDIR}/etc/mosquitto/pwfile.example\"\n\trm -f \"${DESTDIR}/etc/mosquitto/pskfile.example\"\n\trm -f \"${DESTDIR}${prefix}/include/mosquitto.h\"\n\trm -f \"${DESTDIR}${prefix}/include/mosquitto/broker.h\"\n\trm -f \"${DESTDIR}${prefix}/include/mosquitto/broker_control.h\"\n\trm -f \"${DESTDIR}${prefix}/include/mosquitto/broker_plugin.h\"\n\trm -f \"${DESTDIR}${prefix}/include/mosquitto/libmosquittopp.h\"\n\trm -f \"${DESTDIR}${prefix}/include/mosquitto/mqtt_protocol.h\"\n\trm -f \"${DESTDIR}${prefix}/include/mosquitto_broker.h\"\n\trm -f \"${DESTDIR}${prefix}/include/mosquitto_plugin.h\"\n\trm -f \"${DESTDIR}${prefix}/include/mosquittopp.h\"\n\trm -f \"${DESTDIR}${prefix}/include/mqtt_protocol.h\"\n\ndist : reallyclean\n\tset -e; for d in ${DISTDIRS}; do $(MAKE) -C $${d} dist; done\n\tmkdir -p dist/mosquitto-${VERSION}\n\tcp -r ${DISTFILES} dist/mosquitto-${VERSION}/\n\tcd dist; tar -zcf mosquitto-${VERSION}.tar.gz mosquitto-${VERSION}/\n\nsign : dist\n\tcd dist; gpg --detach-sign -a mosquitto-${VERSION}.tar.gz\n\ncopy : sign\n\tcd dist; scp mosquitto-${VERSION}.tar.gz mosquitto-${VERSION}.tar.gz.asc mosquitto:site/mosquitto.org/files/source/\n\tscp ChangeLog.txt mosquitto:site/mosquitto.org/\n\ncoverage :\n\tlcov --capture -d apps -d client -d lib -d plugins -d src --output-file coverage.info --no-external --ignore-errors empty\n\tgenhtml --ignore-errors inconsistent coverage.info --output-directory out\n\nlocaldocker : reallyclean\n\tset -e; for d in ${DISTDIRS}; do $(MAKE) -C $${d} dist; done\n\trm -rf dockertmp/\n\tmkdir -p dockertmp/mosquitto-${VERSION}\n\tcp -r ${DISTFILES} dockertmp/mosquitto-${VERSION}/\n\tcd dockertmp/; tar -zcf mosq.tar.gz mosquitto-${VERSION}/\n\tcp dockertmp/mosq.tar.gz docker/local\n\trm -rf dockertmp/\n\tcd docker/local && docker build . -t eclipse-mosquitto:local --build-arg VERSION=${VERSION}\n"
  },
  {
    "path": "NOTICE.md",
    "content": "# Notices for Mosquitto\n\nThis content is produced and maintained by the Eclipse Mosquitto project.\n\n * Project home: https://projects.eclipse.org/projects/iot.mosquitto\n\n## Trademarks\n\nEclipse Mosquitto trademarks of the Eclipse Foundation. Eclipse, and the\nEclipse Logo are registered trademarks of the Eclipse Foundation.\n\n## Copyright\n\nAll content is the property of the respective authors or their employers.\nFor more information regarding authorship of content, please consult the\nlisted source code repository logs.\n\n## Declared Project Licenses\n\nThis program and the accompanying materials are made available under the terms\nof the Eclipse Public License v2.0 which is available at\nhttp://www.eclipse.org/legal/epl-v10.html, or the BSD 3 Clause license.\n\nSPDX-License-Identifier: EPL-2.0 or BSD-3-Clause\n\n## Source Code\n\nThe project maintains the following source code repositories:\n\n * https://github.com/eclipse-mosquitto/mosquitto\n\n## Third-party Content\n\nThis project makes use of the follow third party projects.\n\nc-ares\n* License: MIT\n* Project: https://c-ares.org/\n* Source: https://github.com/c-ares/c-ares\n\ncJSON (1.7.x)\n* License: MIT\n* Project: https://github.com/DaveGamble/cJSON\n\ngoogletest\n* License: BSD-3-Clause\n* Project: https://github.com/google/googletest\n\nlibedit (3.1)\n* License: BSD-3-Clause\n* Project: https://thrysoee.dk/editline/\n* Source: https://thrysoee.dk/editline/libedit-20250104-3.1.tar.gz\n\nlibwebsockets (4.x)\n* License: MIT\n* Project: https://github.com/warmcat/libwebsockets\n\nmicrohttpd\n* License: GNU LGPL v2.1+\n* Project: https://www.gnu.org/software/libmicrohttpd/\n* Source: git://git.gnunet.org/libmicrohttpd2.git\n\nopenssl (3.x.x)\n* License: Apache 2.0\n* Project: https://openssl.org\n* Source: https://github.com/openssl/openssl\n\npicohttpparser\n* License: MIT\n* Project: github.com/h2o/picohttpparser\n\nuthash (2.3.0)\n* License: BSD revised (https://troydhanson.github.io/uthash/license.html)\n* Project: https://github.com/troydhanson/uthash\n\n## Cryptography\n\nContent may contain encryption software. The country in which you are currently\nmay have restrictions on the import, possession, and use, and/or re-export to\nanother country, of encryption software. BEFORE using any encryption software,\nplease check the country's laws, regulations and policies concerning the import,\npossession, or use, and re-export of encryption software, to see if this is\npermitted.\n"
  },
  {
    "path": "README-compiling.md",
    "content": "The following packages can be used to add features to mosquitto.\n\n* cJSON - required\n* c-ares (libc-ares-dev on Debian based systems) - optional, enable with\n  `WITH_SRV=yes`\n* libedit - for mosquitto_ctrl interactive shell - optional, disable with\n  `WITH_EDITLINE=no`\n* libmicrohttpd - for broker http api support - optional, disable with\n  `WITH_HTTP_API=no`\n* openssl (libssl-dev on Debian based systems) - optional, disable with\n  `WITH_TLS=no`\n* pthreads - for client library thread support. This is required to support the\n  `mosquitto_loop_start()` and `mosquitto_loop_stop()` functions. If compiled\n  without pthread support, the library isn't guaranteed to be thread safe.\n* sqlite3 - for persistence support in the broker - optional, disable with\n  `WITH_SQLITE=no`\n* uthash / utlist - bundled versions of these headers are provided, disable\n  their use with `WITH_BUNDLED_DEPS=no`\n* xsltproc (xsltproc and docbook-xsl on Debian based systems) - only needed\n  when building from git sources - disable with `WITH_DOCS=no`\n\nFor testing, the following packages are required:\n* cunit\n* googletest / gmock\n* microsocks\n* python\n\n\nTo compile you may either use CMake, or on Linux look in the file `config.mk`\nfor compile options and use plain `make`.\n\nUp to version 2.1, the recommendation was to use CMake for Windows and Mac, and\nto use make everywhere else. The recommendation now is to use cmake in all\ncases, and that the plain makefiles will be removed in version 3.0.\n\nIf you have any questions, problems or suggestions (particularly related to\ninstalling on a more unusual device) then please get in touch using the details\nin README.md.\n"
  },
  {
    "path": "README-letsencrypt.md",
    "content": "# Using Lets Encrypt with Mosquitto\n\nOn Unix like operating systems, Mosquitto will attempt to drop root access as\nsoon as it has loaded its configuration file, but before it has activated any\nof that configuration. This means that if you are using Lets Encrypt TLS\ncertificates, it will be unable to access the certificates and private keys\ntypically located in /etc/letsencrypt/live/\n\nTo help with this problem there is an example `deploy` renewal hook script in\n`misc/letsencrypt/mosquitto-copy.sh` which shows how the certificate and\nprivate key for a mosquitto broker can be copied to /etc/mosquitto/certs/ and\ngiven the correct ownership and permissions so the broker can access them, but\nno other user can. It then signals Mosquitto to reload the certificates.\n\nUse of this script allows you to happily use Lets Encrypt certificates with\nMosquitto without needing root access for Mosquitto, and without having to\nrestart Mosquitto.\n"
  },
  {
    "path": "README-tests.md",
    "content": "# Tests\n\n## Running\n\nThe Mosquitto test suite can be invoked using either of\n\n```\nmake test\nmake check\n```\n\nThe tests run in series and due to the nature of some of the tests being made\ncan take a while.\n\n## Parallel Tests\n\nTo run the tests with some parallelism, use\n\n```\nmake ptest\n```\n\nThis runs up to 20 tests in parallel at once, yielding much faster overall run\ntime at the expense of having up to 20 instances of Python running at once.\nThis is not a particularly CPU intensive option, but does require more memory\nand may be unsuitable on e.g. a Raspberry Pi.\n\n## Dependencies\n\nThe tests require Python 3 and CUnit to be installed.\n"
  },
  {
    "path": "README-windows.txt",
    "content": "Mosquitto for Windows\n=====================\n\nMosquitto for Windows comes in 64-bit and 32-bit flavours. All dependencies are\nprovided in the installer.\n\nInstalling\n----------\n\nRunning the installer will present the normal type of graphical installer. If\nyou want to install without starting the graphical part of the installer, you\ncan do so by running it from a cmd prompt with the `/S` switch:\n\n```\nmosquitto-2.0.12-install-windows-x64.exe /S\n```\n\nYou can override the installation directory with the `/D` switch:\n\n```\nmosquitto-2.0.12-install-windows-x64.exe /S /D=:\\mosquitto\n```\n\n\nCapabilities\n------------\n\nSome versions of Windows have limitations on the number of concurrent\nconnections due to the Windows API being used. In modern versions of Windows,\ne.g. Windows 10 or Windows Server 2019, this is approximately 8192 connections.\nIn earlier versions of Windows, this limit is 2048 connections.\n\n\nWindows Service\n---------------\n\nIf you wish, mosquitto can be installed as a Windows service so you can\nstart/stop it from the control panel as well as running it as a normal\nexecutable.\n\nWhen running as a service, the configuration file used is mosquitto.conf in the\ndirectory defined by the %MOSQUITTO_DIR% environment variable. This will be set\nto the directory that you installed to by default.\n\nIf you want to install/uninstall mosquitto as a Windows service run from the\ncommand line as follows:\n\nC:\\Program Files\\mosquitto\\mosquitto install\nC:\\Program Files\\mosquitto\\mosquitto uninstall\n\nIt is possible to install and run multiple instances of a Mosquitto service, as\nof version 2.1. To do this, copy the mosquitto executable to a new *name* and\nrun the service install as above. The service will load a configuration file\nmosquitto.conf from the directory defined by the environment variable\n\"<executable_name>_DIR\". For this reason it is suggested to keep the executable\nname consisting of alphanumeric and '_' characters. Any other character will be\nreplaced with '_'.\n\nFor example, if you copy mosquitto.exe to eclipse_mosquitto.exe, you would run\nthese commands to install/uninstall:\n\nC:\\Program Files\\mosquitto\\eclipse_mosquitto install\nC:\\Program Files\\mosquitto\\eclipse_mosquitto uninstall\n\nAnd the service would try to load the config file at %ECLIPSE_MOSQUITTO_DIR%/mosquitto.conf\n\nThe new service will appear in the service list as \"Mosquitto Broker (eclipse_mosquitto.exe)\".\n\nLogging\n-------\n\nIf you use `log_dest file ...` in your configuration, the log file will be\ncreated with security permissions for the current user only. If running as a\nservice, this means the SYSTEM user. You will only be able to view the log file\nif you add permissions for yourself or whatever user you wish to view the logs.\n\nSignals\n-------\n\nStarting with version 2.1, it is possible to use the mosquitto_signal command\nto send signals to the broker, in a similar way to sending signals on a posix\nbased system. See https://mosquitto.org/man/mosquitto_signal-1.html for more\ndetails.\n"
  },
  {
    "path": "README.md",
    "content": "Eclipse Mosquitto\n=================\n\nMosquitto is an open source implementation of a server for version 5.0, 3.1.1,\nand 3.1 of the MQTT protocol. It also includes a C and C++ client library,\nthe `mosquitto_pub` `mosquitto_rr`, and `mosquitto_sub` utilities for\npublishing and subscribing, and the `mosquitto_ctrl`, `mosquitto_signal`, and\n`mosquitto_passwd` applications for helping administer the broker.\n\n## Links\n\nSee the following links for more information on MQTT:\n\n- Community page: <http://mqtt.org/>\n- MQTT v3.1.1 standard: <https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html>\n- MQTT v5.0 standard: <https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html>\n\nMosquitto project information is available at the following locations:\n\n- Main homepage: <https://mosquitto.org/>\n- Find existing bugs or submit a new bug: <https://github.com/eclipse-mosquitto/mosquitto/issues>\n- Source code repository: <https://github.com/eclipse-mosquitto/mosquitto>\n\nThere is also a public test server available at <https://test.mosquitto.org/>\n\n## Installing\n\nSee <https://mosquitto.org/download/> for details on installing binaries for\nvarious platforms.\n\n## Quick start\n\nIf you have installed a binary package the broker should have been started\nautomatically. If not, it can be started with a very basic configuration:\n\n    mosquitto\n\nThen use `mosquitto_sub` to subscribe to a topic:\n\n    mosquitto_sub -t 'test/topic' -v\n\nAnd to publish a message:\n\n    mosquitto_pub -t 'test/topic' -m 'hello world'\n\nNote that starting the broker like this allows anonymous/unauthenticated access\nbut only from the local computer, so it's only really useful for initial testing.\n\nIf you want to have clients from another computer connect, you will need to\nprovide a configuration file. If you have installed from a binary package, you\nwill probably already have a configuration file at somewhere like\n`/etc/mosquitto/mosquitto.conf`. If you've compiled from source, you can write\nyour config file then run as `mosquitto -c /path/to/mosquitto.conf`.\n\nTo start your config file you define a listener and you will need to think\nabout what authentication you require. It is not advised to run your broker\nwith anonymous access when it is publicly available.\n\nFor details on how to do this, look at the\n[authentication methods](https://mosquitto.org/documentation/authentication-methods/)\navailable and the [dynamic security plugin](https://mosquitto.org/documentation/dynamic-security/).\n\n## Documentation\n\nDocumentation for the broker, clients and client library API can be found in\nthe man pages, which are available online at <https://mosquitto.org/man/>. There\nare also pages with an introduction to the features of MQTT, the\n`mosquitto_passwd` utility for dealing with username/passwords, and a\ndescription of the configuration file options available for the broker.\n\nDetailed client library API documentation can be found at <https://mosquitto.org/api/>\n\n## Building from source\n\nTo build from source the recommended route for end users is to download the\narchive from <https://mosquitto.org/download/>.\n\nOn Windows and Mac, use `cmake` to build. On other platforms, just run `make`\nto build. For Windows, see also `README-windows.md`.\n\nIf you are building from the git repository then the documentation will not\nalready be built. Use `make binary` to skip building the man pages, or install\n`docbook-xsl` on Debian/Ubuntu systems.\n\n### Build Dependencies\n\n* cJSON - required\n* c-ares (libc-ares-dev on Debian based systems) - optional, enable with\n  `WITH_SRV=yes`\n* libedit - for mosquitto_ctrl interactive shell - optional, disable with\n  `WITH_EDITLINE=no`\n* libmicrohttpd - for broker http api support - optional, disable with\n  `WITH_HTTP_API=no`\n* openssl (libssl-dev on Debian based systems) - optional, disable with\n  `WITH_TLS=no`\n* pthreads - for client library thread support. This is required to support the\n  `mosquitto_loop_start()` and `mosquitto_loop_stop()` functions. If compiled\n  without pthread support, the library isn't guaranteed to be thread safe.\n* sqlite3 - for persistence support in the broker - optional, disable with\n  `WITH_SQLITE=no`\n* uthash / utlist - bundled versions of these headers are provided, disable\n  their use with `WITH_BUNDLED_DEPS=no`\n* xsltproc (xsltproc and docbook-xsl on Debian based systems) - only needed\n  when building from git sources - disable with `WITH_DOCS=no`\n\nEquivalent options for enabling/disabling features are available when using the\nCMake build. It is also possible to enable/disable building of specific plugins\nin the CMake build.\n\n### Building mosquitto - Using vcpkg\n\nYou can download and install mosquitto using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:\n\n    git clone https://github.com/Microsoft/vcpkg.git\n    cd vcpkg\n    ./bootstrap-vcpkg.sh\n    ./vcpkg integrate install\n    ./vcpkg install mosquitto\n\nThe mosquitto port in vcpkg is kept up to date by Microsoft team members and\ncommunity contributors. If the version is out of date, please [create an issue\nor pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.\n\n## Credits\n\nMosquitto was written by Roger Light <roger@atchoo.org>. There have been\nsubstantial contributions by other people in the community both in terms of\ncode and other help.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Reporting a Vulnerability\n\nIf you think you have found a security vulnerability in Mosquitto, please follow the steps on [Eclipse Security](https://www.eclipse.org/security/) page to report it.\n"
  },
  {
    "path": "THANKS.txt",
    "content": "These people have reported bugs / provided patches / done something else to aid\nthe mosquitto project. Thanks to you all!\n\nIf you think I've missed you off the list, please rest assured that it wasn't\nintentional and get in touch and I'll fix it.\n\nAdam Rudd\nAlexandre Zia\nAndrew Elwell\nAndy Piper\nAndy Stanford-Clark\nAnton Prokofiev\nAxel Busch\nBart Van Der Meerssche\nBD Walker\nBen Tobin\nBob Blackrock\nBrad Stancel\nBrett Francis\nChris Willing\nChristian Sampaio\nChristoph Krey\nCraig Hollabaugh\nCristian Manuel Vertiz Fernandez\nDan Anderson\nDaniel Deadwyler\nDaniel O'Conner\nDariusz Suchojad\nDarren Oliver\nDavid Huang\nDavid Monro\nDirk O. Kaar\nDominik Obermaier\nDominik Zajac\nEd Morris\nFabian Ruff\nFang Chong\nFrank Hansen\nGary Koh\nGianfranco Costamagna\nGuido Hinderberger\nHiram van Paassen\nJan-Piet Mens\nJoan Zapata\nJoe McIlvain\nKarl Palsson\nLarry Lendo\nMartin Assarsson\nMartin Rauscher\nMartin van Wingerden\nMarty Lee\nMatt Daubney\nMichael C\nMichael Frisch\nMichael Hekel\nMichael Laing\nMichael Rushton\nMike Bush\nMilan Tucic\nNeil Bothwick\nNicholas Humfrey\nNicholas O'Leary\nNithin Kumar\nPatrick Geary\nPaul Diston\nPeter Magnusson\nPryce Jones\nPeter George\nRichard Eagland\nRob Pridham\nRobin Gingras\nRoland de Boo\nSebastian Kroll\nSergio Rubio\nSharon Ben-Asher\nsskaje\nStefan Hudelmaier\nStefano Costa\nStephen Owens\nStephen Woods\nSteven Lougheed\nSurendra Reddy\nSzymon Kochanski\nTammo Besemann\nThomas Hilbig\nTobias Assarsson\nToby Jaffey\nTomas Novotny\nVicente Ruiz\nWayne Ingram\nYun Wu\nYuvraaj Kelkar\n賴 冠廷\n"
  },
  {
    "path": "about.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\"><head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n<title>About</title>\n</head>\n<body lang=\"EN-US\">\n<h2>About This Content</h2>\n\n<p><em>May 8, 2014</em></p>\n<h3>License</h3>\n\n<p>The Eclipse Foundation makes available all content in this plug-in (\"Content\").  Unless otherwise\nindicated below, the Content is provided to you under the terms and conditions of the\nEclipse Public License Version 2.0 (\"EPL\") and Eclipse Distribution License Version 1.0 (\"EDL\").\nA copy of the EPL is available at <a href=\"https://www.eclipse.org/legal/epl-2.0/\">https://www.eclipse.org/legal/epl-2.0/</a>\nand a copy of the EDL is available at\n<a href=\"http://www.eclipse.org/org/documents/edl-v10.php\">http://www.eclipse.org/org/documents/edl-v10.php</a>.\nFor purposes of the EPL, \"Program\" will mean the Content.</p>\n\n<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is\nbeing redistributed by another party (\"Redistributor\") and different terms and conditions may\napply to your use of any object code in the Content.  Check the Redistributor's license that was\nprovided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise\nindicated below, the terms and conditions of the EPL still apply to any source code in the Content\nand such source code may be obtained at <a href=\"http://www.eclipse.org/\">http://www.eclipse.org</a>.</p>\n\n\n\t\t<h3>Third Party Content</h3>\n\t\t<p>The Content includes items that have been sourced from third parties as set out below. If you\n\t\tdid not receive this Content directly from the Eclipse Foundation, the following is provided\n\t\tfor informational purposes only, and you should look to the Redistributor's license for\n\t\tterms and conditions of use.</p>\n\n\t\t<h4>libwebsockets 2.4.2</h4>\n\t\t<p>This project makes use of the libwebsockets library.</p>\n\t\t<p>The use of libwebsockets is based on the terms and conditions of the\n\t\tLGPL 2.1 with some specific exceptions.\n\t\t<a href=\"https://github.com/warmcat/libwebsockets/blob/v2.4.2/LICENSE\">https://github.com/warmcat/libwebsockets/blob/v2.4.2/LICENSE</a></p>\n\t\t<p>When libwebsockets is distributed with the project, it is being used\n\t\tsubject to the Static Linking Exception (Section 2) of the License. As\n\t\ta result, the content is not subject to the LGPL 2.1.</p>\n\n</body></html>\n"
  },
  {
    "path": "aclfile.example",
    "content": "# This affects access control for clients with no username.\ntopic read $SYS/#\n\n# This only affects clients with username \"roger\".\nuser roger\ntopic foo/bar\n\n# This affects all clients.\n# Note that this is the only topic it is possible to grant access to writing to\n# the $SYS tree. All other topics are always denied.\npattern write $SYS/broker/connection/%c/state\n"
  },
  {
    "path": "apps/CMakeLists.txt",
    "content": "add_subdirectory(db_dump)\nadd_subdirectory(mosquitto_ctrl)\nadd_subdirectory(mosquitto_passwd)\nadd_subdirectory(mosquitto_signal)\n"
  },
  {
    "path": "apps/Makefile",
    "content": "R=..\ninclude ${R}/config.mk\n\nDIRS= \\\n\t\tmosquitto_ctrl \\\n\t\tmosquitto_passwd \\\n\t\tmosquitto_signal\n\nifeq ($(WITH_PERSISTENCE),yes)\nDIRS+=db_dump\nendif\n\n.PHONY : all binary check clean reallyclean test install uninstall\n\nall :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d}; done\n\nbinary :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\nclean :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\nreallyclean :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\ncheck : test\ntest :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\ninstall :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\nuninstall :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n"
  },
  {
    "path": "apps/db_dump/CMakeLists.txt",
    "content": "add_executable(mosquitto_db_dump\n\tdb_dump.c db_dump.h\n\tjson.c\n\tprint.c\n\tstubs.c\n\n\t../../common/json_help.c\n\t../../common/json_help.h\n\n\t../../lib/packet_datatypes.c\n\t../../lib/property_mosq.c\n\n\t../../src/persist_read.c\n\t../../src/persist_read_v234.c\n\t../../src/persist_read_v5.c\n\t../../src/topic_tok.c\n)\n\ntarget_compile_definitions(mosquitto_db_dump PRIVATE\n\tWITH_BROKER\n\tWITH_PERSISTENCE\n)\n\ntarget_include_directories(mosquitto_db_dump PRIVATE\n\t\"${mosquitto_SOURCE_DIR}/common\"\n\t\"${mosquitto_SOURCE_DIR}/lib\"\n\t\"${mosquitto_SOURCE_DIR}/libcommon\"\n\t\"${mosquitto_SOURCE_DIR}/src\"\n\t\"${OPENSSL_INCLUDE_DIR}\"\n\t\"${CJSON_INCLUDE_DIRS}\"\n)\n\nif(WITH_TLS)\n\ttarget_link_libraries(mosquitto_db_dump PRIVATE OpenSSL::SSL)\nendif()\n\nif(WIN32)\n\ttarget_link_libraries(mosquitto_db_dump PRIVATE ws2_32)\nendif()\n\ntarget_link_libraries(mosquitto_db_dump PRIVATE common-options libmosquitto_common)\n\ninstall(TARGETS mosquitto_db_dump\n\tRUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n)\n"
  },
  {
    "path": "apps/db_dump/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=-I${R} -I${R}/lib -I${R}/src -I${R}/common -DWITH_BROKER\nLOCAL_LDFLAGS+=\nLOCAL_LDADD+=-lcjson -lm ${LIBMOSQ_COMMON}\n\n# ------------------------------------------\n#  Compile time options\n# ------------------------------------------\ninclude ${R}/make/broker.mk\n\n# ------------------------------------------\n#  Targets\n# ------------------------------------------\n\nOBJS = \\\n\tdb_dump.o \\\n\tjson.o \\\n\tprint.o \\\n\tstubs.o\n\nBROKER_OBJS = \\\n\t${R}/src/packet_datatypes.o \\\n\t${R}/src/persist_read.o \\\n\t${R}/src/persist_read_v234.o \\\n\t${R}/src/persist_read_v5.o \\\n\t${R}/src/property_mosq.o \\\n\t${R}/src/topic_tok.o\n\nOBJS_EXTERNAL = \\\n\tjson_help.o\n\nifeq ($(UNAME),Linux)\n    LIBS:=$(LIBS) -lrt\nendif\n\n.PHONY: all clean reallyclean\n\nifeq ($(WITH_FUZZING),yes)\nall : mosquitto_db_dump.a\nelse\nall : mosquitto_db_dump\nendif\n\nmosquitto_db_dump : ${OBJS} ${BROKER_OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}${CC} $^ -o $@ ${LOCAL_LDFLAGS} ${LOCAL_LDADD}\n\nmosquitto_db_dump.a : ${OBJS} ${BROKER_OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}$(AR) cr $@ $^\n\n${OBJS} : %.o:%.c db_dump.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n${BROKER_OBJS} :\n\t$(MAKE) -C ${R}/src $(notdir $@)\n\njson_help.o : ${R}/common/json_help.c ${R}/common/json_help.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nreallyclean: clean\n\nclean :\n\t-rm -f $(OBJS) $(OBJS_EXTERNAL) $(BROKER_OBJS)  mosquitto_db_dump mosquitto_db_dump.a *.gcda *.gcno\n\ninstall:\n\t$(INSTALL) -d \"${DESTDIR}$(prefix)/bin\"\n\t$(INSTALL) ${STRIP_OPTS} mosquitto_db_dump \"${DESTDIR}${prefix}/bin/mosquitto_db_dump\"\n\nuninstall:\n"
  },
  {
    "path": "apps/db_dump/db_dump.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <time.h>\n\n#include \"db_dump.h\"\n#include <mosquitto_broker_internal.h>\n#include <persist.h>\n\n#define mosquitto_malloc(A) malloc((A))\n#define mosquitto_free(A) free((A))\n#define _mosquitto_malloc(A) malloc((A))\n#define _mosquitto_free(A) free((A))\n#include <uthash.h>\n\n#include \"db_dump.h\"\n\n#ifdef __ANDROID__\n#include <sys/endian.h>\n#endif\n\nstruct client_data {\n\tUT_hash_handle hh_id;\n\tchar *id;\n\tuint32_t subscriptions;\n\tuint32_t subscription_size;\n\tint messages;\n\tlong message_size;\n};\n\nstruct base_msg_chunk {\n\tUT_hash_handle hh;\n\tdbid_t store_id;\n\tuint32_t length;\n};\n\nstruct mosquitto_db db;\n\nextern uint32_t db_version;\nstatic int stats = 0;\nstatic int client_stats = 0;\nstatic int do_print = 1;\nstatic int do_json = 0;\n\n/* Counts */\nstatic long cfg_count = 0;\nstatic long client_count = 0;\nstatic long client_msg_count = 0;\nstatic long base_msg_count = 0;\nstatic long retain_count = 0;\nstatic long sub_count = 0;\n/* ====== */\n\n\nstruct client_data *clients_by_id = NULL;\nstruct base_msg_chunk *msgs_by_id = NULL;\n\n\nstatic void free__sub(struct P_sub *chunk)\n{\n\tfree(chunk->clientid);\n\tfree(chunk->topic);\n}\n\n\nstatic void free__client(struct P_client *chunk)\n{\n\tfree(chunk->username);\n\tfree(chunk->clientid);\n}\n\n\nstatic void free__client_msg(struct P_client_msg *chunk)\n{\n\tfree(chunk->clientid);\n}\n\n\nstatic void free__base_msg(struct P_base_msg *chunk)\n{\n\tfree(chunk->topic);\n\tfree(chunk->payload);\n\tmosquitto_property_free_all(&chunk->properties);\n}\n\n\nstatic int dump__cfg_chunk_process(FILE *db_fd, uint32_t length)\n{\n\tstruct PF_cfg chunk;\n\tint rc;\n\n\tcfg_count++;\n\n\tmemset(&chunk, 0, sizeof(struct PF_cfg));\n\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_cfg_read_v56(db_fd, &chunk);\n\t}else{\n\t\trc = persist__chunk_cfg_read_v234(db_fd, &chunk);\n\t}\n\tif(rc){\n\t\tfprintf(stderr, \"Error: Corrupt persistent database.\\n\");\n\t\treturn rc;\n\t}\n\n\tif(do_print){\n\t\tprintf(\"DB_CHUNK_CFG:\\n\");\n\t}\n\tif(do_print){\n\t\tprintf(\"\\tLength: %d\\n\", length);\n\t}\n\tif(do_print){\n\t\tprintf(\"\\tShutdown: %d\\n\", chunk.shutdown);\n\t}\n\tif(do_print){\n\t\tprintf(\"\\tDB ID size: %d\\n\", chunk.dbid_size);\n\t}\n\tif(chunk.dbid_size != sizeof(dbid_t)){\n\t\tfprintf(stderr, \"Error: Incompatible database configuration (dbid size is %d bytes, expected %zu)\",\n\t\t\t\tchunk.dbid_size, sizeof(dbid_t));\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(do_print){\n\t\tprintf(\"\\tLast DB ID: %\" PRIu64 \"\\n\", chunk.last_db_id);\n\t}\n\n\treturn 0;\n}\n\n\nstatic int dump__client_chunk_process(FILE *db_fd, uint32_t length)\n{\n\tstruct P_client chunk;\n\tint rc = 0;\n\tstruct client_data *cc = NULL;\n\n\tclient_count++;\n\n\tmemset(&chunk, 0, sizeof(struct P_client));\n\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_client_read_v56(db_fd, &chunk, db_version);\n\t}else{\n\t\trc = persist__chunk_client_read_v234(db_fd, &chunk, db_version);\n\t}\n\tif(rc){\n\t\tfprintf(stderr, \"Error: Corrupt persistent database.\\n\");\n\t\treturn rc;\n\t}\n\n\tif(client_stats && chunk.clientid){\n\t\tcc = calloc(1, sizeof(struct client_data));\n\t\tif(!cc){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\tfree(chunk.clientid);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tcc->id = strdup(chunk.clientid);\n\t\tHASH_ADD_KEYPTR(hh_id, clients_by_id, cc->id, strlen(cc->id), cc);\n\t}\n\n\tif(do_json){\n\t\tjson_add_client(&chunk);\n\t}\n\tif(do_print){\n\t\tprint__client(&chunk, length);\n\t}\n\tfree__client(&chunk);\n\n\treturn 0;\n}\n\n\nstatic int dump__client_msg_chunk_process(FILE *db_fd, uint32_t length)\n{\n\tstruct P_client_msg chunk;\n\tstruct client_data *cc;\n\tstruct base_msg_chunk *msc;\n\tint rc;\n\n\tclient_msg_count++;\n\n\tmemset(&chunk, 0, sizeof(struct P_client_msg));\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_client_msg_read_v56(db_fd, &chunk, length);\n\t}else{\n\t\trc = persist__chunk_client_msg_read_v234(db_fd, &chunk);\n\t}\n\tif(rc){\n\t\tfprintf(stderr, \"Error: Corrupt persistent database.\\n\");\n\t\treturn rc;\n\t}\n\n\tif(client_stats && chunk.clientid){\n\t\tHASH_FIND(hh_id, clients_by_id, chunk.clientid, strlen(chunk.clientid), cc);\n\t\tif(cc){\n\t\t\tcc->messages++;\n\t\t\tcc->message_size += length;\n\n\t\t\tHASH_FIND(hh, msgs_by_id, &chunk.F.store_id, sizeof(dbid_t), msc);\n\t\t\tif(msc){\n\t\t\t\tcc->message_size += msc->length;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(do_json){\n\t\tjson_add_client_msg(&chunk);\n\t}\n\tif(do_print){\n\t\tprint__client_msg(&chunk, length);\n\t}\n\tfree__client_msg(&chunk);\n\treturn 0;\n}\n\n\nstatic int dump__base_msg_chunk_process(FILE *db_fptr, uint32_t length)\n{\n\tstruct P_base_msg chunk;\n\tstruct mosquitto__base_msg *stored = NULL;\n\tint64_t message_expiry_interval64;\n\tuint32_t message_expiry_interval;\n\tint rc = 0;\n\tstruct base_msg_chunk *mcs;\n\n\tbase_msg_count++;\n\n\tmemset(&chunk, 0, sizeof(struct P_base_msg));\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_base_msg_read_v56(db_fptr, &chunk, length);\n\t}else{\n\t\trc = persist__chunk_base_msg_read_v234(db_fptr, &chunk, db_version);\n\t}\n\tif(rc){\n\t\tfprintf(stderr, \"Error: Corrupt persistent database.\\n\");\n\t\treturn rc;\n\t}\n\n\tif(chunk.F.expiry_time > 0){\n\t\tmessage_expiry_interval64 = chunk.F.expiry_time - time(NULL);\n\t\tif(message_expiry_interval64 < 0 || message_expiry_interval64 > UINT32_MAX){\n\t\t\tmessage_expiry_interval = 0;\n\t\t}else{\n\t\t\tmessage_expiry_interval = (uint32_t)message_expiry_interval64;\n\t\t}\n\t}else{\n\t\tmessage_expiry_interval = 0;\n\t}\n\n\tstored = mosquitto_calloc(1, sizeof(struct mosquitto__base_msg));\n\tif(stored == NULL){\n\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\tmosquitto_free(chunk.source.id);\n\t\tmosquitto_free(chunk.source.username);\n\t\tmosquitto_free(chunk.topic);\n\t\tmosquitto_free(chunk.payload);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tstored->data.store_id = chunk.F.store_id;\n\tstored->data.source_mid = chunk.F.source_mid;\n\tstored->data.topic = chunk.topic;\n\tstored->data.qos = chunk.F.qos;\n\tstored->data.retain = chunk.F.retain;\n\tstored->data.payloadlen = chunk.F.payloadlen;\n\tstored->data.payload =  chunk.payload;\n\tstored->data.properties = chunk.properties;\n\n\trc = db__message_store(&chunk.source, stored, &message_expiry_interval,\n\t\t\tmosq_mo_client);\n\n\tif(do_json){\n\t\tjson_add_base_msg(&chunk);\n\t}\n\n\tmosquitto_free(chunk.source.id);\n\tmosquitto_free(chunk.source.username);\n\tchunk.source.id = NULL;\n\tchunk.source.username = NULL;\n\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tstored->source_listener = chunk.source.listener;\n\t\tstored->data.store_id = chunk.F.store_id;\n\n\t\tHASH_ADD(hh, db.msg_store, data.store_id, sizeof(dbid_t), stored);\n\t}else{\n\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\treturn rc;\n\t}\n\n\tif(client_stats){\n\t\tmcs = calloc(1, sizeof(struct base_msg_chunk));\n\t\tif(!mcs){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tmcs->store_id = chunk.F.store_id;\n\t\tmcs->length = length;\n\t\tHASH_ADD(hh, msgs_by_id, store_id, sizeof(dbid_t), mcs);\n\t}\n\n\tif(do_print){\n\t\tprint__base_msg(&chunk, length);\n\t}\n\tfree__base_msg(&chunk);\n\n\treturn 0;\n}\n\n\nstatic int dump__retain_chunk_process(FILE *db_fd, uint32_t length)\n{\n\tstruct P_retain chunk;\n\tint rc;\n\n\tretain_count++;\n\tif(do_print){\n\t\tprintf(\"DB_CHUNK_RETAIN:\\n\");\n\t}\n\tif(do_print){\n\t\tprintf(\"\\tLength: %d\\n\", length);\n\t}\n\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_retain_read_v56(db_fd, &chunk);\n\t}else{\n\t\trc = persist__chunk_retain_read_v234(db_fd, &chunk);\n\t}\n\tif(rc){\n\t\tfprintf(stderr, \"Error: Corrupt persistent database.\\n\");\n\t\treturn rc;\n\t}\n\n\tif(do_json){\n\t\tjson_add_retained_msg(&chunk);\n\t}\n\n\tif(do_print){\n\t\tprintf(\"\\tStore ID: %\" PRIu64 \"\\n\", chunk.F.store_id);\n\t}\n\treturn 0;\n}\n\n\nstatic int dump__sub_chunk_process(FILE *db_fd, uint32_t length)\n{\n\tint rc = 0;\n\tstruct P_sub chunk;\n\tstruct client_data *cc;\n\n\tsub_count++;\n\n\tmemset(&chunk, 0, sizeof(struct P_sub));\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_sub_read_v56(db_fd, &chunk);\n\t}else{\n\t\trc = persist__chunk_sub_read_v234(db_fd, &chunk);\n\t}\n\tif(rc){\n\t\tfprintf(stderr, \"Error: Corrupt persistent database.\\n\");\n\t\treturn rc;\n\t}\n\n\tif(client_stats && chunk.clientid){\n\t\tHASH_FIND(hh_id, clients_by_id, chunk.clientid, strlen(chunk.clientid), cc);\n\t\tif(cc){\n\t\t\tcc->subscriptions++;\n\t\t\tcc->subscription_size += length;\n\t\t}\n\t}\n\n\tif(do_json){\n\t\tjson_add_subscription(&chunk);\n\t}\n\tif(do_print){\n\t\tprint__sub(&chunk, length);\n\t}\n\tfree__sub(&chunk);\n\n\treturn 0;\n}\n\n\nstatic void report_client_stats(void)\n{\n\tif(client_stats){\n\t\tstruct client_data *cc, *cc_tmp;\n\n\t\tHASH_ITER(hh_id, clients_by_id, cc, cc_tmp){\n\t\t\tprintf(\"SC: %d SS: %d MC: %d MS: %ld   \", cc->subscriptions, cc->subscription_size, cc->messages, cc->message_size);\n\t\t\tprintf(\"%s\\n\", cc->id);\n\t\t}\n\t}\n}\n\n\nstatic void cleanup_client_stats()\n{\n\tstruct base_msg_chunk *msg, *msg_tmp;\n\tstruct client_data *cc, *cc_tmp;\n\n\tHASH_ITER(hh, msgs_by_id, msg, msg_tmp){\n\t\tHASH_DELETE(hh, msgs_by_id, msg);\n\t\tfree(msg);\n\t}\n\n\tHASH_ITER(hh_id, clients_by_id, cc, cc_tmp){\n\t\tHASH_DELETE(hh_id, clients_by_id, cc);\n\t\tfree(cc->id);\n\t\tfree(cc);\n\t}\n}\n\n\nstatic void cleanup_msg_store()\n{\n\tstruct mosquitto__base_msg *msg, *msg_tmp;\n\n\tHASH_ITER(hh, db.msg_store, msg, msg_tmp){\n\t\tHASH_DELETE(hh, db.msg_store, msg);\n\t\tfree(msg);\n\t}\n}\n\n#ifdef WITH_FUZZING\n\n\nint db_dump_fuzz_main(int argc, char *argv[])\n#else\n\n\nint main(int argc, char *argv[])\n#endif\n{\n\tFILE *fd;\n\tchar header[15];\n\tint rc = 0;\n\tuint32_t crc;\n\tuint32_t i32temp;\n\tuint32_t length;\n\tuint32_t chunk;\n\tchar *filename;\n\n\tif(argc == 2){\n\t\tfilename = argv[1];\n\t}else if(argc == 3 && !strcmp(argv[1], \"--stats\")){\n\t\tstats = 1;\n\t\tdo_print = 0;\n\t\tfilename = argv[2];\n\t}else if(argc == 3 && !strcmp(argv[1], \"--client-stats\")){\n\t\tclient_stats = 1;\n\t\tdo_print = 0;\n\t\tfilename = argv[2];\n\t}else if(argc == 3 && !strcmp(argv[1], \"--json\")){\n\t\tdo_print = 0;\n\t\tdo_json = 1;\n\t\tfilename = argv[2];\n\t}else{\n\t\tfprintf(stderr, \"Usage: db_dump [--stats | --client-stats | --json] <mosquitto db filename>\\n\");\n\t\treturn 1;\n\t}\n\n\tif(do_json){\n\t\tjson_init();\n\t}\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tfd = fopen(filename, \"rb\");\n\tif(!fd){\n\t\tfprintf(stderr, \"Error: Unable to open %s\\n\", filename);\n\t\treturn 0;\n\t}\n\tread_e(fd, &header, 15);\n\tif(!memcmp(header, magic, 15)){\n\t\tif(do_print){\n\t\t\tprintf(\"Mosquitto DB dump\\n\");\n\t\t}\n\t\t/* Restore DB as normal */\n\t\tread_e(fd, &crc, sizeof(uint32_t));\n\t\tif(do_print){\n\t\t\tprintf(\"CRC: %d\\n\", crc);\n\t\t}\n\t\tread_e(fd, &i32temp, sizeof(uint32_t));\n\t\tdb_version = ntohl(i32temp);\n\t\tif(do_print){\n\t\t\tprintf(\"DB version: %d\\n\", db_version);\n\t\t}\n\n\t\tif(db_version > MOSQ_DB_VERSION){\n\t\t\tif(do_print){\n\t\t\t\tprintf(\"Warning: mosquitto_db_dump does not support this DB version, continuing but expecting errors.\\n\");\n\t\t\t}\n\t\t}\n\n\t\twhile(persist__chunk_header_read(fd, &chunk, &length) == MOSQ_ERR_SUCCESS){\n\t\t\tswitch(chunk){\n\t\t\t\tcase DB_CHUNK_CFG:\n\t\t\t\t\tif(dump__cfg_chunk_process(fd, length)){\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DB_CHUNK_BASE_MSG:\n\t\t\t\t\tif(dump__base_msg_chunk_process(fd, length)){\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DB_CHUNK_CLIENT_MSG:\n\t\t\t\t\tif(dump__client_msg_chunk_process(fd, length)){\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DB_CHUNK_RETAIN:\n\t\t\t\t\tif(dump__retain_chunk_process(fd, length)){\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DB_CHUNK_SUB:\n\t\t\t\t\tif(dump__sub_chunk_process(fd, length)){\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DB_CHUNK_CLIENT:\n\t\t\t\t\tif(dump__client_chunk_process(fd, length)){\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tfprintf(stderr, \"Warning: Unsupported chunk \\\"%d\\\" of length %d in persistent database file at position %ld. Ignoring.\\n\", chunk, length, ftell(fd));\n\t\t\t\t\tif(fseek(fd, length, SEEK_CUR)){\n\t\t\t\t\t\tfprintf(stderr, \"Error: %s\\n\", strerror(errno));\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}else{\n\t\tfprintf(stderr, \"Error: Unrecognised file format.\\n\");\n\t\trc = 1;\n\t}\n\n\tfclose(fd);\n\n\tif(do_json){\n\t\tjson_print();\n\t\tjson_cleanup();\n\t}\n\tif(stats){\n\t\tprintf(\"DB_CHUNK_CFG:        %ld\\n\", cfg_count);\n\t\tprintf(\"DB_CHUNK_BASE_MSG:   %ld\\n\", base_msg_count);\n\t\tprintf(\"DB_CHUNK_CLIENT_MSG: %ld\\n\", client_msg_count);\n\t\tprintf(\"DB_CHUNK_RETAIN:     %ld\\n\", retain_count);\n\t\tprintf(\"DB_CHUNK_SUB:        %ld\\n\", sub_count);\n\t\tprintf(\"DB_CHUNK_CLIENT:     %ld\\n\", client_count);\n\t}\n\n\treport_client_stats();\n\tcleanup_client_stats();\n\tcleanup_msg_store();\n\n\treturn rc;\nerror:\n\tcleanup_msg_store();\n\tif(fd){\n\t\tfclose(fd);\n\t}\n\treturn 1;\n}\n"
  },
  {
    "path": "apps/db_dump/db_dump.h",
    "content": "#ifndef DB_DUMP_H\n#define DB_DUMP_H\n/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <persist.h>\n\nvoid print__client(struct P_client *chunk, uint32_t length);\nvoid print__client_msg(struct P_client_msg *chunk, uint32_t length);\nvoid print__base_msg(struct P_base_msg *chunk, uint32_t length);\nvoid print__sub(struct P_sub *chunk, uint32_t length);\n\nvoid json_init(void);\nvoid json_print(void);\nvoid json_cleanup(void);\nvoid json_add_base_msg(struct P_base_msg *msg);\nvoid json_add_client(struct P_client *chunk);\nvoid json_add_client_msg(struct P_client_msg *chunk);\nvoid json_add_retained_msg(struct P_retain *msg);\nvoid json_add_subscription(struct P_sub *chunk);\n\n#endif\n"
  },
  {
    "path": "apps/db_dump/json.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <time.h>\n#include <cjson/cJSON.h>\n\n#include \"db_dump.h\"\n#include \"json_help.h\"\n#include <mosquitto_broker_internal.h>\n#include <persist.h>\n\n#define mosquitto_malloc(A) malloc((A))\n#define mosquitto_free(A) free((A))\n#define _mosquitto_malloc(A) malloc((A))\n#define _mosquitto_free(A) free((A))\n#include <uthash.h>\n\n#include \"db_dump.h\"\n\ncJSON *j_tree = NULL;\ncJSON *j_base_msgs = NULL;\ncJSON *j_clients = NULL;\ncJSON *j_client_msgs = NULL;\ncJSON *j_retained_msgs = NULL;\ncJSON *j_subscriptions = NULL;\n\n\nvoid json_init(void)\n{\n\tj_tree = cJSON_CreateObject();\n\tif(!j_tree){\n\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\texit(1);\n\t}\n\n\tif((j_base_msgs = cJSON_AddArrayToObject(j_tree, \"base-messages\")) == NULL\n\t\t\t|| (j_clients = cJSON_AddArrayToObject(j_tree, \"clients\")) == NULL\n\t\t\t|| (j_client_msgs = cJSON_AddArrayToObject(j_tree, \"client-messages\")) == NULL\n\t\t\t|| (j_retained_msgs = cJSON_AddArrayToObject(j_tree, \"retained-messages\")) == NULL\n\t\t\t|| (j_subscriptions = cJSON_AddArrayToObject(j_tree, \"subscriptions\")) == NULL\n\t\t\t){\n\n\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\texit(1);\n\t}\n}\n\n\nvoid json_print(void)\n{\n\tchar *jstr = cJSON_Print(j_tree);\n\tprintf(\"%s\\n\", jstr);\n\tfree(jstr);\n}\n\n\nvoid json_cleanup(void)\n{\n\tcJSON_Delete(j_tree);\n}\n\n\nvoid json_add_base_msg(struct P_base_msg *chunk)\n{\n\tcJSON *j_base_msg = NULL;\n\n\tj_base_msg = cJSON_CreateObject();\n\tcJSON_AddItemToArray(j_base_msgs, j_base_msg);\n\n\tcJSON_AddUIntToObject(j_base_msg, \"storeid\", chunk->F.store_id);\n\tcJSON_AddIntToObject(j_base_msg, \"expiry-time\", chunk->F.expiry_time);\n\tcJSON_AddNumberToObject(j_base_msg, \"source-mid\", chunk->F.source_mid);\n\tcJSON_AddNumberToObject(j_base_msg, \"source-port\", chunk->F.source_port);\n\tcJSON_AddNumberToObject(j_base_msg, \"qos\", chunk->F.qos);\n\tcJSON_AddNumberToObject(j_base_msg, \"retain\", chunk->F.retain);\n\tcJSON_AddStringToObject(j_base_msg, \"topic\", chunk->topic);\n\tif(chunk->source.id){\n\t\tcJSON_AddStringToObject(j_base_msg, \"clientid\", chunk->source.id);\n\t}\n\tif(chunk->source.username){\n\t\tcJSON_AddStringToObject(j_base_msg, \"username\", chunk->source.username);\n\t}\n\tif(chunk->F.payloadlen > 0){\n#ifdef WITH_TLS\n\t\tchar *payload;\n\t\tif(mosquitto_base64_encode(chunk->payload, chunk->F.payloadlen, &payload) == MOSQ_ERR_SUCCESS){\n\t\t\tcJSON_AddStringToObject(j_base_msg, \"payload\", payload);\n\t\t\tmosquitto_free(payload);\n\t\t}\n#else\n\t\tfprintf(stderr, \"Warning: payload not output due to missing base64 support.\\n\");\n#endif\n\t}\n\tif(chunk->properties){\n\t\tcJSON *j_props = mosquitto_properties_to_json(chunk->properties);\n\t\tif(j_props){\n\t\t\tcJSON_AddItemToObject(j_base_msg, \"properties\", j_props);\n\t\t}\n\t}\n}\n\n\nvoid json_add_client(struct P_client *chunk)\n{\n\tcJSON *j_client;\n\n\tj_client = cJSON_CreateObject();\n\tcJSON_AddItemToArray(j_clients, j_client);\n\n\tcJSON_AddStringToObject(j_client, \"clientid\", chunk->clientid);\n\tif(chunk->username){\n\t\tcJSON_AddStringToObject(j_client, \"username\", chunk->username);\n\t}\n\tcJSON_AddIntToObject(j_client, \"session-expiry-time\", chunk->F.session_expiry_time);\n\tcJSON_AddNumberToObject(j_client, \"session-expiry-interval\", chunk->F.session_expiry_interval);\n\tcJSON_AddNumberToObject(j_client, \"last-mid\", chunk->F.last_mid);\n\tcJSON_AddNumberToObject(j_client, \"listener-port\", chunk->F.listener_port);\n\n}\n\n\nvoid json_add_client_msg(struct P_client_msg *chunk)\n{\n\tcJSON *j_client_msg;\n\n\tj_client_msg = cJSON_CreateObject();\n\tcJSON_AddItemToArray(j_client_msgs, j_client_msg);\n\n\tcJSON_AddStringToObject(j_client_msg, \"clientid\", chunk->clientid);\n\tcJSON_AddNumberToObject(j_client_msg, \"storeid\", chunk->subscription_identifier);\n\tcJSON_AddNumberToObject(j_client_msg, \"mid\", chunk->F.mid);\n\tcJSON_AddNumberToObject(j_client_msg, \"qos\", chunk->F.qos);\n\tcJSON_AddNumberToObject(j_client_msg, \"state\", chunk->F.state);\n\tcJSON_AddNumberToObject(j_client_msg, \"retain-dup\", chunk->F.retain_dup);\n\tcJSON_AddNumberToObject(j_client_msg, \"direction\", chunk->F.direction);\n\tcJSON_AddNumberToObject(j_client_msg, \"subscription-identifier\", chunk->subscription_identifier);\n}\n\n\nvoid json_add_subscription(struct P_sub *chunk)\n{\n\tcJSON *j_subscription;\n\n\tj_subscription = cJSON_CreateObject();\n\tcJSON_AddItemToArray(j_subscriptions, j_subscription);\n\n\tcJSON_AddStringToObject(j_subscription, \"clientid\", chunk->clientid);\n\tcJSON_AddStringToObject(j_subscription, \"topic\", chunk->topic);\n\tcJSON_AddNumberToObject(j_subscription, \"qos\", chunk->F.qos);\n\tcJSON_AddNumberToObject(j_subscription, \"options\", chunk->F.options);\n\tcJSON_AddNumberToObject(j_subscription, \"identifier\", chunk->F.identifier);\n}\n\n\nvoid json_add_retained_msg(struct P_retain *chunk)\n{\n\tcJSON *j_retained_msg;\n\n\tj_retained_msg = cJSON_CreateObject();\n\tcJSON_AddItemToArray(j_retained_msgs, j_retained_msg);\n\tcJSON_AddUIntToObject(j_retained_msg, \"storeid\", chunk->F.store_id);\n}\n"
  },
  {
    "path": "apps/db_dump/print.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <inttypes.h>\n#include <stdio.h>\n\n#include \"db_dump.h\"\n#include <mosquitto_broker_internal.h>\n#include <mosquitto/mqtt_protocol.h>\n#include <persist.h>\n#include <property_mosq.h>\n\n\nstatic void print__properties(mosquitto_property *properties)\n{\n\tint i;\n\n\tif(properties == NULL){\n\t\treturn;\n\t}\n\n\tprintf(\"\\tProperties:\\n\");\n\n\twhile(properties){\n\t\tswitch(mosquitto_property_identifier(properties)){\n\t\t\t/* Only properties for base messages are valid for saving */\n\t\t\tcase MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:\n\t\t\t\tprintf(\"\\t\\tPayload format indicator: %d\\n\", mosquitto_property_byte_value(properties));\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_CONTENT_TYPE:\n\t\t\t\tprintf(\"\\t\\tContent type: %s\\n\", mosquitto_property_string_value(properties));\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_RESPONSE_TOPIC:\n\t\t\t\tprintf(\"\\t\\tResponse topic: %s\\n\", mosquitto_property_string_value(properties));\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_CORRELATION_DATA:\n\t\t\t\tprintf(\"\\t\\tCorrelation data: \");\n\t\t\t\tconst uint8_t *bin = mosquitto_property_binary_value(properties);\n\t\t\t\tfor(i=0; i<mosquitto_property_binary_value_length(properties); i++){\n\t\t\t\t\tprintf(\"%02X\", bin[i]);\n\t\t\t\t}\n\t\t\t\tprintf(\"\\n\");\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_USER_PROPERTY:\n\t\t\t\tprintf(\"\\t\\tUser property: %s , %s\\n\", mosquitto_property_string_name(properties), mosquitto_property_string_value(properties));\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tprintf(\"\\t\\tInvalid property type: %d\\n\", mosquitto_property_identifier(properties));\n\t\t\t\tbreak;\n\t\t}\n\n\t\tproperties = mosquitto_property_next(properties);\n\t}\n}\n\n\nvoid print__client(struct P_client *chunk, uint32_t length)\n{\n\tprintf(\"DB_CHUNK_CLIENT:\\n\");\n\tprintf(\"\\tLength: %d\\n\", length);\n\tprintf(\"\\tClient ID: %s\\n\", chunk->clientid);\n\tif(chunk->username){\n\t\tprintf(\"\\tUsername: %s\\n\", chunk->username);\n\t}\n\tif(chunk->F.listener_port > 0){\n\t\tprintf(\"\\tListener port: %u\\n\", chunk->F.listener_port);\n\t}\n\tprintf(\"\\tLast MID: %d\\n\", chunk->F.last_mid);\n\tprintf(\"\\tSession expiry time: %\" PRIu64 \"\\n\", chunk->F.session_expiry_time);\n\tprintf(\"\\tSession expiry interval: %u\\n\", chunk->F.session_expiry_interval);\n}\n\n\nvoid print__client_msg(struct P_client_msg *chunk, uint32_t length)\n{\n\tprintf(\"DB_CHUNK_CLIENT_MSG:\\n\");\n\tprintf(\"\\tLength: %d\\n\", length);\n\tprintf(\"\\tClient ID: %s\\n\", chunk->clientid);\n\tprintf(\"\\tStore ID: %\" PRIu64 \"\\n\", chunk->F.store_id);\n\tprintf(\"\\tMID: %d\\n\", chunk->F.mid);\n\tprintf(\"\\tQoS: %d\\n\", chunk->F.qos);\n\tprintf(\"\\tRetain: %d\\n\", (chunk->F.retain_dup&0xF0)>>4);\n\tprintf(\"\\tDirection: %d\\n\", chunk->F.direction);\n\tprintf(\"\\tState: %d\\n\", chunk->F.state);\n\tprintf(\"\\tDup: %d\\n\", chunk->F.retain_dup&0x0F);\n\tif(chunk->subscription_identifier){\n\t\tprintf(\"\\tSubscription identifier: %d\\n\", chunk->subscription_identifier);\n\t}\n}\n\n\nvoid print__base_msg(struct P_base_msg *chunk, uint32_t length)\n{\n\tuint8_t *payload;\n\n\tprintf(\"DB_CHUNK_BASE_MSG:\\n\");\n\tprintf(\"\\tLength: %d\\n\", length);\n\tprintf(\"\\tStore ID: %\" PRIu64 \"\\n\", chunk->F.store_id);\n\t/* printf(\"\\tSource ID: %s\\n\", chunk->source_id); */\n\t/* printf(\"\\tSource Username: %s\\n\", chunk->source_username); */\n\tprintf(\"\\tSource Port: %d\\n\", chunk->F.source_port);\n\tprintf(\"\\tSource MID: %d\\n\", chunk->F.source_mid);\n\tprintf(\"\\tTopic: %s\\n\", chunk->topic);\n\tprintf(\"\\tQoS: %d\\n\", chunk->F.qos);\n\tprintf(\"\\tRetain: %d\\n\", chunk->F.retain);\n\tprintf(\"\\tPayload Length: %d\\n\", chunk->F.payloadlen);\n\tprintf(\"\\tExpiry Time: %\" PRIu64 \"\\n\", chunk->F.expiry_time);\n\n\tpayload = chunk->payload;\n\tif(chunk->F.payloadlen < 256){\n\t\t/* Print payloads with UTF-8 data below an arbitrary limit of 256 bytes */\n\t\tif(mosquitto_validate_utf8((char *)payload, (uint16_t)chunk->F.payloadlen) == MOSQ_ERR_SUCCESS){\n\t\t\tprintf(\"\\tPayload: %s\\n\", payload);\n\t\t}\n\t}\n\tprint__properties(chunk->properties);\n}\n\n\nvoid print__sub(struct P_sub *chunk, uint32_t length)\n{\n\tprintf(\"DB_CHUNK_SUB:\\n\");\n\tprintf(\"\\tLength: %u\\n\", length);\n\tprintf(\"\\tClient ID: %s\\n\", chunk->clientid);\n\tprintf(\"\\tTopic: %s\\n\", chunk->topic);\n\tprintf(\"\\tQoS: %d\\n\", chunk->F.qos);\n\tprintf(\"\\tSubscription ID: %d\\n\", chunk->F.identifier);\n\tprintf(\"\\tOptions: 0x%02X\\n\", chunk->F.options);\n}\n\n\n"
  },
  {
    "path": "apps/db_dump/stubs.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto_internal.h\"\n#include \"util_mosq.h\"\n\n#ifndef UNUSED\n#  define UNUSED(A) (void)(A)\n#endif\n\nstruct mosquitto *context__init(void)\n{\n\treturn NULL;\n}\n\n\nvoid context__add_to_by_id(struct mosquitto *context)\n{\n\tUNUSED(context);\n}\n\n\nint db__message_store(const struct mosquitto *source, struct mosquitto__base_msg *base_msg, uint32_t *message_expiry_interval, enum mosquitto_msg_origin origin)\n{\n\tUNUSED(source); UNUSED(base_msg); UNUSED(message_expiry_interval); UNUSED(origin); return 0;\n}\n\n\nvoid db__msg_store_ref_inc(struct mosquitto__base_msg *base_msg)\n{\n\tUNUSED(base_msg);\n}\n\n\nint log__printf(struct mosquitto *mosq, unsigned int level, const char *fmt, ...)\n{\n\tUNUSED(mosq); UNUSED(level); UNUSED(fmt); return 0;\n}\n\n\nint retain__store(const char *topic, struct mosquitto__base_msg *base_msg, char **split_topics, bool persist)\n{\n\tUNUSED(topic); UNUSED(base_msg); UNUSED(split_topics); UNUSED(persist); return 0;\n}\n\n\nint sub__add(struct mosquitto *context, const struct mosquitto_subscription *sub)\n{\n\tUNUSED(context); UNUSED(sub); return 0;\n}\n\n\nvoid db__msg_add_to_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *msg)\n{\n\tUNUSED(msg_data); UNUSED(msg);\n}\n\n\nvoid db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *msg)\n{\n\tUNUSED(msg_data); UNUSED(msg);\n}\n\n\nint session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)\n{\n\tUNUSED(context); UNUSED(expiry_time); return 0;\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/CMakeLists.txt",
    "content": "if(WITH_TLS)\n\tset(SRC\n\t\tmosquitto_ctrl.c mosquitto_ctrl.h\n\t\tbroker.c\n\t\tclient.c\n\t\tdynsec.c\n\t\tdynsec_client.c\n\t\tdynsec_group.c\n\t\tdynsec_role.c\n\t\t../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h\n\t\toptions.c\n\t\t../../common/json_help.c ../../common/json_help.h\n\t)\n\n\tif(WITH_CTRL_SHELL AND LINEEDITING_FOUND)\n\t\tadd_library(ctrl_shell OBJECT\n\t\t\tctrl_shell.c\n\t\t\tctrl_shell.h\n\t\t\tctrl_shell_broker.c\n\t\t\tctrl_shell_client.c\n\t\t\tctrl_shell_completion_tree.c\n\t\t\tctrl_shell_dynsec.c\n\t\t\tctrl_shell_post_connect.c\n\t\t\tctrl_shell_pre_connect.c\n\t\t\tctrl_shell_printf.c\n\t\t)\n\t\ttarget_compile_definitions(ctrl_shell PRIVATE WITH_CTRL_SHELL)\n\t\ttarget_include_directories(ctrl_shell PRIVATE\n\t\t\t\"${mosquitto_SOURCE_DIR}\"\n\t\t\t\"${mosquitto_SOURCE_DIR}/common\"\n\t\t\t\"${mosquitto_SOURCE_DIR}/include\"\n\t\t\t\"${CJSON_INCLUDE_DIRS}\"\n\t\t)\n\t\ttarget_link_libraries(ctrl_shell PRIVATE\n\t\t\tLineEditing::LineEditing\n\t\t\tOpenSSL::SSL\n\t\t)\n\n\t\tadd_library(ctrl_shell_io OBJECT\n\t\t\tctrl_shell_io.c\n\t\t)\n\t\ttarget_compile_definitions(ctrl_shell_io PRIVATE WITH_CTRL_SHELL)\n\t\ttarget_include_directories(ctrl_shell_io PRIVATE\n\t\t\t\"${mosquitto_SOURCE_DIR}\"\n\t\t\t\"${mosquitto_SOURCE_DIR}/common\"\n\t\t\t\"${mosquitto_SOURCE_DIR}/include\"\n\t\t\t\"${CJSON_INCLUDE_DIRS}\"\n\t\t)\n\t\ttarget_link_libraries(ctrl_shell_io PRIVATE\n\t\t\tLineEditing::LineEditing\n\t\t\tOpenSSL::SSL\n\t\t)\n\tendif()\n\tadd_executable(mosquitto_ctrl ${SRC})\n\n\ttarget_include_directories(mosquitto_ctrl PRIVATE\n\t\t\"${mosquitto_SOURCE_DIR}/apps/mosquitto_passwd\"\n\t\t\"${mosquitto_SOURCE_DIR}/common\"\n\t\t\"${mosquitto_SOURCE_DIR}/plugins/common\"\n\t\t\"${mosquitto_SOURCE_DIR}/plugins/dynamic-security\"\n\t\t\"${ARGON2_INCLUDE_DIRS}\"\n\t\t\"${CJSON_INCLUDE_DIRS}\"\n\t)\n\n\tif(WITH_CTRL_SHELL AND LINEEDITING_FOUND)\n\t\ttarget_compile_definitions(mosquitto_ctrl PRIVATE WITH_CTRL_SHELL)\n\t\ttarget_link_libraries(mosquitto_ctrl PRIVATE\n\t\t\tLineEditing::LineEditing\n\t\t\tctrl_shell\n\t\t\tctrl_shell_io\n\t\t)\n\tendif()\n\n\tif(WITH_STATIC_LIBRARIES)\n\t\ttarget_link_libraries(mosquitto_ctrl PRIVATE libmosquitto_static)\n\telse()\n\t\ttarget_link_libraries(mosquitto_ctrl PRIVATE libmosquitto)\n\tendif()\n\n\tif(UNIX)\n\t\tif(APPLE)\n\t\t\ttarget_link_libraries(mosquitto_ctrl PRIVATE dl)\n\t\telseif(${CMAKE_SYSTEM_NAME} MATCHES \"OpenBSD\")\n\t\t\t#\n\t\telseif(${CMAKE_SYSTEM_NAME} MATCHES \"NetBSD\")\n\t\t\t#\n\t\telseif(QNX)\n\t\t\t#\n\t\telse()\n\t\t\ttarget_link_libraries(mosquitto_ctrl PRIVATE dl)\n\t\tendif()\n\tendif()\n\n\ttarget_link_libraries(mosquitto_ctrl\n\t  PRIVATE\n\t  \tcommon-options\n\t\tlibmosquitto_common\n\t\tOpenSSL::SSL\n\t\tcJSON\n\t)\n\n\tif(WITH_THREADING)\n\t\tif(WIN32)\n\t\t\ttarget_link_libraries(mosquitto_ctrl PRIVATE PThreads4W::PThreads4W)\n\t\telse()\n\t\t\tset(THREADS_PREFER_PTHREAD_FLAG ON)\n\t\t\tfind_package(Threads REQUIRED)\n\t\t\ttarget_link_libraries(mosquitto_ctrl PRIVATE Threads::Threads)\n\t\tendif()\n\tendif()\n\n\n\tinstall(TARGETS mosquitto_ctrl\n\t\tRUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n\t)\nendif()\n"
  },
  {
    "path": "apps/mosquitto_ctrl/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\n.PHONY: all install uninstall clean reallyclean\n\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=-I${R}/lib -I${R}/apps/mosquitto_passwd -I${R}/plugins/dynamic-security -I${R}/common\nLOCAL_LDFLAGS+=\nLOCAL_LDADD+=-lcjson -ldl ${LIBMOSQ} ${LIBMOSQ_COMMON} -lcrypto -lssl\n\n# ------------------------------------------\n#  Compile time options\n# ------------------------------------------\n\nifeq ($(WITH_SOCKS),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_SOCKS\nendif\n\nifeq ($(WITH_THREADING),yes)\nLOCAL_LDFLAGS+=-pthread\nendif\n\n\nifeq ($(WITH_EDITLINE),yes)\nLOCAL_LDADD+=-ledit\nLOCAL_CPPFLAGS+=-DWITH_CTRL_SHELL -DWITH_EDITLINE\nendif\n\nOBJS= \\\n\tmosquitto_ctrl.o \\\n\tbroker.o \\\n\tclient.o \\\n\tdynsec.o \\\n\tdynsec_client.o \\\n\tdynsec_group.o \\\n\tdynsec_role.o \\\n\toptions.o\n\nifeq ($(WITH_EDITLINE),yes)\nOBJS+= \\\n\tctrl_shell.o \\\n\tctrl_shell_broker.o \\\n\tctrl_shell_client.o \\\n\tctrl_shell_completion_tree.o \\\n\tctrl_shell_dynsec.o \\\n\tctrl_shell_io.o \\\n\tctrl_shell_post_connect.o \\\n\tctrl_shell_pre_connect.o \\\n\tctrl_shell_printf.o\nendif\n\nOBJS_EXTERNAL= \\\n\tget_password.o \\\n\tjson_help.o\n\nEXAMPLE_OBJS= example.o\n\nTARGET:=mosquitto_ctrl mosquitto_ctrl_example.so\n\nall : ${TARGET}\n\nmosquitto_ctrl : ${OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}${CC} $^ -o $@ $(LOCAL_LDFLAGS) $(LOCAL_LDADD)\n\nmosquitto_ctrl_example.so : ${EXAMPLE_OBJS}\n\t$(CROSS_COMPILE)$(CC) $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) ${LOCAL_LDFLAGS} -fPIC -shared $< -o $@\n\n${OBJS} : %.o: %.c mosquitto_ctrl.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nexample.o : example.c mosquitto_ctrl.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -fPIC -c $< -o $@\n\nget_password.o : ${R}/apps/mosquitto_passwd/get_password.c ${R}/apps/mosquitto_passwd/get_password.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\njson_help.o : ${R}/common/json_help.c ${R}/common/json_help.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n${R}/lib/libmosquitto.so.${SOVERSION} :\n\t$(MAKE) -C ${R}/lib\n\n${R}/lib/libmosquitto.a :\n\t$(MAKE) -C ${R}/lib libmosquitto.a\n\ninstall : all\nifeq ($(WITH_TLS),yes)\n\t$(INSTALL) -d \"${DESTDIR}$(prefix)/bin\"\n\t$(INSTALL) ${STRIP_OPTS} mosquitto_ctrl \"${DESTDIR}${prefix}/bin/mosquitto_ctrl\"\nendif\n\nuninstall :\n\t-rm -f \"${DESTDIR}${prefix}/bin/mosquitto_ctrl\"\n\nclean :\n\t-rm -f *.o mosquitto_ctrl *.gcda *.gcno *.so\n\nreallyclean : clean\n\t-rm -rf *.orig *.db\n"
  },
  {
    "path": "apps/mosquitto_ctrl/broker.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#define CJSON_VERSION_FULL (CJSON_VERSION_MAJOR*1000000+CJSON_VERSION_MINOR*1000+CJSON_VERSION_PATCH)\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"json_help.h\"\n#include \"mosquitto_ctrl.h\"\n#include \"mosquitto.h\"\n\n\nvoid broker__print_usage(void)\n{\n\tprintf(\"\\nBroker Control module\\n\");\n\tprintf(\"=======================\\n\");\n\n\tprintf(\"List plugins    :          listPlugins\\n\");\n\tprintf(\"List listeners  :          listListeners\\n\");\n}\n\n\n/* ################################################################\n * #\n * # Payload callback\n * #\n * ################################################################ */\n\n\nstatic void print_listeners(cJSON *j_response)\n{\n\tcJSON *j_data, *j_listeners, *j_listener, *jtmp;\n\tconst char *stmp;\n\tint i=1;\n\n\tj_data = cJSON_GetObjectItem(j_response, \"data\");\n\tif(j_data == NULL || !cJSON_IsObject(j_data)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tj_listeners = cJSON_GetObjectItem(j_data, \"listeners\");\n\tif(j_listeners == NULL || !cJSON_IsArray(j_listeners)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tcJSON_ArrayForEach(j_listener, j_listeners){\n\t\tprintf(\"Listener %d:\\n\", i);\n\n\t\tjtmp = cJSON_GetObjectItem(j_listener, \"port\");\n\t\tif(jtmp && cJSON_IsNumber(jtmp)){\n\t\t\tprintf(\"  Port:              %d\\n\", jtmp->valueint);\n\t\t}\n\n\t\tif(json_get_string(j_listener, \"protocol\", &stmp, false) == MOSQ_ERR_SUCCESS){\n\t\t\tprintf(\"  Protocol:          %s\\n\", stmp);\n\t\t}\n\n\t\tif(json_get_string(j_listener, \"socket-path\", &stmp, false) == MOSQ_ERR_SUCCESS){\n\t\t\tprintf(\"  Socket path:       %s\\n\", stmp);\n\t\t}\n\n\t\tif(json_get_string(j_listener, \"bind-address\", &stmp, false) == MOSQ_ERR_SUCCESS){\n\t\t\tprintf(\"  Bind address:      %s\\n\", stmp);\n\t\t}\n\n\t\tjtmp = cJSON_GetObjectItem(j_listener, \"tls\");\n\t\tprintf(\"  TLS:               %s\\n\", jtmp && cJSON_IsBool(jtmp) && cJSON_IsTrue(jtmp)?\"true\":\"false\");\n\t\tprintf(\"\\n\");\n\t\ti++;\n\t}\n}\n\n\nstatic void print_plugin_info(cJSON *j_response)\n{\n\tcJSON *j_data, *j_plugins, *j_plugin, *jtmp, *j_eps;\n\tconst char *stmp;\n\tbool first;\n\n\tj_data = cJSON_GetObjectItem(j_response, \"data\");\n\tif(j_data == NULL || !cJSON_IsObject(j_data)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tj_plugins = cJSON_GetObjectItem(j_data, \"plugins\");\n\tif(j_plugins == NULL || !cJSON_IsArray(j_plugins)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tcJSON_ArrayForEach(j_plugin, j_plugins){\n\t\tif(json_get_string(j_plugin, \"name\", &stmp, false) != MOSQ_ERR_SUCCESS){\n\t\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tprintf(\"Plugin:            %s\\n\", stmp);\n\n\t\tif(json_get_string(j_plugin, \"version\", &stmp, false) == MOSQ_ERR_SUCCESS){\n\t\t\tprintf(\"Version:           %s\\n\", stmp);\n\t\t}\n\n\t\tj_eps = cJSON_GetObjectItem(j_plugin, \"control-endpoints\");\n\t\tif(j_eps && cJSON_IsArray(j_eps)){\n\t\t\tfirst = true;\n\t\t\tcJSON_ArrayForEach(jtmp, j_eps){\n\t\t\t\tif(jtmp && cJSON_IsString(jtmp) && jtmp->valuestring){\n\t\t\t\t\tif(first){\n\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t\tprintf(\"Control endpoints: %s\\n\", jtmp->valuestring);\n\t\t\t\t\t}else{\n\t\t\t\t\t\tprintf(\"                   %s\\n\", jtmp->valuestring);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void broker__payload_callback(struct mosq_ctrl *ctrl, long payloadlen, const void *payload)\n{\n\tcJSON *tree, *j_responses, *j_response, *j_command;\n\n\tUNUSED(ctrl);\n\n#if CJSON_VERSION_FULL < 1007013\n\tUNUSED(payloadlen);\n\ttree = cJSON_Parse(payload);\n#else\n\ttree = cJSON_ParseWithLength(payload, (size_t)payloadlen);\n#endif\n\tif(tree == NULL){\n\t\tfprintf(stderr, \"Error: Payload not JSON.\\n\");\n\t\treturn;\n\t}\n\n\tj_responses = cJSON_GetObjectItem(tree, \"responses\");\n\tif(j_responses == NULL || !cJSON_IsArray(j_responses)){\n\t\tfprintf(stderr, \"Error: Payload missing data.\\n\");\n\t\tcJSON_Delete(tree);\n\t\treturn;\n\t}\n\n\tj_response = cJSON_GetArrayItem(j_responses, 0);\n\tif(j_response == NULL){\n\t\tfprintf(stderr, \"Error: Payload missing data.\\n\");\n\t\tcJSON_Delete(tree);\n\t\treturn;\n\t}\n\n\tj_command = cJSON_GetObjectItem(j_response, \"command\");\n\tif(j_command == NULL){\n\t\tfprintf(stderr, \"Error: Payload missing data.\\n\");\n\t\tcJSON_Delete(tree);\n\t\treturn;\n\t}\n\n\tconst char *error;\n\tif(json_get_string(j_response, \"error\", &error, false) == MOSQ_ERR_SUCCESS){\n\t\tfprintf(stderr, \"%s: Error: %s.\\n\", j_command->valuestring, error);\n\t}else{\n\t\tif(!strcasecmp(j_command->valuestring, \"listPlugins\")){\n\t\t\tprint_plugin_info(j_response);\n\t\t}else if(!strcasecmp(j_command->valuestring, \"listListeners\")){\n\t\t\tprint_listeners(j_response);\n\t\t}else{\n\t\t\t/* fprintf(stderr, \"%s: Success\\n\", j_command->valuestring); */\n\t\t}\n\t}\n\tcJSON_Delete(tree);\n}\n\n\nstatic int broker__list_plugins(int argc, char *argv[], cJSON *j_command)\n{\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"listPlugins\") == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int broker__list_listeners(int argc, char *argv[], cJSON *j_command)\n{\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"listListeners\") == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* ################################################################\n * #\n * # Main\n * #\n * ################################################################ */\n\n\nint broker__main(int argc, char *argv[], struct mosq_ctrl *ctrl)\n{\n\tint rc = -1;\n\tcJSON *j_tree;\n\tcJSON *j_commands, *j_command;\n\n\tif(!strcasecmp(argv[0], \"help\")){\n\t\tbroker__print_usage();\n\t\treturn -1;\n\t}\n\n\t/* The remaining commands need a network connection and JSON command. */\n\n\tctrl->payload_callback = broker__payload_callback;\n\tctrl->request_topic = strdup(\"$CONTROL/broker/v1\");\n\tctrl->response_topic = strdup(\"$CONTROL/broker/v1/response\");\n\tif(ctrl->request_topic == NULL || ctrl->response_topic == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tj_tree = cJSON_CreateObject();\n\tif(j_tree == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tj_commands = cJSON_AddArrayToObject(j_tree, \"commands\");\n\tif(j_commands == NULL){\n\t\tcJSON_Delete(j_tree);\n\t\tj_tree = NULL;\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tj_command = cJSON_CreateObject();\n\tif(j_command == NULL){\n\t\tcJSON_Delete(j_tree);\n\t\tj_tree = NULL;\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(j_commands, j_command);\n\n\tif(!strcasecmp(argv[0], \"listPlugins\")){\n\t\trc = broker__list_plugins(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"listListeners\")){\n\t\trc = broker__list_listeners(argc-1, &argv[1], j_command);\n\n\t}else{\n\t\tfprintf(stderr, \"Command '%s' not recognised.\\n\", argv[0]);\n\t\tcJSON_Delete(j_tree);\n\t\tj_tree = NULL;\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tctrl->payload = cJSON_PrintUnformatted(j_tree);\n\t\tcJSON_Delete(j_tree);\n\t\tif(ctrl->payload == NULL){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\treturn rc;\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/client.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n#include \"mosquitto_ctrl.h\"\n\nstatic int run = 1;\n\n\nstatic void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\tstruct mosq_ctrl *ctrl = obj;\n\n\tUNUSED(properties);\n\n\tif(ctrl->payload_callback){\n\t\tctrl->payload_callback(ctrl, msg->payloadlen, msg->payload);\n\t}\n\n\tmosquitto_disconnect_v5(mosq, 0, NULL);\n\trun = 0;\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(obj);\n\tUNUSED(mid);\n\tUNUSED(properties);\n\n\tif(reason_code > 127){\n\t\tfprintf(stderr, \"Publish error: %s\\n\", mosquitto_reason_string(reason_code));\n\t\trun = 0;\n\t\tmosquitto_disconnect_v5(mosq, 0, NULL);\n\t}\n}\n\n\nstatic void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos, const mosquitto_property *properties)\n{\n\tstruct mosq_ctrl *ctrl = obj;\n\n\tUNUSED(mid);\n\tUNUSED(properties);\n\n\tif(qos_count == 1){\n\t\tif(granted_qos[0] < 128){\n\t\t\t/* Success */\n\t\t\tmosquitto_publish(mosq, NULL, ctrl->request_topic, (int)strlen(ctrl->payload), ctrl->payload, ctrl->cfg.qos, 0);\n\t\t\tfree(ctrl->request_topic);\n\t\t\tctrl->request_topic = NULL;\n\t\t\tfree(ctrl->payload);\n\t\t\tctrl->payload = NULL;\n\t\t}else{\n\t\t\tif(ctrl->cfg.protocol_version == MQTT_PROTOCOL_V5){\n\t\t\t\tfprintf(stderr, \"Subscribe error: %s\\n\", mosquitto_reason_string(granted_qos[0]));\n\t\t\t}else{\n\t\t\t\tfprintf(stderr, \"Subscribe error: Subscription refused.\\n\");\n\t\t\t}\n\t\t\trun = 0;\n\t\t\tmosquitto_disconnect_v5(mosq, 0, NULL);\n\t\t}\n\t}else{\n\t\trun = 0;\n\t\tmosquitto_disconnect_v5(mosq, 0, NULL);\n\t}\n}\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int reason_code, int flags, const mosquitto_property *properties)\n{\n\tstruct mosq_ctrl *ctrl = obj;\n\n\tUNUSED(flags);\n\tUNUSED(properties);\n\n\tif(reason_code == 0){\n\t\tif(ctrl->response_topic){\n\t\t\tmosquitto_subscribe(mosq, NULL, ctrl->response_topic, ctrl->cfg.qos);\n\t\t\tfree(ctrl->response_topic);\n\t\t\tctrl->response_topic = NULL;\n\t\t}\n\t}else{\n\t\tif(ctrl->cfg.protocol_version == MQTT_PROTOCOL_V5){\n\t\t\tif(reason_code == MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION){\n\t\t\t\tfprintf(stderr, \"Connection error: %s. Try connecting to an MQTT v5 broker, or use MQTT v3.x mode.\\n\", mosquitto_reason_string(reason_code));\n\t\t\t}else{\n\t\t\t\tfprintf(stderr, \"Connection error: %s\\n\", mosquitto_reason_string(reason_code));\n\t\t\t}\n\t\t}else{\n\t\t\tfprintf(stderr, \"Connection error: %s\\n\", mosquitto_connack_string(reason_code));\n\t\t}\n\t\trun = 0;\n\t\tmosquitto_disconnect_v5(mosq, 0, NULL);\n\t}\n}\n\n\nint client_request_response(struct mosq_ctrl *ctrl)\n{\n\tstruct mosquitto *mosq;\n\tint rc;\n\ttime_t start;\n\n\tif(ctrl->cfg.cafile == NULL && ctrl->cfg.capath == NULL && !ctrl->cfg.tls_use_os_certs && ctrl->cfg.port != 8883\n#  ifdef FINAL_WITH_TLS_PSK\n\t\t\t&& !ctrl->cfg.psk\n#  endif\n\t\t\t){\n\t\tfprintf(stderr, \"Warning: You are running mosquitto_ctrl without encryption.\\nThis means all of the configuration changes you are making are visible on the network, including passwords.\\n\\n\");\n\t}\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(ctrl->cfg.id, true, ctrl);\n\trc = client_opts_set(mosq, &ctrl->cfg);\n\tif(rc){\n\t\tgoto cleanup;\n\t}\n\n\tmosquitto_connect_v5_callback_set(mosq, on_connect);\n\tmosquitto_subscribe_v5_callback_set(mosq, on_subscribe);\n\tmosquitto_publish_v5_callback_set(mosq, on_publish);\n\tmosquitto_message_v5_callback_set(mosq, on_message);\n\n\trc = client_connect(mosq, &ctrl->cfg);\n\tif(rc){\n\t\tgoto cleanup;\n\t}\n\n\tstart = time(NULL);\n\twhile(run && start+10 > time(NULL)){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\ncleanup:\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn rc;\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell.c",
    "content": "/*\nCopyright (c) 2023 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <config.h>\n\n#include <arpa/inet.h>\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <pthread.h>\n#include <signal.h>\n#include <stdarg.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <termios.h>\n#include <time.h>\n\n#ifndef WIN32\n#  include <netinet/in.h>\n#  include <unistd.h>\n#endif\n\n#include \"ctrl_shell.h\"\n#include \"ctrl_shell_internal.h\"\n\n#define UNUSED(A) (void)(A)\n\n#ifdef WITH_CTRL_SHELL\n\n#define FREE(A) do{free(A); A = NULL;}while(0)\n\nconst char *ANSI_URL = NULL;\nconst char *ANSI_MODULE = NULL;\nconst char *ANSI_INPUT = NULL;\nconst char *ANSI_ERROR = NULL;\nconst char **ANSI_LABEL = NULL;\nconst char *ANSI_RESET = \"\\001\\e[0m\\002\";\nconst char *ANSI_TOPIC = NULL;\nconst char *ANSI_POSITIVE = NULL;\nconst char *ANSI_NEGATIVE = NULL;\n\nconst char *ANSI_LABEL_none[] = {\"\", \"\"};\n\nconst char ANSI_URL_dark[] =     \"\\001\\e[38;5;155m\\002\";\nconst char ANSI_MODULE_dark[] =  \"\\001\\e[38;5;214m\\002\";\nconst char ANSI_INPUT_dark[] =   \"\\001\\e[38;5;80m\\002\";\nconst char ANSI_ERROR_dark[] =   \"\\001\\e[38;5;198m\\002\";\nconst char *ANSI_LABEL_dark[] =  {\n\t\"\\001\\e[38;5;207m\\002\",\n\t\"\\001\\e[38;5;219m\\002\",\n};\nconst char ANSI_TOPIC_dark[] =    \"\\001\\e[93m\\002\";\nconst char ANSI_POSITIVE_dark[] =    \"\\001\\e[92m\\002\";\nconst char ANSI_NEGATIVE_dark[] =    \"\\001\\e[95m\\002\";\n\nconst char ANSI_URL_light[] =    \"\\001\\e[38;5;23m\\002\";\nconst char ANSI_MODULE_light[] = \"\\001\\e[38;5;130m\\002\";\nconst char ANSI_INPUT_light[] =  \"\\001\\e[38;5;27m\\002\";\nconst char ANSI_ERROR_light[] =  \"\\001\\e[38;5;196m\\002\";\nconst char *ANSI_LABEL_light[] =  {\n\t\"\\001\\e[38;5;165m\\002\",\n\t\"\\001\\e[38;5;171m\\002\",\n};\nconst char ANSI_TOPIC_light[] =    \"\\001\\e[33m\\002\";\nconst char ANSI_POSITIVE_light[] =    \"\\001\\e[32m\\002\";\nconst char ANSI_NEGATIVE_light[] =    \"\\001\\e[35m\\002\";\n\n\nchar prompt[200];\nstruct ctrl_shell data;\nstatic int generator_arg = -1;\nstruct completion_tree_cmd *current_cmd_match = NULL;\n\n\nstatic void signal_winch(int signal)\n{\n\tUNUSED(signal);\n\trl_resize_terminal();\n}\n\n\nstatic void signal_term(int signal)\n{\n\tUNUSED(signal);\n\tdata.run = 0;\n}\n\n\nstatic void term_set_flag(bool set, unsigned int flag)\n{\n\tstruct termios ts;\n\n\ttcgetattr(0, &ts);\n\tif(set){\n\t\tts.c_lflag |= (flag);\n\t}else{\n\t\tts.c_lflag &= (unsigned int)(~flag);\n\t}\n\ttcsetattr(0, TCSANOW, &ts);\n}\n\n\nstatic void term_set_echo(bool echo)\n{\n\tterm_set_flag(echo, ECHO);\n}\n\n\nstatic void term_set_canon(bool canon)\n{\n\tterm_set_flag(canon, ICANON);\n}\n\n\nvoid ctrl_shell_rtrim(char *buf)\n{\n\tsize_t slen = strlen(buf);\n\twhile(slen > 0 && isspace((unsigned char)buf[slen-1])){\n\t\tbuf[slen-1] = '\\0';\n\t\tslen = strlen(buf);\n\t}\n}\n\n\nbool ctrl_shell_get_password(char *buf, size_t len)\n{\n\tctrl_shell_printf(\"%spassword:%s\", ANSI_INPUT, ANSI_RESET);\n\tterm_set_echo(false);\n\tif(ctrl_shell_fgets(buf, (int)len, stdin) == NULL){\n\t\tterm_set_echo(true);\n\t\treturn false;\n\t}\n\tterm_set_echo(true);\n\tctrl_shell_printf(\"\\n\");\n\n\tctrl_shell_rtrim(buf);\n\treturn true;\n}\n\n\nstatic int response_wait(void)\n{\n\tstruct timespec timeout;\n\tint rc = 0;\n\n\tdata.response_received = false;\n\n\tclock_gettime(CLOCK_REALTIME, &timeout);\n\ttimeout.tv_sec += 2;\n\twhile(data.response_received == false){\n\t\tif(pthread_cond_timedwait(&data.response_cond, &data.response_mutex, &timeout) == ETIMEDOUT){\n\t\t\tctrl_shell_printf(\"Timed out with no response.\\n\");\n\t\t\trc = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\nint ctrl_shell_publish_blocking(cJSON *j_command)\n{\n\tint rc = 0;\n\n\tcJSON *j_commands = cJSON_CreateObject();\n\tcJSON *j_array = cJSON_AddArrayToObject(j_commands, \"commands\");\n\tcJSON_AddItemToArray(j_array, j_command);\n\n\tchar *payload = cJSON_PrintUnformatted(j_commands);\n\tcJSON_Delete(j_commands);\n\n\tpthread_mutex_lock(&data.response_mutex);\n\n\tmosquitto_publish(data.mosq, NULL, data.request_topic, (int)strlen(payload), payload, 1, false);\n\tFREE(payload);\n\n\t/* Check for publish callback */\n\trc = response_wait();\n\tif(rc){\n\t\tpthread_mutex_unlock(&data.response_mutex);\n\t\treturn rc;\n\t}\n\n\tif(data.publish_rc >= 128){\n\t\tpthread_mutex_unlock(&data.response_mutex);\n\t\treturn 1;\n\t}\n\n\t/* Check for message callback */\n\trc = response_wait();\n\tpthread_mutex_unlock(&data.response_mutex);\n\n\treturn rc;\n}\n\n\nvoid ctrl_shell__connect_blocking(const char *hostname, int port)\n{\n\tpthread_mutex_lock(&data.response_mutex);\n\n\tint rc = mosquitto_connect(data.mosq, hostname, port, 60);\n\trc = mosquitto_loop_start(data.mosq);\n\n\t/* FIXME - do something with the error */\n\tUNUSED(rc);\n\n\tresponse_wait();\n\tpthread_mutex_unlock(&data.response_mutex);\n}\n\n\nvoid ctrl_shell_line_callback_set(void (*callback)(char *line))\n{\n\tdata.line_callback = callback;\n}\n\n\nint ctrl_shell_command_generic_arg0(const char *command)\n{\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", command);\n\n\treturn ctrl_shell_publish_blocking(j_command);\n}\n\n\nint ctrl_shell_command_generic_arg1(const char *command, const char *itemlabel, char **saveptr)\n{\n\tconst char *item;\n\n\titem = strtok_r(NULL, \" \", saveptr);\n\tif(!item){\n\t\tctrl_shell_printf(\"%s %s\\n\", command, itemlabel);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", command);\n\tcJSON_AddStringToObject(j_command, itemlabel, item);\n\n\treturn ctrl_shell_publish_blocking(j_command);\n}\n\n\nint ctrl_shell_command_generic_int_arg1(const char *command, const char *itemlabel, char **saveptr)\n{\n\tconst char *item;\n\n\titem = strtok_r(NULL, \" \", saveptr);\n\tif(!item){\n\t\tctrl_shell_printf(\"%s %s\\n\", command, itemlabel);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tint intval = atoi(item);\n\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", command);\n\tcJSON_AddNumberToObject(j_command, itemlabel, intval);\n\n\treturn ctrl_shell_publish_blocking(j_command);\n}\n\n\nint ctrl_shell_command_generic_arg2(const char *command, const char *itemlabel1, const char *itemlabel2, char **saveptr)\n{\n\tconst char *item1, *item2;\n\n\titem1 = strtok_r(NULL, \" \", saveptr);\n\titem2 = strtok_r(NULL, \" \", saveptr);\n\tif(!item1 || !item2){\n\t\tctrl_shell_printf(\"%s %s %s\\n\", command, itemlabel1, itemlabel2);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", command);\n\tcJSON_AddStringToObject(j_command, itemlabel1, item1);\n\tcJSON_AddStringToObject(j_command, itemlabel2, item2);\n\n\treturn ctrl_shell_publish_blocking(j_command);\n}\n\n\nstatic int ctrl_shell__subscribe_blocking(const char *topic, void (*module_on_subscribe)(void))\n{\n\tint rc = 0;\n\n\tfor(int i=0; i<data.subscription_list_count; i++){\n\t\tif(!strcmp(data.subscription_list[i], topic)){\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tchar **new_subscriptions = realloc(data.subscription_list, (size_t)(data.subscription_list_count+1)*sizeof(char *));\n\tif(new_subscriptions){\n\t\tdata.subscription_list = new_subscriptions;\n\t\tdata.subscription_list_count++;\n\t\tdata.subscription_list[data.subscription_list_count-1] = strdup(topic);\n\t}\n\n\tpthread_mutex_lock(&data.response_mutex);\n\n\tmosquitto_subscribe(data.mosq, NULL, topic, 1);\n\n\tresponse_wait();\n\tpthread_mutex_unlock(&data.response_mutex);\n\n\tif(data.subscribe_rc >= 128){\n\t\trc = 1;\n\t}else{\n\t\tif(module_on_subscribe){\n\t\t\tmodule_on_subscribe();\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\nbool ctrl_shell_callback_final(char *line)\n{\n\tif(!line || !strcasecmp(line, \"exit\")){\n\t\tdata.run = 0;\n\t}else if(!strcasecmp(line, \"disconnect\")){\n\t\tif(data.mosq){\n\t\t\tctrl_shell__disconnect();\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}else if(!strcasecmp(line, \"return\")){\n\t\tif(data.mod_cleanup){\n\t\t\tdata.mod_cleanup();\n\t\t}\n\t\tctrl_shell__post_connect_init();\n\t}else{\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n\nvoid ctrl_shell_print_help_final(const char *command, const char *modul)\n{\n\tif(!strcasecmp(command, \"disconnect\")){\n\t\tctrl_shell_print_help_command(\"disconnect\");\n\t\tctrl_shell_printf(\"\\nDisconnect from the broker\\n\");\n\t}else if(!strcasecmp(command, \"exit\")){\n\t\tctrl_shell_print_help_command(\"exit\");\n\t\tctrl_shell_printf(\"\\nQuit the program\\n\");\n\t}else if(!strcasecmp(command, \"help\")){\n\t\tctrl_shell_print_help_command(\"help <command>\");\n\t\tctrl_shell_printf(\"\\nFind help on a command using 'help <command>'\\n\");\n\t\tctrl_shell_printf(\"Press tab multiple times to find currently available commands.\\n\");\n\t}else if(modul && !strcasecmp(command, \"return\")){\n\t\tctrl_shell_print_help_command(\"return\");\n\t\tctrl_shell_printf(\"\\nLeave %s mode.\\n\", modul);\n\t}else{\n\t\tctrl_shell_printf(\"Unknown command '%s'\\n\", command);\n\t}\n}\n\n\nstatic void calc_generator_arg(int start)\n{\n\tchar *text_heap;\n\tchar *text_arg, *saveptr = NULL;\n\tint ga = 0;\n\n\tif(start == 0){\n\t\tgenerator_arg = -1;\n\t\treturn;\n\t}\n\n\ttext_heap = strdup(rl_line_buffer);\n\tif(!text_heap){\n\t\treturn;\n\t}\n\ttext_heap[start] = '\\0';\n\ttext_arg = strtok_r(text_heap, \" \", &saveptr);\n\twhile(text_arg){\n\t\tga++;\n\t\ttext_arg = strtok_r(NULL, \" \", &saveptr);\n\t}\n\tFREE(text_heap);\n\n\tgenerator_arg = ga-1;\n}\n\n\nchar *completion_generator(const char *text, int state)\n{\n\tstatic size_t len;\n\tstatic struct completion_tree_cmd *cmd, *cmd_prev;\n\tstatic struct completion_tree_arg *arg;\n\n\tif(!data.commands){\n\t\treturn NULL;\n\t}\n\tif(!state){\n\t\tlen = strlen(text);\n\t\tif(generator_arg < 0){\n\t\t\tcmd = data.commands->commands;\n\t\t}else if(current_cmd_match && generator_arg < current_cmd_match->arg_list_count){\n\t\t\targ = current_cmd_match->arg_lists[generator_arg]->args;\n\t\t}else{\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tif(generator_arg < 0){\n\t\twhile(cmd){\n\t\t\tchar *name = cmd->name;\n\t\t\tcmd_prev = cmd;\n\t\t\tcmd = cmd->next;\n\n\t\t\tif(strncasecmp(name, text, len) == 0){\n\t\t\t\tcurrent_cmd_match = cmd_prev;\n\t\t\t\treturn strdup(name);\n\t\t\t}\n\t\t}\n\t}else{\n\t\twhile(arg){\n\t\t\tchar *name = arg->name;\n\t\t\targ = arg->next;\n\n\t\t\tif(strncasecmp(name, text, len) == 0){\n\t\t\t\treturn strdup(name);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\nvoid ctrl_shell_completion_commands_set(struct completion_tree_root *new_commands)\n{\n\tdata.commands = new_commands;\n}\n\n\nchar **completion_matcher(const char *text, int start, int end)\n{\n\tchar **matches;\n\n\tUNUSED(end);\n\n\trl_attempted_completion_over = 1;\n\tcalc_generator_arg(start);\n\tmatches = rl_completion_matches(text, completion_generator);\n\treturn matches;\n}\n\n\nint my_get_address(int sock, char *buf, size_t len, uint16_t *remote_port)\n{\n\tstruct sockaddr_storage addr;\n\tsocklen_t addrlen;\n\n\tif(sock < 0){\n\t\tmemset(buf, 0, len);\n\t\t*remote_port = 0;\n\t\treturn 1;\n\t}\n\n\tmemset(&addr, 0, sizeof(struct sockaddr_storage));\n\taddrlen = sizeof(addr);\n\tif(!getpeername(sock, (struct sockaddr *)&addr, &addrlen)){\n\t\tif(addr.ss_family == AF_INET){\n\t\t\tif(remote_port){\n\t\t\t\t*remote_port = ntohs(((struct sockaddr_in *)&addr)->sin_port);\n\t\t\t}\n\t\t\tif(inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr.s_addr, buf, (socklen_t)len)){\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}else if(addr.ss_family == AF_INET6){\n\t\t\tif(remote_port){\n\t\t\t\t*remote_port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port);\n\t\t\t}\n\t\t\tif(inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, buf, (socklen_t)len)){\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\treturn 1;\n}\n\n\nstatic void on_connect_reconnect(struct mosquitto *mosq, void *userdata, int rc)\n{\n\tUNUSED(userdata);\n\tUNUSED(rc);\n\n\tfor(int i=0; i<data.subscription_list_count; i++){\n\t\tif(data.subscription_list[i]){\n\t\t\tmosquitto_subscribe(mosq, NULL, data.subscription_list[i], 1);\n\t\t}\n\t}\n}\n\n\nvoid ctrl_shell__on_connect(struct mosquitto *mosq, void *userdata, int rc)\n{\n\tUNUSED(userdata);\n\n\tmosquitto_connect_callback_set(mosq, on_connect_reconnect);\n\n\tdata.connect_rc = rc;\n\n\tdata.response_received = true;\n\tpthread_mutex_unlock(&data.response_mutex);\n\tpthread_cond_signal(&data.response_cond);\n}\n\n\nvoid ctrl_shell__on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg)\n{\n\tUNUSED(mosq);\n\tUNUSED(userdata);\n\n\tcJSON *j_tree = cJSON_Parse((char *)msg->payload);\n\tcJSON *j_endpoint_error = cJSON_GetObjectItem(j_tree, \"error\");\n\tif(j_endpoint_error && !strcmp(j_endpoint_error->valuestring, \"endpoint not available\")){\n\t\tctrl_shell_printf(\"%s$CONTROL endpoint for this module not available.%s\\n\", ANSI_ERROR, ANSI_RESET);\n\t}else{\n\t\tcJSON *j_responses = cJSON_GetObjectItem(j_tree, \"responses\");\n\t\tcJSON *j_command_obj = cJSON_GetArrayItem(j_responses, 0);\n\t\tcJSON *j_command = cJSON_GetObjectItem(j_command_obj, \"command\");\n\t\tcJSON *j_error = cJSON_GetObjectItem(j_command_obj, \"error\");\n\t\tcJSON *j_data = cJSON_GetObjectItem(j_command_obj, \"data\");\n\t\tif(j_error){\n\t\t\tctrl_shell_printf(\"%s%s%s\\n\", ANSI_ERROR, j_error->valuestring, ANSI_RESET);\n\t\t}else if(j_data && data.response_callback){\n\t\t\tdata.response_callback(j_command->valuestring, j_data, msg->payload);\n\t\t}else if(j_command){\n\t\t\tctrl_shell_printf(\"OK\\n\");\n\t\t}else{\n\t\t\tctrl_shell_printf(\"Invalid response from broker.\\n\");\n\t\t}\n\t}\n\n\tcJSON_Delete(j_tree);\n\n\tdata.response_received = true;\n\tpthread_mutex_unlock(&data.response_mutex);\n\tpthread_cond_signal(&data.response_cond);\n}\n\n\nvoid ctrl_shell__on_publish(struct mosquitto *mosq, void *userdata, int mid, int reason_code, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(userdata);\n\tUNUSED(mid);\n\tUNUSED(props);\n\n\tif(reason_code >= 128){\n\t\tctrl_shell_printf(\"Publish failed, check you have permission to access this module.\\n\");\n\t\tdata.publish_rc = reason_code;\n\t}\n\n\tdata.response_received = true;\n\tpthread_mutex_unlock(&data.response_mutex);\n\tpthread_cond_signal(&data.response_cond);\n}\n\n\nvoid ctrl_shell__on_subscribe(struct mosquitto *mosq, void *userdata, int mid, int qos_count, const int *granted_qos)\n{\n\tUNUSED(mosq);\n\tUNUSED(userdata);\n\tUNUSED(mid);\n\n\tif(qos_count == 1 && granted_qos[0] >= 128){\n\t\tctrl_shell_printf(\"Subscribe failed, check you have permission to access this module.\\n\");\n\t\tdata.subscribe_rc = granted_qos[0];\n\t}\n\n\tdata.response_received = true;\n\tpthread_mutex_unlock(&data.response_mutex);\n\tpthread_cond_signal(&data.response_cond);\n}\n\n\nvoid ctrl_shell__load_module(void (*mod_init)(struct ctrl_shell__module *mod))\n{\n\tstruct ctrl_shell__module mod;\n\tmemset(&mod, 0, sizeof(mod));\n\tmod_init(&mod);\n\n\tdata.request_topic = mod.request_topic;\n\tdata.response_callback = mod.response_callback;\n\tdata.mod_cleanup = mod.cleanup;\n\n\tctrl_shell_completion_commands_set(mod.completion_commands);\n\tctrl_shell_line_callback_set(mod.line_callback);\n\tctrl_shell__subscribe_blocking(mod.response_topic, mod.on_subscribe);\n}\n\n\nvoid set_no_colour(void)\n{\n\tANSI_URL = \"\";\n\tANSI_MODULE = \"\";\n\tANSI_INPUT = \"\";\n\tANSI_ERROR = \"\";\n\tANSI_LABEL = ANSI_LABEL_none;\n\tANSI_RESET = \"\";\n\tANSI_TOPIC = \"\";\n\tANSI_POSITIVE = \"\";\n\tANSI_NEGATIVE = \"\";\n}\n\n\nstatic void set_bg_light(void)\n{\n\tANSI_URL = ANSI_URL_light;\n\tANSI_MODULE = ANSI_MODULE_light;\n\tANSI_INPUT = ANSI_INPUT_light;\n\tANSI_ERROR = ANSI_ERROR_light;\n\tANSI_LABEL = ANSI_LABEL_light;\n\tANSI_TOPIC = ANSI_TOPIC_light;\n\tANSI_POSITIVE = ANSI_POSITIVE_light;\n\tANSI_NEGATIVE = ANSI_NEGATIVE_light;\n}\n\n\nstatic void set_bg_dark(void)\n{\n\tANSI_URL = ANSI_URL_dark;\n\tANSI_MODULE = ANSI_MODULE_dark;\n\tANSI_INPUT = ANSI_INPUT_dark;\n\tANSI_ERROR = ANSI_ERROR_dark;\n\tANSI_LABEL = ANSI_LABEL_dark;\n\tANSI_TOPIC = ANSI_TOPIC_dark;\n\tANSI_POSITIVE = ANSI_POSITIVE_dark;\n\tANSI_NEGATIVE = ANSI_NEGATIVE_dark;\n}\n\n\nstatic int get_bg(void)\n{\n\tint opt;\n\n\t/* Set non-blocking */\n\topt = fcntl(STDIN_FILENO, F_GETFL, 0);\n\tif(fcntl(STDIN_FILENO, F_SETFL, opt | O_NONBLOCK) < 0){\n\t\tfprintf(stderr, \"Error: Unable to set terminal flags required.\");\n\t\treturn 1;\n\t}\n\n\tset_bg_dark();\n\n\tterm_set_echo(false);\n\tterm_set_canon(false);\n\tctrl_shell_printf(\"\\e]10;?\\a\\e]11;?\\a\");\n\tfflush(stdout);\n\n\tchar buf[50] = {0};\n\tssize_t rl;\n\tusleep(100000);\n\tdo{\n\t\trl = read(STDIN_FILENO, buf, sizeof(buf));\n\t}while(rl == 0);\n\tif(fcntl(STDIN_FILENO, F_SETFL, opt) < 0){\n\t\tfprintf(stderr, \"Error: Unable to reset terminal flags.\");\n\t\treturn 1;\n\t}\n\tterm_set_echo(true);\n\tterm_set_canon(true);\n\n\tfor(int i=0; i<rl; i++){\n\t\tif(buf[i] == 7 || buf[i] == 27 || buf[i] == ']'){\n\t\t\tbuf[i] = ' ';\n\t\t}\n\t}\n\tint r_fg = 0, g_fg = 0, b_fg = 0;\n\tint r_bg = 0, g_bg = 0, b_bg = 0;\n\tint s = sscanf(buf+2, \"10;rgb:%x/%x/%x   11;rgb:%x/%x/%x\", &r_fg, &g_fg, &b_fg, &r_bg, &g_bg, &b_bg);\n\tif(s != 6){\n\t\treturn 1;\n\t}\n\tint fg, bg;\n\n\tfg = r_fg + b_fg + g_fg;\n\tbg = r_bg + b_bg + g_bg;\n\n\tif(fg > bg){\n\t\tset_bg_dark();\n\t}else{\n\t\tset_bg_light();\n\t}\n\treturn 0;\n}\n\n\nvoid ctrl_shell__cleanup(void)\n{\n\tFREE(data.hostname);\n\tfor(int i=0; i<data.subscription_list_count; i++){\n\t\tFREE(data.subscription_list[i]);\n\t}\n\tFREE(data.subscription_list);\n\n\tctrl_shell__pre_connect_cleanup();\n\tctrl_shell__post_connect_cleanup();\n\tif(data.mod_cleanup){\n\t\tdata.mod_cleanup();\n\t}\n\tFREE(data.username);\n\tFREE(data.password);\n\tFREE(data.clientid);\n\tFREE(data.tls_cafile);\n\tFREE(data.tls_capath);\n\tFREE(data.tls_certfile);\n\tFREE(data.tls_keyfile);\n}\n\n\nvoid ctrl_shell__main(struct mosq_config *config)\n{\n\tmemset(&data, 0, sizeof(data));\n\tdata.url_scheme = \"mqtt\";\n\tdata.run = 1;\n\tdata.port = PORT_UNDEFINED;\n\tdata.connect_rc = -1;\n\n\tif(config && config->no_colour){\n\t\tset_no_colour();\n\t}else{\n\t\tif(get_bg()){\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif(config){\n\t\tif(config->host){\n\t\t\tdata.hostname = strdup(config->host);\n\t\t}\n\t\tif(config->port != PORT_UNDEFINED){\n\t\t\tdata.port = config->port;\n\t\t}\n\t\tif(config->username){\n\t\t\tdata.username = strdup(config->username);\n\t\t}\n\t\tif(config->password){\n\t\t\tdata.password = strdup(config->password);\n\t\t}\n\t\tif(config->id){\n\t\t\tdata.clientid = strdup(config->id);\n\t\t}\n\t\tif(config->cafile){\n\t\t\tdata.tls_cafile = strdup(config->cafile);\n\t\t}\n\t\tif(config->capath){\n\t\t\tdata.tls_capath = strdup(config->capath);\n\t\t}\n\t\tif(config->certfile){\n\t\t\tdata.tls_certfile = strdup(config->certfile);\n\t\t}\n\t\tif(config->keyfile){\n\t\t\tdata.tls_keyfile = strdup(config->keyfile);\n\t\t}\n\n\t}\n\n\tpthread_mutex_init(&data.response_mutex, NULL);\n\tpthread_cond_init(&data.response_cond, NULL);\n\n\trl_readline_name = \"mosquitto_ctrl\";\n\trl_completion_entry_function = completion_generator;\n\trl_attempted_completion_function = completion_matcher;\n\trl_bind_key('\\t', rl_complete);\n\n\tsignal(SIGWINCH, signal_winch);\n\tsignal(SIGTERM, signal_term);\n\tsignal(SIGINT, signal_term);\n\n\tctrl_shell_printf(\"mosquitto_ctrl shell v\" VERSION \"\\n\");\n\n\tif(data.hostname){\n\t\tif(ctrl_shell__connect()){\n\t\t\tctrl_shell__cleanup();\n\t\t\treturn;\n\t\t}\n\t}else{\n\t\tctrl_shell__pre_connect_init();\n\t}\n\n\twhile(data.run){\n\t\tcurrent_cmd_match = NULL;\n\t\tchar *line = readline(prompt);\n\t\tif(data.line_callback){\n\t\t\tdata.line_callback(line);\n\t\t}\n\t}\n\tclear_history();\n\n\tctrl_shell_printf(\"\\n\");\n\n\tctrl_shell__cleanup();\n}\n\n\nstatic void print_label(unsigned int level, const char *label)\n{\n\tchar *str = calloc(1, level*2 + strlen(label) + 30);\n\n\tfor(unsigned int i=0; i<level; i++){\n\t\tsprintf(&str[strlen(str)], \"  \");\n\t}\n\tif(level < 2){\n\t\tsprintf(&str[strlen(str)], \"%s\", ANSI_LABEL[level]);\n\t}else{\n\t\tsprintf(&str[strlen(str)], \"%s\", ANSI_LABEL[1]);\n\t}\n\tsprintf(&str[strlen(str)], \"%s%s\", label, ANSI_RESET);\n\n\tctrl_shell_printf(\"%s\", str);\n\tfree(str);\n}\n\n\nvoid ctrl_shell_print_label(unsigned int level, const char *label)\n{\n\tprint_label(level, label);\n\tctrl_shell_printf(\"\\n\");\n}\n\n\nvoid ctrl_shell_print_label_value(unsigned int level, const char *label, int align, const char *fmt, ...)\n{\n\tva_list va;\n\tint levels = align-((int)strlen(label)+2*(int)level)+2;\n\tchar *str = calloc(1, (size_t)levels + 200);\n\n\tprint_label(level, label);\n\n\tfor(int i=0; i<align-((int)strlen(label)+2*(int)level)+2; i++){\n\t\tsprintf(&str[strlen(str)], \" \");\n\t}\n\n\tva_start(va, fmt);\n\tvsprintf(&str[strlen(str)], fmt, va);\n\tva_end(va);\n\n\tctrl_shell_printf(\"%s\", str);\n\tfree(str);\n}\n\n\nvoid ctrl_shell_print_value(unsigned int level, const char *fmt, ...)\n{\n\tva_list va;\n\tchar *str = calloc(1, level*2 + 200);\n\n\tfor(unsigned int i=0; i<level; i++){\n\t\tsprintf(&str[strlen(str)], \"  \");\n\t}\n\tva_start(va, fmt);\n\tvsprintf(&str[strlen(str)], fmt, va);\n\tva_end(va);\n\tctrl_shell_printf(\"%s\", str);\n\tfree(str);\n}\n\n\nvoid ctrl_shell_print_help_command(const char *cmd)\n{\n\tctrl_shell_printf(\"%s%s%s\\n\", ANSI_INPUT, cmd, ANSI_RESET);\n}\n\n\nvoid ctrl_shell_print_help_desc(const char *desc)\n{\n\tctrl_shell_printf(\"  %s\\n\", desc);\n}\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell.h",
    "content": "/*\nCopyright (c) 2023 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef CTRL_SHELL_H\n#define CTRL_SHELL_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <cjson/cJSON.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdio.h>\n\nextern const char *ANSI_URL;\nextern const char *ANSI_MODULE;\nextern const char *ANSI_INPUT;\nextern const char *ANSI_ERROR;\nextern const char **ANSI_LABEL;\nextern const char *ANSI_RESET;\nextern const char *ANSI_TOPIC;\nextern const char *ANSI_POSITIVE;\nextern const char *ANSI_NEGATIVE;\n\n/* This relatively complex structure is used to store the command tree. It\n * allows for commands to be shared between different parts of the tree, and\n * for commands to have multiple arguments. The completion_tree_arg_list\n * members exist to have a fixed memory location that never changes, so that\n * we can reallocate the list for where we have dynamically updated arguments,\n * for e.g. dynsec clients, where we want many commands to use the same list.\n */\n\nstruct completion_tree_arg {\n\tstruct completion_tree_arg *next;\n\tchar name[];\n};\n\nstruct completion_tree_arg_list {\n\tstruct completion_tree_arg *args;\n\tbool is_shared;\n};\n\nstruct completion_tree_cmd {\n\tstruct completion_tree_cmd *next;\n\tstruct completion_tree_arg_list **arg_lists;\n\tint arg_list_count;\n\tchar name[];\n};\n\nstruct completion_tree_root {\n\tstruct completion_tree_cmd *commands;\n};\n\nextern char prompt[200];\n\n/* Helper functions for sending commands to the broker. */\nint ctrl_shell_command_generic_arg0(const char *command);\nint ctrl_shell_command_generic_arg1(const char *command, const char *itemlabel, char **saveptr);\nint ctrl_shell_command_generic_arg2(const char *command, const char *itemlabel1, const char *itemlabel2, char **saveptr);\nint ctrl_shell_command_generic_int_arg1(const char *command, const char *itemlabel, char **saveptr);\n\n\nvoid ctrl_shell_completion_commands_set(struct completion_tree_root *new_commands);\nvoid ctrl_shell_line_callback_set(void (*callback)(char *line));\n\n/* Helper functions for building the command tree. */\nvoid completion_tree_arg_list_free(struct completion_tree_arg_list *arg_list);\nvoid completion_tree_arg_list_args_free(struct completion_tree_arg_list *arg_list);\nvoid completion_tree_cmd_free(struct completion_tree_cmd *cmd);\nvoid completion_tree_free(struct completion_tree_root *tree);\nstruct completion_tree_cmd *completion_tree_cmd_add(struct completion_tree_root *root, struct completion_tree_arg_list *help_arg_list, const char *name);\nstruct completion_tree_arg_list *completion_tree_cmd_new_arg_list(void);\nvoid completion_tree_cmd_append_arg_list(struct completion_tree_cmd *cmd, struct completion_tree_arg_list *new_list);\nstruct completion_tree_arg_list *completion_tree_cmd_add_arg_list(struct completion_tree_cmd *cmd);\nvoid completion_tree_arg_list_add_arg(struct completion_tree_arg_list *arg_list, const char *name);\n\nvoid ctrl_shell_pre_connect_init(void);\nvoid ctrl_shell_post_connect_init(void);\n\nint ctrl_shell_publish_blocking(cJSON *j_command);\nbool ctrl_shell_callback_final(char *line);\n\nchar *ctrl_shell_fgets(char *s, int size, FILE *stream);\nbool ctrl_shell_get_password(char *buf, size_t len);\nvoid ctrl_shell_rtrim(char *buf);\n\nvoid ctrl_shell_printf(const char *fmt, ...);\nvoid ctrl_shell_vprintf(const char *fmt, va_list va);\nvoid ctrl_shell_print_label(unsigned int level, const char *label);\nvoid ctrl_shell_print_label_value(unsigned int level, const char *label, int align, const char *fmt, ...);\nvoid ctrl_shell_print_value(unsigned int level, const char *fmt, ...);\nvoid ctrl_shell_print_help_command(const char *cmd);\nvoid ctrl_shell_print_help_desc(const char *desc);\nvoid ctrl_shell_print_help_final(const char *command, const char *modul);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell_broker.c",
    "content": "/*\nCopyright (c) 2023 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <config.h>\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"ctrl_shell_internal.h\"\n#include \"json_help.h\"\n\n#ifdef WITH_CTRL_SHELL\n\n#define UNUSED(A) (void)(A)\n\nstatic struct completion_tree_root *commands_broker = NULL;\n\n\nstatic void command_tree_create(void)\n{\n\tstruct completion_tree_cmd *cmd;\n\tstruct completion_tree_arg_list *help_arg_list;\n\n\tif(commands_broker){\n\t\treturn;\n\t}\n\n\tcommands_broker = calloc(1, sizeof(struct completion_tree_root));\n\n\tcmd = completion_tree_cmd_add(commands_broker, NULL, \"help\");\n\thelp_arg_list = completion_tree_cmd_add_arg_list(cmd);\n\n\tcompletion_tree_cmd_add(commands_broker, help_arg_list, \"listPlugins\");\n\tcompletion_tree_cmd_add(commands_broker, help_arg_list, \"listListeners\");\n\tcompletion_tree_cmd_add(commands_broker, help_arg_list, \"disconnect\");\n\tcompletion_tree_cmd_add(commands_broker, help_arg_list, \"return\");\n\tcompletion_tree_cmd_add(commands_broker, help_arg_list, \"exit\");\n}\n\n\nstatic void print_help(char **saveptr)\n{\n\tchar *command = strtok_r(NULL, \" \", saveptr);\n\tif(command){\n\t\tif(!strcasecmp(command, \"listPlugins\")){\n\t\t\tctrl_shell_print_help_command(\"listPlugins\");\n\t\t\tctrl_shell_printf(\"\\nLists currently loaded plugins.\\n\");\n\t\t}else if(!strcasecmp(command, \"listListeners\")){\n\t\t\tctrl_shell_print_help_command(\"listListeners\");\n\t\t\tctrl_shell_printf(\"\\nLists current listeners.\\n\");\n\t\t}else{\n\t\t\tctrl_shell_print_help_final(command, \"broker\");\n\t\t}\n\t}else{\n\t\tctrl_shell_printf(\"This is the mosquitto_ctrl interactive shell, for controlling aspects of a mosquitto broker.\\n\");\n\t\tctrl_shell_printf(\"You are in broker mode, for controlling some core broker functionality.\\n\");\n\t\tctrl_shell_printf(\"Use '%sreturn%s' to leave this mode.\\n\", ANSI_INPUT, ANSI_RESET);\n\n\t\tctrl_shell_printf(\"Find help on a command using '%shelp <command>%s'\\n\", ANSI_INPUT, ANSI_RESET);\n\t\tctrl_shell_printf(\"Press tab multiple times to find currently available commands.\\n\\n\");\n\t}\n}\n\n\nstatic void line_callback(char *line)\n{\n\tif(!line){\n\t\tctrl_shell_callback_final(NULL);\n\t\treturn;\n\t}\n\tctrl_shell_rtrim(line);\n\tif(strlen(line) > 0){\n\t\tadd_history(line);\n\t}else{\n\t\tfree(line);\n\t\treturn;\n\t}\n\n\tchar *saveptr = NULL;\n\tchar *command = strtok_r(line, \" \", &saveptr);\n\n\tif(!command){\n\t\tfree(line);\n\t\treturn;\n\t}\n\n\tif(!strcasecmp(command, \"listPlugins\")){\n\t\tctrl_shell_command_generic_arg0(\"listPlugins\");\n\t}else if(!strcasecmp(command, \"listListeners\")){\n\t\tctrl_shell_command_generic_arg0(\"listListeners\");\n\t}else if(!strcasecmp(command, \"help\")){\n\t\tprint_help(&saveptr);\n\t}else{\n\t\tif(!ctrl_shell_callback_final(line)){\n\t\t\tctrl_shell_printf(\"Unknown command '%s'\\n\", command);\n\t\t}\n\t}\n\n\tfree(line);\n}\n\n\nstatic void print_plugins(cJSON *j_data)\n{\n\tcJSON *j_plugins, *j_plugin;\n\n\tj_plugins = cJSON_GetObjectItem(j_data, \"plugins\");\n\n\tcJSON_ArrayForEach(j_plugin, j_plugins){\n\t\tconst char *name;\n\t\tif(json_get_string(j_plugin, \"name\", &name, false) != MOSQ_ERR_SUCCESS){\n\t\t\tctrl_shell_printf(\"Invalid response from broker.\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tctrl_shell_print_label(0, \"Plugin:\");\n\t\tctrl_shell_print_value(1, \"%s\\n\", name);\n\n\t\tcJSON *j_endpoints, *j_endpoint;\n\t\tj_endpoints = cJSON_GetObjectItem(j_plugin, \"control-endpoints\");\n\t\tif(j_endpoints){\n\t\t\tctrl_shell_print_label(0, \"Control endpoints:\");\n\t\t\tcJSON_ArrayForEach(j_endpoint, j_endpoints){\n\t\t\t\tctrl_shell_print_value(1, \"%s\\n\", j_endpoint->valuestring);\n\t\t\t}\n\t\t}\n\t\tctrl_shell_print_value(0, \"\\n\");\n\t}\n}\n\n\nstatic void print_listeners(cJSON *j_data)\n{\n\tcJSON *j_listeners, *j_listener;\n\n\tj_listeners = cJSON_GetObjectItem(j_data, \"listeners\");\n\n\tcJSON_ArrayForEach(j_listener, j_listeners){\n\t\tint port;\n\t\tconst char *protocol;\n\t\tbool tls;\n\n\t\tif(json_get_int(j_listener, \"port\", &port, false, -1) != MOSQ_ERR_SUCCESS\n\t\t\t\t|| json_get_string(j_listener, \"protocol\", &protocol, false) != MOSQ_ERR_SUCCESS\n\t\t\t\t|| json_get_bool(j_listener, \"tls\", &tls, false, false) != MOSQ_ERR_SUCCESS){\n\n\t\t\tctrl_shell_printf(\"Invalid response from broker.\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tctrl_shell_print_label(0, \"Listener:\");\n\t\tctrl_shell_print_label(1, \"Port:\");\n\t\tctrl_shell_print_value(2, \"%d\\n\", port);\n\t\tctrl_shell_print_label(1, \"Protocol:\");\n\t\tctrl_shell_print_value(2, \"%s\\n\", protocol);\n\t\tctrl_shell_print_label(1, \"TLS:\");\n\t\tctrl_shell_print_value(2, \"%s\\n\\n\", tls?\"true\":\"false\");\n\t}\n\n}\n\n\nstatic void handle_response(const char *command, cJSON *j_data, const char *payload)\n{\n\tif(!strcmp(command, \"listPlugins\")){\n\t\tprint_plugins(j_data);\n\t}else if(!strcmp(command, \"listListeners\")){\n\t\tprint_listeners(j_data);\n\t}else{\n\t\tctrl_shell_printf(\"%s %s\\n\", command, payload);\n\t}\n}\n\n\nstatic void on_subscribe(void)\n{\n}\n\n\nstatic void ctrl_shell__broker_cleanup(void)\n{\n\tcompletion_tree_free(commands_broker);\n\tcommands_broker = NULL;\n}\n\n\nvoid ctrl_shell__broker_init(struct ctrl_shell__module *mod)\n{\n\tcommand_tree_create();\n\n\tmod->completion_commands = commands_broker;\n\tmod->request_topic = \"$CONTROL/broker/v1\";\n\tmod->response_topic = \"$CONTROL/broker/v1/response\";\n\tmod->line_callback = line_callback;\n\tmod->response_callback = handle_response;\n\tmod->on_subscribe = on_subscribe;\n\tmod->cleanup = ctrl_shell__broker_cleanup;\n}\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell_client.c",
    "content": "/*\nCopyright (c) 2023 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <config.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"ctrl_shell_internal.h\"\n\n#ifdef WITH_CTRL_SHELL\n\n#define UNUSED(A) (void)(A)\n\n\nint ctrl_shell__connect(void)\n{\n\tif(data.port == PORT_UNDEFINED){\n\t\tdata.port = 1883;\n\t}\n\tif(data.mosq){\n\t\tmosquitto_destroy(data.mosq);\n\t}\n\tdata.mosq = mosquitto_new(data.clientid, true, NULL);\n\tif(!strcmp(data.url_scheme, \"mqtts\") || !strcmp(data.url_scheme, \"wss\")){\n\t\tmosquitto_int_option(data.mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);\n\t}\n\tif(data.transport == MOSQ_T_WEBSOCKETS){\n\t\tmosquitto_int_option(data.mosq, MOSQ_OPT_TRANSPORT, data.transport);\n\t}\n\tif(data.username && data.password){\n\t\tmosquitto_username_pw_set(data.mosq, data.username, data.password);\n\t}\n\tif(data.tls_cafile || data.tls_capath || data.tls_certfile || data.tls_keyfile){\n\t\tint rc = mosquitto_tls_set(data.mosq, data.tls_cafile, data.tls_capath, data.tls_certfile, data.tls_keyfile, NULL);\n\t\tif(rc){\n\t\t\tif(rc == MOSQ_ERR_INVAL){\n\t\t\t\tctrl_shell_printf(\"%sError setting TLS options: File not found.%s\\n\", ANSI_ERROR, ANSI_RESET);\n\t\t\t}else{\n\t\t\t\tctrl_shell_printf(\"%sError setting TLS options: %s.%s\\n\", ANSI_ERROR, mosquitto_strerror(rc), ANSI_RESET);\n\t\t\t}\n\t\t}\n\t}\n\tmosquitto_int_option(data.mosq, MOSQ_OPT_PROTOCOL_VERSION, 5);\n\n\tmosquitto_connect_callback_set(data.mosq, ctrl_shell__on_connect);\n\tmosquitto_subscribe_callback_set(data.mosq, ctrl_shell__on_subscribe);\n\tmosquitto_publish_v5_callback_set(data.mosq, ctrl_shell__on_publish);\n\tmosquitto_message_callback_set(data.mosq, ctrl_shell__on_message);\n\tctrl_shell__connect_blocking(data.hostname, data.port);\n\tif(data.connect_rc){\n\t\tctrl_shell_printf(\"%sUnable to connect: %s%s\\n\", ANSI_ERROR, mosquitto_reason_string(data.connect_rc), ANSI_RESET);\n\t\treturn 1;\n\t}\n\tctrl_shell__post_connect_init();\n\n\treturn 0;\n}\n\n\nvoid ctrl_shell__disconnect(void)\n{\n\tif(!data.mosq){\n\t\treturn;\n\t}\n\n\tmosquitto_disconnect(data.mosq);\n\tmosquitto_loop_stop(data.mosq, false);\n\tmosquitto_destroy(data.mosq);\n\tdata.mosq = NULL;\n\n\tfor(int i=0; i<data.subscription_list_count; i++){\n\t\tfree(data.subscription_list[i]);\n\t}\n\tfree(data.subscription_list);\n\tdata.subscription_list = NULL;\n\tdata.subscription_list_count = 0;\n\n\tctrl_shell__post_connect_cleanup();\n\tctrl_shell__pre_connect_init();\n}\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell_completion_tree.c",
    "content": "/*\nCopyright (c) 2023 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <config.h>\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"ctrl_shell_internal.h\"\n\n#ifdef WITH_CTRL_SHELL\n\n#define UNUSED(A) (void)(A)\n\n\nvoid completion_tree_arg_list_args_free(struct completion_tree_arg_list *arg_list)\n{\n\tstruct completion_tree_arg *arg, *next;\n\n\tif(!arg_list){\n\t\treturn;\n\t}\n\n\targ = arg_list->args;\n\twhile(arg){\n\t\tnext = arg->next;\n\t\tfree(arg);\n\t\targ = next;\n\t}\n\targ_list->args = NULL;\n}\n\n\nvoid completion_tree_arg_list_free(struct completion_tree_arg_list *arg_list)\n{\n\tif(!arg_list){\n\t\treturn;\n\t}\n\tif(arg_list->is_shared){\n\t\treturn;\n\t}\n\n\tcompletion_tree_arg_list_args_free(arg_list);\n\tfree(arg_list);\n}\n\n\nvoid completion_tree_cmd_free(struct completion_tree_cmd *cmd)\n{\n\tif(!cmd){\n\t\treturn;\n\t}\n\n\tfor(int i=0; i<cmd->arg_list_count; i++){\n\t\tcompletion_tree_arg_list_free(cmd->arg_lists[i]);\n\t}\n\tfree(cmd->arg_lists);\n\tfree(cmd);\n}\n\n\nvoid completion_tree_free(struct completion_tree_root *tree)\n{\n\tstruct completion_tree_cmd *cmd, *next;\n\n\tif(!tree){\n\t\treturn;\n\t}\n\n\tcmd = tree->commands;\n\twhile(cmd){\n\t\tnext = cmd->next;\n\t\tcompletion_tree_cmd_free(cmd);\n\t\tcmd = next;\n\t}\n\tfree(tree);\n}\n\nstruct completion_tree_cmd *completion_tree_cmd_add(struct completion_tree_root *root, struct completion_tree_arg_list *help_arg_list, const char *name)\n{\n\tstruct completion_tree_cmd *new_node;\n\n\tnew_node = calloc(1, sizeof(struct completion_tree_cmd) + strlen(name) + 1);\n\tif(!new_node){\n\t\treturn NULL;\n\t}\n\n\tstrcpy(new_node->name, name);\n\n\tnew_node->next = root->commands;\n\troot->commands = new_node;\n\n\tcompletion_tree_arg_list_add_arg(help_arg_list, name);\n\n\treturn new_node;\n}\n\nstruct completion_tree_arg_list *completion_tree_cmd_new_arg_list(void)\n{\n\treturn calloc(1, sizeof(struct completion_tree_arg_list));\n}\n\n\nvoid completion_tree_cmd_append_arg_list(struct completion_tree_cmd *cmd, struct completion_tree_arg_list *new_list)\n{\n\tstruct completion_tree_arg_list **arg_list;\n\n\targ_list = realloc(cmd->arg_lists, (size_t)(cmd->arg_list_count+1)*sizeof(struct completion_tree_arg_list *));\n\tif(!arg_list){\n\t\treturn;\n\t}\n\n\tcmd->arg_lists = arg_list;\n\n\tcmd->arg_lists[cmd->arg_list_count] = new_list;\n\tcmd->arg_list_count++;\n}\n\nstruct completion_tree_arg_list *completion_tree_cmd_add_arg_list(struct completion_tree_cmd *cmd)\n{\n\tif(!cmd){\n\t\treturn NULL;\n\t}\n\n\tstruct completion_tree_arg_list *new_list;\n\tnew_list = completion_tree_cmd_new_arg_list();\n\tif(!new_list){\n\t\treturn NULL;\n\t}\n\n\tcompletion_tree_cmd_append_arg_list(cmd, new_list);\n\n\treturn new_list;\n}\n\n\nvoid completion_tree_arg_list_add_arg(struct completion_tree_arg_list *arg_list, const char *name)\n{\n\tif(!arg_list || !name){\n\t\treturn;\n\t}\n\n\tstruct completion_tree_arg *new_node;\n\n\tnew_node = calloc(1, sizeof(struct completion_tree_arg) + strlen(name) + 1);\n\tif(!new_node){\n\t\treturn;\n\t}\n\n\tstrcpy(new_node->name, name);\n\n\tnew_node->next = arg_list->args;\n\targ_list->args = new_node;\n}\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell_dynsec.c",
    "content": "/*\nCopyright (c) 2023 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <config.h>\n\n#include <ctype.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"ctrl_shell_internal.h\"\n#include \"json_help.h\"\n\n#ifdef WITH_CTRL_SHELL\n\n#define UNUSED(A) (void)(A)\n\nstatic struct completion_tree_root *commands_dynsec = NULL;\nstatic struct completion_tree_arg_list *tree_clients = NULL;\nstatic struct completion_tree_arg_list *tree_groups = NULL;\nstatic struct completion_tree_arg_list *tree_roles = NULL;\nstatic bool do_print_list = false;\n\n\nstatic void command_tree_create(void)\n{\n\tstruct completion_tree_cmd *cmd;\n\tstruct completion_tree_arg_list *arg_list;\n\tstruct completion_tree_arg_list *help_arg_list;\n\n\tcompletion_tree_arg_list_args_free(tree_clients);\n\tcompletion_tree_arg_list_args_free(tree_groups);\n\tcompletion_tree_arg_list_args_free(tree_roles);\n\n\tif(commands_dynsec){\n\t\treturn;\n\t}\n\n\tcommands_dynsec = calloc(1, sizeof(struct completion_tree_root));\n\n\tif(!tree_clients){\n\t\ttree_clients = completion_tree_cmd_new_arg_list();\n\t\ttree_clients->is_shared = true;\n\t}\n\n\tif(!tree_groups){\n\t\ttree_groups = completion_tree_cmd_new_arg_list();\n\t\ttree_groups->is_shared = true;\n\t}\n\n\tif(!tree_roles){\n\t\ttree_roles = completion_tree_cmd_new_arg_list();\n\t\ttree_roles->is_shared = true;\n\t}\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, NULL, \"help\");\n\thelp_arg_list = completion_tree_cmd_add_arg_list(cmd);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"addClientRole\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_roles);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"addGroupClient\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_groups);\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"addGroupRole\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_groups);\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_roles);\n\n\t{\n\t\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"addRoleACL\");\n\t\tcompletion_tree_cmd_append_arg_list(cmd, tree_roles);\n\n\t\targ_list = completion_tree_cmd_add_arg_list(cmd);\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"publishClientReceive\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"publishClientSend\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"subscribeLiteral\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"subscribePattern\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"unsubscribeLiteral\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"unsubscribePattern\");\n\n\t\targ_list = completion_tree_cmd_add_arg_list(cmd);\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"allow\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"deny\");\n\t}\n\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"createClient\");\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"createGroup\");\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"createRole\");\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"deleteClient\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"deleteGroup\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_groups);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"deleteRole\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_roles);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"disableClient\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"enableClient\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"getAnonymousGroup\");\n\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"getDetails\");\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"getClient\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"getDefaultACLAccess\");\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"getGroup\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_groups);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"getRole\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_roles);\n\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"listClients\");\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"listGroups\");\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"listRoles\");\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"removeClientRole\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_roles);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"removeGroupClient\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_groups);\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"removeGroupRole\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_groups);\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_roles);\n\n\t{\n\t\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"removeRoleACL\");\n\t\tcompletion_tree_cmd_append_arg_list(cmd, tree_roles);\n\n\t\targ_list = completion_tree_cmd_add_arg_list(cmd);\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"publishClientReceive\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"publishClientSend\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"subscribeLiteral\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"subscribePattern\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"unsubscribeLiteral\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"unsubscribePattern\");\n\t}\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"setAnonymousGroup\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_groups);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"setClientId\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\n\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"setClientPassword\");\n\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\n\t{\n\t\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"modifyClient\");\n\t\tcompletion_tree_cmd_append_arg_list(cmd, tree_clients);\n\n\t\targ_list = completion_tree_cmd_add_arg_list(cmd);\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"textName\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"textDescription\");\n\t}\n\n\t{\n\t\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"modifyGroup\");\n\t\tcompletion_tree_cmd_append_arg_list(cmd, tree_groups);\n\n\t\targ_list = completion_tree_cmd_add_arg_list(cmd);\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"textName\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"textDescription\");\n\t}\n\n\t{\n\t\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"modifyRole\");\n\t\tcompletion_tree_cmd_append_arg_list(cmd, tree_roles);\n\n\t\targ_list = completion_tree_cmd_add_arg_list(cmd);\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"allowWildcardSubs\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"textName\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"textDescription\");\n\t}\n\n\t{\n\t\tcmd = completion_tree_cmd_add(commands_dynsec, help_arg_list, \"setDefaultACLAccess\");\n\n\t\targ_list = completion_tree_cmd_add_arg_list(cmd);\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"publishClientReceive\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"publishClientSend\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"subscribe\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"unsubscribe\");\n\n\t\targ_list = completion_tree_cmd_add_arg_list(cmd);\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"allow\");\n\t\tcompletion_tree_arg_list_add_arg(arg_list, \"deny\");\n\t}\n\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"disconnect\");\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"return\");\n\tcompletion_tree_cmd_add(commands_dynsec, help_arg_list, \"exit\");\n}\n\n\nstatic void print_help(char **saveptr)\n{\n\tchar *command = strtok_r(NULL, \" \", saveptr);\n\tif(command){\n\t\tif(!strcasecmp(command, \"addClientRole\")){\n\t\t\tctrl_shell_print_help_command(\"addClientRole <username> <rolename>\");\n\t\t\tctrl_shell_printf(\"\\nAdds a role directly to a client.\\n\");\n\t\t}else if(!strcasecmp(command, \"addGroupClient\")){\n\t\t\tctrl_shell_print_help_command(\"addGroupClient <groupname> <username>\");\n\t\t\tctrl_shell_printf(\"\\nAdds a client to a group.\\n\");\n\t\t}else if(!strcasecmp(command, \"addGroupRole\")){\n\t\t\tctrl_shell_print_help_command(\"addGroupRole <groupname> <rolename>\");\n\t\t\tctrl_shell_printf(\"\\nAdds a role to a group.\\n\");\n\t\t}else if(!strcasecmp(command, \"addRoleACL\")){\n\t\t\tctrl_shell_print_help_command(\"addRoleACL <rolename> publishClientReceive allow|deny [priority] <topic>\");\n\t\t\tctrl_shell_print_help_command(\"addRoleACL <rolename> publishClientSend allow|deny [priority] <topic>\");\n\t\t\tctrl_shell_print_help_command(\"addRoleACL <rolename> subscribeLiteral allow|deny [priority] <topic>\");\n\t\t\tctrl_shell_print_help_command(\"addRoleACL <rolename> subscribePattern allow|deny [priority] <topic>\");\n\t\t\tctrl_shell_print_help_command(\"addRoleACL <rolename> unsubscribeLiteral allow|deny [priority] <topic>\");\n\t\t\tctrl_shell_print_help_command(\"addRoleACL <rolename> unsubscribePattern allow|deny [priority] <topic>\");\n\t\t\tctrl_shell_printf(\"\\nAdds an ACL to a role, with an optional priority.\\n\");\n\t\t\tctrl_shell_printf(\"\\nACLs of a specific type within a role are processed in order from highest to lowest priority with the first matching ACL applying.\\n\");\n\t\t}else if(!strcasecmp(command, \"createClient\")){\n\t\t\tctrl_shell_print_help_command(\"createClient <username> [password [clientid]]\");\n\t\t\tctrl_shell_printf(\"\\nCreate a client with password and optional client id.\\n\");\n\t\t}else if(!strcasecmp(command, \"createGroup\")){\n\t\t\tctrl_shell_print_help_command(\"createGroup <groupname>\");\n\t\t\tctrl_shell_printf(\"\\nCreate a new group.\\n\");\n\t\t}else if(!strcasecmp(command, \"createRole\")){\n\t\t\tctrl_shell_print_help_command(\"createRole <rolename>\");\n\t\t\tctrl_shell_printf(\"\\nCreate a new role.\\n\");\n\t\t}else if(!strcasecmp(command, \"deleteClient\")){\n\t\t\tctrl_shell_print_help_command(\"deleteClient <username>\");\n\t\t\tctrl_shell_printf(\"\\nDelete a client\\n\");\n\t\t}else if(!strcasecmp(command, \"deleteGroup\")){\n\t\t\tctrl_shell_print_help_command(\"deleteGroup <groupname>\");\n\t\t\tctrl_shell_printf(\"\\nDelete a group\\n\");\n\t\t}else if(!strcasecmp(command, \"deleteRole\")){\n\t\t\tctrl_shell_print_help_command(\"deleteRole <rolename>\");\n\t\t\tctrl_shell_printf(\"\\nDelete a role\\n\");\n\t\t}else if(!strcasecmp(command, \"disableClient\")){\n\t\t\tctrl_shell_print_help_command(\"disableClient <username>\");\n\t\t\tctrl_shell_printf(\"\\nDisable a client. This client will not be able to log in, and will be kicked if it has an existing session.\\n\");\n\t\t}else if(!strcasecmp(command, \"enableClient\")){\n\t\t\tctrl_shell_print_help_command(\"enableClient <username>\");\n\t\t\tctrl_shell_printf(\"\\nEnable a client. Disabled clients are unable to log in.\\n\");\n\t\t}else if(!strcasecmp(command, \"getAnonymousGroup\")){\n\t\t\tctrl_shell_print_help_command(\"getAnonymousGroup\");\n\t\t\tctrl_shell_printf(\"\\nPrint the group configured as the anonymous group.\\n\");\n\t\t}else if(!strcasecmp(command, \"getDetails\")){\n\t\t\tctrl_shell_print_help_command(\"getDetails\");\n\t\t\tctrl_shell_printf(\"\\nPrint details including the client, group, and role count, and the current change index.\\n\");\n\t\t}else if(!strcasecmp(command, \"getClient\")){\n\t\t\tctrl_shell_print_help_command(\"getClient <username>\");\n\t\t\tctrl_shell_printf(\"\\nPrint details of a client and its groups and direct roles.\\n\");\n\t\t}else if(!strcasecmp(command, \"getDefaultACLAccess\")){\n\t\t\tctrl_shell_print_help_command(\"getDefaultACLAccess\");\n\t\t\tctrl_shell_printf(\"\\nPrint the default allow/deny values for the different classes of ACL.\\n\");\n\t\t}else if(!strcasecmp(command, \"getGroup\")){\n\t\t\tctrl_shell_print_help_command(\"getGroup <groupname>\");\n\t\t\tctrl_shell_printf(\"\\nPrint details of a group and its roles.\\n\");\n\t\t}else if(!strcasecmp(command, \"getRole\")){\n\t\t\tctrl_shell_print_help_command(\"getRole <rolename>\");\n\t\t\tctrl_shell_printf(\"\\nPrint details of a role and its ACLs.\\n\");\n\t\t}else if(!strcasecmp(command, \"listClients\")){\n\t\t\tctrl_shell_print_help_command(\"listClients [count [offset]]\");\n\t\t\tctrl_shell_printf(\"\\nPrint a list of clients configured in the dynsec plugin, with an optional total count and list offset.\\n\");\n\t\t}else if(!strcasecmp(command, \"listGroups\")){\n\t\t\tctrl_shell_print_help_command(\"listGroups [count [offset]]\");\n\t\t\tctrl_shell_printf(\"\\nPrint a list of groups configured in the dynsec plugin, with an optional total count and list offset.\\n\");\n\t\t}else if(!strcasecmp(command, \"listRoles\")){\n\t\t\tctrl_shell_print_help_command(\"listRoles [count [offset]]\");\n\t\t\tctrl_shell_printf(\"\\nPrint a list of roles configured in the dynsec plugin, with an optional total count and list offset.\\n\");\n\t\t}else if(!strcasecmp(command, \"removeClientRole\")){\n\t\t\tctrl_shell_print_help_command(\"removeClientRole <username> <rolename>\");\n\t\t\tctrl_shell_printf(\"\\nRemoves a role from a client, where the role was directly attached to the client.\\n\");\n\t\t}else if(!strcasecmp(command, \"removeGroupClient\")){\n\t\t\tctrl_shell_print_help_command(\"removeGroupClient <groupname> <username>\");\n\t\t\tctrl_shell_printf(\"\\nRemoves a client from a group.\\n\");\n\t\t}else if(!strcasecmp(command, \"removeGroupRole\")){\n\t\t\tctrl_shell_print_help_command(\"removeGroupRole <groupname> <rolename>\");\n\t\t\tctrl_shell_printf(\"\\nRemoves a role from a group.\\n\");\n\t\t}else if(!strcasecmp(command, \"removeRoleACL\")){\n\t\t\tctrl_shell_print_help_command(\"removeRoleACL <rolename> publishClientReceive <topic>\");\n\t\t\tctrl_shell_print_help_command(\"removeRoleACL <rolename> publishClientSend <topic>\");\n\t\t\tctrl_shell_print_help_command(\"removeRoleACL <rolename> subscribeLiteral <topic>\");\n\t\t\tctrl_shell_print_help_command(\"removeRoleACL <rolename> subscribePattern <topic>\");\n\t\t\tctrl_shell_print_help_command(\"removeRoleACL <rolename> unsubscribeLiteral <topic>\");\n\t\t\tctrl_shell_print_help_command(\"removeRoleACL <rolename> unsubscribePattern <topic>\");\n\t\t\tctrl_shell_printf(\"\\nRemoves an ACL from a role.\\n\");\n\t\t}else if(!strcasecmp(command, \"setAnonymousGroup\")){\n\t\t\tctrl_shell_print_help_command(\"setAnonymousGroup <groupname>\");\n\t\t\tctrl_shell_printf(\"\\nSets the anonymous group to a new group.\\n\");\n\t\t}else if(!strcasecmp(command, \"setClientId\")){\n\t\t\tctrl_shell_print_help_command(\"setClientId <username>\");\n\t\t\tctrl_shell_print_help_command(\"setClientId <username> <clientid>\");\n\t\t\tctrl_shell_printf(\"\\nSets or clears the clientid associated with a client. If a client has a clientid, all three of username, password, and clientid must match for a client to be able to authenticate.\\n\");\n\t\t}else if(!strcasecmp(command, \"setClientPassword\")){\n\t\t\tctrl_shell_print_help_command(\"setClientPassword <username> [password]\");\n\t\t\tctrl_shell_printf(\"\\nSets a new password for a client.\\n\");\n\t\t}else if(!strcasecmp(command, \"setDefaultACLAccess\")){\n\t\t\tctrl_shell_print_help_command(\"setDefaultACLAccess publishClientReceive allow|deny\");\n\t\t\tctrl_shell_print_help_command(\"setDefaultACLAccess publishClientSend allow|deny\");\n\t\t\tctrl_shell_print_help_command(\"setDefaultACLAccess subscribe allow|deny\");\n\t\t\tctrl_shell_print_help_command(\"setDefaultACLAccess unsubscribe allow|deny\");\n\t\t\tctrl_shell_printf(\"\\nSets the default ACL access to use for an ACL type. The default access will be applied if no other ACL rules match.\\n\");\n\t\t\tctrl_shell_printf(\"Setting a rule to 'allow' means that if no ACLs match, it will be accepted.\\n\");\n\t\t\tctrl_shell_printf(\"Setting a rule to 'deny' means that if no ACLs match, it will be denied.\\n\");\n\t\t}else if(!strcasecmp(command, \"modifyClient\")){\n\t\t\tctrl_shell_print_help_command(\"modifyClient <username> textName <textname>\");\n\t\t\tctrl_shell_print_help_command(\"modifyClient <username> textDescription <textdescription>\");\n\t\t\tctrl_shell_printf(\"\\nModify the text name or text description for a client.\\n\");\n\t\t\tctrl_shell_printf(\"These are free-text fields for your own use.\\n\");\n\t\t}else if(!strcasecmp(command, \"modifyGroup\")){\n\t\t\tctrl_shell_print_help_command(\"modifyGroup <groupname> textName <textname>\");\n\t\t\tctrl_shell_print_help_command(\"modifyGroup <groupname> textDescription <textdescription>\");\n\t\t\tctrl_shell_printf(\"\\nModify the text name or text description for a group.\\n\");\n\t\t\tctrl_shell_printf(\"These are free-text fields for your own use.\\n\");\n\t\t}else if(!strcasecmp(command, \"modifyRole\")){\n\t\t\tctrl_shell_print_help_command(\"modifyRole <rolename> allowWildcardSubs true|false\");\n\t\t\tctrl_shell_print_help_command(\"modifyRole <rolename> textName <textname>\");\n\t\t\tctrl_shell_print_help_command(\"modifyRole <rolename> textDescription <textdescription>\");\n\t\t\tctrl_shell_printf(\"\\nModify the text name or text description for a role.\\n\");\n\t\t\tctrl_shell_printf(\"These are free-text fields for your own use.\\n\");\n\t\t}else{\n\t\t\tctrl_shell_print_help_final(command, \"dynsec\");\n\t\t}\n\t}else{\n\t\tctrl_shell_printf(\"This is the mosquitto_ctrl interactive shell, for controlling aspects of a mosquitto broker.\\n\");\n\t\tctrl_shell_printf(\"You are in dynsec mode, for controlling the dynamic-security clients, groups, and roles used in authentication and authorisation.\\n\");\n\t\tctrl_shell_printf(\"Use '%sreturn%s' to leave dynsec mode.\\n\", ANSI_INPUT, ANSI_RESET);\n\n\t\tctrl_shell_printf(\"Find help on a command using '%shelp <command>%s'\\n\", ANSI_INPUT, ANSI_RESET);\n\t\tctrl_shell_printf(\"Press tab multiple times to find currently available commands.\\n\\n\");\n\t}\n}\n\n\nstatic int send_set_default_acl_access(char **saveptr)\n{\n\tconst char *acltype, *allow_s;\n\n\tacltype = strtok_r(NULL, \" \", saveptr);\n\tif(!acltype ||\n\t\t\t(\n\t\t\t\tstrcasecmp(acltype, \"publishClientReceive\")\n\t\t\t\t&& strcasecmp(acltype, \"publishClientSend\")\n\t\t\t\t&& strcasecmp(acltype, \"subscribe\")\n\t\t\t\t&& strcasecmp(acltype, \"unsubscribe\")\n\t\t\t)){\n\t\tctrl_shell_printf(\"setDefaultACLAccess acltype allow|deny\\n\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tallow_s = strtok_r(NULL, \" \", saveptr);\n\tif(!allow_s ||\n\t\t\t(\n\t\t\t\tstrcasecmp(allow_s, \"allow\")\n\t\t\t\t&& strcasecmp(allow_s, \"deny\")\n\t\t\t)){\n\t\tctrl_shell_printf(\"setDefaultACLAccess acltype allow|deny\\n\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", \"setDefaultACLAccess\");\n\tcJSON *j_acls = cJSON_AddArrayToObject(j_command, \"acls\");\n\tcJSON *j_acl = cJSON_CreateObject();\n\tcJSON_AddItemToArray(j_acls, j_acl);\n\tcJSON_AddStringToObject(j_acl, \"acltype\", acltype);\n\tcJSON_AddBoolToObject(j_acl, \"allow\", !strcmp(allow_s, \"allow\"));\n\n\treturn ctrl_shell_publish_blocking(j_command);\n}\n\n\nstatic int list_update(const char *command)\n{\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", command);\n\n\tdo_print_list = false;\n\n\treturn ctrl_shell_publish_blocking(j_command);\n}\n\n\nstatic int list_generic(const char *command, char **saveptr)\n{\n\tconst char *count, *offset;\n\n\tcount = strtok_r(NULL, \" \", saveptr);\n\toffset = strtok_r(NULL, \" \", saveptr);\n\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", command);\n\tif(count){\n\t\tcJSON_AddNumberToObject(j_command, \"count\", atoi(count));\n\t}\n\tif(offset){\n\t\tcJSON_AddNumberToObject(j_command, \"offset\", atoi(offset));\n\t}\n\n\treturn ctrl_shell_publish_blocking(j_command);\n}\n\n\nstatic int send_create_client(char **saveptr)\n{\n\tchar *username = strtok_r(NULL, \" \", saveptr);\n\tif(!username){\n\t\tctrl_shell_printf(\"createClient username [password [clientid]]\\n\");\n\t\tctrl_shell_printf(\"createClient username password [clientid]\\n\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tchar *password = strtok_r(NULL, \" \", saveptr);\n\tchar pwbuf1[200];\n\tchar pwbuf2[200];\n\tchar *clientid = NULL;\n\tif(password){\n\t\tclientid = strtok_r(NULL, \" \", saveptr);\n\t}else{\n\t\tif(!ctrl_shell_get_password(pwbuf1, sizeof(pwbuf1))\n\t\t\t\t|| !ctrl_shell_get_password(pwbuf2, sizeof(pwbuf2))){\n\n\t\t\tctrl_shell_printf(\"No password.\\n\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\tif(strcmp(pwbuf1, pwbuf2)){\n\t\t\tctrl_shell_printf(\"Passwords do not match.\\n\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\tpassword = pwbuf1;\n\t}\n\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", \"createClient\");\n\tcJSON_AddStringToObject(j_command, \"username\", username);\n\tcJSON_AddStringToObject(j_command, \"password\", password);\n\tif(clientid){\n\t\tcJSON_AddStringToObject(j_command, \"clientid\", clientid);\n\t}\n\tctrl_shell_publish_blocking(j_command);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int send_add_role_acl(char **saveptr)\n{\n\tchar *rolename = strtok_r(NULL, \" \", saveptr);\n\tchar *acltype = strtok_r(NULL, \" \", saveptr);\n\tchar *allow_s = strtok_r(NULL, \" \", saveptr);\n\tchar *s_priority = strtok_r(NULL, \" \", saveptr);\n\tchar *topic = strtok_r(NULL, \" \", saveptr);\n\tint priority = -1;\n\n\tif(s_priority){\n\t\tif(topic){\n\t\t\tpriority = atoi(s_priority);\n\t\t}else{\n\t\t\ttopic = s_priority;\n\t\t}\n\t}\n\n\tif(!rolename || !acltype || !allow_s || !topic){\n\t\tctrl_shell_printf(\"addRoleACL rolename acltype allow|deny [priority] topic\\n\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(strcasecmp(acltype, \"publishClientReceive\")\n\t\t\t&& strcasecmp(acltype, \"publishClientSend\")\n\t\t\t&& strcasecmp(acltype, \"subscribeLiteral\")\n\t\t\t&& strcasecmp(acltype, \"subscribePattern\")\n\t\t\t&& strcasecmp(acltype, \"unsubscribeLiteral\")\n\t\t\t&& strcasecmp(acltype, \"unsubscribePattern\")\n\t\t\t){\n\n\t\tctrl_shell_printf(\"addRoleACL rolename acltype allow|deny [priority] topic\\n\");\n\t\tctrl_shell_printf(\"Invalid acltype '%s'\\n\", acltype);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(strcasecmp(allow_s, \"allow\") && strcasecmp(allow_s, \"deny\")){\n\t\tctrl_shell_printf(\"addRoleACL rolename acltype allow|deny [priority] topic\\n\");\n\t\tctrl_shell_printf(\"Invalid allow/deny '%s'\\n\", allow_s);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", \"addRoleACL\");\n\tcJSON_AddStringToObject(j_command, \"rolename\", rolename);\n\tcJSON_AddStringToObject(j_command, \"acltype\", acltype);\n\tcJSON_AddNumberToObject(j_command, \"priority\", priority);\n\tcJSON_AddStringToObject(j_command, \"topic\", topic);\n\tcJSON_AddBoolToObject(j_command, \"allow\", !strcasecmp(allow_s, \"allow\"));\n\n\treturn ctrl_shell_publish_blocking(j_command);\n}\n\n\nstatic int send_remove_role_acl(char **saveptr)\n{\n\tchar *rolename = strtok_r(NULL, \" \", saveptr);\n\tchar *acltype = strtok_r(NULL, \" \", saveptr);\n\tchar *topic = strtok_r(NULL, \" \", saveptr);\n\n\tif(!rolename || !acltype || !topic){\n\t\tctrl_shell_printf(\"removeRoleACL rolename acltype topic\\n\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(strcasecmp(acltype, \"publishClientReceive\")\n\t\t\t&& strcasecmp(acltype, \"publishClientSend\")\n\t\t\t&& strcasecmp(acltype, \"subscribeLiteral\")\n\t\t\t&& strcasecmp(acltype, \"subscribePattern\")\n\t\t\t&& strcasecmp(acltype, \"unsubscribeLiteral\")\n\t\t\t&& strcasecmp(acltype, \"unsubscribePattern\")\n\t\t\t){\n\n\n\t\tctrl_shell_printf(\"removeRoleACL rolename acltype topic\\n\");\n\t\tctrl_shell_printf(\"Invalid acltype '%s'\\n\", acltype);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", \"removeRoleACL\");\n\tcJSON_AddStringToObject(j_command, \"rolename\", rolename);\n\tcJSON_AddStringToObject(j_command, \"acltype\", acltype);\n\tcJSON_AddStringToObject(j_command, \"topic\", topic);\n\n\treturn ctrl_shell_publish_blocking(j_command);\n\n}\n\n\nstatic int send_modify(const char *command, const char *objectname, char **saveptr)\n{\n\tchar *name = strtok_r(NULL, \" \", saveptr);\n\tchar *itemlabel = strtok_r(NULL, \" \", saveptr);\n\tchar *itemvalue = *saveptr;\n\tif(!name || !itemlabel || !itemvalue){\n\t\tctrl_shell_printf(\"%s %s <property> <value>\\n\", command, objectname);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(strcasecmp(itemlabel, \"textName\") && strcasecmp(itemlabel, \"textDescription\") && strcasecmp(itemlabel, \"allowWildcardSubs\")){\n\t\tctrl_shell_printf(\"%s %s <property> <value>\\n\", command, objectname);\n\t\tctrl_shell_printf(\"Unknown property '%s'\\n\", itemlabel);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(!strcasecmp(itemlabel, \"allowWildcardSubs\")){\n\t\tif(strcasecmp(itemvalue, \"true\") && strcasecmp(itemvalue, \"false\")){\n\t\t\tctrl_shell_printf(\"%s %s <property> <value>\\n\", command, objectname);\n\t\t\tctrl_shell_printf(\"Invalid value '%s'\\n\", itemvalue);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", command);\n\tcJSON_AddStringToObject(j_command, objectname, name);\n\tif(!strcasecmp(itemlabel, \"allowWildcardSubs\")){\n\t\tcJSON_AddBoolToObject(j_command, itemlabel, !strcasecmp(itemvalue, \"true\"));\n\t}else{\n\t\tcJSON_AddStringToObject(j_command, itemlabel, itemvalue);\n\t}\n\n\treturn ctrl_shell_publish_blocking(j_command);\n}\n\n\nstatic int send_set_client_password(char **saveptr)\n{\n\tchar *username, *password;\n\tchar pwbuf1[200], pwbuf2[200];\n\n\tusername = strtok_r(NULL, \" \", saveptr);\n\tif(!username){\n\t\tctrl_shell_printf(\"setClientPassword <username> [password]\\n\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tpassword = strtok_r(NULL, \" \", saveptr);\n\tif(!password){\n\t\tif(!ctrl_shell_get_password(pwbuf1, sizeof(pwbuf1))\n\t\t\t\t|| !ctrl_shell_get_password(pwbuf2, sizeof(pwbuf2))){\n\n\t\t\tctrl_shell_printf(\"No password.\\n\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\tif(strcmp(pwbuf1, pwbuf2)){\n\t\t\tctrl_shell_printf(\"Passwords do not match.\\n\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\tpassword = pwbuf1;\n\t}\n\n\tcJSON *j_command = cJSON_CreateObject();\n\tcJSON_AddStringToObject(j_command, \"command\", \"setClientPassword\");\n\tcJSON_AddStringToObject(j_command, \"username\", username);\n\tcJSON_AddStringToObject(j_command, \"password\", password);\n\n\treturn ctrl_shell_publish_blocking(j_command);\n}\n\n\nstatic void line_callback(char *line)\n{\n\tif(!line){\n\t\tctrl_shell_callback_final(NULL);\n\t\treturn;\n\t}\n\tctrl_shell_rtrim(line);\n\tif(strlen(line) > 0){\n\t\tadd_history(line);\n\t}else{\n\t\tfree(line);\n\t\treturn;\n\t}\n\n\tchar *saveptr = NULL;\n\tchar *command = strtok_r(line, \" \", &saveptr);\n\n\tif(!command){\n\t\tfree(line);\n\t\treturn;\n\t}\n\n\tif(!strcasecmp(command, \"addClientRole\")){\n\t\tctrl_shell_command_generic_arg2(\"addClientRole\", \"username\", \"rolename\", &saveptr);\n\t}else if(!strcasecmp(command, \"addGroupClient\")){\n\t\tctrl_shell_command_generic_arg2(\"addGroupClient\", \"groupname\", \"username\", &saveptr);\n\t}else if(!strcasecmp(command, \"addGroupRole\")){\n\t\tctrl_shell_command_generic_arg2(\"addGroupRole\", \"groupname\", \"rolename\", &saveptr);\n\t}else if(!strcasecmp(command, \"addRoleACL\")){\n\t\tsend_add_role_acl(&saveptr);\n\t}else if(!strcasecmp(command, \"createClient\")){\n\t\tif(send_create_client(&saveptr) == MOSQ_ERR_SUCCESS){\n\t\t\tlist_update(\"listClients\");\n\t\t}\n\t}else if(!strcasecmp(command, \"createGroup\")){\n\t\tif(ctrl_shell_command_generic_arg1(\"createGroup\", \"groupname\", &saveptr) == MOSQ_ERR_SUCCESS){\n\t\t\tlist_update(\"listGroups\");\n\t\t}\n\t}else if(!strcasecmp(command, \"createRole\")){\n\t\tif(ctrl_shell_command_generic_arg1(\"createRole\", \"rolename\", &saveptr) == MOSQ_ERR_SUCCESS){\n\t\t\tlist_update(\"listRoles\");\n\t\t}\n\t}else if(!strcasecmp(command, \"deleteClient\")){\n\t\tif(ctrl_shell_command_generic_arg1(\"deleteClient\", \"username\", &saveptr) == MOSQ_ERR_SUCCESS){\n\t\t\tlist_update(\"listClients\");\n\t\t}\n\t}else if(!strcasecmp(command, \"deleteGroup\")){\n\t\tif(ctrl_shell_command_generic_arg1(\"deleteGroup\", \"groupname\", &saveptr) == MOSQ_ERR_SUCCESS){\n\t\t\tlist_update(\"listGroups\");\n\t\t}\n\t}else if(!strcasecmp(command, \"deleteRole\")){\n\t\tif(ctrl_shell_command_generic_arg1(\"deleteRole\", \"rolename\", &saveptr) == MOSQ_ERR_SUCCESS){\n\t\t\tlist_update(\"listRoles\");\n\t\t}\n\t}else if(!strcasecmp(command, \"disableClient\")){\n\t\tctrl_shell_command_generic_arg1(\"disableClient\", \"username\", &saveptr);\n\t}else if(!strcasecmp(command, \"enableClient\")){\n\t\tctrl_shell_command_generic_arg1(\"enableClient\", \"username\", &saveptr);\n\t}else if(!strcasecmp(command, \"getAnonymousGroup\")){\n\t\tctrl_shell_command_generic_arg0(\"getAnonymousGroup\");\n\t}else if(!strcasecmp(command, \"getDetails\")){\n\t\tctrl_shell_command_generic_arg0(\"getDetails\");\n\t}else if(!strcasecmp(command, \"getClient\")){\n\t\tctrl_shell_command_generic_arg1(\"getClient\", \"username\", &saveptr);\n\t}else if(!strcasecmp(command, \"getDefaultACLAccess\")){\n\t\tctrl_shell_command_generic_arg0(\"getDefaultACLAccess\");\n\t}else if(!strcasecmp(command, \"getGroup\")){\n\t\tctrl_shell_command_generic_arg1(\"getGroup\", \"groupname\", &saveptr);\n\t}else if(!strcasecmp(command, \"getRole\")){\n\t\tctrl_shell_command_generic_arg1(\"getRole\", \"rolename\", &saveptr);\n\t}else if(!strcasecmp(command, \"listClients\")){\n\t\tdo_print_list = true;\n\t\tlist_generic(\"listClients\", &saveptr);\n\t}else if(!strcasecmp(command, \"listGroups\")){\n\t\tdo_print_list = true;\n\t\tlist_generic(\"listGroups\", &saveptr);\n\t}else if(!strcasecmp(command, \"listRoles\")){\n\t\tdo_print_list = true;\n\t\tlist_generic(\"listRoles\", &saveptr);\n\t}else if(!strcasecmp(command, \"removeClientRole\")){\n\t\tctrl_shell_command_generic_arg2(\"removeClientRole\", \"username\", \"rolename\", &saveptr);\n\t}else if(!strcasecmp(command, \"removeGroupClient\")){\n\t\tctrl_shell_command_generic_arg2(\"removeGroupClient\", \"groupname\", \"username\", &saveptr);\n\t}else if(!strcasecmp(command, \"removeGroupRole\")){\n\t\tctrl_shell_command_generic_arg2(\"removeGroupRole\", \"groupname\", \"rolename\", &saveptr);\n\t}else if(!strcasecmp(command, \"removeRoleACL\")){\n\t\tsend_remove_role_acl(&saveptr);\n\t}else if(!strcasecmp(command, \"setAnonymousGroup\")){\n\t\tctrl_shell_command_generic_arg1(\"setAnonymousGroup\", \"groupname\", &saveptr);\n\t}else if(!strcasecmp(command, \"setClientId\")){\n\t\tctrl_shell_command_generic_arg2(\"setClientId\", \"username\", \"clientid\", &saveptr);\n\t}else if(!strcasecmp(command, \"setClientPassword\")){\n\t\tsend_set_client_password(&saveptr);\n\t}else if(!strcasecmp(command, \"modifyClient\")){\n\t\tsend_modify(\"modifyClient\", \"username\", &saveptr);\n\t}else if(!strcasecmp(command, \"modifyGroup\")){\n\t\tsend_modify(\"modifyGroup\", \"groupname\", &saveptr);\n\t}else if(!strcasecmp(command, \"modifyRole\")){\n\t\tsend_modify(\"modifyRole\", \"rolename\", &saveptr);\n\t}else if(!strcasecmp(command, \"setDefaultACLAccess\")){\n\t\tsend_set_default_acl_access(&saveptr);\n\t}else if(!strcasecmp(command, \"help\")){\n\t\tprint_help(&saveptr);\n\t}else{\n\t\tif(!ctrl_shell_callback_final(line)){\n\t\t\tctrl_shell_printf(\"Unknown command '%s'\\n\", command);\n\t\t}\n\t}\n\n\tfree(line);\n}\n\n\nstatic void print_json_value(cJSON *value, const char *null_value)\n{\n\tif(value){\n\t\tif(cJSON_IsString(value)){\n\t\t\tif(value->valuestring){\n\t\t\t\tctrl_shell_print_value(0, \"%s\", value->valuestring);\n\t\t\t}\n\t\t}else{\n\t\t\tchar buffer[1024];\n\t\t\tcJSON_PrintPreallocated(value, buffer, sizeof(buffer), 0);\n\t\t\tctrl_shell_print_value(0, \"%s\", buffer);\n\t\t}\n\t}else if(null_value){\n\t\tctrl_shell_print_value(0, \"%s\", null_value);\n\t}\n}\n\n\nstatic void print_json_array(cJSON *j_list, const char *label, const char *element_name, const char *optional_element_name, const char *optional_element_null_value)\n{\n\tcJSON *j_elem;\n\n\tif(j_list && cJSON_IsArray(j_list) && cJSON_GetArraySize(j_list) > 0){\n\t\tctrl_shell_print_label(0, label);\n\t\tcJSON_ArrayForEach(j_elem, j_list){\n\t\t\tif(cJSON_IsObject(j_elem)){\n\t\t\t\tconst char *stmp;\n\t\t\t\tif(json_get_string(j_elem, element_name, &stmp, false) != MOSQ_ERR_SUCCESS){\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tctrl_shell_print_value(1, \"%s\", stmp);\n\t\t\t\tif(optional_element_name){\n\t\t\t\t\tctrl_shell_print_value(0, \" (%s: \", optional_element_name);\n\t\t\t\t\tprint_json_value(cJSON_GetObjectItem(j_elem, optional_element_name), optional_element_null_value);\n\t\t\t\t\tctrl_shell_print_value(0, \")\");\n\t\t\t\t}\n\t\t\t}else if(cJSON_IsString(j_elem) && j_elem->valuestring){\n\t\t\t\tctrl_shell_print_value(1, \"%s\", j_elem->valuestring);\n\t\t\t}\n\t\t\tctrl_shell_print_value(0, \"\\n\");\n\t\t}\n\t}\n}\n\n\nstatic void print_details(cJSON *j_data)\n{\n\tint64_t clientcount;\n\tint64_t groupcount;\n\tint64_t rolecount;\n\tint64_t changeindex;\n\tint align = (int)strlen(\"Change index: \");\n\tjson_get_int64(j_data, \"clientCount\", &clientcount, true, 0);\n\tjson_get_int64(j_data, \"groupCount\", &groupcount, true, 0);\n\tjson_get_int64(j_data, \"roleCount\", &rolecount, true, 0);\n\tjson_get_int64(j_data, \"changeIndex\", &changeindex, true, 0);\n\n\tctrl_shell_print_label_value(0, \"Client count:\", align, \"%ld\\n\", clientcount);\n\tctrl_shell_print_label_value(0, \"Group count:\", align, \"%ld\\n\", groupcount);\n\tctrl_shell_print_label_value(0, \"Role count:\", align, \"%ld\\n\", rolecount);\n\tctrl_shell_print_label_value(0, \"Change index:\", align, \"%ld\\n\", changeindex);\n}\n\n\nstatic void print_client(cJSON *j_data)\n{\n\tcJSON *j_client, *jtmp;\n\n\tj_client = cJSON_GetObjectItem(j_data, \"client\");\n\tif(j_client == NULL){\n\t\tctrl_shell_printf(\"Invalid response from broker.\\n\");\n\t\treturn;\n\t}\n\n\tconst char *username;\n\tif(json_get_string(j_client, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_printf(\"Invalid response from broker.\\n\");\n\t\treturn;\n\t}\n\tctrl_shell_print_label(0, \"Username:\");\n\tctrl_shell_print_value(1, \"%s\\n\", username);\n\n\tconst char *clientid;\n\tif(json_get_string(j_client, \"clientid\", &clientid, false) == MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_print_label(0, \"Clientid:\");\n\t\tctrl_shell_print_value(1, \"%s\\n\", clientid);\n\t}\n\n\tjtmp = cJSON_GetObjectItem(j_client, \"disabled\");\n\tif(jtmp && cJSON_IsBool(jtmp) && cJSON_IsTrue(jtmp)){\n\t\tctrl_shell_print_label(0, \"Disabled:\");\n\t\tctrl_shell_print_value(1, \"true\\n\");\n\t}\n\n\tconst char *textname;\n\tif(json_get_string(j_client, \"textname\", &textname, false) == MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_print_label(0, \"Text name:\");\n\t\tctrl_shell_print_value(1, \"%s\\n\", textname);\n\t}\n\n\tconst char *textdescription;\n\tif(json_get_string(j_client, \"textdescription\", &textdescription, false) == MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_print_label(0, \"Text description:\");\n\t\tctrl_shell_print_value(1, \"%s\\n\", textdescription);\n\t}\n\n\tprint_json_array(cJSON_GetObjectItem(j_client, \"roles\"), \"Roles:\", \"rolename\", \"priority\", \"-1\");\n\tprint_json_array(cJSON_GetObjectItem(j_client, \"groups\"), \"Groups:\", \"groupname\", \"priority\", \"-1\");\n}\n\n\nstatic void print_group(cJSON *j_data)\n{\n\tcJSON *j_group;\n\n\tj_group = cJSON_GetObjectItem(j_data, \"group\");\n\tif(j_group == NULL){\n\t\tctrl_shell_printf(\"Invalid response from broker.\\n\");\n\t\treturn;\n\t}\n\n\tconst char *groupname;\n\tif(json_get_string(j_group, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_printf(\"Invalid response from broker.\\n\");\n\t\treturn;\n\t}\n\tctrl_shell_print_label(0, \"Group name:\");\n\tctrl_shell_print_value(1, \"%s\\n\", groupname);\n\n\tconst char *textname;\n\tif(json_get_string(j_group, \"textname\", &textname, false) == MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_print_label(0, \"Text name:\");\n\t\tctrl_shell_print_value(1, \"%s\\n\", textname);\n\t}\n\n\tconst char *textdescription;\n\tif(json_get_string(j_group, \"textdescription\", &textdescription, false) == MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_print_label(0, \"Text description:\");\n\t\tctrl_shell_print_value(1, \"%s\\n\", textdescription);\n\t}\n\n\tprint_json_array(cJSON_GetObjectItem(j_group, \"roles\"), \"Roles:\", \"rolename\", \"priority\", \"-1\");\n\tprint_json_array(cJSON_GetObjectItem(j_group, \"clients\"), \"Clients:\", \"username\", NULL, NULL);\n}\n\n\nstatic void print_role(cJSON *j_data)\n{\n\tcJSON *j_role;\n\n\tj_role = cJSON_GetObjectItem(j_data, \"role\");\n\tif(j_role == NULL){\n\t\tctrl_shell_printf(\"Invalid response from broker.\\n\");\n\t\treturn;\n\t}\n\n\tconst char *rolename;\n\tif(json_get_string(j_role, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_printf(\"Invalid response from broker.\\n\");\n\t\treturn;\n\t}\n\tctrl_shell_print_label(0, \"Role name:\");\n\tctrl_shell_print_value(1, \"%s\\n\", rolename);\n\n\tconst char *textname;\n\tif(json_get_string(j_role, \"textname\", &textname, false) == MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_print_label(0, \"Text name:\");\n\t\tctrl_shell_print_value(1, \"%s\\n\", textname);\n\t}\n\n\tconst char *textdescription;\n\tif(json_get_string(j_role, \"textdescription\", &textdescription, false) == MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_print_label(0, \"Text description:\");\n\t\tctrl_shell_print_value(1, \"%s\\n\", textdescription);\n\t}\n\n\tbool allowwildcardsubs;\n\tif(json_get_bool(j_role, \"allowwildcardsubs\", &allowwildcardsubs, false, false) == MOSQ_ERR_SUCCESS){\n\t\tctrl_shell_print_label(0, \"Allow wildcard subscriptions:\");\n\t\tctrl_shell_print_value(1, \"%s\\n\", allowwildcardsubs?\"true\":\"false\");\n\t}\n\n\tcJSON *j_acls = cJSON_GetObjectItem(j_role, \"acls\");\n\tif(j_acls && cJSON_GetArraySize(j_acls) > 0){\n\t\tctrl_shell_print_label(0, \"ACLs:\");\n\n\t\tcJSON *j_acl;\n\t\tcJSON_ArrayForEach(j_acl, j_acls){\n\t\t\tconst char *acltype;\n\t\t\tconst char *topic;\n\t\t\tint priority;\n\t\t\tbool allow;\n\n\t\t\tif(json_get_string(j_acl, \"acltype\", &acltype, false) == MOSQ_ERR_SUCCESS\n\t\t\t\t\t&& json_get_string(j_acl, \"topic\", &topic, false) == MOSQ_ERR_SUCCESS\n\t\t\t\t\t&& json_get_int(j_acl, \"priority\", &priority, true, -1) == MOSQ_ERR_SUCCESS\n\t\t\t\t\t&& json_get_bool(j_acl, \"allow\", &allow, false, false) == MOSQ_ERR_SUCCESS\n\t\t\t\t\t){\n\n\t\t\t\tconst char *ANSI_ALLOW = allow?ANSI_POSITIVE:ANSI_NEGATIVE;\n\t\t\t\tctrl_shell_print_value(1, \"%-*s %s%s%s %s%s%s (priority %d)\\n\", (int)strlen(\"publishClientReceive\"), acltype, ANSI_ALLOW, allow?\"allow\":\"deny\", ANSI_RESET, ANSI_TOPIC, topic, ANSI_RESET, priority);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void print_default_acls(cJSON *j_data)\n{\n\tcJSON *j_acls = cJSON_GetObjectItem(j_data, \"acls\");\n\n\tif(j_acls && cJSON_GetArraySize(j_acls) > 0){\n\t\tcJSON *j_acl;\n\t\tcJSON_ArrayForEach(j_acl, j_acls){\n\t\t\tconst char *acltype;\n\t\t\tbool allow;\n\n\t\t\tif(json_get_string(j_acl, \"acltype\", &acltype, false) == MOSQ_ERR_SUCCESS\n\t\t\t\t\t&& json_get_bool(j_acl, \"allow\", &allow, false, false) == MOSQ_ERR_SUCCESS\n\t\t\t\t\t){\n\n\t\t\t\tctrl_shell_print_value(0, \"%-*s %s\\n\", (int)strlen(\"publishClientReceive\"), acltype, allow?\"allow\":\"deny\");\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void response_callback(const char *command, cJSON *j_data, const char *payload)\n{\n\tUNUSED(payload);\n\n\tif(!strcmp(command, \"listClients\")){\n\t\tcompletion_tree_arg_list_args_free(tree_clients);\n\n\t\tcJSON *clients, *client;\n\n\t\tclients = cJSON_GetObjectItem(j_data, \"clients\");\n\t\tcJSON_ArrayForEach(client, clients){\n\t\t\tif(do_print_list){\n\t\t\t\tctrl_shell_print_value(0, \"%s\\n\", client->valuestring);\n\t\t\t}\n\t\t\tcompletion_tree_arg_list_add_arg(tree_clients, client->valuestring);\n\t\t}\n\t\tdo_print_list = false;\n\t}else if(!strcmp(command, \"listGroups\")){\n\t\tcompletion_tree_arg_list_args_free(tree_groups);\n\n\t\tcJSON *groups, *group;\n\n\t\tgroups = cJSON_GetObjectItem(j_data, \"groups\");\n\t\tcJSON_ArrayForEach(group, groups){\n\t\t\tif(do_print_list){\n\t\t\t\tctrl_shell_print_value(0, \"%s\\n\", group->valuestring);\n\t\t\t}\n\t\t\tcompletion_tree_arg_list_add_arg(tree_groups, group->valuestring);\n\t\t}\n\t\tdo_print_list = false;\n\t}else if(!strcmp(command, \"listRoles\")){\n\t\tcompletion_tree_arg_list_args_free(tree_roles);\n\n\t\tcJSON *roles, *role;\n\n\t\troles = cJSON_GetObjectItem(j_data, \"roles\");\n\t\tcJSON_ArrayForEach(role, roles){\n\t\t\tif(do_print_list){\n\t\t\t\tctrl_shell_print_value(0, \"%s\\n\", role->valuestring);\n\t\t\t}\n\t\t\tcompletion_tree_arg_list_add_arg(tree_roles, role->valuestring);\n\t\t}\n\t\tdo_print_list = false;\n\t}else if(!strcasecmp(command, \"getAnonymousGroup\")){\n\t\tcJSON *group, *groupname;\n\n\t\tgroup = cJSON_GetObjectItem(j_data, \"group\");\n\t\tgroupname = cJSON_GetObjectItem(group, \"groupname\");\n\t\tctrl_shell_print_value(0, \"%s\\n\", groupname->valuestring);\n\t}else if(!strcasecmp(command, \"getDetails\")){\n\t\tprint_details(j_data);\n\t}else if(!strcasecmp(command, \"getClient\")){\n\t\tprint_client(j_data);\n\t}else if(!strcasecmp(command, \"getGroup\")){\n\t\tprint_group(j_data);\n\t}else if(!strcasecmp(command, \"getRole\")){\n\t\tprint_role(j_data);\n\t}else if(!strcasecmp(command, \"getDefaultACLAccess\")){\n\t\tprint_default_acls(j_data);\n\t}else{\n\t\t//ctrl_shell_printf(\"%s %s\\n\", command, payload);\n\t}\n}\n\n\nstatic void on_subscribe(void)\n{\n\tint rc;\n\n\trc = list_update(\"listClients\");\n\tif(rc){\n\t\tctrl_shell_printf(\"Check the dynsec module is configured on the broker.\\n\");\n\t\treturn;\n\t}\n\tlist_update(\"listGroups\");\n\tlist_update(\"listRoles\");\n}\n\n\nstatic void ctrl_shell__dynsec_cleanup(void)\n{\n\tcompletion_tree_free(commands_dynsec);\n\tcommands_dynsec = NULL;\n\n\tcompletion_tree_arg_list_args_free(tree_clients);\n\tcompletion_tree_arg_list_args_free(tree_groups);\n\tcompletion_tree_arg_list_args_free(tree_roles);\n\n\tfree(tree_clients);\n\tfree(tree_groups);\n\tfree(tree_roles);\n\n\ttree_clients = NULL;\n\ttree_groups = NULL;\n\ttree_roles = NULL;\n}\n\n\nvoid ctrl_shell__dynsec_init(struct ctrl_shell__module *mod)\n{\n\tcommand_tree_create();\n\n\tmod->completion_commands = commands_dynsec;\n\tmod->request_topic = \"$CONTROL/dynamic-security/v1\";\n\tmod->response_topic = \"$CONTROL/dynamic-security/v1/response\";\n\tmod->line_callback = line_callback;\n\tmod->response_callback = response_callback;\n\tmod->on_subscribe = on_subscribe;\n\tmod->cleanup = ctrl_shell__dynsec_cleanup;\n}\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell_internal.h",
    "content": "/*\nCopyright (c) 2023 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef CTRL_SHELL_INTERNAL_H\n#define CTRL_SHELL_INTERNAL_H\n\n#ifdef WITH_CTRL_SHELL\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n\n#ifdef WITH_EDITLINE\n#include <editline/readline.h>\n#elif defined(WITH_READLINE)\n#include <readline/history.h>\n#include <readline/readline.h>\n#endif\n#include <pthread.h>\n\n#include \"ctrl_shell.h\"\n#include \"mosquitto_ctrl.h\"\n\nstruct ctrl_shell {\n\tchar **subscription_list;\n\tint subscription_list_count;\n\tint run;\n\tstruct mosquitto *mosq;\n\trl_vcpfunc_t *line_callback;\n\tpthread_cond_t response_cond;\n\tpthread_mutex_t response_mutex;\n\tbool response_received;\n\tconst char *request_topic;\n\tstruct completion_tree_root *commands;\n\tvoid (*response_callback)(const char *command, cJSON *data, const char *payload);\n\tvoid (*mod_cleanup)(void);\n\tconst char *url_scheme;\n\tchar *username;\n\tchar *password;\n\tchar *clientid;\n\tchar *tls_cafile;\n\tchar *tls_capath;\n\tchar *tls_certfile;\n\tchar *tls_keyfile;\n\tchar *hostname;\n\tint port;\n\tint connect_rc;\n\tint subscribe_rc;\n\tint publish_rc;\n\tint transport;\n};\n\nstruct ctrl_shell__module {\n\tstruct completion_tree_root *completion_commands;\n\tconst char *request_topic;\n\tconst char *response_topic;\n\tvoid (*response_callback)(const char *command, cJSON *data, const char *payload);\n\tvoid (*line_callback)(char *line);\n\tvoid (*on_subscribe)(void);\n\tvoid (*cleanup)(void);\n};\n\nextern struct ctrl_shell data;\nextern char prompt[200];\n\nvoid term__set_echo(bool echo);\n\nint do_connect(void);\n\nvoid ctrl_shell__pre_connect_init(void);\nvoid ctrl_shell__pre_connect_cleanup(void);\nvoid ctrl_shell__post_connect_init(void);\nvoid ctrl_shell__post_connect_cleanup(void);\nvoid ctrl_shell__load_module(void (*mod_init)(struct ctrl_shell__module *mod));\n\nvoid ctrl_shell__connect_blocking(const char *hostname, int port);\nvoid ctrl_shell__on_connect(struct mosquitto *mosq, void *userdata, int rc);\nvoid ctrl_shell__on_subscribe(struct mosquitto *mosq, void *userdata, int mid, int qos_count, const int *granted_qos);\nvoid ctrl_shell__on_publish(struct mosquitto *mosq, void *userdata, int mid, int reason_code, const mosquitto_property *props);\nvoid ctrl_shell__on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg);\n\nvoid ctrl_shell__broker_init(struct ctrl_shell__module *mod);\nvoid ctrl_shell__dynsec_init(struct ctrl_shell__module *mod);\n\nvoid ctrl_shell__main(struct mosq_config *config);\n\nint ctrl_shell__connect(void);\nvoid ctrl_shell__disconnect(void);\n\nvoid ctrl_shell__output(const char *buf);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell_io.c",
    "content": "/*\nCopyright (c) 2025 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include \"config.h\"\n\n#include <stdio.h>\n#include \"ctrl_shell.h\"\n\n\nvoid ctrl_shell__output(const char *buf)\n{\n\tprintf(\"%s\", buf);\n}\n\n\nchar *ctrl_shell_fgets(char *s, int size, FILE *stream)\n{\n\treturn fgets(s, size, stream);\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell_post_connect.c",
    "content": "/*\nCopyright (c) 2023 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <config.h>\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"ctrl_shell_internal.h\"\n\n#ifdef WITH_CTRL_SHELL\n\n#define UNUSED(A) (void)(A)\n\nstatic struct completion_tree_root *commands_post_connect = NULL;\n\n\nstatic void command_tree_create(void)\n{\n\tstruct completion_tree_cmd *cmd;\n\tstruct completion_tree_arg_list *help_arg_list;\n\n\tif(commands_post_connect){\n\t\treturn;\n\t}\n\n\tcommands_post_connect = calloc(1, sizeof(struct completion_tree_root));\n\n\tcmd = completion_tree_cmd_add(commands_post_connect, NULL, \"help\");\n\thelp_arg_list = completion_tree_cmd_add_arg_list(cmd);\n\n\tcompletion_tree_cmd_add(commands_post_connect, help_arg_list, \"broker\");\n\tcompletion_tree_cmd_add(commands_post_connect, help_arg_list, \"disconnect\");\n\tcompletion_tree_cmd_add(commands_post_connect, help_arg_list, \"dynsec\");\n\tcompletion_tree_cmd_add(commands_post_connect, help_arg_list, \"exit\");\n}\n\n\nstatic void print_help(char **saveptr)\n{\n\tchar *command = strtok_r(NULL, \" \", saveptr);\n\tif(command){\n\t\tif(!strcasecmp(command, \"dynsec\")){\n\t\t\tctrl_shell_print_help_command(\"dynsec\");\n\t\t\tctrl_shell_printf(\"\\nStart the dynamic-security control mode.\\n\");\n\t\t}else if(!strcasecmp(command, \"broker\")){\n\t\t\tctrl_shell_print_help_command(\"broker\");\n\t\t\tctrl_shell_printf(\"\\nStart the broker control mode.\\n\");\n\t\t}else{\n\t\t\tctrl_shell_print_help_final(command, NULL);\n\t\t}\n\t}else{\n\t\tctrl_shell_printf(\"This is the mosquitto_ctrl interactive shell, for controlling aspects of a mosquitto broker.\\n\");\n\n\t\tctrl_shell_printf(\"Find help on a command using '%shelp <command>%s'\\n\", ANSI_INPUT, ANSI_RESET);\n\t\tctrl_shell_printf(\"Press tab multiple times to find currently available commands.\\n\\n\");\n\n\t\tctrl_shell_printf(\"Example workflow:\\n\\n\");\n\t\tctrl_shell_printf(\"> auth\\n\");\n\t\tctrl_shell_printf(\"username: admin\\n\");\n\t\tctrl_shell_printf(\"password:\\n\");\n\t\tctrl_shell_printf(\"> connect mqtt://localhost\\n\");\n\t\tctrl_shell_printf(\"mqtt://localhost:1883> dynsec\\n\");\n\t\tctrl_shell_printf(\"mqtt://localhost:1883|dynsec> createGroup newgroup\\n\");\n\t\tctrl_shell_printf(\"OK\\n\\n\");\n\t}\n}\n\nstruct module_data {\n\tconst char *name;\n\tvoid (*mod_init)(struct ctrl_shell__module *mod);\n};\n\nconst struct module_data mod_data[] = {\n\t{ \"broker\", ctrl_shell__broker_init },\n\t{ \"dynsec\", ctrl_shell__dynsec_init },\n};\n\n\nstatic void line_callback(char *line)\n{\n\tif(!line){\n\t\tctrl_shell_callback_final(NULL);\n\t\treturn;\n\t}\n\n\tctrl_shell_rtrim(line);\n\tif(strlen(line) > 0){\n\t\tadd_history(line);\n\t}else{\n\t\tfree(line);\n\t\treturn;\n\t}\n\n\tchar *saveptr = NULL;\n\tbool found = false;\n\tchar *command = strtok_r(line, \" \", &saveptr);\n\n\tif(!command){\n\t\tfree(line);\n\t\treturn;\n\t}\n\n\tfor(size_t i=0; i<sizeof(mod_data)/sizeof(struct module_data); i++){\n\t\tif(!strcasecmp(command, mod_data[i].name)){\n\t\t\tsnprintf(prompt, sizeof(prompt), \"%s%s://%s:%d%s|%s%s%s>%s \",\n\t\t\t\t\tANSI_URL, data.url_scheme, data.hostname, data.port, ANSI_RESET,\n\t\t\t\t\tANSI_MODULE, mod_data[i].name, ANSI_INPUT, ANSI_RESET);\n\t\t\tctrl_shell__load_module(mod_data[i].mod_init);\n\t\t\tfound = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif(!strcasecmp(command, \"help\")){\n\t\tfound = true;\n\t\tprint_help(&saveptr);\n\t}\n\tif(found == false){\n\t\tif(!ctrl_shell_callback_final(line)){\n\t\t\tctrl_shell_printf(\"Unknown command '%s'\\n\", command);\n\t\t}\n\t}\n\n\tfree(line);\n}\n\n\nvoid ctrl_shell__post_connect_cleanup(void)\n{\n\tcompletion_tree_free(commands_post_connect);\n\tcommands_post_connect = NULL;\n}\n\n\nvoid ctrl_shell__post_connect_init(void)\n{\n\tcommand_tree_create();\n\n\tctrl_shell_completion_commands_set(commands_post_connect);\n\tctrl_shell_line_callback_set(line_callback);\n\n\tsnprintf(prompt, sizeof(prompt), \"%s%s://%s:%d%s>%s \", ANSI_URL, data.url_scheme, data.hostname, data.port, ANSI_INPUT, ANSI_RESET);\n}\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell_pre_connect.c",
    "content": "/*\nCopyright (c) 2023 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <config.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"ctrl_shell_internal.h\"\n\n#ifdef WITH_CTRL_SHELL\n\n#define UNUSED(A) (void)(A)\n\nstatic struct completion_tree_root *commands_pre_connect = NULL;\n\n\nstatic void command_tree_create(void)\n{\n\tstruct completion_tree_cmd *cmd;\n\tstruct completion_tree_arg_list *help_arg_list;\n\n\tif(commands_pre_connect){\n\t\treturn;\n\t}\n\n\tcommands_pre_connect = calloc(1, sizeof(struct completion_tree_root));\n\n\tcmd = completion_tree_cmd_add(commands_pre_connect, NULL, \"help\");\n\thelp_arg_list = completion_tree_cmd_add_arg_list(cmd);\n\n\tcompletion_tree_cmd_add(commands_pre_connect, help_arg_list, \"auth\");\n\tcompletion_tree_cmd_add(commands_pre_connect, help_arg_list, \"connect\");\n\tcompletion_tree_cmd_add(commands_pre_connect, help_arg_list, \"exit\");\n}\n\n\nvoid print_help(char **saveptr)\n{\n\tchar *command = strtok_r(NULL, \" \", saveptr);\n\tif(command){\n\t\tif(!strcasecmp(command, \"auth\")){\n\t\t\tctrl_shell_print_help_command(\"auth [username]\");\n\t\t\tctrl_shell_printf(\"\\nSet a username and password prior to connecting to a broker.\\n\");\n\t\t}else if(!strcasecmp(command, \"connect\")){\n\t\t\tctrl_shell_print_help_command(\"connect\");\n\t\t\tctrl_shell_print_help_command(\"connect mqtt://hostname[:port]\");\n\t\t\tctrl_shell_print_help_command(\"connect mqtts://hostname[:port]\");\n\t\t\tctrl_shell_print_help_command(\"connect ws://hostname[:port]\");\n\t\t\tctrl_shell_print_help_command(\"connect wss://hostname[:port]\");\n\t\t\tctrl_shell_printf(\"\\nConnect to a broker using the provided transport and port.\\n\");\n\t\t\tctrl_shell_printf(\"If no URL is provided, connects to mqtt://localhost:1883\\n\");\n\t\t}else if(!strcasecmp(command, \"exit\")){\n\t\t\tctrl_shell_print_help_command(\"exit\");\n\t\t\tctrl_shell_printf(\"\\nQuit the program\\n\");\n\t\t}else if(!strcasecmp(command, \"help\")){\n\t\t\tctrl_shell_print_help_command(\"help <command>\");\n\t\t\tctrl_shell_printf(\"\\nFind help on a command using '%shelp <command>%s'\\n\", ANSI_INPUT, ANSI_RESET);\n\t\t\tctrl_shell_printf(\"Press tab multiple times to find currently available commands.\\n\");\n\t\t}else{\n\t\t\tctrl_shell_printf(\"Unknown command '%s'\\n\", command);\n\t\t}\n\t}else{\n\t\tctrl_shell_printf(\"This is the mosquitto_ctrl interactive shell, for controlling aspects of a mosquitto broker.\\n\");\n\n\t\tctrl_shell_printf(\"Find help on a command using '%shelp <command>%s'\\n\", ANSI_INPUT, ANSI_RESET);\n\t\tctrl_shell_printf(\"Press tab multiple times to find currently available commands.\\n\\n\");\n\n\t\tctrl_shell_printf(\"Example workflow:\\n\\n\");\n\t\tctrl_shell_printf(\"> auth\\n\");\n\t\tctrl_shell_printf(\"username: admin\\n\");\n\t\tctrl_shell_printf(\"password:\\n\");\n\t\tctrl_shell_printf(\"> connect mqtt://localhost\\n\");\n\t\tctrl_shell_printf(\"mqtt://localhost:1883> dynsec\\n\");\n\t\tctrl_shell_printf(\"mqtt://localhost:1883|dynsec> createGroup newgroup\\n\");\n\t\tctrl_shell_printf(\"OK\\n\\n\");\n\t}\n}\n\n\nstatic void line_callback(char *line)\n{\n\tchar *saveptr = NULL;\n\tchar *command;\n\n\tif(!line){\n\t\tctrl_shell_callback_final(NULL);\n\t\treturn;\n\t}\n\n\tctrl_shell_rtrim(line);\n\tif(strlen(line) > 0){\n\t\tadd_history(line);\n\t}else{\n\t\tfree(line);\n\t\treturn;\n\t}\n\n\tcommand = strtok_r(line, \" \", &saveptr);\n\tif(!command){\n\t\tfree(line);\n\t\treturn;\n\t}\n\n\tif(!strcasecmp(command, \"auth\")){\n\t\tfree(data.username);\n\t\tchar *username_i = strtok_r(NULL, \" \", &saveptr);\n\t\tif(username_i){\n\t\t\tdata.username = strdup(username_i);\n\t\t}else{\n\t\t\tchar promptbuf[50];\n\t\t\tsnprintf(promptbuf, sizeof(promptbuf), \"%susername:%s \", ANSI_INPUT, ANSI_RESET);\n\t\t\tdata.username = readline(promptbuf);\n\t\t}\n\n\t\tchar pwbuf[200];\n\t\tif(!ctrl_shell_get_password(pwbuf, sizeof(pwbuf))){\n\t\t\tctrl_shell_printf(\"No password.\\n\");\n\t\t\tfree(line);\n\t\t\treturn;\n\t\t}\n\t\tfree(data.password);\n\t\tdata.password = strdup(pwbuf);\n\t}else if(!strcasecmp(command, \"connect\")){\n\t\tchar *url = strtok_r(NULL, \" \", &saveptr);\n\t\tif(url){\n\t\t\tif(!strncasecmp(url, \"mqtt://\", 7)){\n\t\t\t\turl += 7;\n\t\t\t\tdata.port = 1883;\n\t\t\t\tdata.url_scheme = \"mqtt\";\n\t\t\t}else if(!strncasecmp(url, \"mqtts://\", 8)){\n#ifdef WITH_TLS\n\t\t\t\turl += 8;\n\t\t\t\tdata.port = 8883;\n\t\t\t\tdata.url_scheme = \"mqtts\";\n#else\n\t\t\t\tctrl_shell_printf(\"TLS support not available.\\n\");\n\t\t\t\tfree(line);\n\t\t\t\treturn;\n#endif\n\t\t\t}else if(!strncasecmp(url, \"ws://\", 5)){\n\t\t\t\turl += 5;\n\t\t\t\tdata.port = 1883;\n\t\t\t\tdata.transport = MOSQ_T_WEBSOCKETS;\n\t\t\t\tdata.url_scheme = \"ws\";\n\t\t\t}else if(!strncasecmp(url, \"wss://\", 6)){\n#ifdef WITH_TLS\n\t\t\t\turl += 6;\n\t\t\t\tdata.port = 8883;\n\t\t\t\tdata.transport = MOSQ_T_WEBSOCKETS;\n\t\t\t\tdata.url_scheme = \"wss\";\n#else\n\t\t\t\tctrl_shell_printf(\"TLS support not available.\\n\");\n\t\t\t\tfree(line);\n\t\t\t\treturn;\n#endif\n\t\t\t}\n\t\t\tchar *hostname_i = strtok_r(url, \":\", &saveptr);\n\t\t\tchar *port_i = strtok_r(NULL, \":\", &saveptr);\n\n\t\t\tif(!hostname_i){\n\t\t\t\tctrl_shell_printf(\"connect mqtt[s]://<hostname>:port\\n\");\n\t\t\t\tfree(line);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfree(data.hostname);\n\t\t\tdata.hostname = strdup(hostname_i);\n\t\t\tif(port_i){\n\t\t\t\tdata.port = atoi(port_i);\n\t\t\t}\n\t\t}else{\n\t\t\tif(data.hostname == NULL){\n\t\t\t\tdata.hostname = strdup(\"localhost\");\n\t\t\t}\n\t\t\tif(data.port == PORT_UNDEFINED){\n\t\t\t\tdata.port = 1883;\n\t\t\t}\n\t\t}\n\n\t\tctrl_shell__connect();\n\t}else if(!strcasecmp(command, \"help\")){\n\t\tprint_help(&saveptr);\n\t}else if(!strcasecmp(command, \"exit\")){\n\t\tdata.run = 0;\n\t}else{\n\t\tctrl_shell_printf(\"Unknown command '%s'\\n\", command);\n\t}\n\n\tfree(line);\n}\n\n\nvoid ctrl_shell__pre_connect_cleanup(void)\n{\n\tcompletion_tree_free(commands_pre_connect);\n\tcommands_pre_connect = NULL;\n}\n\n\nvoid ctrl_shell__pre_connect_init(void)\n{\n\tcommand_tree_create();\n\tctrl_shell_completion_commands_set(commands_pre_connect);\n\tctrl_shell_line_callback_set(line_callback);\n\tsnprintf(prompt, sizeof(prompt), \"%s>%s \", ANSI_INPUT, ANSI_RESET);\n}\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/ctrl_shell_printf.c",
    "content": "/*\nCopyright (c) 2025 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include \"config.h\"\n\n#include <stdarg.h>\n#include <stdio.h>\n\n#include \"ctrl_shell_internal.h\"\n\n\nvoid ctrl_shell_vprintf(const char *fmt, va_list va)\n{\n\tchar buf[2000];\n\tvsnprintf(buf, sizeof(buf), fmt, va);\n\tctrl_shell__output(buf);\n}\n\n\nvoid ctrl_shell_printf(const char *fmt, ...)\n{\n\tva_list va;\n\n\tva_start(va, fmt);\n\tctrl_shell_vprintf(fmt, va);\n\tva_end(va);\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/dynsec.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#define CJSON_VERSION_FULL (CJSON_VERSION_MAJOR*1000000+CJSON_VERSION_MINOR*1000+CJSON_VERSION_PATCH)\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifndef WIN32\n#  include <errno.h>\n#  include <fcntl.h>\n#  include <strings.h>\n#endif\n\n#include \"mosquitto_ctrl.h\"\n#include \"mosquitto.h\"\n#include \"json_help.h\"\n#include \"get_password.h\"\n\n#define MAX_STRING_LEN 4096\n\n\nvoid dynsec__print_usage(void)\n{\n\tprintf(\"\\nDynamic Security module\\n\");\n\tprintf(\"=======================\\n\");\n\tprintf(\"\\nInitialisation\\n--------------\\n\");\n\tprintf(\"Create a new configuration file with an admin user:\\n\");\n\tprintf(\"    mosquitto_ctrl dynsec init <new-config-file> <admin-username> [admin-password]\\n\");\n\n\tprintf(\"\\nGeneral\\n-------\\n\");\n\tprintf(\"Get ACL default access:          getDefaultACLAccess\\n\");\n\tprintf(\"Set ACL default access:          setDefaultACLAccess <acltype> allow|deny\\n\");\n\tprintf(\"Get group for anonymous clients: getAnonymousGroup\\n\");\n\tprintf(\"Set group for anonymous clients: setAnonymousGroup   <groupname>\\n\");\n\n\tprintf(\"\\nClients\\n-------\\n\");\n\tprintf(\"Create a new client:         createClient      <username> [-i clientid] [-p password]\\n\");\n\tprintf(\"Delete a client:             deleteClient      <username>\\n\");\n\tprintf(\"Set a client password:       setClientPassword <username> [password]\\n\");\n\tprintf(\"Set a client password on an existing file:\\n\");\n\tprintf(\"    mosquitto_ctrl -f <file> dynsec setClientPassword <username> <password>\\n\");\n\tprintf(\"Set a client id:             setClientId       <username> [clientid]\\n\");\n\tprintf(\"Add a role to a client:      addClientRole     <username> <rolename> [priority]\\n\");\n\tprintf(\"    Higher priority (larger numerical value) roles are evaluated first.\\n\");\n\tprintf(\"Remove role from a client:   removeClientRole  <username> <rolename>\\n\");\n\tprintf(\"Get client information:      getClient         <username>\\n\");\n\tprintf(\"List all clients:            listClients       [count [offset]]\\n\");\n\tprintf(\"Enable client:               enableClient      <username>\\n\");\n\tprintf(\"Disable client:              disableClient     <username>\\n\");\n\n\tprintf(\"\\nGroups\\n------\\n\");\n\tprintf(\"Create a new group:          createGroup       <groupname>\\n\");\n\tprintf(\"Delete a group:              deleteGroup       <groupname>\\n\");\n\tprintf(\"Add a role to a group:       addGroupRole      <groupname> <rolename> [priority]\\n\");\n\tprintf(\"    Higher priority (larger numerical value) roles are evaluated first.\\n\");\n\tprintf(\"Remove role from a group:    removeGroupRole   <groupname> <rolename>\\n\");\n\tprintf(\"Add client to a group:       addGroupClient    <groupname> <username> [priority]\\n\");\n\tprintf(\"    Priority sets the group priority for the given client only.\\n\");\n\tprintf(\"    Higher priority (larger numerical value) groups are evaluated first.\\n\");\n\tprintf(\"Remove client from a group:  removeGroupClient <groupname> <username>\\n\");\n\tprintf(\"Get group information:       getGroup          <groupname>\\n\");\n\tprintf(\"List all groups:             listGroups        [count [offset]]\\n\");\n\n\tprintf(\"\\nRoles\\n------\\n\");\n\tprintf(\"Create a new role:           createRole        <rolename>\\n\");\n\tprintf(\"Delete a role:               deleteRole        <rolename>\\n\");\n\tprintf(\"Add an ACL to a role:        addRoleACL        <rolename> <aclspec> [priority]\\n\");\n\tprintf(\"    Higher priority (larger numerical value) ACLs are evaluated first.\\n\");\n\tprintf(\"Remove ACL from a role:      removeRoleACL     <rolename> <aclspec>\\n\");\n\tprintf(\"Get role information:        getRole           <rolename>\\n\");\n\tprintf(\"List all roles:              listRoles         [count [offset]]\\n\");\n\tprintf(\"\\naclspec:                     <acltype> <topicFilter> allow|deny\\n\");\n\tprintf(\"acltype:                     publishClientSend|publishClientReceive\\n\");\n\tprintf(\"                              |subscribeLiteral|subscribePattern\\n\");\n\tprintf(\"                              |unsubscribeLiteral|unsubscribePattern\\n\");\n\tprintf(\"\\nFor more information see:\\n\");\n\tprintf(\"    https://mosquitto.org/documentation/dynamic-security/\\n\\n\");\n}\n\n\n/* ################################################################\n * #\n * # Payload callback\n * #\n * ################################################################ */\n\n\nstatic void print_list(cJSON *j_response, const char *arrayname, const char *keyname)\n{\n\tcJSON *j_data, *j_array, *j_elem;\n\n\tj_data = cJSON_GetObjectItem(j_response, \"data\");\n\tif(j_data == NULL){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tj_array = cJSON_GetObjectItem(j_data, arrayname);\n\tif(j_array == NULL || !cJSON_IsArray(j_array)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tcJSON_ArrayForEach(j_elem, j_array){\n\t\tif(cJSON_IsObject(j_elem)){\n\t\t\tconst char *stmp;\n\t\t\tif(json_get_string(j_elem, keyname, &stmp, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\tprintf(\"%s\\n\", stmp);\n\t\t\t}\n\t\t}else if(cJSON_IsString(j_elem) && j_elem->valuestring){\n\t\t\tprintf(\"%s\\n\", j_elem->valuestring);\n\t\t}\n\t}\n}\n\n\nstatic void print_json_value(cJSON *value, const char *null_value)\n{\n\tif(value){\n\t\tif(cJSON_IsString(value)){\n\t\t\tif(value->valuestring){\n\t\t\t\tprintf(\"%s\", value->valuestring);\n\t\t\t}\n\t\t}else{\n\t\t\tchar buffer[MAX_STRING_LEN];\n\t\t\tcJSON_PrintPreallocated(value, buffer, sizeof(buffer), 0);\n\t\t\tprintf(\"%s\", buffer);\n\t\t}\n\t}else if(null_value){\n\t\tprintf(\"%s\", null_value);\n\t}\n}\n\n\nstatic void print_json_array(cJSON *j_list, int slen, const char *label, const char *element_name, const char *optional_element_name, const char *optional_element_null_value)\n{\n\tcJSON *j_elem;\n\n\tif(j_list && cJSON_IsArray(j_list)){\n\t\tcJSON_ArrayForEach(j_elem, j_list){\n\t\t\tif(cJSON_IsObject(j_elem)){\n\t\t\t\tconst char *stmp;\n\n\t\t\t\tif(json_get_string(j_elem, element_name, &stmp, false) != MOSQ_ERR_SUCCESS){\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tprintf(\"%-*s %s\", (int)slen, label, stmp);\n\t\t\t\tif(optional_element_name){\n\t\t\t\t\tprintf(\" (%s: \", optional_element_name);\n\t\t\t\t\tprint_json_value(cJSON_GetObjectItem(j_elem, optional_element_name), optional_element_null_value);\n\t\t\t\t\tprintf(\")\");\n\t\t\t\t}\n\t\t\t}else if(cJSON_IsString(j_elem) && j_elem->valuestring){\n\t\t\t\tprintf(\"%-*s %s\", (int)slen, label, j_elem->valuestring);\n\t\t\t}\n\t\t\tlabel = \"\";\n\t\t\tprintf(\"\\n\");\n\t\t}\n\t}else{\n\t\tprintf(\"%s\\n\", label);\n\t}\n}\n\n\nstatic void print_client(cJSON *j_response)\n{\n\tcJSON *j_data, *j_client, *jtmp;\n\tconst int label_width = (int)strlen(\"Connections:\");\n\n\tj_data = cJSON_GetObjectItem(j_response, \"data\");\n\tif(j_data == NULL || !cJSON_IsObject(j_data)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tj_client = cJSON_GetObjectItem(j_data, \"client\");\n\tif(j_client == NULL || !cJSON_IsObject(j_client)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tconst char *username;\n\tif(json_get_string(j_client, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\tprintf(\"%-*s %s\\n\",  label_width, \"Username:\", username);\n\n\tconst char *clientid;\n\tif(json_get_string(j_client, \"clientid\", &clientid, false) == MOSQ_ERR_SUCCESS){\n\t\tprintf(\"%-*s %s\\n\",  label_width, \"Clientid:\", clientid);\n\t}else{\n\t\tprintf(\"Clientid:\\n\");\n\t}\n\n\tjtmp = cJSON_GetObjectItem(j_client, \"disabled\");\n\tif(jtmp && cJSON_IsBool(jtmp)){\n\t\tprintf(\"%-*s %s\\n\",  label_width, \"Disabled:\", cJSON_IsTrue(jtmp)?\"true\":\"false\");\n\t}\n\n\tprint_json_array(cJSON_GetObjectItem(j_client, \"roles\"), label_width, \"Roles:\",  \"rolename\", \"priority\", \"-1\");\n\tprint_json_array(cJSON_GetObjectItem(j_client, \"groups\"), label_width, \"Groups:\", \"groupname\", \"priority\", \"-1\");\n\tprint_json_array(cJSON_GetObjectItem(j_client, \"connections\"), label_width, \"Connections:\", \"address\", NULL, NULL);\n}\n\n\nstatic void print_group(cJSON *j_response)\n{\n\tcJSON *j_data, *j_group;\n\tint label_width = (int)strlen(\"Groupname:\");\n\tconst char *groupname;\n\n\tj_data = cJSON_GetObjectItem(j_response, \"data\");\n\tif(j_data == NULL || !cJSON_IsObject(j_data)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tj_group = cJSON_GetObjectItem(j_data, \"group\");\n\tif(j_group == NULL || !cJSON_IsObject(j_group)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tif(json_get_string(j_group, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\tprintf(\"Groupname: %s\\n\", groupname);\n\n\tprint_json_array(cJSON_GetObjectItem(j_group, \"roles\"), label_width, \"Roles:\",  \"rolename\", \"priority\", \"-1\");\n\tprint_json_array(cJSON_GetObjectItem(j_group, \"clients\"), label_width, \"Clients:\",  \"username\", NULL, NULL);\n}\n\n\nstatic void print_role(cJSON *j_response)\n{\n\tcJSON *j_data, *j_role, *j_array, *j_elem, *jtmp;\n\tbool first;\n\n\tj_data = cJSON_GetObjectItem(j_response, \"data\");\n\tif(j_data == NULL || !cJSON_IsObject(j_data)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tj_role = cJSON_GetObjectItem(j_data, \"role\");\n\tif(j_role == NULL || !cJSON_IsObject(j_role)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tconst char *rolename;\n\tif(json_get_string(j_role, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\tprintf(\"Rolename: %s\\n\", rolename);\n\n\tj_array = cJSON_GetObjectItem(j_role, \"acls\");\n\tif(j_array && cJSON_IsArray(j_array)){\n\t\tfirst = true;\n\t\tcJSON_ArrayForEach(j_elem, j_array){\n\t\t\tconst char *acltype;\n\n\t\t\tif(json_get_string(j_elem, \"acltype\", &acltype, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\tif(first){\n\t\t\t\t\tfirst = false;\n\t\t\t\t\tprintf(\"ACLs:     %-20s\", acltype);\n\t\t\t\t}else{\n\t\t\t\t\tprintf(\"          %-20s\", acltype);\n\t\t\t\t}\n\n\t\t\t\tjtmp = cJSON_GetObjectItem(j_elem, \"allow\");\n\t\t\t\tif(jtmp && cJSON_IsBool(jtmp)){\n\t\t\t\t\tprintf(\" : %s\", cJSON_IsTrue(jtmp)?\"allow\":\"deny \");\n\t\t\t\t}\n\n\t\t\t\tconst char *topic;\n\t\t\t\tif(json_get_string(j_elem, \"topic\", &topic, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\t\tprintf(\" : %s\", topic);\n\t\t\t\t}\n\t\t\t\tjtmp = cJSON_GetObjectItem(j_elem, \"priority\");\n\t\t\t\tif(jtmp && cJSON_IsNumber(jtmp)){\n\t\t\t\t\tprintf(\" (priority: %d)\", (int)jtmp->valuedouble);\n\t\t\t\t}else{\n\t\t\t\t\tprintf(\" (priority: -1)\");\n\t\t\t\t}\n\t\t\t\tprintf(\"\\n\");\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void print_anonymous_group(cJSON *j_response)\n{\n\tcJSON *j_data, *j_group;\n\tconst char *groupname;\n\n\tj_data = cJSON_GetObjectItem(j_response, \"data\");\n\tif(j_data == NULL || !cJSON_IsObject(j_data)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tj_group = cJSON_GetObjectItem(j_data, \"group\");\n\tif(j_group == NULL || !cJSON_IsObject(j_group)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tif(json_get_string(j_group, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\tprintf(\"%s\\n\", groupname);\n}\n\n\nstatic void print_default_acl_access(cJSON *j_response)\n{\n\tcJSON *j_data, *j_acls, *j_acl;\n\n\tj_data = cJSON_GetObjectItem(j_response, \"data\");\n\tif(j_data == NULL || !cJSON_IsObject(j_data)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tj_acls = cJSON_GetObjectItem(j_data, \"acls\");\n\tif(j_acls == NULL || !cJSON_IsArray(j_acls)){\n\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\treturn;\n\t}\n\n\tcJSON_ArrayForEach(j_acl, j_acls){\n\t\tconst char *acltype;\n\t\tbool allow;\n\n\t\tif(json_get_string(j_acl, \"acltype\", &acltype, false) != MOSQ_ERR_SUCCESS\n\t\t\t\t|| json_get_bool(j_acl, \"allow\", &allow, false, false) != MOSQ_ERR_SUCCESS){\n\n\t\t\tfprintf(stderr, \"Error: Invalid response from server.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tprintf(\"%-20s : %s\\n\", acltype, allow?\"allow\":\"deny\");\n\t}\n}\n\n\nstatic void dynsec__payload_callback(struct mosq_ctrl *ctrl, long payloadlen, const void *payload)\n{\n\tcJSON *tree, *j_responses, *j_response;\n\tconst char *command, *error;\n\n\tUNUSED(ctrl);\n\n#if CJSON_VERSION_FULL < 1007013\n\tUNUSED(payloadlen);\n\ttree = cJSON_Parse(payload);\n#else\n\ttree = cJSON_ParseWithLength(payload, (size_t)payloadlen);\n#endif\n\tif(tree == NULL){\n\t\tfprintf(stderr, \"Error: Payload not JSON.\\n\");\n\t\treturn;\n\t}\n\n\tj_responses = cJSON_GetObjectItem(tree, \"responses\");\n\tif(j_responses == NULL || !cJSON_IsArray(j_responses)){\n\t\tfprintf(stderr, \"Error: Payload missing data.\\n\");\n\t\tcJSON_Delete(tree);\n\t\treturn;\n\t}\n\n\tj_response = cJSON_GetArrayItem(j_responses, 0);\n\tif(j_response == NULL){\n\t\tfprintf(stderr, \"Error: Payload missing data.\\n\");\n\t\tcJSON_Delete(tree);\n\t\treturn;\n\t}\n\n\tif(json_get_string(j_response, \"command\", &command, false) != MOSQ_ERR_SUCCESS){\n\t\tfprintf(stderr, \"Error: Payload missing data.\\n\");\n\t\tcJSON_Delete(tree);\n\t\treturn;\n\t}\n\n\tif(json_get_string(j_response, \"error\", &error, false) == MOSQ_ERR_SUCCESS){\n\t\tfprintf(stderr, \"%s: Error: %s.\\n\", command, error);\n\t}else{\n\t\tif(!strcasecmp(command, \"listClients\")){\n\t\t\tprint_list(j_response, \"clients\", \"username\");\n\t\t}else if(!strcasecmp(command, \"listGroups\")){\n\t\t\tprint_list(j_response, \"groups\", \"groupname\");\n\t\t}else if(!strcasecmp(command, \"listRoles\")){\n\t\t\tprint_list(j_response, \"roles\", \"rolename\");\n\t\t}else if(!strcasecmp(command, \"getClient\")){\n\t\t\tprint_client(j_response);\n\t\t}else if(!strcasecmp(command, \"getGroup\")){\n\t\t\tprint_group(j_response);\n\t\t}else if(!strcasecmp(command, \"getRole\")){\n\t\t\tprint_role(j_response);\n\t\t}else if(!strcasecmp(command, \"getDefaultACLAccess\")){\n\t\t\tprint_default_acl_access(j_response);\n\t\t}else if(!strcasecmp(command, \"getAnonymousGroup\")){\n\t\t\tprint_anonymous_group(j_response);\n\t\t}else{\n\t\t\t/* fprintf(stderr, \"%s: Success\\n\", command); */\n\t\t}\n\t}\n\tcJSON_Delete(tree);\n}\n\n\n/* ################################################################\n * #\n * # Default ACL access\n * #\n * ################################################################ */\n\n\nstatic int dynsec__set_default_acl_access(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *acltype, *access;\n\tbool b_access;\n\tcJSON *j_acls, *j_acl;\n\n\tif(argc == 2){\n\t\tacltype = argv[0];\n\t\taccess = argv[1];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(strcasecmp(acltype, \"publishClientSend\")\n\t\t\t&& strcasecmp(acltype, \"publishClientReceive\")\n\t\t\t&& strcasecmp(acltype, \"subscribe\")\n\t\t\t&& strcasecmp(acltype, \"unsubscribe\")){\n\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(!strcasecmp(access, \"allow\")){\n\t\tb_access = true;\n\t}else if(!strcasecmp(access, \"deny\")){\n\t\tb_access = false;\n\t}else{\n\t\tfprintf(stderr, \"Error: access must be \\\"allow\\\" or \\\"deny\\\".\\n\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"setDefaultACLAccess\") == NULL\n\t\t\t|| (j_acls = cJSON_AddArrayToObject(j_command, \"acls\")) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tj_acl = cJSON_CreateObject();\n\tif(j_acl == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(j_acls, j_acl);\n\tif(cJSON_AddStringToObject(j_acl, \"acltype\", acltype) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_acl, \"allow\", b_access) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int dynsec__get_default_acl_access(int argc, char *argv[], cJSON *j_command)\n{\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"getDefaultACLAccess\") == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* ################################################################\n * #\n * # Init\n * #\n * ################################################################ */\n\n\nstatic cJSON *init_add_acl_to_role(cJSON *j_acls, const char *type, const char *topic)\n{\n\tcJSON *j_acl;\n\n\tj_acl = cJSON_CreateObject();\n\tif(j_acl == NULL){\n\t\treturn NULL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_acl, \"acltype\", type) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_acl, \"topic\", topic) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_acl, \"allow\", true) == NULL\n\t\t\t){\n\n\t\tcJSON_Delete(j_acl);\n\t\treturn NULL;\n\t}\n\tcJSON_AddItemToArray(j_acls, j_acl);\n\treturn j_acl;\n}\n\n\nstatic cJSON *init_add_role(const char *rolename)\n{\n\tcJSON *j_role, *j_acls;\n\n\tj_role = cJSON_CreateObject();\n\tif(j_role == NULL){\n\t\treturn NULL;\n\t}\n\tif(cJSON_AddStringToObject(j_role, \"rolename\", rolename) == NULL){\n\t\tcJSON_Delete(j_role);\n\t\treturn NULL;\n\t}\n\n\tj_acls = cJSON_CreateArray();\n\tif(j_acls == NULL){\n\t\tcJSON_Delete(j_role);\n\t\treturn NULL;\n\t}\n\tcJSON_AddItemToObject(j_role, \"acls\", j_acls);\n\tif(init_add_acl_to_role(j_acls, \"publishClientSend\", \"$CONTROL/dynamic-security/#\") == NULL\n\t\t\t|| init_add_acl_to_role(j_acls, \"publishClientReceive\", \"$CONTROL/dynamic-security/#\") == NULL\n\t\t\t|| init_add_acl_to_role(j_acls, \"subscribePattern\", \"$CONTROL/dynamic-security/#\") == NULL\n\t\t\t|| init_add_acl_to_role(j_acls, \"publishClientReceive\", \"$SYS/#\") == NULL\n\t\t\t|| init_add_acl_to_role(j_acls, \"subscribePattern\", \"$SYS/#\") == NULL\n\t\t\t|| init_add_acl_to_role(j_acls, \"publishClientReceive\", \"#\") == NULL\n\t\t\t|| init_add_acl_to_role(j_acls, \"subscribePattern\", \"#\") == NULL\n\t\t\t|| init_add_acl_to_role(j_acls, \"unsubscribePattern\", \"#\") == NULL\n\t\t\t){\n\n\t\tcJSON_Delete(j_role);\n\t\treturn NULL;\n\t}\n\treturn j_role;\n}\n\n\nstatic cJSON *init_add_client(const char *username, const char *password, const char *rolename)\n{\n\tcJSON *j_client, *j_roles, *j_role;\n\tstruct mosquitto_pw *pw;\n\n\tif(mosquitto_pw_new(&pw, MOSQ_PW_DEFAULT) || mosquitto_pw_hash_encoded(pw, password)){\n\t\tmosquitto_pw_cleanup(pw);\n\t\treturn NULL;\n\t}\n\n\tj_client = cJSON_CreateObject();\n\tif(j_client == NULL){\n\t\tmosquitto_pw_cleanup(pw);\n\t\treturn NULL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_client, \"username\", username) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_client, \"textName\", \"Dynsec admin user\") == NULL\n\t\t\t){\n\n\t\tcJSON_Delete(j_client);\n\t\tmosquitto_pw_cleanup(pw);\n\t\treturn NULL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_client, \"encoded_password\", mosquitto_pw_get_encoded(pw)) == NULL){\n\t\tcJSON_Delete(j_client);\n\t\tmosquitto_pw_cleanup(pw);\n\t\treturn NULL;\n\t}\n\tmosquitto_pw_cleanup(pw);\n\n\tj_roles = cJSON_CreateArray();\n\tif(j_roles == NULL){\n\t\tcJSON_Delete(j_client);\n\t\treturn NULL;\n\t}\n\tcJSON_AddItemToObject(j_client, \"roles\", j_roles);\n\n\tj_role = cJSON_CreateObject();\n\tif(j_role == NULL){\n\t\tcJSON_Delete(j_client);\n\t\treturn NULL;\n\t}\n\tcJSON_AddItemToArray(j_roles, j_role);\n\tif(cJSON_AddStringToObject(j_role, \"rolename\", rolename) == NULL){\n\t\tcJSON_Delete(j_client);\n\t\treturn NULL;\n\t}\n\n\treturn j_client;\n}\n\n\nstatic cJSON *init_create(const char *username, const char *password, const char *rolename)\n{\n\tcJSON *tree, *j_clients, *j_client, *j_roles, *j_role;\n\tcJSON *j_default_access;\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\treturn NULL;\n\t}\n\n\tif((j_clients = cJSON_AddArrayToObject(tree, \"clients\")) == NULL\n\t\t\t|| (j_roles = cJSON_AddArrayToObject(tree, \"roles\")) == NULL\n\t\t\t|| (j_default_access = cJSON_AddObjectToObject(tree, \"defaultACLAccess\")) == NULL\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\treturn NULL;\n\t}\n\n\t/* Set default behaviour:\n\t * * Client can not publish to the broker by default.\n\t * * Broker *CAN* publish to the client by default.\n\t * * Client con not subscribe to topics by default.\n\t * * Client *CAN* unsubscribe from topics by default.\n\t */\n\tif(cJSON_AddBoolToObject(j_default_access, \"publishClientSend\", false) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_default_access, \"publishClientReceive\", true) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_default_access, \"subscribe\", false) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_default_access, \"unsubscribe\", true) == NULL\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\treturn NULL;\n\t}\n\n\tj_client = init_add_client(username, password, rolename);\n\tif(j_client == NULL){\n\t\tcJSON_Delete(tree);\n\t\treturn NULL;\n\t}\n\tcJSON_AddItemToArray(j_clients, j_client);\n\n\tj_role = init_add_role(rolename);\n\tif(j_role == NULL){\n\t\tcJSON_Delete(tree);\n\t\treturn NULL;\n\t}\n\tcJSON_AddItemToArray(j_roles, j_role);\n\n\treturn tree;\n}\n\n\n/* mosquitto_ctrl dynsec init <filename> <admin-user> <admin-password> [role-name] */\nstatic int dynsec_init(int argc, char *argv[])\n{\n\tchar *filename;\n\tchar *admin_user;\n\tchar *admin_password;\n\tchar *json_str;\n\tcJSON *tree;\n\tFILE *fptr;\n\tchar prompt[200], verify_prompt[200];\n\tchar password[200];\n\tint rc;\n\n\tif(argc < 2){\n\t\tfprintf(stderr, \"dynsec init: Not enough arguments - filename, or admin-user missing.\\n\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(argc > 3){\n\t\tfprintf(stderr, \"dynsec init: Too many arguments.\\n\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tfilename = argv[0];\n\tadmin_user = argv[1];\n\n\tif(argc == 3){\n\t\tadmin_password = argv[2];\n\t}else{\n\t\tsnprintf(prompt, sizeof(prompt), \"New password for %s: \", admin_user);\n\t\tsnprintf(verify_prompt, sizeof(verify_prompt), \"Reenter password for %s: \", admin_user);\n\t\trc = get_password(prompt, verify_prompt, false, password, sizeof(password));\n\t\tif(rc){\n\t\t\tmosquitto_lib_cleanup();\n\t\t\treturn -1;\n\t\t}\n\t\tadmin_password = password;\n\t}\n\n\ttree = init_create(admin_user, admin_password, \"admin\");\n\tif(tree == NULL){\n\t\tfprintf(stderr, \"dynsec init: Out of memory.\\n\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tjson_str = cJSON_PrintUnformatted(tree);\n\tcJSON_Delete(tree);\n\n#ifdef WIN32\n\tfptr = mosquitto_fopen(filename, \"wb\", true);\n#else\n\tint fd = open(filename, O_CREAT | O_EXCL | O_WRONLY, 0640);\n\tif(fd < 0){\n\t\tfree(json_str);\n\t\tfprintf(stderr, \"dynsec init: Unable to open '%s' for writing (%s).\\n\", filename, strerror(errno));\n\t\treturn -1;\n\t}\n\tfptr = fdopen(fd, \"wb\");\n#endif\n\tif(fptr){\n\t\tfprintf(fptr, \"%s\", json_str);\n\t\tfree(json_str);\n\t\tfclose(fptr);\n\t}else{\n\t\tfree(json_str);\n\t\tfprintf(stderr, \"dynsec init: Unable to open '%s' for writing.\\n\", filename);\n\t\treturn -1;\n\t}\n\n\tprintf(\"The client '%s' has been created in the file '%s'.\\n\", admin_user, filename);\n\tprintf(\"This client is configured to allow you to administer the dynamic security plugin only.\\n\");\n\tprintf(\"It does not have access to publish messages to normal topics.\\n\");\n\tprintf(\"You should create your application clients to do that, for example:\\n\");\n\tprintf(\"   mosquitto_ctrl <connect options> dynsec createClient <username>\\n\");\n\tprintf(\"   mosquitto_ctrl <connect options> dynsec createRole <rolename>\\n\");\n\tprintf(\"   mosquitto_ctrl <connect options> dynsec addRoleACL <rolename> publishClientSend my/topic [priority]\\n\");\n\tprintf(\"   mosquitto_ctrl <connect options> dynsec addClientRole <username> <rolename> [priority]\\n\");\n\tprintf(\"See https://mosquitto.org/documentation/dynamic-security/ for details of all commands.\\n\");\n\n\treturn -1; /* Suppress client connection */\n}\n\n\n/* ################################################################\n * #\n * # Main\n * #\n * ################################################################ */\n\n\nint dynsec__main(int argc, char *argv[], struct mosq_ctrl *ctrl)\n{\n\tint rc = -1;\n\tcJSON *j_tree;\n\tcJSON *j_commands, *j_command;\n\n\tif(!strcasecmp(argv[0], \"help\")){\n\t\tdynsec__print_usage();\n\t\treturn -1;\n\t}else if(!strcasecmp(argv[0], \"init\")){\n\t\treturn dynsec_init(argc-1, &argv[1]);\n\t}else if(ctrl->cfg.data_file && !strcasecmp(argv[0], \"setClientPassword\")){\n\t\treturn dynsec_client__file_set_password(argc-1, &argv[1], ctrl->cfg.data_file);\n\t}\n\n\t/* The remaining commands need a network connection and JSON command. */\n\n\tctrl->payload_callback = dynsec__payload_callback;\n\tctrl->request_topic = strdup(\"$CONTROL/dynamic-security/v1\");\n\tctrl->response_topic = strdup(\"$CONTROL/dynamic-security/v1/response\");\n\tif(ctrl->request_topic == NULL || ctrl->response_topic == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tj_tree = cJSON_CreateObject();\n\tif(j_tree == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tj_commands = cJSON_AddArrayToObject(j_tree, \"commands\");\n\tif(j_commands == NULL){\n\t\tcJSON_Delete(j_tree);\n\t\tj_tree = NULL;\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tj_command = cJSON_CreateObject();\n\tif(j_command == NULL){\n\t\tcJSON_Delete(j_tree);\n\t\tj_tree = NULL;\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(j_commands, j_command);\n\n\tif(!strcasecmp(argv[0], \"setDefaultACLAccess\")){\n\t\trc = dynsec__set_default_acl_access(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"getDefaultACLAccess\")){\n\t\trc = dynsec__get_default_acl_access(argc-1, &argv[1], j_command);\n\n\t}else if(!strcasecmp(argv[0], \"createClient\")){\n\t\trc = dynsec_client__create(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"deleteClient\")){\n\t\trc = dynsec_client__delete(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"getClient\")){\n\t\trc = dynsec_client__get(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"listClients\")){\n\t\trc = dynsec_client__list_all(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"setClientId\")){\n\t\trc = dynsec_client__set_id(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"setClientPassword\")){\n\t\trc = dynsec_client__set_password(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"addClientRole\")){\n\t\trc = dynsec_client__add_remove_role(argc-1, &argv[1], j_command, argv[0]);\n\t}else if(!strcasecmp(argv[0], \"removeClientRole\")){\n\t\trc = dynsec_client__add_remove_role(argc-1, &argv[1], j_command, argv[0]);\n\t}else if(!strcasecmp(argv[0], \"enableClient\")){\n\t\trc = dynsec_client__enable_disable(argc-1, &argv[1], j_command, argv[0]);\n\t}else if(!strcasecmp(argv[0], \"disableClient\")){\n\t\trc = dynsec_client__enable_disable(argc-1, &argv[1], j_command, argv[0]);\n\n\t}else if(!strcasecmp(argv[0], \"createGroup\")){\n\t\trc = dynsec_group__create(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"deleteGroup\")){\n\t\trc = dynsec_group__delete(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"getGroup\")){\n\t\trc = dynsec_group__get(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"listGroups\")){\n\t\trc = dynsec_group__list_all(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"addGroupRole\")){\n\t\trc = dynsec_group__add_remove_role(argc-1, &argv[1], j_command, argv[0]);\n\t}else if(!strcasecmp(argv[0], \"removeGroupRole\")){\n\t\trc = dynsec_group__add_remove_role(argc-1, &argv[1], j_command, argv[0]);\n\t}else if(!strcasecmp(argv[0], \"addGroupClient\")){\n\t\trc = dynsec_group__add_remove_client(argc-1, &argv[1], j_command, argv[0]);\n\t}else if(!strcasecmp(argv[0], \"removeGroupClient\")){\n\t\trc = dynsec_group__add_remove_client(argc-1, &argv[1], j_command, argv[0]);\n\t}else if(!strcasecmp(argv[0], \"setAnonymousGroup\")){\n\t\trc = dynsec_group__set_anonymous(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"getAnonymousGroup\")){\n\t\trc = dynsec_group__get_anonymous(argc-1, &argv[1], j_command);\n\n\t}else if(!strcasecmp(argv[0], \"createRole\")){\n\t\trc = dynsec_role__create(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"deleteRole\")){\n\t\trc = dynsec_role__delete(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"getRole\")){\n\t\trc = dynsec_role__get(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"listRoles\")){\n\t\trc = dynsec_role__list_all(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"addRoleACL\")){\n\t\trc = dynsec_role__add_acl(argc-1, &argv[1], j_command);\n\t}else if(!strcasecmp(argv[0], \"removeRoleACL\")){\n\t\trc = dynsec_role__remove_acl(argc-1, &argv[1], j_command);\n\n\t}else{\n\t\tfprintf(stderr, \"Command '%s' not recognised.\\n\", argv[0]);\n\t\tcJSON_Delete(j_tree);\n\t\tj_tree = NULL;\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tctrl->payload = cJSON_PrintUnformatted(j_tree);\n\t\tcJSON_Delete(j_tree);\n\t\tif(ctrl->payload == NULL){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}else{\n\t\tcJSON_Delete(j_tree);\n\t}\n\treturn rc;\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/dynsec_client.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n#include \"mosquitto_ctrl.h\"\n#include \"get_password.h\"\n#include \"json_help.h\"\n#include \"dynamic_security.h\"\n\n\nint dynsec_client__create(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *username = NULL, *password = NULL, *clientid = NULL;\n\tchar prompt[200], verify_prompt[200];\n\tchar password_buf[200];\n\tint rc;\n\tint i;\n\tbool request_password = true;\n\n\tif(argc == 0){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tusername = argv[0];\n\n\tfor(i=1; i<argc; i++){\n\t\tif(!strcmp(argv[i], \"-c\") || !strcmp(argv[i], \"-i\")){\n\t\t\tif(i+1 == argc){\n\t\t\t\tfprintf(stderr, \"Error: -i argument given, but no clientid provided.\\n\");\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tclientid = argv[i+1];\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-p\")){\n\t\t\tif(i+1 == argc){\n\t\t\t\tfprintf(stderr, \"Error: -p argument given, but no password provided.\\n\");\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tpassword = argv[i+1];\n\t\t\ti++;\n\t\t\trequest_password = false;\n\t\t}\n\t}\n\n\tif(request_password){\n\t\tprintf(\"Enter new password for %s. Press return for no password (user will be unable to login).\\n\", username);\n\t\tsnprintf(prompt, sizeof(prompt), \"New password for %s: \", username);\n\t\tsnprintf(verify_prompt, sizeof(verify_prompt), \"Reenter password for %s: \", username);\n\t\trc = get_password(prompt, verify_prompt, true, password_buf, sizeof(password_buf));\n\t\tif(rc == 0){\n\t\t\tpassword = password_buf;\n\t\t}else if(rc == 2){\n\t\t\tfprintf(stderr, \"Error: Passwords do not match.\\n\");\n\t\t\treturn -1;\n\t\t}else{\n\t\t\tpassword = NULL;\n\t\t\tprintf(\"\\n\");\n\t\t}\n\t}\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"createClient\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"username\", username) == NULL\n\t\t\t|| (clientid && cJSON_AddStringToObject(j_command, \"clientid\", clientid) == NULL)\n\t\t\t|| (password && cJSON_AddStringToObject(j_command, \"password\", password) == NULL)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_client__delete(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *username = NULL;\n\n\tif(argc == 1){\n\t\tusername = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"deleteClient\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"username\", username) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_client__enable_disable(int argc, char *argv[], cJSON *j_command, const char *command)\n{\n\tchar *username = NULL;\n\n\tif(argc == 1){\n\t\tusername = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", command) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"username\", username) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_client__set_id(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *username = NULL, *clientid = NULL;\n\n\tif(argc == 2){\n\t\tusername = argv[0];\n\t\tclientid = argv[1];\n\t}else if(argc == 1){\n\t\tusername = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"setClientId\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"username\", username) == NULL\n\t\t\t|| (clientid && cJSON_AddStringToObject(j_command, \"clientid\", clientid) == NULL)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_client__file_set_password(int argc, char *argv[], const char *file)\n{\n\tchar *username = NULL, *password = NULL;\n\tlong len;\n\tFILE *fptr;\n\tchar *fstr;\n\tcJSON *j_tree, *j_clients, *j_client;\n\tstruct dynsec__client client;\n\tchar *json_str;\n\tint i;\n\tint iterations = -1;\n\n\tmemset(&client, 0, sizeof(client));\n\n\tif(argc >= 2){\n\t\tusername = argv[0];\n\t\tpassword = argv[1];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tfor(i=2; i<argc; i++){\n\t\tif(!strcmp(argv[i], \"-i\")){\n\t\t\tif(i+1 == argc){\n\t\t\t\tfprintf(stderr, \"Error: -i argument given, but no iterations provided.\\n\");\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\titerations = atoi(argv[i+1]);\n\t\t\ti++;\n\t\t}else{\n\t\t\tfprintf(stderr, \"Error: Unknown argument: %s\\n\", argv[i]);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\n\tfptr = fopen(file, \"rb\");\n\tif(fptr == NULL){\n\t\tfprintf(stderr, \"Error: Unable to open %s.\\n\", file);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tfseek(fptr, 0, SEEK_END);\n\tlen = ftell(fptr);\n\tfseek(fptr, 0, SEEK_SET);\n\tif(len <= 0){\n\t\tfprintf(stderr, \"Error: %s is empty.\\n\", file);\n\t\tfclose(fptr);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tfstr = calloc(1, (size_t)len+1);\n\tif(fstr == NULL){\n\t\tfclose(fptr);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tif(fread(fstr, 1, (size_t)len, fptr) != (size_t)len){\n\t\tfprintf(stderr, \"Error: Incomplete read of %s.\\n\", file);\n\t\tfclose(fptr);\n\t\tfree(fstr);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tfclose(fptr);\n\n\tj_tree = cJSON_Parse(fstr);\n\tfree(fstr);\n\n\tif(j_tree == NULL){\n\t\tfprintf(stderr, \"Error: %s is not valid JSON.\\n\", file);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tj_clients = cJSON_GetObjectItem(j_tree, \"clients\");\n\tif(j_clients == NULL || !cJSON_IsArray(j_clients)){\n\t\tfprintf(stderr, \"Error: %s is not a valid dynamic-security config file.\\n\", file);\n\t\tcJSON_Delete(j_tree);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcJSON_ArrayForEach(j_client, j_clients){\n\t\tif(cJSON_IsObject(j_client) == true){\n\t\t\tconst char *username_json;\n\t\t\tif(json_get_string(j_client, \"username\", &username_json, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\tif(!strcmp(username_json, username)){\n\t\t\t\t\tif(iterations == -1){\n\t\t\t\t\t\tif(mosquitto_pw_new(&client.pw, MOSQ_PW_DEFAULT)){\n\t\t\t\t\t\t\tcJSON_Delete(j_tree);\n\t\t\t\t\t\t\tfprintf(stderr, \"Error: Problem generating password hash.\\n\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\tif(mosquitto_pw_new(&client.pw, MOSQ_PW_SHA512_PBKDF2)){\n\t\t\t\t\t\t\tcJSON_Delete(j_tree);\n\t\t\t\t\t\t\tfprintf(stderr, \"Error: Problem generating password hash.\\n\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmosquitto_pw_set_param(client.pw, MOSQ_PW_PARAM_ITERATIONS, iterations);\n\t\t\t\t\t}\n\t\t\t\t\tif(!client.pw || mosquitto_pw_hash_encoded(client.pw, password)){\n\t\t\t\t\t\tcJSON_Delete(j_tree);\n\t\t\t\t\t\tmosquitto_pw_cleanup(client.pw);\n\t\t\t\t\t\tclient.pw = NULL;\n\t\t\t\t\t\tfprintf(stderr, \"Error: Problem generating password hash.\\n\");\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\n\t\t\t\t\tcJSON *j_encoded_password = cJSON_CreateString(mosquitto_pw_get_encoded(client.pw));\n\t\t\t\t\tif(!j_encoded_password){\n\t\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\t\t\tcJSON_Delete(j_tree);\n\t\t\t\t\t\tmosquitto_pw_cleanup(client.pw);\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_pw_cleanup(client.pw);\n\n\t\t\t\t\tcJSON_DeleteItemFromObject(j_client, \"password\");\n\t\t\t\t\tcJSON_DeleteItemFromObject(j_client, \"salt\");\n\t\t\t\t\tcJSON_DeleteItemFromObject(j_client, \"iterations\");\n\t\t\t\t\tcJSON_DeleteItemFromObject(j_client, \"encoded_password\");\n\t\t\t\t\tcJSON_AddItemToObject(j_client, \"encoded_password\", j_encoded_password);\n\n\t\t\t\t\tjson_str = cJSON_PrintUnformatted(j_tree);\n\t\t\t\t\tcJSON_Delete(j_tree);\n\t\t\t\t\tif(json_str == NULL){\n\t\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tfptr = fopen(file, \"wb\");\n\t\t\t\t\tif(fptr == NULL){\n\t\t\t\t\t\tfprintf(stderr, \"Error: Unable to write to %s.\\n\", file);\n\t\t\t\t\t\tfree(json_str);\n\t\t\t\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t\t\t\t}\n\t\t\t\t\tfprintf(fptr, \"%s\", json_str);\n\t\t\t\t\tfree(json_str);\n\t\t\t\t\tfclose(fptr);\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfprintf(stderr, \"Error: Client %s not found.\\n\", username);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_client__set_password(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *username = NULL, *password = NULL;\n\tchar prompt[200], verify_prompt[200];\n\tchar password_buf[200];\n\tint rc;\n\n\tif(argc == 2){\n\t\tusername = argv[0];\n\t\tpassword = argv[1];\n\t}else if(argc == 1){\n\t\tusername = argv[0];\n\n\t\tsnprintf(prompt, sizeof(prompt), \"New password for %s: \", username);\n\t\tsnprintf(verify_prompt, sizeof(verify_prompt), \"Reenter password for %s: \", username);\n\t\trc = get_password(prompt, verify_prompt, false, password_buf, sizeof(password_buf));\n\t\tif(rc){\n\t\t\treturn -1;\n\t\t}\n\t\tpassword = password_buf;\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"setClientPassword\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"username\", username) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"password\", password) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_client__get(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *username = NULL;\n\n\tif(argc == 1){\n\t\tusername = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"getClient\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"username\", username) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_client__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command)\n{\n\tchar *username = NULL, *rolename = NULL;\n\tint priority = -1;\n\n\tif(argc == 2){\n\t\tusername = argv[0];\n\t\trolename = argv[1];\n\t}else if(argc == 3){\n\t\tusername = argv[0];\n\t\trolename = argv[1];\n\t\tpriority = atoi(argv[2]);\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", command) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"username\", username) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"rolename\", rolename) == NULL\n\t\t\t|| (priority != -1 && cJSON_AddIntToObject(j_command, \"priority\", priority) == NULL)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_client__list_all(int argc, char *argv[], cJSON *j_command)\n{\n\tint count = -1, offset = -1;\n\n\tif(argc == 0){\n\t\t/* All clients */\n\t}else if(argc == 1){\n\t\tcount = atoi(argv[0]);\n\t}else if(argc == 2){\n\t\tcount = atoi(argv[0]);\n\t\toffset = atoi(argv[1]);\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"listClients\") == NULL\n\t\t\t|| (count > 0 && cJSON_AddIntToObject(j_command, \"count\", count) == NULL)\n\t\t\t|| (offset > 0 && cJSON_AddIntToObject(j_command, \"offset\", offset) == NULL)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/dynsec_group.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n#include \"mosquitto_ctrl.h\"\n#include \"json_help.h\"\n\n\nint dynsec_group__create(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *groupname = NULL;\n\n\tif(argc == 1){\n\t\tgroupname = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"createGroup\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"groupname\", groupname) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_group__delete(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *groupname = NULL;\n\n\tif(argc == 1){\n\t\tgroupname = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"deleteGroup\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"groupname\", groupname) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_group__get_anonymous(int argc, char *argv[], cJSON *j_command)\n{\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"getAnonymousGroup\") == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_group__set_anonymous(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *groupname = NULL;\n\n\tif(argc == 1){\n\t\tgroupname = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"setAnonymousGroup\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"groupname\", groupname) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_group__get(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *groupname = NULL;\n\n\tif(argc == 1){\n\t\tgroupname = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"getGroup\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"groupname\", groupname) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_group__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command)\n{\n\tchar *groupname = NULL, *rolename = NULL;\n\tint priority = -1;\n\n\tif(argc == 2){\n\t\tgroupname = argv[0];\n\t\trolename = argv[1];\n\t}else if(argc == 3){\n\t\tgroupname = argv[0];\n\t\trolename = argv[1];\n\t\tpriority = atoi(argv[2]);\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", command) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"groupname\", groupname) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"rolename\", rolename) == NULL\n\t\t\t|| (priority != -1 && cJSON_AddIntToObject(j_command, \"priority\", priority) == NULL)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_group__list_all(int argc, char *argv[], cJSON *j_command)\n{\n\tint count = -1, offset = -1;\n\n\tif(argc == 0){\n\t\t/* All groups */\n\t}else if(argc == 1){\n\t\tcount = atoi(argv[0]);\n\t}else if(argc == 2){\n\t\tcount = atoi(argv[0]);\n\t\toffset = atoi(argv[1]);\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"listGroups\") == NULL\n\t\t\t|| (count > 0 && cJSON_AddIntToObject(j_command, \"count\", count) == NULL)\n\t\t\t|| (offset > 0 && cJSON_AddIntToObject(j_command, \"offset\", offset) == NULL)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_group__add_remove_client(int argc, char *argv[], cJSON *j_command, const char *command)\n{\n\tchar *username, *groupname;\n\tint priority = -1;\n\n\tif(argc == 2){\n\t\tgroupname = argv[0];\n\t\tusername = argv[1];\n\t}else if(argc == 3){\n\t\tgroupname = argv[0];\n\t\tusername = argv[1];\n\t\tpriority = atoi(argv[2]);\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", command) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"username\", username) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"groupname\", groupname) == NULL\n\t\t\t|| (priority != -1 && cJSON_AddIntToObject(j_command, \"priority\", priority) == NULL)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/dynsec_role.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifndef WIN32\n#  include <strings.h>\n#endif\n\n#include \"mosquitto.h\"\n#include \"mosquitto_ctrl.h\"\n#include \"json_help.h\"\n\n\nint dynsec_role__create(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *rolename = NULL;\n\n\tif(argc == 1){\n\t\trolename = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"createRole\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"rolename\", rolename) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_role__delete(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *rolename = NULL;\n\n\tif(argc == 1){\n\t\trolename = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"deleteRole\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"rolename\", rolename) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_role__get(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *rolename = NULL;\n\n\tif(argc == 1){\n\t\trolename = argv[0];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"getRole\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"rolename\", rolename) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_role__list_all(int argc, char *argv[], cJSON *j_command)\n{\n\tint count = -1, offset = -1;\n\n\tif(argc == 0){\n\t\t/* All roles */\n\t}else if(argc == 1){\n\t\tcount = atoi(argv[0]);\n\t}else if(argc == 2){\n\t\tcount = atoi(argv[0]);\n\t\toffset = atoi(argv[1]);\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"listRoles\") == NULL\n\t\t\t|| (count > 0 && cJSON_AddIntToObject(j_command, \"count\", count) == NULL)\n\t\t\t|| (offset > 0 && cJSON_AddIntToObject(j_command, \"offset\", offset) == NULL)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_role__add_acl(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *rolename, *acltype, *topic, *action;\n\tbool allow;\n\tint priority = -1;\n\n\tif(argc == 5){\n\t\trolename = argv[0];\n\t\tacltype = argv[1];\n\t\ttopic = argv[2];\n\t\taction = argv[3];\n\t\tpriority = atoi(argv[4]);\n\t}else if(argc == 4){\n\t\trolename = argv[0];\n\t\tacltype = argv[1];\n\t\ttopic = argv[2];\n\t\taction = argv[3];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(strcasecmp(acltype, \"publishClientSend\")\n\t\t\t&& strcasecmp(acltype, \"publishClientReceive\")\n\t\t\t&& strcasecmp(acltype, \"subscribeLiteral\")\n\t\t\t&& strcasecmp(acltype, \"subscribePattern\")\n\t\t\t&& strcasecmp(acltype, \"unsubscribeLiteral\")\n\t\t\t&& strcasecmp(acltype, \"unsubscribePattern\")){\n\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!strcasecmp(action, \"allow\")){\n\t\tallow = true;\n\t}else if(!strcasecmp(action, \"deny\")){\n\t\tallow = false;\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"addRoleACL\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"rolename\", rolename) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"acltype\", acltype) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"topic\", topic) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_command, \"allow\", allow) == NULL\n\t\t\t|| (priority != -1 && cJSON_AddIntToObject(j_command, \"priority\", priority) == NULL)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_role__remove_acl(int argc, char *argv[], cJSON *j_command)\n{\n\tchar *rolename, *acltype, *topic;\n\n\tif(argc == 3){\n\t\trolename = argv[0];\n\t\tacltype = argv[1];\n\t\ttopic = argv[2];\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(strcasecmp(acltype, \"publishClientSend\")\n\t\t\t&& strcasecmp(acltype, \"publishClientReceive\")\n\t\t\t&& strcasecmp(acltype, \"subscribeLiteral\")\n\t\t\t&& strcasecmp(acltype, \"subscribePattern\")\n\t\t\t&& strcasecmp(acltype, \"unsubscribeLiteral\")\n\t\t\t&& strcasecmp(acltype, \"unsubscribePattern\")){\n\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_command, \"command\", \"removeRoleACL\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"rolename\", rolename) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"acltype\", acltype) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_command, \"topic\", topic) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/example.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifndef WIN32\n#  include <strings.h>\n#endif\n\n#include \"mosquitto_ctrl.h\"\n\n\nvoid ctrl_help(void)\n{\n\tprintf(\"\\nExample module\\n\");\n\tprintf(\"==============\\n\");\n\tprintf(\"    mosquitto_ctrl example help\\n\");\n}\n\n\nint ctrl_main(int argc, char *argv[], struct mosq_ctrl *ctrl)\n{\n\tUNUSED(argc);\n\tUNUSED(ctrl);\n\n\tif(!strcasecmp(argv[0], \"help\")){\n\t\tctrl_help();\n\t\treturn -1;\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/mosquitto_ctrl.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifndef WIN32\n#  include <strings.h>\n#endif\n\n#include \"lib_load.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_ctrl.h\"\n#include \"ctrl_shell_internal.h\"\n\n\nstatic void print_version(void)\n{\n\tint major, minor, revision;\n\n\tmosquitto_lib_version(&major, &minor, &revision);\n\tprintf(\"mosquitto_ctrl version %s running on libmosquitto %d.%d.%d.\\n\", VERSION, major, minor, revision);\n}\n\n\nstatic void print_usage(void)\n{\n\tprintf(\"mosquitto_ctrl is a tool for administering certain Mosquitto features.\\n\");\n\tprint_version();\n\tprintf(\"\\nGeneral usage: mosquitto_ctrl <module> <module-command> <command-options>\\n\");\n\tprintf(\"For module specific help use: mosquitto_ctrl <module> help\\n\");\n\tprintf(\"\\nModules available: broker dynsec\\n\");\n\tprintf(\"\\nFor more information see:\\n\");\n\tprintf(\"    https://mosquitto.org/man/mosquitto_ctrl-1.html\\n\\n\");\n}\n\n\nint main(int argc, char *argv[])\n{\n\tstruct mosq_ctrl ctrl;\n\tint rc = MOSQ_ERR_SUCCESS;\n\tFUNC_ctrl_main l_ctrl_main = NULL;\n\tvoid *lib = NULL;\n\tchar lib_name[200];\n\n\tif(argc == 1){\n#ifdef WITH_CTRL_SHELL\n\t\tctrl_shell__main(NULL);\n#else\n\t\tprint_usage();\n#endif\n\t\treturn 0;\n\t}\n\n\tmemset(&ctrl, 0, sizeof(ctrl));\n\tinit_config(&ctrl.cfg);\n\n\t/* Shift program name out of args */\n\targc--;\n\targv++;\n\n\trc = ctrl_config_parse(&ctrl.cfg, &argc, &argv);\n\tif(rc){\n\t\tclient_config_cleanup(&ctrl.cfg);\n\t\tprint_usage();\n\t\treturn rc;\n\t}\n\n#ifdef WITH_CTRL_SHELL\n\tif(argc == 0){\n\t\tctrl_shell__main(&ctrl.cfg);\n\t\treturn 0;\n\t}else\n#endif\n\t{\n\t\tif(argc < 2){\n\t\t\tprint_usage();\n\t\t\tclient_config_cleanup(&ctrl.cfg);\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\t/* In built modules */\n\tif(!strcasecmp(argv[0], \"broker\")){\n\t\tl_ctrl_main = broker__main;\n\t}else if(!strcasecmp(argv[0], \"dynsec\")){\n\t\tl_ctrl_main = dynsec__main;\n\t}else{\n\t\t/* Attempt external module */\n\t\tsnprintf(lib_name, sizeof(lib_name), \"mosquitto_ctrl_%s.so\", argv[0]);\n\t\tlib = LIB_LOAD(lib_name);\n\t\tif(lib){\n\t\t\tl_ctrl_main = (FUNC_ctrl_main)LIB_SYM(lib, \"ctrl_main\");\n\t\t}\n\t}\n\tif(l_ctrl_main == NULL){\n\t\tfprintf(stderr, \"Error: Module '%s' not supported.\\n\", argv[0]);\n\t\trc = MOSQ_ERR_NOT_SUPPORTED;\n\t}\n\n\tif(l_ctrl_main){\n\t\trc = l_ctrl_main(argc-1, &argv[1], &ctrl);\n\t\tif(rc < 0){\n\t\t\t/* Usage print */\n\t\t\trc = 0;\n\t\t}else if(rc == MOSQ_ERR_SUCCESS){\n\t\t\tif(ctrl.cfg.data_file == NULL){\n\t\t\t\trc = client_request_response(&ctrl);\n\t\t\t}\n\t\t}else if(rc == MOSQ_ERR_UNKNOWN){\n\t\t\t/* Message printed already */\n\t\t}else{\n\t\t\tfprintf(stderr, \"Error: %s.\\n\", mosquitto_strerror(rc));\n\t\t}\n\t}\n\tfree(ctrl.payload);\n\tfree(ctrl.request_topic);\n\tfree(ctrl.response_topic);\n\n\tclient_config_cleanup(&ctrl.cfg);\n\treturn rc;\n}\n"
  },
  {
    "path": "apps/mosquitto_ctrl/mosquitto_ctrl.h",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef MOSQUITTO_CTRL_H\n#define MOSQUITTO_CTRL_H\n\n#include <cjson/cJSON.h>\n#ifndef __cplusplus\n#include <stdbool.h>\n#endif\n\n#include \"mosquitto.h\"\n\n#define PORT_UNDEFINED -1\n#define PORT_UNIX 0\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct mosq_config {\n\tchar *id;\n\tint protocol_version;\n\tint keepalive;\n\tchar *host;\n\tint port;\n\tint qos;\n\tchar *bind_address;\n\tbool debug;\n\tbool quiet;\n\tchar *username;\n\tchar *password;\n\tchar *options_file;\n\tchar *cafile;\n\tchar *capath;\n\tchar *certfile;\n\tchar *keyfile;\n\tchar *ciphers;\n\tbool insecure;\n\tchar *tls_alpn;\n\tchar *tls_version;\n\tchar *tls_engine;\n\tchar *tls_engine_kpass_sha1;\n\tbool tls_use_os_certs;\n\tchar *keyform;\n\tchar *psk;\n\tchar *psk_identity;\n\tbool verbose; /* sub */\n\tunsigned int timeout; /* sub */\n\tchar *socks5_host;\n\tint socks5_port;\n\tchar *socks5_username;\n\tchar *socks5_password;\n\tchar *data_file;\n\tbool no_colour;\n};\n\nstruct mosq_ctrl {\n\tstruct mosq_config cfg;\n\tchar *request_topic;\n\tchar *response_topic;\n\tchar *payload;\n\tvoid (*payload_callback)(struct mosq_ctrl *, long, const void *);\n\tvoid *userdata;\n};\n\ntypedef int (*FUNC_ctrl_main)(int argc, char *argv[], struct mosq_ctrl *ctrl);\n\nvoid init_config(struct mosq_config *cfg);\nint ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[]);\nint client_config_load(struct mosq_config *cfg);\nvoid client_config_cleanup(struct mosq_config *cfg);\n\nint client_request_response(struct mosq_ctrl *ctrl);\nint client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg);\nint client_connect(struct mosquitto *mosq, struct mosq_config *cfg);\n\nvoid broker__print_usage(void);\nint broker__main(int argc, char *argv[], struct mosq_ctrl *ctrl);\n\nvoid dynsec__print_usage(void);\nint dynsec__main(int argc, char *argv[], struct mosq_ctrl *ctrl);\n\nint dynsec_client__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command);\nint dynsec_client__create(int argc, char *argv[], cJSON *j_command);\nint dynsec_client__delete(int argc, char *argv[], cJSON *j_command);\nint dynsec_client__enable_disable(int argc, char *argv[], cJSON *j_command, const char *command);\nint dynsec_client__file_set_password(int argc, char *argv[], const char *file);\nint dynsec_client__get(int argc, char *argv[], cJSON *j_command);\nint dynsec_client__list_all(int argc, char *argv[], cJSON *j_command);\nint dynsec_client__set_id(int argc, char *argv[], cJSON *j_command);\nint dynsec_client__set_password(int argc, char *argv[], cJSON *j_command);\n\nint dynsec_group__add_remove_client(int argc, char *argv[], cJSON *j_command, const char *command);\nint dynsec_group__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command);\nint dynsec_group__create(int argc, char *argv[], cJSON *j_command);\nint dynsec_group__delete(int argc, char *argv[], cJSON *j_command);\nint dynsec_group__get(int argc, char *argv[], cJSON *j_command);\nint dynsec_group__list_all(int argc, char *argv[], cJSON *j_command);\nint dynsec_group__set_anonymous(int argc, char *argv[], cJSON *j_command);\nint dynsec_group__get_anonymous(int argc, char *argv[], cJSON *j_command);\n\nint dynsec_role__create(int argc, char *argv[], cJSON *j_command);\nint dynsec_role__delete(int argc, char *argv[], cJSON *j_command);\nint dynsec_role__get(int argc, char *argv[], cJSON *j_command);\nint dynsec_role__list_all(int argc, char *argv[], cJSON *j_command);\nint dynsec_role__add_acl(int argc, char *argv[], cJSON *j_command);\nint dynsec_role__remove_acl(int argc, char *argv[], cJSON *j_command);\n\n/* Functions to implement as an external module: */\nvoid ctrl_help(void);\nint ctrl_main(int argc, char *argv[], struct mosq_ctrl *ctrl);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/options.c",
    "content": "/*\nCopyright (c) 2014-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#ifndef WIN32\n#include <unistd.h>\n#include <strings.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#define snprintf sprintf_s\n#define strncasecmp _strnicmp\n#endif\n\n#include <mosquitto.h>\n#include \"mosquitto_ctrl.h\"\n#include \"get_password.h\"\n\n#ifdef WITH_SOCKS\nstatic int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url);\n#endif\nstatic int client_config_line_proc(struct mosq_config *cfg, int *argc, char **argvp[]);\n\n\nvoid init_config(struct mosq_config *cfg)\n{\n\tcfg->qos = 1;\n\tcfg->port = PORT_UNDEFINED;\n\tcfg->protocol_version = MQTT_PROTOCOL_V5;\n}\n\n\nvoid client_config_cleanup(struct mosq_config *cfg)\n{\n\tfree(cfg->id);\n\tfree(cfg->host);\n\tfree(cfg->bind_address);\n\tfree(cfg->username);\n\tfree(cfg->password);\n\tfree(cfg->options_file);\n#ifdef WITH_TLS\n\tfree(cfg->cafile);\n\tfree(cfg->capath);\n\tfree(cfg->certfile);\n\tfree(cfg->keyfile);\n\tfree(cfg->ciphers);\n\tfree(cfg->tls_alpn);\n\tfree(cfg->tls_version);\n\tfree(cfg->tls_engine);\n\tfree(cfg->tls_engine_kpass_sha1);\n\tfree(cfg->keyform);\n#  ifdef FINAL_WITH_TLS_PSK\n\tfree(cfg->psk);\n\tfree(cfg->psk_identity);\n#  endif\n#endif\n#ifdef WITH_SOCKS\n\tfree(cfg->socks5_host);\n\tfree(cfg->socks5_username);\n\tfree(cfg->socks5_password);\n#endif\n\tfree(cfg->data_file);\n}\n\n\nint ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[])\n{\n\tint rc;\n\n\tinit_config(cfg);\n\n\t/* Deal with real argc/argv */\n\trc = client_config_line_proc(cfg, argc, argv);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\t/* Load options from config file - this must be after `-o` has been processed */\n\trc = client_config_load(cfg);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n#ifdef WITH_TLS\n\tif((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){\n\t\tfprintf(stderr, \"Error: Both certfile and keyfile must be provided if one of them is set.\\n\");\n\t\treturn 1;\n\t}\n\tif((cfg->keyform && !cfg->keyfile)){\n\t\tfprintf(stderr, \"Error: If keyform is set, keyfile must be also specified.\\n\");\n\t\treturn 1;\n\t}\n\tif((cfg->tls_engine_kpass_sha1 && (!cfg->keyform || !cfg->tls_engine))){\n\t\tfprintf(stderr, \"Error: when using tls-engine-kpass-sha1, both tls-engine and keyform must also be provided.\\n\");\n\t\treturn 1;\n\t}\n#endif\n#ifdef FINAL_WITH_TLS_PSK\n\tif((cfg->cafile || cfg->capath) && cfg->psk){\n\t\tfprintf(stderr, \"Error: Only one of --psk or --cafile/--capath may be used at once.\\n\");\n\t\treturn 1;\n\t}\n\tif(cfg->psk && !cfg->psk_identity){\n\t\tfprintf(stderr, \"Error: --psk-identity required if --psk used.\\n\");\n\t\treturn 1;\n\t}\n#endif\n\n\tif(!cfg->host){\n\t\tcfg->host = strdup(\"localhost\");\n\t\tif(!cfg->host){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* Process a tokenised single line from a file or set of real argc/argv */\nstatic int client_config_line_proc(struct mosq_config *cfg, int *argc, char **argvp[])\n{\n\tchar **argv = *argvp;\n\n\twhile((*argc) && argv[0][0] == '-'){\n\t\tif(!strcmp(argv[0], \"-A\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: -A argument given but no address specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->bind_address = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n#ifdef WITH_TLS\n\t\t}else if(!strcmp(argv[0], \"--cafile\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --cafile argument given but no file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->cafile = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--capath\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --capath argument given but no directory specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->capath = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--cert\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --cert argument given but no file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->certfile = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--ciphers\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --ciphers argument given but no ciphers specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->ciphers = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n#endif\n\t\t}else if(!strcmp(argv[0], \"-d\") || !strcmp(argv[0], \"--debug\")){\n\t\t\tcfg->debug = true;\n\t\t}else if(!strcmp(argv[0], \"-f\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: -f argument given but no data file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->data_file = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--help\")){\n\t\t\treturn 1;\n\t\t}else if(!strcmp(argv[0], \"-h\") || !strcmp(argv[0], \"--host\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: -h argument given but no host specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->host = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n#ifdef WITH_TLS\n\t\t}else if(!strcmp(argv[0], \"--insecure\")){\n\t\t\tcfg->insecure = true;\n#endif\n\t\t}else if(!strcmp(argv[0], \"-i\") || !strcmp(argv[0], \"--id\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: -i argument given but no id specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->id = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n#ifdef WITH_TLS\n\t\t}else if(!strcmp(argv[0], \"--key\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --key argument given but no file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->keyfile = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--keyform\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --keyform argument given but no keyform specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->keyform = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n#endif\n\t\t}else if(!strcmp(argv[0], \"-L\") || !strcmp(argv[0], \"--url\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: -L argument given but no URL specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tchar *url = argv[1];\n\t\t\t\tchar *topic;\n\t\t\t\tchar *tmp;\n\n\t\t\t\tif(!strncasecmp(url, \"mqtt://\", 7)){\n\t\t\t\t\turl += 7;\n\t\t\t\t\tcfg->port = 1883;\n\t\t\t\t}else if(!strncasecmp(url, \"mqtts://\", 8)){\n\t\t\t\t\turl += 8;\n\t\t\t\t\tcfg->port = 8883;\n\t\t\t\t\tcfg->tls_use_os_certs = true;\n\t\t\t\t}else{\n\t\t\t\t\tfprintf(stderr, \"Error: Unsupported URL scheme.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\ttopic = strchr(url, '/');\n\t\t\t\tif(!topic){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid URL for -L argument specified - topic missing.\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\t*topic++ = 0;\n\n\t\t\t\ttmp = strchr(url, '@');\n\t\t\t\tif(tmp){\n\t\t\t\t\t*tmp++ = 0;\n\t\t\t\t\tchar *colon = strchr(url, ':');\n\t\t\t\t\tif(colon){\n\t\t\t\t\t\t*colon = 0;\n\t\t\t\t\t\tcfg->password = strdup(colon + 1);\n\t\t\t\t\t}\n\t\t\t\t\tif(strlen(url) == 0){\n\t\t\t\t\t\tfprintf(stderr, \"Error: Empty username in URL.\\n\");\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\tcfg->username = strdup(url);\n\t\t\t\t\turl = tmp;\n\t\t\t\t}\n\t\t\t\tcfg->host = url;\n\n\t\t\t\ttmp = strchr(url, ':');\n\t\t\t\tif(tmp){\n\t\t\t\t\t*tmp++ = 0;\n\t\t\t\t\tif(strlen(tmp) == 0){\n\t\t\t\t\t\tcfg->host = NULL; /* Prevent free of non-heap memory later */\n\t\t\t\t\t\tfprintf(stderr, \"Error: Empty port in URL.\\n\");\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\tcfg->port = atoi(tmp);\n\t\t\t\t}\n\t\t\t\t/* Now we've removed the port, time to get the host on the heap */\n\t\t\t\tcfg->host = strdup(cfg->host);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"-o\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: -o argument given but no options file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->options_file = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"-p\") || !strcmp(argv[0], \"--port\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: -p argument given but no port specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->port = atoi(argv[1]);\n\t\t\t\tif(cfg->port<0 || cfg->port>65535){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid port given: %d\\n\", cfg->port);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"-P\") || !strcmp(argv[0], \"--pw\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: -P argument given but no password specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->password = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n#ifdef WITH_SOCKS\n\t\t}else if(!strcmp(argv[0], \"--proxy\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --proxy argument given but no proxy url specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(mosquitto__parse_socks_url(cfg, argv[1])){\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n#endif\n#ifdef FINAL_WITH_TLS_PSK\n\t\t}else if(!strcmp(argv[0], \"--psk\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --psk argument given but no key specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->psk = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--psk-identity\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --psk-identity argument given but no identity specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->psk_identity = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n#endif\n\t\t}else if(!strcmp(argv[0], \"-q\") || !strcmp(argv[0], \"--qos\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: -q argument given but no QoS specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->qos = atoi(argv[1]);\n\t\t\t\tif(cfg->qos<0 || cfg->qos>2){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid QoS given: %d\\n\", cfg->qos);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--quiet\")){\n\t\t\tcfg->quiet = true;\n#ifdef WITH_TLS\n\t\t}else if(!strcmp(argv[0], \"--tls-alpn\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --tls-alpn argument given but no protocol specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->tls_alpn = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--tls-engine\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --tls-engine argument given but no engine_id specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->tls_engine = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--tls-engine-kpass-sha1\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->tls_engine_kpass_sha1 = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--tls-use-os-certs\")){\n\t\t\tcfg->tls_use_os_certs = true;\n\t\t}else if(!strcmp(argv[0], \"--tls-version\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --tls-version argument given but no version specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->tls_version = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n#endif\n\t\t}else if(!strcmp(argv[0], \"-u\") || !strcmp(argv[0], \"--username\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: -u argument given but no username specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->username = strdup(argv[1]);\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"--unix\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --unix argument given but no socket path specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->host = strdup(argv[1]);\n\t\t\t\tcfg->port = 0;\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"-V\") || !strcmp(argv[0], \"--protocol-version\")){\n\t\t\tif((*argc) == 1){\n\t\t\t\tfprintf(stderr, \"Error: --protocol-version argument given but no version specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(!strcmp(argv[1], \"mqttv31\") || !strcmp(argv[1], \"31\")){\n\t\t\t\t\tcfg->protocol_version = MQTT_PROTOCOL_V31;\n\t\t\t\t}else if(!strcmp(argv[1], \"mqttv311\") || !strcmp(argv[1], \"311\")){\n\t\t\t\t\tcfg->protocol_version = MQTT_PROTOCOL_V311;\n\t\t\t\t}else if(!strcmp(argv[1], \"mqttv5\") || !strcmp(argv[1], \"5\")){\n\t\t\t\t\tcfg->protocol_version = MQTT_PROTOCOL_V5;\n\t\t\t\t}else{\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid protocol version argument given.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\targv++;\n\t\t\t(*argc)--;\n\t\t}else if(!strcmp(argv[0], \"-v\") || !strcmp(argv[0], \"--verbose\")){\n\t\t\tcfg->verbose = 1;\n\t\t}else if(!strcmp(argv[0], \"--version\")){\n\t\t\treturn 1;\n\t\t}else{\n\t\t\tgoto unknown_option;\n\t\t}\n\t\targv++;\n\t\t(*argc)--;\n\t}\n\t*argvp = argv;\n\n\treturn MOSQ_ERR_SUCCESS;\n\nunknown_option:\n\tfprintf(stderr, \"Error: Unknown option '%s'.\\n\", argv[0]);\n\treturn 1;\n}\n\n\nstatic char *get_default_cfg_location(void)\n{\n\tchar *loc = NULL;\n\tsize_t len;\n#ifndef WIN32\n\tchar *env;\n#else\n\tchar env[1024];\n\tint rc;\n#endif\n\n#ifndef WIN32\n\tenv = getenv(\"XDG_CONFIG_HOME\");\n\tif(env){\n\t\tlen = strlen(env) + strlen(\"/mosquitto_ctrl\") + 1;\n\t\tloc = malloc(len);\n\t\tif(!loc){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t\tsnprintf(loc, len, \"%s/mosquitto_ctrl\", env);\n\t\tloc[len-1] = '\\0';\n\t}else{\n\t\tenv = getenv(\"HOME\");\n\t\tif(env){\n\t\t\tlen = strlen(env) + strlen(\"/.config/mosquitto_ctrl\") + 1;\n\t\t\tloc = malloc(len);\n\t\t\tif(!loc){\n\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tsnprintf(loc, len, \"%s/.config/mosquitto_ctrl\", env);\n\t\t\tloc[len-1] = '\\0';\n\t\t}\n\t}\n\n#else\n\trc = GetEnvironmentVariable(\"USERPROFILE\", env, 1024);\n\tif(rc > 0 && rc < 1024){\n\t\tlen = strlen(env) + strlen(\"\\\\mosquitto_ctrl.conf\") + 1;\n\t\tloc = malloc(len);\n\t\tif(!loc){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t\tsnprintf(loc, len, \"%s\\\\mosquitto_ctrl.conf\", env);\n\t\tloc[len-1] = '\\0';\n\t}\n#endif\n\treturn loc;\n}\n\n\nint client_config_load(struct mosq_config *cfg)\n{\n\tint rc;\n\tFILE *fptr = NULL;\n\tchar line[1024];\n\tint count;\n\tchar **local_args, **args;\n\tchar *default_cfg;\n\n\tif(cfg->options_file){\n\t\tfptr = fopen(cfg->options_file, \"rt\");\n\t}else{\n\t\tdefault_cfg = get_default_cfg_location();\n\t\tif(default_cfg){\n\t\t\tfptr = fopen(default_cfg, \"rt\");\n\t\t\tfree(default_cfg);\n\t\t}\n\t}\n\n\tif(fptr){\n\t\tlocal_args = malloc(3*sizeof(char *));\n\t\tif(local_args == NULL){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\tfclose(fptr);\n\t\t\treturn 1;\n\t\t}\n\t\twhile(fgets(line, sizeof(line), fptr)){\n\t\t\tif(line[0] == '#'){\n\t\t\t\t/* Comments */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twhile(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){\n\t\t\t\tline[strlen(line)-1] = 0;\n\t\t\t}\n\t\t\tlocal_args[0] = strtok(line, \" \");\n\t\t\tif(local_args[0]){\n\t\t\t\tlocal_args[1] = strtok(NULL, \" \");\n\t\t\t\tif(local_args[1]){\n\t\t\t\t\tcount = 2;\n\t\t\t\t}else{\n\t\t\t\t\tcount = 1;\n\t\t\t\t}\n\t\t\t\targs = local_args;\n\t\t\t\trc = client_config_line_proc(cfg, &count, &args);\n\t\t\t\tif(rc){\n\t\t\t\t\tfclose(fptr);\n\t\t\t\t\tfree(local_args);\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfclose(fptr);\n\t\tfree(local_args);\n\t}\n\treturn 0;\n}\n\n\nint client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)\n{\n\tint rc;\n\tchar prompt[1000];\n\tchar password[1000];\n\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, cfg->protocol_version);\n\n\tif(cfg->username && cfg->password == NULL){\n\t\t/* Ask for password */\n\t\tsnprintf(prompt, sizeof(prompt), \"Password for %s: \", cfg->username);\n\t\trc = get_password(prompt, NULL, false, password, sizeof(password));\n\t\tif(rc){\n\t\t\tfprintf(stderr, \"Error getting password.\\n\");\n\t\t\treturn 1;\n\t\t}\n\t\tcfg->password = strdup(password);\n\t\tif(cfg->password == NULL){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tif((cfg->username || cfg->password) && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){\n\t\tfprintf(stderr, \"Error: Problem setting username and/or password.\\n\");\n\t\treturn 1;\n\t}\n#ifdef WITH_TLS\n\tif(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){\n\t\tfprintf(stderr, \"Error: Problem setting key form, it must be one of 'pem' or 'engine'.\\n\");\n\t\treturn 1;\n\t}\n\tif(cfg->cafile || cfg->capath){\n\t\trc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL);\n\t\tif(rc){\n\t\t\tif(rc == MOSQ_ERR_INVAL){\n\t\t\t\tfprintf(stderr, \"Error: Problem setting TLS options: File not found.\\n\");\n\t\t\t}else{\n\t\t\t\tfprintf(stderr, \"Error: Problem setting TLS options: %s.\\n\", mosquitto_strerror(rc));\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n#  ifdef FINAL_WITH_TLS_PSK\n\t}else if(cfg->psk){\n\t\tif(mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){\n\t\t\tfprintf(stderr, \"Error: Problem setting TLS-PSK options.\\n\");\n\t\t\tmosquitto_lib_cleanup();\n\t\t\treturn 1;\n\t\t}\n#  endif\n\t}else if(cfg->port == 8883){\n\t\tmosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);\n\t}\n\tif(cfg->tls_use_os_certs){\n\t\tmosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);\n\t}\n\n\tmosquitto_tls_insecure_set(mosq, cfg->insecure);\n\tif(cfg->tls_engine && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE, cfg->tls_engine)){\n\t\tfprintf(stderr, \"Error: Problem setting TLS engine, is %s a valid engine?\\n\", cfg->tls_engine);\n\t\treturn 1;\n\t}\n\tif(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){\n\t\tfprintf(stderr, \"Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\\n\");\n\t\treturn 1;\n\t}\n\tif(cfg->tls_alpn && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ALPN, cfg->tls_alpn)){\n\t\tfprintf(stderr, \"Error: Problem setting TLS ALPN protocol.\\n\");\n\t\treturn 1;\n\t}\n\tif((cfg->tls_version || cfg->ciphers) && mosquitto_tls_opts_set(mosq, 1, cfg->tls_version, cfg->ciphers)){\n\t\tfprintf(stderr, \"Error: Problem setting TLS options, check the options are valid.\\n\");\n\t\treturn 1;\n\t}\n#endif\n#ifdef WITH_SOCKS\n\tif(cfg->socks5_host){\n\t\trc = mosquitto_socks5_set(mosq, cfg->socks5_host, cfg->socks5_port, cfg->socks5_username, cfg->socks5_password);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint client_connect(struct mosquitto *mosq, struct mosq_config *cfg)\n{\n#ifndef WIN32\n\tchar *err;\n#else\n\tchar err[1024];\n#endif\n\tint rc;\n\tint port;\n\n\tif(cfg->port == PORT_UNDEFINED){\n#ifdef WITH_TLS\n\t\tif(cfg->cafile || cfg->capath\n#  ifdef FINAL_WITH_TLS_PSK\n\t\t\t\t|| cfg->psk\n#  endif\n\t\t\t\t){\n\t\t\tport = 8883;\n\t\t}else\n#endif\n\t\t{\n\t\t\tport = 1883;\n\t\t}\n\t}else{\n\t\tport = cfg->port;\n\t}\n\n\trc = mosquitto_connect_bind_v5(mosq, cfg->host, port, 60, cfg->bind_address, NULL);\n\tif(rc>0){\n\t\tif(rc == MOSQ_ERR_ERRNO){\n#ifndef WIN32\n\t\t\terr = strerror(errno);\n#else\n\t\t\tFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL);\n#endif\n\t\t\tfprintf(stderr, \"Error: %s.\\n\", err);\n\t\t}else{\n\t\t\tfprintf(stderr, \"Unable to connect (%s).\\n\", mosquitto_strerror(rc));\n\t\t}\n\t\treturn rc;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n#ifdef WITH_SOCKS\n\n\n/* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */\nstatic int mosquitto__urldecode(char *str)\n{\n\tsize_t i, j;\n\tsize_t len;\n\tif(!str){\n\t\treturn 0;\n\t}\n\n\tif(!strchr(str, '%')){\n\t\treturn 0;\n\t}\n\n\tlen = strlen(str);\n\tfor(i=0; i<len; i++){\n\t\tif(str[i] == '%'){\n\t\t\tif(i+2 >= len){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(str[i+1] == '2' && str[i+2] == '5'){\n\t\t\t\tstr[i] = '%';\n\t\t\t\tlen -= 2;\n\t\t\t\tfor(j=i+1; j<len; j++){\n\t\t\t\t\tstr[j] = str[j+2];\n\t\t\t\t}\n\t\t\t\tstr[j] = '\\0';\n\t\t\t}else if(str[i+1] == '3' && (str[i+2] == 'A' || str[i+2] == 'a')){\n\t\t\t\tstr[i] = ':';\n\t\t\t\tlen -= 2;\n\t\t\t\tfor(j=i+1; j<len; j++){\n\t\t\t\t\tstr[j] = str[j+2];\n\t\t\t\t}\n\t\t\t\tstr[j] = '\\0';\n\t\t\t}else if(str[i+1] == '4' && str[i+2] == '0'){\n\t\t\t\tstr[i] = ':';\n\t\t\t\tlen -= 2;\n\t\t\t\tfor(j=i+1; j<len; j++){\n\t\t\t\t\tstr[j] = str[j+2];\n\t\t\t\t}\n\t\t\t\tstr[j] = '\\0';\n\t\t\t}else{\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t}\n\treturn 0;\n}\n\n\nstatic int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url)\n{\n\tchar *str;\n\tsize_t i;\n\tchar *username = NULL, *password = NULL, *host = NULL, *port = NULL;\n\tchar *username_or_host = NULL;\n\tsize_t start;\n\tsize_t len;\n\tbool have_auth = false;\n\tint port_int;\n\n\tif(!strncmp(url, \"socks5h://\", strlen(\"socks5h://\"))){\n\t\tstr = url + strlen(\"socks5h://\");\n\t}else{\n\t\tfprintf(stderr, \"Error: Unsupported proxy protocol: %s\\n\", url);\n\t\treturn 1;\n\t}\n\n\t// socks5h://username:password@host:1883\n\t// socks5h://username:password@host\n\t// socks5h://username@host:1883\n\t// socks5h://username@host\n\t// socks5h://host:1883\n\t// socks5h://host\n\n\tstart = 0;\n\tfor(i=0; i<strlen(str); i++){\n\t\tif(str[i] == ':'){\n\t\t\tif(i == start){\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tif(have_auth){\n\t\t\t\t/* Have already seen a @ , so this must be of form\n\t\t\t\t * socks5h://username[:password]@host:port */\n\t\t\t\tif(host){\n\t\t\t\t\t/* Already seen a host, must be malformed. */\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tlen = i-start;\n\t\t\t\thost = malloc(len + 1);\n\t\t\t\tif(!host){\n\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tmemcpy(host, &(str[start]), len);\n\t\t\t\thost[len] = '\\0';\n\t\t\t\tstart = i+1;\n\t\t\t}else if(!username_or_host){\n\t\t\t\t/* Haven't seen a @ before, so must be of form\n\t\t\t\t * socks5h://host:port or\n\t\t\t\t * socks5h://username:password@host[:port] */\n\t\t\t\tlen = i-start;\n\t\t\t\tusername_or_host = malloc(len + 1);\n\t\t\t\tif(!username_or_host){\n\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tmemcpy(username_or_host, &(str[start]), len);\n\t\t\t\tusername_or_host[len] = '\\0';\n\t\t\t\tstart = i+1;\n\t\t\t}\n\t\t}else if(str[i] == '@'){\n\t\t\tif(i == start){\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\thave_auth = true;\n\t\t\tif(username_or_host){\n\t\t\t\t/* Must be of form socks5h://username:password@... */\n\t\t\t\tusername = username_or_host;\n\t\t\t\tusername_or_host = NULL;\n\n\t\t\t\tlen = i-start;\n\t\t\t\tpassword = malloc(len + 1);\n\t\t\t\tif(!password){\n\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tmemcpy(password, &(str[start]), len);\n\t\t\t\tpassword[len] = '\\0';\n\t\t\t\tstart = i+1;\n\t\t\t}else{\n\t\t\t\t/* Haven't seen a : yet, so must be of form\n\t\t\t\t * socks5h://username@... */\n\t\t\t\tif(username){\n\t\t\t\t\t/* Already got a username, must be malformed. */\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tlen = i-start;\n\t\t\t\tusername = malloc(len + 1);\n\t\t\t\tif(!username){\n\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tmemcpy(username, &(str[start]), len);\n\t\t\t\tusername[len] = '\\0';\n\t\t\t\tstart = i+1;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Deal with remainder */\n\tif(i > start){\n\t\tlen = i-start;\n\t\tif(host){\n\t\t\t/* Have already seen a @ , so this must be of form\n\t\t\t * socks5h://username[:password]@host:port */\n\t\t\tport = malloc(len + 1);\n\t\t\tif(!port){\n\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tmemcpy(port, &(str[start]), len);\n\t\t\tport[len] = '\\0';\n\t\t}else if(username_or_host){\n\t\t\t/* Haven't seen a @ before, so must be of form\n\t\t\t * socks5h://host:port */\n\t\t\thost = username_or_host;\n\t\t\tusername_or_host = NULL;\n\t\t\tport = malloc(len + 1);\n\t\t\tif(!port){\n\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tmemcpy(port, &(str[start]), len);\n\t\t\tport[len] = '\\0';\n\t\t}else{\n\t\t\thost = malloc(len + 1);\n\t\t\tif(!host){\n\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tmemcpy(host, &(str[start]), len);\n\t\t\thost[len] = '\\0';\n\t\t}\n\t}\n\n\tif(!host){\n\t\tfprintf(stderr, \"Error: Invalid proxy.\\n\");\n\t\tgoto cleanup;\n\t}\n\n\tif(mosquitto__urldecode(username)){\n\t\tfprintf(stderr, \"Error: Invalid URL encoding in username.\\n\");\n\t\tgoto cleanup;\n\t}\n\tif(mosquitto__urldecode(password)){\n\t\tfprintf(stderr, \"Error: Invalid URL encoding in password.\\n\");\n\t\tgoto cleanup;\n\t}\n\tif(port){\n\t\tport_int = atoi(port);\n\t\tif(port_int < 1 || port_int > 65535){\n\t\t\tfprintf(stderr, \"Error: Invalid proxy port %d\\n\", port_int);\n\t\t\tgoto cleanup;\n\t\t}\n\t\tfree(port);\n\t}else{\n\t\tport_int = 1080;\n\t}\n\n\tcfg->socks5_username = username;\n\tcfg->socks5_password = password;\n\tcfg->socks5_host = host;\n\tcfg->socks5_port = port_int;\n\n\treturn 0;\ncleanup:\n\tfree(username_or_host);\n\tfree(username);\n\tfree(password);\n\tfree(host);\n\tfree(port);\n\treturn 1;\n}\n#endif\n"
  },
  {
    "path": "apps/mosquitto_ctrl/test/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all check test-compile test test-broker test-lib clean coverage\n\nLOCAL_CPPFLAGS+= \\\n\t-DWITH_THREADING \\\n\t-DWITH_TLS \\\n\t-I../ \\\n\t-I${R}/include \\\n\t-I${R} \\\n\t-I${R}/lib \\\n\t-I${R}/lib/mock \\\n\t-I${R}/test/mock\n\nLOCAL_CXXFLAGS+=-std=c++20 -Wall -ggdb -D TEST_SOURCE_DIR='\"$(realpath .)\"'\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=-lcjson -lgmock -lgtest_main -lgtest ${R}/libcommon/libmosquitto_common.a\n\nOBJS = \\\n\t../ctrl_shell_broker.o \\\n\t../ctrl_shell.o \\\n\t../ctrl_shell_client.o \\\n\t../ctrl_shell_completion_tree.o \\\n\t../ctrl_shell_dynsec.o \\\n\t../ctrl_shell_ha.o \\\n\t../ctrl_shell_inspect.o \\\n\t../ctrl_shell_license.o \\\n\t../ctrl_shell_post_connect.o \\\n\t../ctrl_shell_pre_connect.o \\\n\t../ctrl_shell_printf.o \\\n\t../ctrl_shell_topictree.o\n\nLIBMOSQ_MOCKS = \\\n\t${R}/lib/mock/libmosquitto_mock.o \\\n\t${R}/lib/mock/actions_publish_mock.o \\\n\t${R}/lib/mock/actions_subscribe_mock.o \\\n\t${R}/lib/mock/callbacks_mock.o \\\n\t${R}/lib/mock/connect_mock.o \\\n\t${R}/lib/mock/loop_mock.o \\\n\t${R}/lib/mock/options_mock.o \\\n\t${R}/lib/mock/thread_mosq_mock.o\n\nLIB_OBJS = \\\n\t${OBJS} \\\n\tjson_help.o \\\n\tctrl_shell_mock.o \\\n\t${R}/test/mock/editline_mock.o \\\n\t${R}/test/mock/pthread_mock.o \\\n\t${LIBMOSQ_MOCKS}\n\nTEST_OBJS = \\\n\tctrl_shell_broker_test.o \\\n\tctrl_shell_dynsec_test.o \\\n\tctrl_shell_help_test.o \\\n\tctrl_shell_pre_connect_test.o\n\nALL_TESTS = \\\n\tctrl_shell_broker_test \\\n\tctrl_shell_dynsec_test \\\n\tctrl_shell_help_test \\\n\tctrl_shell_pre_connect_test\n\nall : test-compile\n\ncheck : test\n\n# DEPS\n\n${LIBMOSQ_MOCKS}:\n\t$(MAKE) -C ${R}/lib\n\n${OBJS} :\n\t$(MAKE) -C ../\n\njson_help.o : ${R}/common/json_help.c ${R}/common/json_help.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n# MOCKS\n\nctrl_shell_mock.o : ctrl_shell_mock.cpp ctrl_shell_mock.hpp\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_CXXFLAGS) -c $< -o $@\n\n${R}/test/mock/editline_mock.o : ${R}/test/mock/editline_mock.cpp ${R}/test/mock/editline_mock.hpp\n\t$(MAKE) -C ${R}/test/mock test-compile\n\n${R}/test/mock/pthread_mock.o : ${R}/test/mock/pthread_mock.cpp ${R}/test/mock/pthread_mock.hpp\n\t$(MAKE) -C ${R}/test/mock test-compile\n\n# TESTS\n\n${TEST_OBJS} : %.o: %.cpp\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_CXXFLAGS) -c $< -o $@\n\nctrl_shell_broker_test : ctrl_shell_broker_test.o ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\nctrl_shell_dynsec_test : ctrl_shell_dynsec_test.o ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\nctrl_shell_help_test : ctrl_shell_help_test.o ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\nctrl_shell_pre_connect_test : ctrl_shell_pre_connect_test.o ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\n\ntest-compile : $(ALL_TESTS)\n\ntest : test-compile\n\t./ctrl_shell_help_test\n\t./ctrl_shell_dynsec_test\n\nclean :\n\t-rm -rf $(ALL_TESTS)\n\t-rm -rf *.o *.gcda *.gcno coverage.info out/\n\ncoverage :\n\tlcov --capture --directory . --output-file coverage.info\n\tgenhtml coverage.info --output-directory out\n\ninstall:\n\nuninstall:\n"
  },
  {
    "path": "apps/mosquitto_passwd/CMakeLists.txt",
    "content": "if(WITH_TLS)\n\tadd_executable(mosquitto_passwd\n\t\tmosquitto_passwd.c\n\t\tget_password.c get_password.h\n\t)\n\n\ttarget_include_directories(mosquitto_passwd PRIVATE\n\t\t\"${mosquitto_SOURCE_DIR}\"\n\t\t\"${mosquitto_SOURCE_DIR}/common\"\n\t\t\"${mosquitto_SOURCE_DIR}/include\"\n\t\t\"${mosquitto_SOURCE_DIR}/lib\"\n\t\t\"${mosquitto_SOURCE_DIR}/src\"\n\t)\n\n    target_link_libraries(mosquitto_passwd\n\t  PRIVATE\n\t  \tcommon-options\n\t\tlibmosquitto_common\n\t\tOpenSSL::SSL\n\t)\n\n\tinstall(TARGETS mosquitto_passwd\n\t\tRUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n\t)\nendif()\n"
  },
  {
    "path": "apps/mosquitto_passwd/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=-I${R}/lib\nLOCAL_LDFLAGS+=\nLOCAL_LDADD+=-lcrypto ${LIBMOSQ_COMMON}\n\n.PHONY: all install uninstall clean reallyclean\n\nOBJS= \\\n\tmosquitto_passwd.o \\\n\tget_password.o \\\n\nOBJS_EXTERNAL=\n\n\nifeq ($(WITH_TLS),yes)\nifeq ($(WITH_FUZZING),yes)\nall : mosquitto_passwd.a\nelse\nall : mosquitto_passwd\nendif\nelse\nall:\nendif\n\nmosquitto_passwd : ${OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}${CC} ${LOCAL_LDFLAGS} $^ -o $@ $(LOCAL_LDADD)\n\nmosquitto_passwd.a : ${OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}$(AR) cr $@ $^\n\n${OBJS} : %.o: %.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\ninstall : all\nifeq ($(WITH_TLS),yes)\n\t$(INSTALL) -d \"${DESTDIR}$(prefix)/bin\"\n\t$(INSTALL) ${STRIP_OPTS} mosquitto_passwd \"${DESTDIR}${prefix}/bin/mosquitto_passwd\"\nendif\n\nuninstall :\n\t-rm -f \"${DESTDIR}${prefix}/bin/mosquitto_passwd\"\n\nclean :\n\t-rm -f *.o *.a mosquitto_passwd *.gcda *.gcno\n\nreallyclean : clean\n\t-rm -rf *.orig *.db\n"
  },
  {
    "path": "apps/mosquitto_passwd/get_password.c",
    "content": "/*\nCopyright (c) 2012-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef WIN32\n#  include <windows.h>\n#  include <process.h>\n#   define snprintf sprintf_s\n#   include <io.h>\n#   include <windows.h>\n#else\n#  include <unistd.h>\n#  include <termios.h>\n#  include <sys/stat.h>\n#endif\n\n#include \"get_password.h\"\n\n#define MAX_BUFFER_LEN 65500\n\n\nvoid get_password__reset_term(void)\n{\n#ifndef WIN32\n\tstruct termios ts;\n\n\ttcgetattr(0, &ts);\n\tts.c_lflag |= ECHO | ICANON;\n\ttcsetattr(0, TCSANOW, &ts);\n#endif\n}\n\n\nstatic int gets_quiet(char *s, int len)\n{\n#ifdef WIN32\n\tHANDLE h;\n\tDWORD con_orig, con_quiet = 0;\n\tDWORD read_len = 0;\n\n\tmemset(s, 0, len);\n\th  = GetStdHandle(STD_INPUT_HANDLE);\n\tGetConsoleMode(h, &con_orig);\n\tcon_quiet = con_orig;\n\tcon_quiet &= ~ENABLE_ECHO_INPUT;\n\tcon_quiet |= ENABLE_LINE_INPUT;\n\tSetConsoleMode(h, con_quiet);\n\tif(!ReadConsole(h, s, len, &read_len, NULL)){\n\t\tSetConsoleMode(h, con_orig);\n\t\treturn 1;\n\t}\n\twhile(s[strlen(s)-1] == 10 || s[strlen(s)-1] == 13){\n\t\ts[strlen(s)-1] = 0;\n\t}\n\tif(strlen(s) == 0){\n\t\treturn 1;\n\t}\n\tSetConsoleMode(h, con_orig);\n\n\treturn 0;\n#else\n\tstruct termios ts_quiet;\n\tchar *rs;\n\n\tmemset(s, 0, (size_t)len);\n\ttcgetattr(0, &ts_quiet);\n\tts_quiet.c_lflag &= (unsigned int)(~(ECHO | ICANON));\n\ttcsetattr(0, TCSANOW, &ts_quiet);\n\n\trs = fgets(s, len, stdin);\n\tget_password__reset_term();\n\n\tif(!rs){\n\t\treturn 1;\n\t}else{\n\t\twhile(strlen(s) > 0 && (s[strlen(s)-1] == 10 || s[strlen(s)-1] == 13)){\n\t\t\ts[strlen(s)-1] = 0;\n\t\t}\n\t\tif(strlen(s) == 0){\n\t\t\treturn 1;\n\t\t}\n\t}\n\treturn 0;\n#endif\n}\n\n\nint get_password(const char *prompt, const char *verify_prompt, bool quiet, char *password, size_t len)\n{\n\tchar pw1[MAX_BUFFER_LEN], pw2[MAX_BUFFER_LEN];\n\tsize_t minLen;\n\tminLen = len < MAX_BUFFER_LEN ? len : MAX_BUFFER_LEN;\n\n\tprintf(\"%s\", prompt);\n\tfflush(stdout);\n\tif(gets_quiet(pw1, (int)minLen)){\n\t\tif(!quiet){\n\t\t\tfprintf(stderr, \"Error: Empty password.\\n\");\n\t\t}\n\t\treturn 1;\n\t}\n\tprintf(\"\\n\");\n\n\tif(verify_prompt){\n\t\tprintf(\"%s\", verify_prompt);\n\t\tfflush(stdout);\n\t\tif(gets_quiet(pw2, (int)minLen)){\n\t\t\tif(!quiet){\n\t\t\t\tfprintf(stderr, \"Error: Empty password.\\n\");\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\t\tprintf(\"\\n\");\n\n\t\tif(strcmp(pw1, pw2)){\n\t\t\tif(!quiet){\n\t\t\t\tfprintf(stderr, \"Error: Passwords do not match.\\n\");\n\t\t\t}\n\t\t\treturn 2;\n\t\t}\n\t}\n\n\tstrncpy(password, pw1, minLen);\n\treturn 0;\n}\n"
  },
  {
    "path": "apps/mosquitto_passwd/get_password.h",
    "content": "#ifndef GET_PASSWORD_H\n#define GET_PASSWORD_H\n/*\nCopyright (c) 2012-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <stdbool.h>\n\nvoid get_password__reset_term(void);\nint get_password(const char *prompt, const char *verify_prompt, bool quiet, char *password, size_t len);\n\n#endif\n"
  },
  {
    "path": "apps/mosquitto_passwd/mosquitto_passwd.c",
    "content": "/*\nCopyright (c) 2012-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <ctype.h>\n#include <errno.h>\n#include <openssl/evp.h>\n#include <openssl/rand.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n#include \"get_password.h\"\n\n#ifdef WIN32\n#  include <windows.h>\n#  include <process.h>\n#   ifndef __cplusplus\n#       if defined(_MSC_VER) && _MSC_VER < 1900\n#           define bool char\n#           define true 1\n#           define false 0\n#       else\n#           include <stdbool.h>\n#       endif\n#   endif\n#   define snprintf sprintf_s\n#   include <io.h>\n#   include <windows.h>\n#else\n#  include <stdbool.h>\n#  include <unistd.h>\n#  include <termios.h>\n#  include <sys/stat.h>\n#endif\n\n#define MAX_BUFFER_LEN 65500\n\nstruct cb_helper {\n\tconst char *line;\n\tconst char *username;\n\tconst char *password;\n\tint iterations;\n\tbool found;\n};\n\nstatic enum mosquitto_pwhash_type hashtype = MOSQ_PW_SHA512_PBKDF2;\n\n#ifdef WIN32\n\n\nstatic FILE *mpw_tmpfile(void)\n{\n\treturn tmpfile();\n}\n#else\n\nstatic char unsigned alphanum[] = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\n\nstatic unsigned char tmpfile_path[36];\n\n\nstatic FILE *mpw_tmpfile(void)\n{\n\tint fd;\n\tsize_t i;\n\n\tif(RAND_bytes(tmpfile_path, sizeof(tmpfile_path)) != 1){\n\t\treturn NULL;\n\t}\n\n\tstrcpy((char *)tmpfile_path, \"/tmp/\");\n\n\tfor(i=strlen((char *)tmpfile_path); i<sizeof(tmpfile_path)-8; i++){\n\t\ttmpfile_path[i] = alphanum[tmpfile_path[i]%(sizeof(alphanum)-1)];\n\t}\n\ttmpfile_path[sizeof(tmpfile_path)-8] = '-';\n\tfor(i=sizeof(tmpfile_path)-7; i<sizeof(tmpfile_path)-1; i++){\n\t\ttmpfile_path[i] = 'X';\n\t}\n\ttmpfile_path[sizeof(tmpfile_path)-1] = '\\0';\n\n\tumask(077);\n\tfd = mkstemp((char *)tmpfile_path);\n\tif(fd < 0){\n\t\treturn NULL;\n\t}\n\tunlink((char *)tmpfile_path);\n\n\treturn fdopen(fd, \"w+\");\n}\n#endif\n\n\nstatic void print_usage(void)\n{\n\tprintf(\"mosquitto_passwd is a tool for managing password files for mosquitto.\\n\\n\");\n\tprintf(\"Usage: mosquitto_passwd [-H argon2id | -H sha512-pbkdf2] [-c | -D] passwordfile username\\n\");\n\tprintf(\"       mosquitto_passwd [-H argon2id | -H sha512-pbkdf2] [-c] -b passwordfile username password\\n\");\n\tprintf(\"       mosquitto_passwd -U passwordfile\\n\");\n\tprintf(\" -b : run in batch mode to allow passing passwords on the command line.\\n\");\n\tprintf(\" -c : create a new password file. This will overwrite existing files.\\n\");\n\tprintf(\" -D : delete the username rather than adding/updating its password.\\n\");\n\tprintf(\" -H : specify the hashing algorithm. Defaults to argon2id, which is recommended.\\n\");\n\tprintf(\"      Mosquitto 2.0 and earlier defaulted to sha512-pbkdf2.\\n\");\n\tprintf(\"      Mosquitto 1.6 and earlier defaulted to sha512.\\n\");\n\tprintf(\" -U : update a plain text password file to use hashed passwords.\\n\");\n\tprintf(\"\\nSee https://mosquitto.org/ for more information.\\n\\n\");\n}\n\n\nstatic int output_new_password(FILE *fptr, const char *username, const char *password, int iterations)\n{\n\tint rc;\n\tstruct mosquitto_pw *pw;\n\n\tif(password == NULL){\n\t\tfprintf(stderr, \"Error: Internal error, no password given.\\n\");\n\t\treturn 1;\n\t}\n\tif(mosquitto_pw_new(&pw, hashtype)){\n\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(hashtype == MOSQ_PW_SHA512_PBKDF2 && iterations > 0){\n\t\tmosquitto_pw_set_param(pw, MOSQ_PW_PARAM_ITERATIONS, iterations);\n\t}\n\n\trc = mosquitto_pw_hash_encoded(pw, password);\n\tif(rc){\n\t\tmosquitto_pw_cleanup(pw);\n\t\tfprintf(stderr, \"Error: Unable to hash password.\\n\");\n\t\treturn rc;\n\t}\n\n\tfprintf(fptr, \"%s:%s\\n\", username, mosquitto_pw_get_encoded(pw));\n\tmosquitto_pw_cleanup(pw);\n\n\treturn rc;\n}\n\n\nstatic int pwfile_iterate(FILE *fptr, FILE *ftmp,\n\t\tint (*cb)(FILE *, FILE *, const char *, const char *, const char *, struct cb_helper *),\n\t\tstruct cb_helper *helper)\n{\n\tchar *buf;\n\tint buflen = 1024;\n\tchar *lbuf;\n\tint lbuflen;\n\tint rc = 1;\n\tint line = 0;\n\tchar *username, *password;\n\n\tbuf = malloc((size_t)buflen);\n\tif(buf == NULL){\n\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tlbuflen = buflen;\n\tlbuf = malloc((size_t)lbuflen);\n\tif(lbuf == NULL){\n\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\tfree(buf);\n\t\treturn 1;\n\t}\n\n\twhile(!feof(fptr) && mosquitto_fgets(&buf, &buflen, fptr)){\n\t\tif(lbuflen != buflen){\n\t\t\tfree(lbuf);\n\t\t\tlbuflen = buflen;\n\t\t\tlbuf = malloc((size_t)lbuflen);\n\t\t\tif(lbuf == NULL){\n\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\tfree(buf);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\tmemcpy(lbuf, buf, (size_t)buflen);\n\t\tline++;\n\t\tusername = strtok(buf, \":\");\n\t\tpassword = strtok(NULL, \":\");\n\t\tif(username && password){\n\t\t\tusername = mosquitto_trimblanks(username);\n\t\t\tpassword = mosquitto_trimblanks(password);\n\t\t}\n\n\t\tif(username == NULL || strlen(username) == 0\n\t\t\t\t|| password == NULL || strlen(password) == 0){\n\n\t\t\tfprintf(stderr, \"Error: Corrupt password file at line %d.\\n\", line);\n\t\t\tfree(lbuf);\n\t\t\tfree(buf);\n\t\t\treturn 1;\n\t\t}\n\n\t\trc = cb(fptr, ftmp, username, password, lbuf, helper);\n\t\tif(rc){\n\t\t\tbreak;\n\t\t}\n\t}\n\tfree(lbuf);\n\tfree(buf);\n\n\treturn rc;\n}\n\n\n/* ======================================================================\n * Delete a user from the password file\n * ====================================================================== */\nstatic int delete_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper)\n{\n\tUNUSED(fptr);\n\tUNUSED(password);\n\tUNUSED(line);\n\n\tif(strcmp(username, helper->username)){\n\t\t/* If this isn't the username to delete, write it to the new file */\n\t\tfprintf(ftmp, \"%s\", line);\n\t}else{\n\t\t/* Don't write the matching username to the file. */\n\t\thelper->found = true;\n\t}\n\treturn 0;\n}\n\n\nstatic int delete_pwuser(FILE *fptr, FILE *ftmp, const char *username)\n{\n\tstruct cb_helper helper;\n\tint rc;\n\n\tmemset(&helper, 0, sizeof(helper));\n\thelper.username = username;\n\trc = pwfile_iterate(fptr, ftmp, delete_pwuser_cb, &helper);\n\n\tif(helper.found == false){\n\t\tfprintf(stderr, \"Warning: User %s not found in password file.\\n\", username);\n\t\treturn 1;\n\t}\n\treturn rc;\n}\n\n\n/* ======================================================================\n * Update a plain text password file to use hashes\n * ====================================================================== */\nstatic int update_file_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper)\n{\n\tUNUSED(fptr);\n\tUNUSED(line);\n\n\tif(helper){\n\t\treturn output_new_password(ftmp, username, password, helper->iterations);\n\t}else{\n\t\treturn output_new_password(ftmp, username, password, -1);\n\t}\n}\n\n\nstatic int update_file(FILE *fptr, FILE *ftmp)\n{\n\treturn pwfile_iterate(fptr, ftmp, update_file_cb, NULL);\n}\n\n\n/* ======================================================================\n * Update an existing user password / create a new password\n * ====================================================================== */\nstatic int update_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper)\n{\n\tint rc = 0;\n\n\tUNUSED(fptr);\n\tUNUSED(password);\n\n\tif(helper->found || strcmp(username, helper->username)){\n\t\t/* If this isn't the matching user, then writing out the exiting line */\n\t\tfprintf(ftmp, \"%s\", line);\n\t}else{\n\t\t/* Write out a new line for our matching username */\n\t\thelper->found = true;\n\t\trc = output_new_password(ftmp, username, helper->password, helper->iterations);\n\t}\n\treturn rc;\n}\n\n\nstatic int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *password, int iterations)\n{\n\tstruct cb_helper helper;\n\tint rc;\n\n\tmemset(&helper, 0, sizeof(helper));\n\thelper.username = username;\n\thelper.password = password;\n\thelper.iterations = iterations;\n\trc = pwfile_iterate(fptr, ftmp, update_pwuser_cb, &helper);\n\n\tif(helper.found){\n\t\tprintf(\"Updating password for user %s\\n\", username);\n\t\treturn rc;\n\t}else{\n\t\tprintf(\"Adding password for user %s\\n\", username);\n\t\treturn output_new_password(ftmp, username, password, iterations);\n\t}\n}\n\n\nstatic int copy_contents(FILE *src, FILE *dest)\n{\n\tchar buf[MAX_BUFFER_LEN];\n\tsize_t len;\n\n\trewind(src);\n\trewind(dest);\n\n#ifdef WIN32\n\t_chsize(fileno(dest), 0);\n#else\n\tif(ftruncate(fileno(dest), 0)){\n\t\treturn 1;\n\t}\n#endif\n\n\twhile(!feof(src)){\n\t\tlen = fread(buf, 1, MAX_BUFFER_LEN, src);\n\t\tif(len > 0){\n\t\t\tif(fwrite(buf, 1, len, dest) != len){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}else{\n\t\t\treturn !feof(src);\n\t\t}\n\t}\n\treturn 0;\n}\n\n\nstatic int create_backup(char *backup_file, FILE *fptr)\n{\n\tFILE *fbackup;\n\n#ifdef WIN32\n\tfbackup = mosquitto_fopen(backup_file, \"wt\", true);\n#else\n\tint fd;\n\tumask(077);\n\tfd = mkstemp(backup_file);\n\tif(fd < 0){\n\t\tfprintf(stderr, \"Error creating backup password file \\\"%s\\\", not continuing.\\n\", backup_file);\n\t\treturn 1;\n\t}\n\tfbackup = fdopen(fd, \"wt\");\n#endif\n\tif(!fbackup){\n\t\tfprintf(stderr, \"Error creating backup password file \\\"%s\\\", not continuing.\\n\", backup_file);\n\t\treturn 1;\n\t}\n\n\tif(copy_contents(fptr, fbackup)){\n\t\tfprintf(stderr, \"Error copying data to backup password file \\\"%s\\\", not continuing.\\n\", backup_file);\n\t\tfclose(fbackup);\n\t\treturn 1;\n\t}\n\tfclose(fbackup);\n\trewind(fptr);\n\treturn 0;\n}\n\n\nstatic void handle_sigint(int signal)\n{\n\tget_password__reset_term();\n\n\tUNUSED(signal);\n\n#ifndef WITH_FUZZING\n\texit(0);\n#endif\n}\n\n\nstatic bool is_username_valid(const char *username)\n{\n\tsize_t i;\n\tsize_t slen;\n\n\tif(username){\n\t\tslen = strlen(username);\n\t\tif(slen > 65535){\n\t\t\tfprintf(stderr, \"Error: Username must be less than 65536 characters long.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tfor(i=0; i<slen; i++){\n\t\t\tif(iscntrl((unsigned char)username[i])){\n\t\t\t\tfprintf(stderr, \"Error: Username must not contain control characters.\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tif(strchr(username, ':')){\n\t\t\tfprintf(stderr, \"Error: Username must not contain the ':' character.\\n\");\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n#ifdef WITH_FUZZING\n\n\nint mosquitto_passwd_fuzz_main(int argc, char *argv[])\n#else\n\n\nint main(int argc, char *argv[])\n#endif\n{\n\tchar *password_file_tmp = NULL;\n\tchar *password_file = NULL;\n\tchar *username = NULL;\n\tchar *password_cmd = NULL;\n\tbool batch_mode = false;\n\tbool create_new = false;\n\tbool delete_user = false;\n\tbool use_stdout = false;\n\tFILE *fptr, *ftmp;\n\tchar password[MAX_BUFFER_LEN];\n\tint rc;\n\tbool do_update_file = false;\n\tchar *backup_file;\n\tint idx;\n\tint iterations = -1;\n\n\tsignal(SIGINT, handle_sigint);\n\tsignal(SIGTERM, handle_sigint);\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L || OPENSSL_API_COMPAT < 0x10100000L\n\tOpenSSL_add_all_digests();\n#else\n\tOPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \\\n\t\t\t| OPENSSL_INIT_ADD_ALL_DIGESTS \\\n\t\t\t| OPENSSL_INIT_LOAD_CONFIG, NULL);\n#endif\n\n\tif(argc == 1){\n\t\tprint_usage();\n\t\treturn 1;\n\t}\n\n\tidx = 1;\n\tfor(idx = 1; idx < argc; idx++){\n\t\tif(!strcmp(argv[idx], \"-H\")){\n\t\t\tif(idx+1 == argc){\n\t\t\t\tfprintf(stderr, \"Error: -H argument given but not enough other arguments.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(!strcmp(argv[idx+1], \"argon2id\")){\n\t\t\t\thashtype = MOSQ_PW_ARGON2ID;\n\t\t\t}else if(!strcmp(argv[idx+1], \"sha512-pbkdf2\")){\n\t\t\t\thashtype = MOSQ_PW_SHA512_PBKDF2;\n\t\t\t}else if(!strcmp(argv[idx+1], \"sha512\")){\n\t\t\t\thashtype = MOSQ_PW_SHA512;\n\t\t\t}else{\n\t\t\t\tfprintf(stderr, \"Error: Unknown hash type '%s'\\n\", argv[idx+1]);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tidx++;\n\t\t}else if(!strcmp(argv[idx], \"-b\")){\n\t\t\tbatch_mode = true;\n\t\t}else if(!strcmp(argv[idx], \"-c\")){\n\t\t\tcreate_new = true;\n\t\t}else if(!strcmp(argv[idx], \"-D\")){\n\t\t\tdelete_user = true;\n\t\t}else if(!strcmp(argv[idx], \"-I\")){\n\t\t\tif(idx+1 == argc){\n\t\t\t\tfprintf(stderr, \"Error: -I argument given but not enough other arguments.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\titerations = atoi(argv[idx+1]);\n\t\t\tidx++;\n\t\t\tif(iterations < 1){\n\t\t\t\tfprintf(stderr, \"Error: Number of iterations must be > 0.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}else if(!strcmp(argv[idx], \"-U\")){\n\t\t\tdo_update_file = true;\n\t\t}else{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(create_new && delete_user){\n\t\tfprintf(stderr, \"Error: -c and -D cannot be used together.\\n\");\n\t\treturn 1;\n\t}\n\tif(create_new && do_update_file){\n\t\tfprintf(stderr, \"Error: -c and -U cannot be used together.\\n\");\n\t\treturn 1;\n\t}\n\tif(delete_user && do_update_file){\n\t\tfprintf(stderr, \"Error: -D and -U cannot be used together.\\n\");\n\t\treturn 1;\n\t}\n\tif(delete_user && batch_mode){\n\t\tfprintf(stderr, \"Error: -b and -D cannot be used together.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(create_new){\n\t\tif(batch_mode){\n\t\t\tif(idx+2 >= argc){\n\t\t\t\tfprintf(stderr, \"Error: -c argument given but password file, username, or password missing.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(!strcmp(argv[idx], \"-\")){\n\t\t\t\t\tuse_stdout = true;\n\t\t\t\t}else{\n\t\t\t\t\tpassword_file_tmp = argv[idx];\n\t\t\t\t}\n\t\t\t\tusername = argv[idx+1];\n\t\t\t\tpassword_cmd = argv[idx+2];\n\t\t\t}\n\t\t}else{\n\t\t\tif(idx+1 >= argc){\n\t\t\t\tfprintf(stderr, \"Error: -c argument given but password file or username missing.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(!strcmp(argv[idx], \"-\")){\n\t\t\t\t\tuse_stdout = true;\n\t\t\t\t}else{\n\t\t\t\t\tpassword_file_tmp = argv[idx];\n\t\t\t\t}\n\t\t\t\tusername = argv[idx+1];\n\t\t\t}\n\t\t}\n\t}else if(delete_user){\n\t\tif(idx+1 >= argc){\n\t\t\tfprintf(stderr, \"Error: -D argument given but password file or username missing.\\n\");\n\t\t\treturn 1;\n\t\t}else{\n\t\t\tpassword_file_tmp = argv[idx];\n\t\t\tusername = argv[idx+1];\n\t\t}\n\t}else if(do_update_file){\n\t\tif(idx+1 != argc){\n\t\t\tfprintf(stderr, \"Error: -U argument given but password file missing.\\n\");\n\t\t\treturn 1;\n\t\t}else{\n\t\t\tpassword_file_tmp = argv[idx];\n\t\t}\n\t}else if(batch_mode == true && idx+3 == argc){\n\t\tpassword_file_tmp = argv[idx];\n\t\tusername = argv[idx+1];\n\t\tpassword_cmd = argv[idx+2];\n\t}else if(batch_mode == false && idx+2 == argc){\n\t\tpassword_file_tmp = argv[idx];\n\t\tusername = argv[idx+1];\n\t}else{\n\t\tprint_usage();\n\t\treturn 1;\n\t}\n\n\tif(!is_username_valid(username)){\n\t\treturn 1;\n\t}\n\tif(password_cmd && strlen(password_cmd) > 65535){\n\t\tfprintf(stderr, \"Error: Password must be less than 65536 characters long.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(!use_stdout){\n#ifdef WIN32\n\t\tpassword_file = _fullpath(NULL, password_file_tmp, 0);\n\t\tif(!password_file){\n\t\t\tfprintf(stderr, \"Error getting full path for password file.\\n\");\n\t\t\treturn 1;\n\t\t}\n#else\n\t\tpassword_file = realpath(password_file_tmp, NULL);\n\t\tif(!password_file){\n\t\t\tif(errno == ENOENT){\n\t\t\t\tpassword_file = strdup(password_file_tmp);\n\t\t\t\tif(!password_file){\n\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tfprintf(stderr, \"Error reading password file: %s\\n\", strerror(errno));\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\tif(create_new){\n\t\tif(batch_mode == false){\n\t\t\trc = get_password(\"Password: \", \"Reenter password: \", false, password, MAX_BUFFER_LEN);\n\t\t\tif(rc){\n\t\t\t\tfree(password_file);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tpassword_cmd = password;\n\t\t}\n\t\tif(use_stdout){\n\t\t\tfptr = stdout;\n\t\t}else{\n\t\t\tfptr = mosquitto_fopen(password_file, \"wt\", true);\n\t\t\tif(!fptr){\n\t\t\t\tfprintf(stderr, \"Error: Unable to open file %s for writing. %s.\\n\", password_file, strerror(errno));\n\t\t\t\tfree(password_file);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tfree(password_file);\n\t\t}\n\t\tif(!use_stdout){\n\t\t\tprintf(\"Adding password for user %s\\n\", username);\n\t\t}\n\t\trc = output_new_password(fptr, username, password_cmd, iterations);\n\t\tfclose(fptr);\n\t\treturn rc;\n\t}else{\n\t\tfptr = mosquitto_fopen(password_file, \"r+t\", true);\n\t\tif(!fptr){\n\t\t\tfprintf(stderr, \"Error: Unable to open password file %s. %s.\\n\", password_file, strerror(errno));\n\t\t\tfree(password_file);\n\t\t\treturn 1;\n\t\t}\n\n\t\tsize_t len = strlen(password_file) + strlen(\".backup.XXXXXX\") + 1;\n\t\tbackup_file = malloc(len);\n\t\tif(!backup_file){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\tfree(password_file);\n\t\t\treturn 1;\n\t\t}\n\t\tsnprintf(backup_file, len, \"%s.backup.XXXXXX\", password_file);\n\t\tfree(password_file);\n\t\tpassword_file = NULL;\n\n\t\tif(create_backup(backup_file, fptr)){\n\t\t\tfclose(fptr);\n\t\t\tfree(backup_file);\n\t\t\treturn 1;\n\t\t}\n\n\t\tftmp = mpw_tmpfile();\n\t\tif(!ftmp){\n\t\t\tfprintf(stderr, \"Error: Unable to open temporary file. %s.\\n\", strerror(errno));\n\t\t\tfclose(fptr);\n\t\t\tfree(backup_file);\n\t\t\treturn 1;\n\t\t}\n\t\tif(delete_user){\n\t\t\trc = delete_pwuser(fptr, ftmp, username);\n\t\t}else if(do_update_file){\n\t\t\trc = update_file(fptr, ftmp);\n\t\t}else{\n\t\t\tif(batch_mode){\n\t\t\t\t/* Update password for individual user */\n\t\t\t\trc = update_pwuser(fptr, ftmp, username, password_cmd, iterations);\n\t\t\t}else{\n\t\t\t\trc = get_password(\"Password: \", \"Reenter password: \", false, password, MAX_BUFFER_LEN);\n\t\t\t\tif(rc == 0){\n\t\t\t\t\t/* Update password for individual user */\n\t\t\t\t\trc = update_pwuser(fptr, ftmp, username, password, iterations);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif(rc){\n\t\t\tfclose(fptr);\n\t\t\tfclose(ftmp);\n\t\t\tunlink(backup_file);\n\t\t\tfree(backup_file);\n\t\t\treturn rc;\n\t\t}\n\n\t\tif(copy_contents(ftmp, fptr)){\n\t\t\tfprintf(stderr, \"Error occurred updating password file.\\n\");\n\t\t\tfprintf(stderr, \"Password file may be corrupt, check the backup file: %s.\\n\", backup_file);\n\t\t\trc = 1;\n\t\t}\n\t\tfclose(fptr);\n\t\tfclose(ftmp);\n\n\t\tif(rc == 0){\n\t\t\t/* Everything was ok so backup no longer needed. May contain old\n\t\t\t * passwords so shouldn't be kept around. */\n\t\t\tunlink(backup_file);\n\t\t}\n\t\tfree(backup_file);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "apps/mosquitto_signal/CMakeLists.txt",
    "content": "set(SRC\n\tmosquitto_signal.c\n)\n\nif(WIN32)\n\tset(SRC ${SRC} signal_windows.c)\nelse()\n\tset(SRC ${SRC} signal_unix.c)\nendif()\n\nadd_executable(mosquitto_signal ${SRC})\n\ntarget_include_directories(mosquitto_signal PRIVATE\n\t\"${mosquitto_SOURCE_DIR}\"\n\t\"${mosquitto_SOURCE_DIR}/include\"\n\t\"${mosquitto_SOURCE_DIR}/lib\"\n\t\"${mosquitto_SOURCE_DIR}/src\"\n)\n\ninstall(TARGETS mosquitto_signal\n\tRUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n)\n"
  },
  {
    "path": "apps/mosquitto_signal/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=-I${R}/lib\nLOCAL_LDFLAGS+=\nLOCAL_LDADD+=\n\n.PHONY: all install uninstall clean reallyclean\n\nOBJS= \\\n\tmosquitto_signal.o \\\n\tsignal_unix.o \\\n\nall : mosquitto_signal\n\nmosquitto_signal : ${OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}${CC} ${LOCAL_LDFLAGS} $^ -o $@ $(LOCAL_LDADD)\n\n${OBJS} : %.o: %.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\ninstall : all\nifeq ($(WITH_TLS),yes)\n\t$(INSTALL) -d \"${DESTDIR}$(prefix)/bin\"\n\t$(INSTALL) ${STRIP_OPTS} mosquitto_signal \"${DESTDIR}${prefix}/bin/mosquitto_signal\"\nendif\n\nuninstall :\n\t-rm -f \"${DESTDIR}${prefix}/bin/mosquitto_signal\"\n\nclean :\n\t-rm -f *.o *.a mosquitto_signal *.gcda *.gcno\n\nreallyclean : clean\n\t-rm -rf *.orig *.db\n"
  },
  {
    "path": "apps/mosquitto_signal/mosquitto_signal.c",
    "content": "/*\nCopyright (c) 2024 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto_signal.h\"\n\n\nstatic void print_usage(void)\n{\n\tprintf(\"mosquitto_signal is a tool for sending control signals to mosquitto.\\n\");\n\tprintf(\"                 it is primarily useful on Windows.\\n\");\n\tprintf(\"                 on other systems the `kill` tool can be used.\\n\\n\");\n\tprintf(\"Usage: mosquitto_signal {-a | -p <pid>} <signal>\\n\");\n\tprintf(\"       mosquitto_signal --help\\n\\n\");\n#ifdef WIN32\n\tprintf(\" -a :  signal all processes that match the name 'mosquitto.exe'.\\n\");\n#else\n\tprintf(\" -a :  signal all processes that match the name 'mosquitto'.\\n\");\n#endif\n\tprintf(\" -p :  specify a process ID to signal\\n\\n\");\n\tprintf(\"<signal> may be one of:\\n\");\n\tprintf(\" config-reload - reload the configuration file, if in use.\\n\");\n\tprintf(\" log-rotate    - if using `file` logging ask the broker to close and reopen the\\n\");\n\tprintf(\"                 log file.\\n\");\n\tprintf(\" shutdown      - quit the broker.\\n\");\n\tprintf(\" tree-print    - (debug) print out subscription and retain tree information to\\n\");\n\tprintf(\"                 stdout.\\n\");\n\tprintf(\" xtreport      - (debug) write internal data to xtmosquitto.kcg.<pid>.<iter>\\n\");\n\tprintf(\"\\nSee https://mosquitto.org/ for more information.\\n\\n\");\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint idx;\n\tint pid = -2;\n\tenum mosq_signal msig = 0;\n\n\tif(argc == 1){\n\t\tprint_usage();\n\t\treturn 1;\n\t}\n\n\tidx = 1;\n\tfor(idx = 1; idx < argc; idx++){\n\t\tif(!strcmp(argv[idx], \"--help\")){\n\t\t\tprint_usage();\n\t\t\treturn 1;\n\t\t}else if(!strcmp(argv[idx], \"-a\")){\n\t\t\tpid = -1;\n\t\t}else if(!strcmp(argv[idx], \"-p\")){\n\t\t\tif(idx+1 == argc){\n\t\t\t\tfprintf(stderr, \"Error: -p argument given but process ID missing.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tpid = atoi(argv[idx+1]);\n\t\t\tif(pid < 1){\n\t\t\t\tfprintf(stderr, \"Error: Process ID must be >0.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tidx++;\n\t\t}else{\n\t\t\tbreak;\n\t\t}\n\t}\n\tif(pid == -2){\n\t\tfprintf(stderr, \"Error: One of -a or -p must be used.\\n\");\n\t\treturn 1;\n\t}\n\tif(idx == argc){\n\t\tfprintf(stderr, \"Error: No signal given.\\n\");\n\t\treturn 1;\n\t}\n\tif(!strcmp(argv[idx], \"config-reload\")){\n\t\tmsig = MSIG_CONFIG_RELOAD;\n\t}else if(!strcmp(argv[idx], \"log-rotate\")){\n\t\tmsig = MSIG_LOG_ROTATE;\n\t}else if(!strcmp(argv[idx], \"shutdown\")){\n\t\tmsig = MSIG_SHUTDOWN;\n\t}else if(!strcmp(argv[idx], \"tree-print\")){\n\t\tmsig = MSIG_TREE_PRINT;\n\t}else if(!strcmp(argv[idx], \"xtreport\")){\n\t\tmsig = MSIG_XTREPORT;\n\t}else{\n\t\tfprintf(stderr, \"Error: Unknown signal '%s'.\\n\", argv[idx]);\n\t\treturn 1;\n\t}\n\n\tsend_signal(pid, msig);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "apps/mosquitto_signal/mosquitto_signal.h",
    "content": "#ifndef MOSQUITTO_SIGNAL_H\n#define MOSQUITTO_SIGNAL_H\n\nenum mosq_signal {\n\tMSIG_CONFIG_RELOAD,\n\tMSIG_LOG_ROTATE,\n\tMSIG_SHUTDOWN,\n\tMSIG_TREE_PRINT,\n\tMSIG_XTREPORT,\n};\n\nvoid signal_all(int sig);\nvoid send_signal(int pid, enum mosq_signal msig);\n\n#endif\n"
  },
  {
    "path": "apps/mosquitto_signal/signal_unix.c",
    "content": "/*\nCopyright (c) 2024 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <dirent.h>\n#include <errno.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto_signal.h\"\n\n#ifndef PATH_MAX\n#  define PATH_MAX 4096\n#endif\n\n\nvoid signal_all(int sig)\n{\n\tDIR *dir;\n\tstruct dirent *d;\n\tchar pathbuf[PATH_MAX+1];\n\tchar cmdline[256];\n\tconst char *cmd;\n\tFILE *fptr;\n\tpid_t pid;\n\n\tdir = opendir(\"/proc\");\n\tif(dir == NULL){\n\t\tfprintf(stderr, \"Error reading /proc: %s.\\n\", strerror(errno));\n\t\treturn;\n\t}\n\n\twhile((d = readdir(dir))){\n#ifdef DT_DIR\n\t\tif(d->d_type == DT_DIR)\n#endif\n\t\t{\n\t\t\tpid = atoi(d->d_name);\n\t\t\tif(pid > 0){\n\t\t\t\tsnprintf(pathbuf, sizeof(pathbuf), \"/proc/%s/cmdline\", d->d_name);\n\t\t\t\tfptr = fopen(pathbuf, \"r\");\n\t\t\t\tif(fptr){\n\t\t\t\t\tif(fgets(cmdline, sizeof(cmdline), fptr)){\n\t\t\t\t\t\tcmd = strrchr(cmdline, '/');\n\t\t\t\t\t\tif(cmd){\n\t\t\t\t\t\t\tcmd += 1;\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\tcmd = cmdline;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(!strcmp(cmd, \"mosquitto\")){\n\t\t\t\t\t\t\tif(kill(pid, sig) < 0){\n\t\t\t\t\t\t\t\tfprintf(stderr, \"Unable to signal process %d: %s\\n\", pid, strerror(errno));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfclose(fptr);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tclosedir(dir);\n}\n\n\nvoid send_signal(int pid, enum mosq_signal msig)\n{\n\tint sig;\n\n\tswitch(msig){\n\t\tcase MSIG_CONFIG_RELOAD:\n\t\t\tsig = SIGHUP;\n\t\t\tbreak;\n\t\tcase MSIG_LOG_ROTATE:\n\t\t\tsig = SIGHUP;\n\t\t\tbreak;\n\t\tcase MSIG_SHUTDOWN:\n\t\t\tsig = SIGINT;\n\t\t\tbreak;\n\t\tcase MSIG_TREE_PRINT:\n\t\t\tsig = SIGUSR2;\n\t\t\tbreak;\n#ifdef SIGRTMIN\n\t\tcase MSIG_XTREPORT:\n\t\t\tsig = SIGRTMIN;\n\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\treturn;\n\t}\n\n\tif(pid > 0){\n\t\tif(kill(pid, sig) != 0){\n\t\t\tfprintf(stderr, \"Error sending signal to process %d: %s\\n\", pid, strerror(errno));\n\t\t}\n\t}else{\n\t\tsignal_all(sig);\n\t}\n}\n"
  },
  {
    "path": "apps/mosquitto_signal/signal_windows.c",
    "content": "/*\nCopyright (c) 2024 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef WIN32_LEAN_AND_MEAN\n#  define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n#include <psapi.h>\n\n#include <ctype.h>\n#include <errno.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto_signal.h\"\n\n#undef WITH_TLS\n#include \"config.h\"\n\n\nstatic const char *msig_to_string(enum mosq_sig msig)\n{\n\tswitch(msig){\n\t\tcase MSIG_CONFIG_RELOAD:\n\t\t\treturn \"reload\";\n\t\tcase MSIG_LOG_ROTATE:\n\t\t\treturn \"log_rotate\";\n\t\tcase MSIG_SHUTDOWN:\n\t\t\treturn \"shutdown\";\n\t\tcase MSIG_TREE_PRINT:\n\t\t\treturn \"tree_print\";\n\t\tcase MSIG_XTREPORT:\n\t\t\treturn \"xtreport\";\n\t\tdefault:\n\t\t\treturn \"\";\n\t}\n}\n\n\nvoid signal_all(enum mosq_signal msig)\n{\n\tDWORD processes[2048], cbneeded, count;\n\tint pid;\n\n\tif(!EnumProcesses(processes, sizeof(processes), &cbneeded)){\n\t\tfprintf(stderr, \"Error enumerating processes.\\n\");\n\t\treturn;\n\t}\n\n\tcount = cbneeded / sizeof(DWORD);\n\tfor(DWORD i=0; i<count; i++){\n\t\tif(processes[i]){\n\t\t\tHANDLE hproc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processes[i]);\n\t\t\tif(hproc){\n\t\t\t\tHMODULE hmod;\n\t\t\t\tchar procname[MAX_PATH];\n\t\t\t\tif(EnumProcessModules(hproc, &hmod, sizeof(hmod), &cbneeded)){\n\t\t\t\t\tGetModuleBaseName(hproc, hmod, procname, sizeof(procname));\n\t\t\t\t\tif(!strcasecmp(procname, \"mosquitto.exe\")){\n\t\t\t\t\t\tpid = GetProcessId(hproc);\n\t\t\t\t\t\tsend_signal(pid, msig);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tCloseHandle(hproc);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nvoid send_signal(int pid, enum mosq_signal msig)\n{\n\tHANDLE evt;\n\tchar eventbuf[MAX_PATH+1];\n\tBOOL res;\n\n\tsnprintf(eventbuf, sizeof(eventbuf), \"mosq%d_%s\", pid, msig_to_string(msig));\n\tevt = OpenEvent(EVENT_MODIFY_STATE, FALSE, eventbuf);\n\tif(evt){\n\t\tres = PulseEvent(evt);\n\t\tCloseHandle(evt);\n\t}\n}\n"
  },
  {
    "path": "buildtest.py",
    "content": "#!/usr/bin/python3\n\nbuild_variants = [\n    'WITH_ARGON2',\n    'WITH_ASAN',\n    'WITH_BRIDGE',\n    'WITH_CONTROL',\n    'WITH_EDITLINE',\n    'WITH_EPOLL',\n    'WITH_HTTP_API',\n    'WITH_MEMORY_TRACKING',\n    'WITH_OLD_KEEPALIVE',\n    'WITH_PERSISTENCE',\n    #'WITH_SHARED_LIBRARIES',\n    'WITH_SOCKS',\n    'WITH_SQLITE',\n    'WITH_SRV',\n    'WITH_STATIC_LIBRARIES',\n    'WITH_SYSTEMD',\n    'WITH_SYS_TREE',\n    'WITH_THREADING',\n    'WITH_TLS',\n    'WITH_TLS_PSK',\n    'WITH_UNIX_SOCKETS',\n    'WITH_WEBSOCKETS',\n    'WITH_XTREPORT',\n]\n\nspecial_variants = [\n    'WITH_BUNDLED_DEPS',\n    'WITH_COVERAGE',\n]\n\n\nimport os\nimport random\nimport subprocess\n\ndef run_test(msg, opts):\n    subprocess.run([\"make\", \"clean\"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n    print(\"%s: %s\" % (msg, str(opts)))\n    args = [\"make\", \"test-compile\", \"-j%d\" % (os.cpu_count())] + opts\n    proc = subprocess.run(args, stdout=subprocess.DEVNULL)\n    if proc.returncode != 0:\n        raise RuntimeError(\"BUILD FAILED: %s\" % (' '.join(args)))\n\ndef simple_tests():\n    for bv in build_variants:\n        for enabled in [\"yes\", \"no\"]:\n            opts = \"%s=%s\" % (bv, enabled)\n            run_test(\"SIMPLE BUILD\", [opts])\n\ndef random_tests(count=10):\n    for i in range(1, count):\n        opts = []\n        for bv in build_variants:\n            opts.append(\"%s=%s\" % (bv, random.choice([\"yes\", \"no\"])))\n\n        run_test(\"RANDOM BUILD\", opts)\n\n\nif __name__ == \"__main__\":\n    simple_tests()\n    random_tests(2)\n"
  },
  {
    "path": "client/CMakeLists.txt",
    "content": "set(shared_src client_shared.c client_shared.h client_props.c)\n\nadd_library(client-common INTERFACE)\ntarget_link_libraries(client-common INTERFACE common-options)\ntarget_include_directories(client-common INTERFACE\n\t\"${CJSON_INCLUDE_DIRS}\"\n\t\"${OPENSSL_INCLUDE_DIR}\"\n)\n\ntarget_sources(client-common INTERFACE ${shared_src})\n\nif(WITH_TLS)\n\ttarget_link_libraries(client-common INTERFACE OpenSSL::SSL)\nendif()\n\nif(WITH_SRV)\n\ttarget_compile_definitions(client-common INTERFACE \"-DWITH_SRV\")\nendif()\n\nif(WITH_WEBSOCKETS AND WITH_WEBSOCKETS_BUILTIN)\n\ttarget_compile_definitions(client-common INTERFACE \"-DWITH_WEBSOCKETS=WS_IS_BUILTIN\")\nendif()\n\nadd_executable(mosquitto_pub pub_client.c pub_shared.c)\nadd_executable(mosquitto_sub sub_client.c sub_client_output.c)\nadd_executable(mosquitto_rr rr_client.c pub_shared.c sub_client_output.c)\n\n\nif (WITH_THREADING AND NOT WIN32)\n\tset(THREADS_PREFER_PTHREAD_FLAG ON)\n\tfind_package(Threads REQUIRED)\n\n\ttarget_link_libraries(client-common INTERFACE Threads::Threads)\nendif()\n\nif(CJSON_FOUND)\n\ttarget_link_libraries(client-common INTERFACE cJSON)\nendif()\n\nif(WITH_STATIC_LIBRARIES)\n\ttarget_link_libraries(client-common INTERFACE libmosquitto_static)\nelse()\n\ttarget_link_libraries(client-common INTERFACE libmosquitto)\nendif()\n\nif(QNX)\n\ttarget_link_libraries(client-common INTERFACE socket)\nendif()\n\ntarget_link_libraries(mosquitto_pub PRIVATE client-common libmosquitto_common)\ntarget_link_libraries(mosquitto_sub PRIVATE client-common libmosquitto_common)\ntarget_link_libraries(mosquitto_rr PRIVATE client-common libmosquitto_common)\n\ninstall(TARGETS mosquitto_pub RUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\")\ninstall(TARGETS mosquitto_sub RUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\")\ninstall(TARGETS mosquitto_rr RUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\")\n"
  },
  {
    "path": "client/Makefile",
    "content": "R=..\ninclude ${R}/config.mk\n\n.PHONY: all install uninstall reallyclean clean static static_pub static_sub static_rr\n\nifeq ($(WITH_SHARED_LIBRARIES),yes)\nSHARED_DEP:=${R}/lib/libmosquitto.so.${SOVERSION}\nendif\n\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LDADD+=-lcjson ${SHARED_DEP} ${LIBMOSQ_COMMON}\nSTATIC_LDADD+=-lcjson ${LIBMOSQ_COMMON}\n\nifeq ($(WITH_SOCKS),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_SOCKS\nendif\n\nifeq ($(WITH_THREADING),yes)\n\tLOCAL_CFLAGS+=-pthread\n\tLOCAL_CPPFLAGS+=-DWITH_THREADING\n\tLOCAL_LDFLAGS+=-pthread\n\tSTATIC_LDADD+=-pthread\nendif\n\nifeq ($(WITH_TLS),yes)\n\tLOCAL_LDADD+=-lssl -lcrypto\n\tSTATIC_LDADD+= -lssl -lcrypto\nendif\n\nifeq ($(UNAME),AIX)\n\tLOCAL_LDFLAGS+=-Wl,-bnoipath\nendif\n\nifeq ($(WITH_SHARED_LIBRARIES),yes)\nALL_DEPS:= mosquitto_pub mosquitto_sub mosquitto_rr\nelse\nifeq ($(WITH_STATIC_LIBRARIES),yes)\nALL_DEPS:= static_pub static_sub static_rr\nendif\nendif\n\nall : ${ALL_DEPS}\n\nstatic : static_pub static_sub static_rr\n\t# This makes mosquitto_pub/sub/rr versions that are statically linked with\n\t# libmosquitto only.\n\nstatic_pub : pub_client.o pub_shared.o client_props.o client_shared.o ${R}/lib/libmosquitto.a\n\t${CROSS_COMPILE}${CC} $^ -o mosquitto_pub ${LOCAL_LDFLAGS} ${STATIC_LIB_DEPS} ${STATIC_LDADD}\n\nstatic_sub : sub_client.o sub_client_output.o client_props.o client_shared.o ${R}/lib/libmosquitto.a\n\t${CROSS_COMPILE}${CC} $^ -o mosquitto_sub ${LOCAL_LDFLAGS} ${STATIC_LIB_DEPS} ${STATIC_LDADD}\n\nstatic_rr : rr_client.o client_props.o client_shared.o pub_shared.o sub_client_output.o ${R}/lib/libmosquitto.a\n\t${CROSS_COMPILE}${CC} $^ -o mosquitto_rr ${LOCAL_LDFLAGS} ${STATIC_LIB_DEPS} ${STATIC_LDADD}\n\nmosquitto_pub : pub_client.o pub_shared.o client_shared.o client_props.o\n\t${CROSS_COMPILE}${CC} $(LOCAL_LDFLAGS) $^ -o $@ $(LOCAL_LDADD)\n\nmosquitto_sub : sub_client.o sub_client_output.o client_shared.o client_props.o\n\t${CROSS_COMPILE}${CC} $(LOCAL_LDFLAGS) $^ -o $@ $(LOCAL_LDADD)\n\nmosquitto_rr : rr_client.o client_shared.o client_props.o pub_shared.o sub_client_output.o\n\t${CROSS_COMPILE}${CC} $(LOCAL_LDFLAGS) $^ -o $@ $(LOCAL_LDADD)\n\npub_client.o : pub_client.c ${SHARED_DEP}\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\npub_shared.o : pub_shared.c ${SHARED_DEP}\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nsub_client.o : sub_client.c ${SHARED_DEP}\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nsub_client_output.o : sub_client_output.c sub_client_output.h ${SHARED_DEP}\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nrr_client.o : rr_client.c ${SHARED_DEP}\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nclient_shared.o : client_shared.c client_shared.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nclient_props.o : client_props.c client_shared.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n# The \"testing\" target is intended to make it easy to compile a quick client\n# for testing purposes. testing.c should not be committed as a file.\ntesting : testing.o\n\t${CROSS_COMPILE}${CC} $() $^ -o $@ $(LOCAL_LDADD) $(LOCAL_LDFLAGS)\n\ntesting.o : testing.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n\n${R}/lib/libmosquitto.so.${SOVERSION} :\n\t$(MAKE) -C ${R}/lib\n\n${R}/lib/libmosquitto.a :\n\t$(MAKE) -C ${R}/lib libmosquitto.a\n\ninstall : all\n\t$(INSTALL) -d \"${DESTDIR}$(prefix)/bin\"\n\t$(INSTALL) ${STRIP_OPTS} mosquitto_pub \"${DESTDIR}${prefix}/bin/mosquitto_pub\"\n\t$(INSTALL) ${STRIP_OPTS} mosquitto_sub \"${DESTDIR}${prefix}/bin/mosquitto_sub\"\n\t$(INSTALL) ${STRIP_OPTS} mosquitto_rr \"${DESTDIR}${prefix}/bin/mosquitto_rr\"\n\nuninstall :\n\t-rm -f \"${DESTDIR}${prefix}/bin/mosquitto_pub\"\n\t-rm -f \"${DESTDIR}${prefix}/bin/mosquitto_sub\"\n\t-rm -f \"${DESTDIR}${prefix}/bin/mosquitto_rr\"\n\nreallyclean : clean\n\nclean :\n\t-rm -f *.o mosquitto_pub mosquitto_sub mosquitto_rr *.gcda *.gcno\n"
  },
  {
    "path": "client/args.txt",
    "content": "A - PUB,RR,SUB     (bind to address)\na\nB\nb\nC - SUB            (message count)\nc - PUB,RR,SUB     (clean session)\nD - PUB,RR,SUB     (properties)\nd - PUB,RR,SUB     (debug log)\nE - SUB            (exit after subscribe)\ne - RR             (response topic)\nF - RR,SUB         (output format)\nf - PUB,RR         (file input)\nG\ng\nH\nh - PUB,RR,SUB     (host)\nI - PUB,RR,SUB     (client id prefix)\ni - PUB,RR,SUB     (client id)\nJ\nj\nK\nk - PUB,RR,SUB     (keepalive)\nL - PUB,RR,SUB     (connect url)\nl - PUB            (stdin input)\nM - PUB,RR,SUB     (max inflight)\nm - PUB,RR         (message input)\nN - RR,SUB         (no end of line)\nn - PUB,RR         (null message)\nO\no - CTRL,PUB,RR,SUB (options file)\nP - PUB,RR,SUB     (password)\np - PUB,RR,SUB     (port)\nQ\nq - PUB,RR,SUB     (qos)\nR - RR,SUB         (don't show retained)\nr - PUB,RR         (retain)\nS - PUB,RR,SUB     (SRV lookups)\ns - PUB,RR         (stdin input)\nT - SUB            (filter out topic)\nt - PUB,RR,SUB     (topic)\nU - SUB            (unsubscribe)\nu - PUB,RR,SUB     (username)\nV - PUB,RR,SUB     (version output)\nv - RR,SUB         (verbose)\nW - SUB            (timeout)\nw - SUB            (watch)\nX\nx - PUB,RR,SUB     (session-expiry-interval)\nY\ny\nZ\nz\n"
  },
  {
    "path": "client/client_props.c",
    "content": "/*\nCopyright (c) 2018-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#ifndef WIN32\n#include <unistd.h>\n#include <strings.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#define snprintf sprintf_s\n#define strncasecmp _strnicmp\n#endif\n\n#include \"mosquitto.h\"\n#include \"client_shared.h\"\n\nenum prop_type {\n\tPROP_TYPE_BYTE,\n\tPROP_TYPE_INT16,\n\tPROP_TYPE_INT32,\n\tPROP_TYPE_BINARY,\n\tPROP_TYPE_STRING,\n\tPROP_TYPE_STRING_PAIR,\n};\n\n\n/* This parses property inputs. It should work for any command type, but is limited at the moment.\n *\n * Format:\n *\n * command property value\n * command property key value\n *\n * Example:\n *\n * publish message-expiry-interval 32\n * connect user-property key value\n */\n\n\nint cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx)\n{\n\tchar *cmdname = NULL, *propname = NULL;\n\tchar *key = NULL, *value = NULL;\n\tint cmd, identifier, type;\n\tmosquitto_property **proplist;\n\tint rc;\n\tlong tmpl;\n\tsize_t szt;\n\n\t/* idx now points to \"command\" */\n\tif((*idx)+2 > argc-1){\n\t\t/* Not enough args */\n\t\tfprintf(stderr, \"Error: --property argument given but not enough arguments specified.\\n\\n\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcmdname = argv[*idx];\n\tif(mosquitto_string_to_command(cmdname, &cmd)){\n\t\tfprintf(stderr, \"Error: Invalid command %s given in --property argument.\\n\\n\", cmdname);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tpropname = argv[(*idx)+1];\n\tif(mosquitto_string_to_property_info(propname, &identifier, &type)){\n\t\tfprintf(stderr, \"Error: Invalid property name %s given in --property argument.\\n\\n\", propname);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(mosquitto_property_check_command(cmd, identifier)){\n\t\tfprintf(stderr, \"Error: %s property not allowed for %s in --property argument.\\n\\n\", propname, cmdname);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(identifier == MQTT_PROP_USER_PROPERTY){\n\t\tif((*idx)+3 > argc-1){\n\t\t\t/* Not enough args */\n\t\t\tfprintf(stderr, \"Error: --property argument given but not enough arguments specified.\\n\\n\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\tkey = argv[(*idx)+2];\n\t\tvalue = argv[(*idx)+3];\n\t\t(*idx) += 3;\n\t}else{\n\t\tvalue = argv[(*idx)+2];\n\t\t(*idx) += 2;\n\t}\n\n\tswitch(cmd){\n\t\tcase CMD_CONNECT:\n\t\t\tproplist = &cfg->connect_props;\n\t\t\tbreak;\n\n\t\tcase CMD_PUBLISH:\n\t\t\tif(identifier == MQTT_PROP_TOPIC_ALIAS){\n\t\t\t\tcfg->have_topic_alias = true;\n\t\t\t}\n\t\t\tif(identifier == MQTT_PROP_SUBSCRIPTION_IDENTIFIER){\n\t\t\t\tfprintf(stderr, \"Error: %s property not supported for %s in --property argument.\\n\\n\", propname, cmdname);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tproplist = &cfg->publish_props;\n\t\t\tbreak;\n\n\t\tcase CMD_SUBSCRIBE:\n\t\t\tproplist = &cfg->subscribe_props;\n\t\t\tbreak;\n\n\t\tcase CMD_UNSUBSCRIBE:\n\t\t\tproplist = &cfg->unsubscribe_props;\n\t\t\tbreak;\n\n\t\tcase CMD_DISCONNECT:\n\t\t\tproplist = &cfg->disconnect_props;\n\t\t\tbreak;\n\n\t\tcase CMD_AUTH:\n\t\t\tfprintf(stderr, \"Error: %s property not supported for %s in --property argument.\\n\\n\", propname, cmdname);\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\n\t\tcase CMD_WILL:\n\t\t\tproplist = &cfg->will_props;\n\t\t\tbreak;\n\n\t\tcase CMD_PUBACK:\n\t\tcase CMD_PUBREC:\n\t\tcase CMD_PUBREL:\n\t\tcase CMD_PUBCOMP:\n\t\tcase CMD_SUBACK:\n\t\tcase CMD_UNSUBACK:\n\t\t\tfprintf(stderr, \"Error: %s property not supported for %s in --property argument.\\n\\n\", propname, cmdname);\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\n\t\tdefault:\n\t\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tswitch(type){\n\t\tcase MQTT_PROP_TYPE_BYTE:\n\t\t\ttmpl = atol(value);\n\t\t\tif(tmpl < 0 || tmpl > UINT8_MAX){\n\t\t\t\tfprintf(stderr, \"Error: Property value (%ld) out of range for property %s.\\n\\n\", tmpl, propname);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\trc = mosquitto_property_add_byte(proplist, identifier, (uint8_t )tmpl);\n\t\t\tbreak;\n\t\tcase MQTT_PROP_TYPE_INT16:\n\t\t\ttmpl = atol(value);\n\t\t\tif(tmpl < 0 || tmpl > UINT16_MAX){\n\t\t\t\tfprintf(stderr, \"Error: Property value (%ld) out of range for property %s.\\n\\n\", tmpl, propname);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\trc = mosquitto_property_add_int16(proplist, identifier, (uint16_t )tmpl);\n\t\t\tbreak;\n\t\tcase MQTT_PROP_TYPE_INT32:\n\t\t\ttmpl = atol(value);\n\t\t\tif(tmpl < 0 || tmpl > UINT32_MAX){\n\t\t\t\tfprintf(stderr, \"Error: Property value (%ld) out of range for property %s.\\n\\n\", tmpl, propname);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\trc = mosquitto_property_add_int32(proplist, identifier, (uint32_t )tmpl);\n\t\t\tbreak;\n\t\tcase MQTT_PROP_TYPE_VARINT:\n\t\t\ttmpl = atol(value);\n\t\t\tif(tmpl < 0 || tmpl > UINT32_MAX){\n\t\t\t\tfprintf(stderr, \"Error: Property value (%ld) out of range for property %s.\\n\\n\", tmpl, propname);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\trc = mosquitto_property_add_varint(proplist, identifier, (uint32_t )tmpl);\n\t\t\tbreak;\n\t\tcase MQTT_PROP_TYPE_BINARY:\n\t\t\tszt = strlen(value);\n\t\t\tif(szt > UINT16_MAX){\n\t\t\t\tfprintf(stderr, \"Error: Property value too long for property %s.\\n\\n\", propname);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\trc = mosquitto_property_add_binary(proplist, identifier, value, (uint16_t )szt);\n\t\t\tbreak;\n\t\tcase MQTT_PROP_TYPE_STRING:\n\t\t\trc = mosquitto_property_add_string(proplist, identifier, value);\n\t\t\tbreak;\n\t\tcase MQTT_PROP_TYPE_STRING_PAIR:\n\t\t\trc = mosquitto_property_add_string_pair(proplist, identifier, key, value);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(rc){\n\t\tfprintf(stderr, \"Error adding property %s %d\\n\", propname, type);\n\t\treturn rc;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "client/client_shared.c",
    "content": "/*\nCopyright (c) 2014-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#ifndef WIN32\n#  include <signal.h>\n#  include <strings.h>\n#  include <unistd.h>\n#else\n#  include <process.h>\n#  include <winsock2.h>\n#  define snprintf sprintf_s\n#  define strncasecmp _strnicmp\n#endif\n\n#include <mosquitto.h>\n#include \"client_shared.h\"\n\n#ifdef WITH_SOCKS\nstatic int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url);\n#endif\nstatic int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]);\n#ifdef WITH_TLS\nstatic void tls_keylog_callback(const SSL *ssl, const char *line);\nstatic int tls_ex_index_cfg = -1;\n#endif\n\nconst char hexseplist[32] = {\n\t'!', '\"', '#', '$', '&', '\\'', '(', ')',\n\t'*', '+', ',', '-', '.', '/', ':', ';',\n\t'<', '=', '>', '?', '@', '[', '\\\\', ']',\n\t'^', '_', '`', '{', '|', '}', '~', ' ',\n};\n\n\nstatic int check_format(const char *str)\n{\n\tsize_t i;\n\tsize_t len;\n\n\tlen = strlen(str);\n\tfor(i=0; i<len; i++){\n\t\tif(str[i] == '%'){\n\t\t\tif(i == len-1){\n\t\t\t\t/* error */\n\t\t\t\tfprintf(stderr, \"Error: Incomplete format specifier.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(str[i+1] == '0' || str[i+1] == '-'){\n\t\t\t\t\t/* Flag characters */\n\t\t\t\t\ti++;\n\t\t\t\t\tif(i == len-1){\n\t\t\t\t\t\t/* error */\n\t\t\t\t\t\tfprintf(stderr, \"Error: Incomplete format specifier.\\n\");\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* Field width */\n\t\t\t\twhile(str[i+1] >= '0' && str[i+1] <= '9'){\n\t\t\t\t\ti++;\n\t\t\t\t\tif(i == len-1){\n\t\t\t\t\t\t/* error */\n\t\t\t\t\t\tfprintf(stderr, \"Error: Incomplete format specifier.\\n\");\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif(str[i+1] == '.'){\n\t\t\t\t\t/* Precision specifier */\n\t\t\t\t\ti++;\n\t\t\t\t\tif(i == len-1){\n\t\t\t\t\t\t/* error */\n\t\t\t\t\t\tfprintf(stderr, \"Error: Incomplete format specifier.\\n\");\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\t/* Precision */\n\t\t\t\t\twhile(str[i+1] >= '0' && str[i+1] <= '9'){\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tif(i == len-1){\n\t\t\t\t\t\t\t/* error */\n\t\t\t\t\t\t\tfprintf(stderr, \"Error: Incomplete format specifier.\\n\");\n\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* Hex field separator character */\n\t\t\t\tfor(size_t j=0; j<sizeof(hexseplist); j++){\n\t\t\t\t\tif(str[i+1] == hexseplist[j]){\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif(str[i+1] == '%'){\n\t\t\t\t\t/* Print %, ignore */\n\t\t\t\t}else if(str[i+1] == 'A'){\n\t\t\t\t\t/* MQTT v5 property topic-alias */\n\t\t\t\t}else if(str[i+1] == 'C'){\n\t\t\t\t\t/* MQTT v5 property content-type */\n\t\t\t\t}else if(str[i+1] == 'D'){\n\t\t\t\t\t/* MQTT v5 property correlation-data */\n\t\t\t\t}else if(str[i+1] == 'E'){\n\t\t\t\t\t/* MQTT v5 property message-expiry-interval */\n\t\t\t\t}else if(str[i+1] == 'F'){\n\t\t\t\t\t/* MQTT v5 property payload-format-indicator */\n\t\t\t\t}else if(str[i+1] == 'I'){\n\t\t\t\t\t/* ISO 8601 date+time */\n\t\t\t\t}else if(str[i+1] == 'l'){\n\t\t\t\t\t/* payload length */\n\t\t\t\t}else if(str[i+1] == 'm'){\n\t\t\t\t\t/* mid */\n\t\t\t\t}else if(str[i+1] == 'P'){\n\t\t\t\t\t/* MQTT v5 property user-property */\n\t\t\t\t}else if(str[i+1] == 'p'){\n\t\t\t\t\t/* payload */\n\t\t\t\t}else if(str[i+1] == 'q'){\n\t\t\t\t\t/* qos */\n\t\t\t\t}else if(str[i+1] == 'R'){\n\t\t\t\t\t/* MQTT v5 property response-topic */\n\t\t\t\t}else if(str[i+1] == 'S'){\n\t\t\t\t\t/* MQTT v5 property subscription-identifier */\n\t\t\t\t}else if(str[i+1] == 'r'){\n\t\t\t\t\t/* retain */\n\t\t\t\t}else if(str[i+1] == 't'){\n\t\t\t\t\t/* topic */\n\t\t\t\t}else if(str[i+1] == 'j'){\n\t\t\t\t\t/* JSON output, escaped payload */\n\t\t\t\t}else if(str[i+1] == 'J'){\n\t\t\t\t\t/* JSON output, assuming JSON payload */\n\t\t\t\t}else if(str[i+1] == 'U'){\n\t\t\t\t\t/* Unix time+nanoseconds */\n#ifdef WIN32\n\t\t\t\t\tfprintf(stderr, \"Error: The %%U format option is not supported on Windows.\\n\");\n\t\t\t\t\treturn 1;\n#endif\n\t\t\t\t}else if(str[i+1] == 'x' || str[i+1] == 'X'){\n\t\t\t\t\t/* payload in hex */\n\t\t\t\t}else if(str[i+1] == 'f' || str[i+1] == 'd'){\n\t\t\t\t\t/* payload in float */\n#ifndef __STDC_IEC_559__\n\t\t\t\t\tfprintf(stderr, \"Error: Can't print float, missing __STDC_IEC_559__ standard support.\\n\");\n\t\t\t\t\treturn 1;\n#endif\n\t\t\t\t}else{\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid format specifier '%c'.\\n\", str[i+1]);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}else if(str[i] == '@'){\n\t\t\tif(i == len-1){\n\t\t\t\t/* error */\n\t\t\t\tfprintf(stderr, \"Error: Incomplete format specifier.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(str[i] == '\\\\'){\n\t\t\tif(i == len-1){\n\t\t\t\t/* error */\n\t\t\t\tfprintf(stderr, \"Error: Incomplete escape specifier.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tswitch(str[i+1]){\n\t\t\t\t\tcase '\\\\': /* '\\' */\n\t\t\t\t\tcase '0':  /* 0 (NULL) */\n\t\t\t\t\tcase 'a':  /* alert */\n\t\t\t\t\tcase 'e':  /* escape */\n\t\t\t\t\tcase 'n':  /* new line */\n\t\t\t\t\tcase 'r':  /* carriage return */\n\t\t\t\t\tcase 't':  /* horizontal tab */\n\t\t\t\t\tcase 'v':  /* vertical tab */\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tfprintf(stderr, \"Error: Invalid escape specifier '%c'.\\n\", str[i+1]);\n\t\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\nstatic void init_config(struct mosq_config *cfg, int pub_or_sub)\n{\n\tmemset(cfg, 0, sizeof(*cfg));\n\tcfg->port = PORT_UNDEFINED;\n\tcfg->max_inflight = 20;\n\tcfg->keepalive = 60;\n\tcfg->clean_session = true;\n\tcfg->eol = true;\n\tcfg->repeat_count = 1;\n\tcfg->repeat_delay.tv_sec = 0;\n\tcfg->repeat_delay.tv_usec = 0;\n\tcfg->random_filter = 10000;\n\tif(pub_or_sub == CLIENT_RR){\n\t\tcfg->protocol_version = MQTT_PROTOCOL_V5;\n\t\tcfg->msg_count = 1;\n\t}else{\n\t\tcfg->protocol_version = MQTT_PROTOCOL_V311;\n\t}\n\tcfg->session_expiry_interval = -1; /* -1 means unset here, the user can't set it to -1. */\n\tcfg->transport = MOSQ_T_TCP;\n}\n\n\nvoid client_config_cleanup(struct mosq_config *cfg)\n{\n\tint i;\n\tfree(cfg->id);\n\tfree(cfg->id_prefix);\n\tfree(cfg->host);\n\tfree(cfg->file_input);\n\tmosquitto_FREE(cfg->message);\n\tfree(cfg->topic);\n\tfree(cfg->bind_address);\n\tfree(cfg->username);\n\tfree(cfg->password);\n\tfree(cfg->will_topic);\n\tfree(cfg->will_payload);\n\tfree(cfg->format);\n\tfree(cfg->response_topic);\n#ifdef WITH_TLS\n\tfree(cfg->cafile);\n\tfree(cfg->capath);\n\tfree(cfg->certfile);\n\tfree(cfg->keyfile);\n\tfree(cfg->ciphers);\n\tfree(cfg->tls_alpn);\n\tfree(cfg->tls_version);\n\tfree(cfg->tls_engine);\n\tfree(cfg->tls_engine_kpass_sha1);\n\tfree(cfg->keyform);\n#  ifdef FINAL_WITH_TLS_PSK\n\tfree(cfg->psk);\n\tfree(cfg->psk_identity);\n#  endif\n#endif\n\tif(cfg->topics){\n\t\tfor(i=0; i<cfg->topic_count; i++){\n\t\t\tfree(cfg->topics[i]);\n\t\t}\n\t\tfree(cfg->topics);\n\t}\n\tif(cfg->filter_outs){\n\t\tfor(i=0; i<cfg->filter_out_count; i++){\n\t\t\tfree(cfg->filter_outs[i]);\n\t\t}\n\t\tfree(cfg->filter_outs);\n\t}\n\tif(cfg->unsub_topics){\n\t\tfor(i=0; i<cfg->unsub_topic_count; i++){\n\t\t\tfree(cfg->unsub_topics[i]);\n\t\t}\n\t\tfree(cfg->unsub_topics);\n\t}\n#ifdef WITH_SOCKS\n\tfree(cfg->socks5_host);\n\tfree(cfg->socks5_username);\n\tfree(cfg->socks5_password);\n#endif\n\tmosquitto_property_free_all(&cfg->connect_props);\n\tmosquitto_property_free_all(&cfg->publish_props);\n\tmosquitto_property_free_all(&cfg->subscribe_props);\n\tmosquitto_property_free_all(&cfg->unsubscribe_props);\n\tmosquitto_property_free_all(&cfg->disconnect_props);\n\tmosquitto_property_free_all(&cfg->will_props);\n\tfree(cfg->options_file);\n}\n\n\n/* Find if there is \"-o\" in the options */\nstatic int client_config_options_file(struct mosq_config *cfg, int argc, char *argv[])\n{\n\tint i;\n\n\tfor(i=1; i<argc; i++){\n\t\tif(!strcmp(argv[i], \"-o\")){\n\t\t\tif(cfg->options_file){\n\t\t\t\tfprintf(stderr, \"Error: Duplicate -o argument given.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -o argument given but no options file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->options_file = strdup(argv[i+1]);\n\t\t\t}\n\t\t}\n\t}\n\treturn 0;\n}\n\n\nint client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])\n{\n\tint rc;\n\tFILE *fptr;\n\tchar line[1024];\n\tint count;\n\tchar *loc = NULL;\n\tsize_t len;\n\tchar *args[3];\n\n#ifndef WIN32\n\tchar *env;\n#else\n\tchar env[1024];\n#endif\n\targs[0] = NULL;\n\n\tinit_config(cfg, pub_or_sub);\n\n\tif(client_config_options_file(cfg, argc, argv)){\n\t\treturn 1;\n\t}\n\n\tif(cfg->options_file == NULL){\n\t\t/* Default config file */\n#ifndef WIN32\n\t\tenv = getenv(\"XDG_CONFIG_HOME\");\n\t\tif(env){\n\t\t\tlen = strlen(env) + strlen(\"/mosquitto_pub\") + 1;\n\t\t\tloc = malloc(len);\n\t\t\tif(!loc){\n\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\tsnprintf(loc, len, \"%s/mosquitto_pub\", env);\n\t\t\t}else if(pub_or_sub == CLIENT_SUB){\n\t\t\t\tsnprintf(loc, len, \"%s/mosquitto_sub\", env);\n\t\t\t}else{\n\t\t\t\tsnprintf(loc, len, \"%s/mosquitto_rr\", env);\n\t\t\t}\n\t\t\tloc[len-1] = '\\0';\n\t\t}else{\n\t\t\tenv = getenv(\"HOME\");\n\t\t\tif(env){\n\t\t\t\tlen = strlen(env) + strlen(\"/.config/mosquitto_pub\") + 1;\n\t\t\t\tloc = malloc(len);\n\t\t\t\tif(!loc){\n\t\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\t\tsnprintf(loc, len, \"%s/.config/mosquitto_pub\", env);\n\t\t\t\t}else if(pub_or_sub == CLIENT_SUB){\n\t\t\t\t\tsnprintf(loc, len, \"%s/.config/mosquitto_sub\", env);\n\t\t\t\t}else{\n\t\t\t\t\tsnprintf(loc, len, \"%s/.config/mosquitto_rr\", env);\n\t\t\t\t}\n\t\t\t\tloc[len-1] = '\\0';\n\t\t\t}\n\t\t}\n\n#else\n\t\trc = GetEnvironmentVariable(\"USERPROFILE\", env, 1024);\n\t\tif(rc > 0 && rc < 1024){\n\t\t\tlen = strlen(env) + strlen(\"\\\\mosquitto_pub.conf\") + 1;\n\t\t\tloc = malloc(len);\n\t\t\tif(!loc){\n\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\tsnprintf(loc, len, \"%s\\\\mosquitto_pub.conf\", env);\n\t\t\t}else if(pub_or_sub == CLIENT_SUB){\n\t\t\t\tsnprintf(loc, len, \"%s\\\\mosquitto_sub.conf\", env);\n\t\t\t}else{\n\t\t\t\tsnprintf(loc, len, \"%s\\\\mosquitto_rr.conf\", env);\n\t\t\t}\n\t\t\tloc[len-1] = '\\0';\n\t\t}\n#endif\n\t}\n\n\tif(cfg->options_file){\n\t\tfptr = fopen(cfg->options_file, \"rt\");\n\t}else if(loc){\n\t\tfptr = fopen(loc, \"rt\");\n\t\tfree(loc);\n\t\tloc = NULL;\n\t}else{\n\t\tfptr = NULL;\n\t}\n\tif(fptr){\n\t\twhile(fgets(line, 1024, fptr)){\n\t\t\tif(line[0] == '#'){\n\t\t\t\t/* Comments */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twhile(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){\n\t\t\t\tline[strlen(line)-1] = 0;\n\t\t\t}\n\t\t\t/* All offset by one \"args\" here, because real argc/argv has\n\t\t\t * program name as the first entry. */\n\t\t\targs[1] = strtok(line, \" \");\n\t\t\tif(args[1]){\n\t\t\t\targs[2] = strtok(NULL, \"\");\n\t\t\t\tif(args[2]){\n\t\t\t\t\tcount = 3;\n\t\t\t\t}else{\n\t\t\t\t\tcount = 2;\n\t\t\t\t}\n\t\t\t\trc = client_config_line_proc(cfg, pub_or_sub, count, args);\n\t\t\t\tif(rc){\n\t\t\t\t\tfclose(fptr);\n\t\t\t\t\tfree(loc);\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfclose(fptr);\n\t}\n\n\t/* Deal with real argc/argv */\n\trc = client_config_line_proc(cfg, pub_or_sub, argc, argv);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(cfg->will_payload && !cfg->will_topic){\n\t\tfprintf(stderr, \"Error: Will payload given, but no will topic given.\\n\");\n\t\treturn 1;\n\t}\n\tif(cfg->will_retain && !cfg->will_topic){\n\t\tfprintf(stderr, \"Error: Will retain given, but no will topic given.\\n\");\n\t\treturn 1;\n\t}\n#ifdef WITH_TLS\n\tif((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){\n\t\tfprintf(stderr, \"Error: Both certfile and keyfile must be provided if one of them is set.\\n\");\n\t\treturn 1;\n\t}\n\tif((cfg->keyform && !cfg->keyfile)){\n\t\tfprintf(stderr, \"Error: If keyform is set, keyfile must be also specified.\\n\");\n\t\treturn 1;\n\t}\n\tif((cfg->tls_engine_kpass_sha1 && (!cfg->keyform || !cfg->tls_engine))){\n\t\tfprintf(stderr, \"Error: when using tls-engine-kpass-sha1, both tls-engine and keyform must also be provided.\\n\");\n\t\treturn 1;\n\t}\n#endif\n#ifdef FINAL_WITH_TLS_PSK\n\tif((cfg->cafile || cfg->capath) && cfg->psk){\n\t\tfprintf(stderr, \"Error: Only one of --psk or --cafile/--capath may be used at once.\\n\");\n\t\treturn 1;\n\t}\n\tif(cfg->psk && !cfg->psk_identity){\n\t\tfprintf(stderr, \"Error: --psk-identity required if --psk used.\\n\");\n\t\treturn 1;\n\t}\n#endif\n\n\tif(cfg->protocol_version == 5){\n\t\tif(cfg->clean_session == false && cfg->session_expiry_interval == -1){\n\t\t\t/* User hasn't set session-expiry-interval, but has cleared clean\n\t\t\t * session so default to persistent session. */\n\t\t\tcfg->session_expiry_interval = UINT32_MAX;\n\t\t}\n\t\tif(cfg->session_expiry_interval > 0){\n\t\t\tif(cfg->session_expiry_interval == UINT32_MAX && (cfg->id_prefix || !cfg->id)){\n\t\t\t\tfprintf(stderr, \"Error: You must provide a client id if you are using an infinite session expiry interval.\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\trc = mosquitto_property_add_int32(&cfg->connect_props, MQTT_PROP_SESSION_EXPIRY_INTERVAL, (uint32_t )cfg->session_expiry_interval);\n\t\t\tif(rc){\n\t\t\t\tfprintf(stderr, \"Error adding property session-expiry-interval\\n\");\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(cfg->clean_session == false && (cfg->id_prefix || !cfg->id)){\n\t\t\tfprintf(stderr, \"Error: You must provide a client id if you are using the -c option.\\n\");\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tif(pub_or_sub == CLIENT_SUB){\n\t\tif(cfg->topic_count == 0 && cfg->unsub_topic_count == 0){\n\t\t\tfprintf(stderr, \"Error: You must specify a topic to subscribe to (-t) or unsubscribe from (-U).\\n\");\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tif(!cfg->host){\n\t\tcfg->host = strdup(\"localhost\");\n\t\tif(!cfg->host){\n\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int cfg_add_topic(struct mosq_config *cfg, int type, char *topic, const char *arg)\n{\n\tif(mosquitto_validate_utf8(topic, (int )strlen(topic))){\n\t\tfprintf(stderr, \"Error: Malformed UTF-8 in %s argument.\\n\\n\", arg);\n\t\treturn 1;\n\t}\n\tif(type == CLIENT_PUB || type == CLIENT_RR){\n\t\tif(mosquitto_pub_topic_check(topic) == MOSQ_ERR_INVAL){\n\t\t\tfprintf(stderr, \"Error: Invalid publish topic '%s', does it contain '+' or '#'?\\n\", topic);\n\t\t\treturn 1;\n\t\t}\n\t\tcfg->topic = strdup(topic);\n\t}else if(type == CLIENT_RESPONSE_TOPIC){\n\t\tif(mosquitto_pub_topic_check(topic) == MOSQ_ERR_INVAL){\n\t\t\tfprintf(stderr, \"Error: Invalid response topic '%s', does it contain '+' or '#'?\\n\", topic);\n\t\t\treturn 1;\n\t\t}\n\t\tcfg->response_topic = strdup(topic);\n\t}else{\n\t\tif(mosquitto_sub_topic_check(topic) == MOSQ_ERR_INVAL){\n\t\t\tfprintf(stderr, \"Error: Invalid subscription topic '%s', are all '+' and '#' wildcards correct?\\n\", topic);\n\t\t\treturn 1;\n\t\t}\n\t\tcfg->topic_count++;\n\t\tcfg->topics = realloc(cfg->topics, (size_t )cfg->topic_count*sizeof(char *));\n\t\tif(!cfg->topics){\n\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\treturn 1;\n\t\t}\n\t\tcfg->topics[cfg->topic_count-1] = strdup(topic);\n\t}\n\treturn 0;\n}\n\n\n/* Process a tokenised single line from a file or set of real argc/argv */\nint client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])\n{\n\tint i;\n\tint tmpi;\n\tfloat f;\n\tsize_t szt;\n\n\tfor(i=1; i<argc; i++){\n\t\tif(!strcmp(argv[i], \"-A\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -A argument given but no address specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->bind_address = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n#ifdef WITH_TLS\n\t\t}else if(!strcmp(argv[i], \"--cafile\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --cafile argument given but no file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->cafile = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--capath\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --capath argument given but no directory specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->capath = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--cert\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --cert argument given but no file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->certfile = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--ciphers\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --ciphers argument given but no ciphers specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->ciphers = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n#endif\n\t\t}else if(!strcmp(argv[i], \"-C\")){\n\t\t\tif(pub_or_sub != CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}else{\n\t\t\t\tif(i==argc-1){\n\t\t\t\t\tfprintf(stderr, \"Error: -C argument given but no count specified.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}else{\n\t\t\t\t\tcfg->msg_count = atoi(argv[i+1]);\n\t\t\t\t\tif(cfg->msg_count < 1){\n\t\t\t\t\t\tfprintf(stderr, \"Error: Invalid message count \\\"%d\\\".\\n\\n\", cfg->msg_count);\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}else if(!strcmp(argv[i], \"-c\") || !strcmp(argv[i], \"--disable-clean-session\")){\n\t\t\tcfg->clean_session = false;\n\t\t}else if(!strcmp(argv[i], \"-d\") || !strcmp(argv[i], \"--debug\")){\n\t\t\tcfg->debug = true;\n\t\t}else if(!strcmp(argv[i], \"-D\") || !strcmp(argv[i], \"--property\")){\n\t\t\ti++;\n\t\t\tif(cfg_parse_property(cfg, argc, argv, &i)){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tcfg->protocol_version = MQTT_PROTOCOL_V5;\n\t\t}else if(!strcmp(argv[i], \"-e\")){\n\t\t\tif(pub_or_sub != CLIENT_RR){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -e argument given but no response topic specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(cfg_add_topic(cfg, CLIENT_RESPONSE_TOPIC, argv[i+1], \"-e\")){\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-E\")){\n\t\t\tif(pub_or_sub != CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->exit_after_sub = true;\n\t\t}else if(!strcmp(argv[i], \"-f\") || !strcmp(argv[i], \"--file\")){\n\t\t\tif(pub_or_sub == CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(cfg->pub_mode != MSGMODE_NONE){\n\t\t\t\tfprintf(stderr, \"Error: Only one type of message can be sent at once.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else if(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -f argument given but no file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->pub_mode = MSGMODE_FILE;\n\t\t\t\tcfg->file_input = strdup(argv[i+1]);\n\t\t\t\tif(!cfg->file_input){\n\t\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-F\")){\n\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -F argument given but no format specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->format = strdup(argv[i+1]);\n\t\t\t\tif(!cfg->format){\n\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif(check_format(cfg->format)){\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--help\")){\n\t\t\treturn 2;\n\t\t}else if(!strcmp(argv[i], \"-h\") || !strcmp(argv[i], \"--host\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -h argument given but no host specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->host = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n#ifdef WITH_TLS\n\t\t}else if(!strcmp(argv[i], \"--insecure\")){\n\t\t\tcfg->insecure = true;\n#endif\n\t\t}else if(!strcmp(argv[i], \"-i\") || !strcmp(argv[i], \"--id\")){\n\t\t\tif(cfg->id_prefix){\n\t\t\t\tfprintf(stderr, \"Error: -i and -I argument cannot be used together.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -i argument given but no id specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->id = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-I\") || !strcmp(argv[i], \"--id-prefix\")){\n\t\t\tif(cfg->id){\n\t\t\t\tfprintf(stderr, \"Error: -i and -I argument cannot be used together.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -I argument given but no id prefix specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->id_prefix = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-k\") || !strcmp(argv[i], \"--keepalive\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -k argument given but no keepalive specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->keepalive = atoi(argv[i+1]);\n\t\t\t\tif(cfg->keepalive<5 || cfg->keepalive>UINT16_MAX){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n#ifdef WITH_TLS\n\t\t}else if(!strcmp(argv[i], \"--key\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --key argument given but no file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->keyfile = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--keyform\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --keyform argument given but no keyform specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->keyform = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n#endif\n\t\t}else if(!strcmp(argv[i], \"-L\") || !strcmp(argv[i], \"--url\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -L argument given but no URL specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tchar *url = argv[i+1];\n\t\t\t\tchar *topic;\n\t\t\t\tchar *tmp;\n\n\t\t\t\tif(!strncasecmp(url, \"mqtt://\", 7)){\n\t\t\t\t\turl += 7;\n\t\t\t\t\tcfg->port = 1883;\n\t\t\t\t}else if(!strncasecmp(url, \"mqtts://\", 8)){\n#ifdef WITH_TLS\n\t\t\t\t\turl += 8;\n\t\t\t\t\tcfg->port = 8883;\n\t\t\t\t\tcfg->tls_use_os_certs = true;\n#else\n\t\t\t\t\tfprintf(stderr, \"Error: TLS support not available.\\n\\n\");\n\t\t\t\t\treturn 1;\n#endif\n\t\t\t\t}else if(!strncasecmp(url, \"ws://\", 5)){\n\t\t\t\t\turl += 5;\n\t\t\t\t\tcfg->port = 1883;\n\t\t\t\t\tcfg->transport = MOSQ_T_WEBSOCKETS;\n\t\t\t\t}else if(!strncasecmp(url, \"wss://\", 6)){\n#ifdef WITH_TLS\n\t\t\t\t\turl += 6;\n\t\t\t\t\tcfg->port = 8883;\n\t\t\t\t\tcfg->tls_use_os_certs = true;\n\t\t\t\t\tcfg->transport = MOSQ_T_WEBSOCKETS;\n#else\n\t\t\t\t\tfprintf(stderr, \"Error: TLS support not available.\\n\\n\");\n\t\t\t\t\treturn 1;\n#endif\n\t\t\t\t}else{\n\t\t\t\t\tfprintf(stderr, \"Error: Unsupported URL scheme.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\ttopic = strchr(url, '/');\n\t\t\t\tif(!topic){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid URL for -L argument specified - topic missing.\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\t*topic++ = 0;\n\n\t\t\t\tif(cfg_add_topic(cfg, pub_or_sub, topic, \"-L topic\")){\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\n\t\t\t\ttmp = strchr(url, '@');\n\t\t\t\tif(tmp){\n\t\t\t\t\tchar *colon;\n\t\t\t\t\t*tmp++ = 0;\n\t\t\t\t\tcolon = strchr(url, ':');\n\t\t\t\t\tif(colon){\n\t\t\t\t\t\t*colon = 0;\n\t\t\t\t\t\tcfg->password = strdup(colon + 1);\n\t\t\t\t\t}\n\t\t\t\t\tcfg->username = strdup(url);\n\t\t\t\t\turl = tmp;\n\t\t\t\t}\n\t\t\t\tcfg->host = url;\n\n\t\t\t\ttmp = strchr(url, ':');\n\t\t\t\tif(tmp){\n\t\t\t\t\t*tmp++ = 0;\n\t\t\t\t\tcfg->port = atoi(tmp);\n\t\t\t\t}\n\t\t\t\t/* Now we've removed the port, time to get the host on the heap */\n\t\t\t\tcfg->host = strdup(cfg->host);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-l\") || !strcmp(argv[i], \"--stdin-line\")){\n\t\t\tif(pub_or_sub != CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(cfg->pub_mode != MSGMODE_NONE){\n\t\t\t\tfprintf(stderr, \"Error: Only one type of message can be sent at once.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->pub_mode = MSGMODE_STDIN_LINE;\n\t\t\t}\n\t\t}else if(!strcmp(argv[i], \"--latency\")){\n\t\t\tif(pub_or_sub != CLIENT_RR){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->measure_latency = true;\n\t\t\tcfg->tcp_nodelay = true; /* Remove influence of nagle */\n\t\t}else if(!strcmp(argv[i], \"-m\") || !strcmp(argv[i], \"--message\")){\n\t\t\tif(pub_or_sub == CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(cfg->pub_mode != MSGMODE_NONE){\n\t\t\t\tfprintf(stderr, \"Error: Only one type of message can be sent at once.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else if(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -m argument given but no message specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->message = mosquitto_strdup(argv[i+1]);\n\t\t\t\tif(cfg->message == NULL){\n\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tszt = strlen(cfg->message);\n\t\t\t\tif(szt > MQTT_MAX_PAYLOAD){\n\t\t\t\t\tfprintf(stderr, \"Error: Message length must be less than %u bytes.\\n\\n\", MQTT_MAX_PAYLOAD);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tcfg->msglen = (int )szt;\n\t\t\t\tcfg->pub_mode = MSGMODE_CMD;\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-M\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -M argument given but max_inflight not specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\ttmpi = atoi(argv[i+1]);\n\t\t\t\tif(tmpi < 1){\n\t\t\t\t\tfprintf(stderr, \"Error: Maximum inflight messages must be greater than 0.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tcfg->max_inflight = (unsigned int )tmpi;\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--message-rate\")){\n\t\t\tif(pub_or_sub != CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->message_rate = true;\n\t\t}else if(!strcmp(argv[i], \"--nodelay\")){\n\t\t\tcfg->tcp_nodelay = true;\n\t\t}else if(!strcmp(argv[i], \"--no-tls\")){\n\t\t\tcfg->no_tls = true;\n\t\t}else if(!strcmp(argv[i], \"-n\") || !strcmp(argv[i], \"--null-message\")){\n\t\t\tif(pub_or_sub == CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(cfg->pub_mode != MSGMODE_NONE){\n\t\t\t\tfprintf(stderr, \"Error: Only one type of message can be sent at once.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->pub_mode = MSGMODE_NULL;\n\t\t\t}\n\t\t}else if(!strcmp(argv[i], \"-N\")){\n\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->eol = false;\n\t\t}else if(!strcmp(argv[i], \"-o\")){\n\t\t\t/* Already handled */\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-p\") || !strcmp(argv[i], \"--port\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -p argument given but no port specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->port = atoi(argv[i+1]);\n\t\t\t\tif(cfg->port<0 || cfg->port>65535){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid port given: %d\\n\", cfg->port);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--pretty\")){\n\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->pretty = true;\n\t\t}else if(!strcmp(argv[i], \"-P\") || !strcmp(argv[i], \"--pw\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -P argument given but no password specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->password = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n#ifdef WITH_SOCKS\n\t\t}else if(!strcmp(argv[i], \"--proxy\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --proxy argument given but no proxy url specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(mosquitto__parse_socks_url(cfg, argv[i+1])){\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n#endif\n#ifdef FINAL_WITH_TLS_PSK\n\t\t}else if(!strcmp(argv[i], \"--psk\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --psk argument given but no key specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->psk = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--psk-identity\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --psk-identity argument given but no identity specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->psk_identity = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n#endif\n\t\t}else if(!strcmp(argv[i], \"-q\") || !strcmp(argv[i], \"--qos\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -q argument given but no QoS specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->qos = atoi(argv[i+1]);\n\t\t\t\tif(cfg->qos<0 || cfg->qos>2){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid QoS given: %d\\n\", cfg->qos);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--quiet\")){\n\t\t\tcfg->quiet = true;\n\t\t}else if(!strcmp(argv[i], \"-r\") || !strcmp(argv[i], \"--retain\")){\n\t\t\tif(pub_or_sub != CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->retain = 1;\n\t\t}else if(!strcmp(argv[i], \"-R\")){\n\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->no_retain = true;\n\t\t\tcfg->sub_opts |= MQTT_SUB_OPT_SEND_RETAIN_NEVER;\n\t\t}else if(!strcmp(argv[i], \"--random-filter\")){\n\t\t\tif(pub_or_sub != CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --random-filter argument given but no chance specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->random_filter = (int)(10.0*atof(argv[i+1]));\n\t\t\t\tif(cfg->random_filter > 10000 || cfg->random_filter < 1){\n\t\t\t\t\tfprintf(stderr, \"Error: --random-filter chance must be between 0.1-100.0\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--remove-retained\")){\n\t\t\tif(pub_or_sub != CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->remove_retained = true;\n\t\t}else if(!strcmp(argv[i], \"--repeat\")){\n\t\t\tif(pub_or_sub != CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --repeat argument given but no count specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->repeat_count = atoi(argv[i+1]);\n\t\t\t\tif(cfg->repeat_count < 1){\n\t\t\t\t\tfprintf(stderr, \"Error: --repeat argument must be >0.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--repeat-delay\")){\n\t\t\tif(pub_or_sub != CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --repeat-delay argument given but no time specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tf = (float )atof(argv[i+1]);\n\t\t\t\tif(f < 0.0f){\n\t\t\t\t\tfprintf(stderr, \"Error: --repeat-delay argument must be >=0.0.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tf *= 1.0e6f;\n\t\t\t\tcfg->repeat_delay.tv_sec = (int)f/1000000;\n\t\t\t\tcfg->repeat_delay.tv_usec = (int)f%1000000;\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--retain-as-published\")){\n\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->sub_opts |= MQTT_SUB_OPT_RETAIN_AS_PUBLISHED;\n\t\t}else if(!strcmp(argv[i], \"--retain-handling\")){\n\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --retain-handling argument given but no option specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(!strcmp(argv[i+1], \"always\")){\n\t\t\t\t\tMQTT_SUB_OPT_SET_RETAIN_HANDLING(cfg->sub_opts, MQTT_SUB_OPT_SEND_RETAIN_ALWAYS);\n\t\t\t\t}else if(!strcmp(argv[i+1], \"new\")){\n\t\t\t\t\tMQTT_SUB_OPT_SET_RETAIN_HANDLING(cfg->sub_opts, MQTT_SUB_OPT_SEND_RETAIN_NEW);\n\t\t\t\t}else if(!strcmp(argv[i+1], \"never\")){\n\t\t\t\t\tMQTT_SUB_OPT_SET_RETAIN_HANDLING(cfg->sub_opts, MQTT_SUB_OPT_SEND_RETAIN_NEVER);\n\t\t\t\t}else{\n\t\t\t\t\tfprintf(stderr, \"Error: Unknown value '%s' for --retain-handling.\\n\\n\", argv[i+1]);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--retained-only\")){\n\t\t\tif(pub_or_sub != CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->retained_only = true;\n\t\t}else if(!strcmp(argv[i], \"-s\") || !strcmp(argv[i], \"--stdin-file\")){\n\t\t\tif(pub_or_sub == CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(cfg->pub_mode != MSGMODE_NONE){\n\t\t\t\tfprintf(stderr, \"Error: Only one type of message can be sent at once.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->pub_mode = MSGMODE_STDIN_FILE;\n\t\t\t}\n#ifdef WITH_SRV\n\t\t}else if(!strcmp(argv[i], \"-S\")){\n\t\t\tcfg->use_srv = true;\n#endif\n\t\t}else if(!strcmp(argv[i], \"-t\") || !strcmp(argv[i], \"--topic\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -t argument given but no topic specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(cfg_add_topic(cfg, pub_or_sub, argv[i + 1], \"-t\")){\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}else if(!strcmp(argv[i], \"-T\") || !strcmp(argv[i], \"--filter-out\")){\n\t\t\tif(pub_or_sub != CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -T argument given but no topic filter specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){\n\t\t\t\t\tfprintf(stderr, \"Error: Malformed UTF-8 in -T argument.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid filter topic '%s', are all '+' and '#' wildcards correct?\\n\", argv[i+1]);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tcfg->filter_out_count++;\n\t\t\t\tcfg->filter_outs = realloc(cfg->filter_outs, (size_t )cfg->filter_out_count*sizeof(char *));\n\t\t\t\tif(!cfg->filter_outs){\n\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tcfg->filter_outs[cfg->filter_out_count-1] = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n#ifdef WITH_TLS\n\t\t}else if(!strcmp(argv[i], \"--tls-alpn\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --tls-alpn argument given but no protocol specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->tls_alpn = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--tls-engine\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --tls-engine argument given but no engine_id specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->tls_engine = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--tls-engine-kpass-sha1\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->tls_engine_kpass_sha1 = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--tls-use-os-certs\")){\n\t\t\tcfg->tls_use_os_certs = true;\n\t\t}else if(!strcmp(argv[i], \"--tls-version\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --tls-version argument given but no version specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->tls_version = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--tls-keylog\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --tls-keylog argument given but no file specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->tls_keylog = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n#endif\n\t\t}else if(!strcmp(argv[i], \"-U\") || !strcmp(argv[i], \"--unsubscribe\")){\n\t\t\tif(pub_or_sub != CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -U argument given but no unsubscribe topic specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){\n\t\t\t\t\tfprintf(stderr, \"Error: Malformed UTF-8 in -U argument.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid unsubscribe topic '%s', are all '+' and '#' wildcards correct?\\n\", argv[i+1]);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tcfg->unsub_topic_count++;\n\t\t\t\tcfg->unsub_topics = realloc(cfg->unsub_topics, (size_t )cfg->unsub_topic_count*sizeof(char *));\n\t\t\t\tif(!cfg->unsub_topics){\n\t\t\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tcfg->unsub_topics[cfg->unsub_topic_count-1] = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-u\") || !strcmp(argv[i], \"--username\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -u argument given but no username specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->username = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--unix\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --unix argument given but no socket path specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->host = strdup(argv[i+1]);\n\t\t\t\tcfg->port = 0;\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-V\") || !strcmp(argv[i], \"--protocol-version\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --protocol-version argument given but no version specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(!strcmp(argv[i+1], \"mqttv31\") || !strcmp(argv[i+1], \"31\")){\n\t\t\t\t\tcfg->protocol_version = MQTT_PROTOCOL_V31;\n\t\t\t\t}else if(!strcmp(argv[i+1], \"mqttv311\") || !strcmp(argv[i+1], \"311\")){\n\t\t\t\t\tcfg->protocol_version = MQTT_PROTOCOL_V311;\n\t\t\t\t}else if(!strcmp(argv[i+1], \"mqttv5\") || !strcmp(argv[i+1], \"5\")){\n\t\t\t\t\tcfg->protocol_version = MQTT_PROTOCOL_V5;\n\t\t\t\t}else{\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid protocol version argument given.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}else if(!strcmp(argv[i], \"-v\") || !strcmp(argv[i], \"--verbose\")){\n\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n\t\t\tcfg->verbose = 1;\n\t\t}else if(!strcmp(argv[i], \"--version\")){\n\t\t\treturn 3;\n\t\t}else if(!strcmp(argv[i], \"-W\")){\n\t\t\tif(pub_or_sub == CLIENT_PUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}else{\n\t\t\t\tif(i==argc-1){\n\t\t\t\t\tfprintf(stderr, \"Error: -W argument given but no timeout specified.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}else{\n\t\t\t\t\ttmpi = atoi(argv[i+1]);\n\t\t\t\t\tif(tmpi < 1){\n\t\t\t\t\t\tfprintf(stderr, \"Error: Invalid timeout \\\"%d\\\".\\n\\n\", tmpi);\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\tcfg->timeout = (unsigned int )tmpi;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}else if(!strcmp(argv[i], \"-w\") || !strcmp(argv[i], \"--watch\")){\n\t\t\tif(pub_or_sub != CLIENT_SUB){\n\t\t\t\tgoto unknown_option;\n\t\t\t}\n#ifdef WIN32\n\t\t\tfprintf(stderr, \"Error: --watch not supported on Windows.\\n\\n\");\n\t\t\treturn 1;\n#else\n\t\t\tcfg->watch = true;\n#endif\n\t\t}else if(!strcmp(argv[i], \"--will-payload\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --will-payload argument given but no will payload specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->will_payload = strdup(argv[i+1]);\n\t\t\t\tcfg->will_payloadlen = (int )strlen(cfg->will_payload);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--will-qos\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --will-qos argument given but no will QoS specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tcfg->will_qos = atoi(argv[i+1]);\n\t\t\t\tif(cfg->will_qos < 0 || cfg->will_qos > 2){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid will QoS %d.\\n\\n\", cfg->will_qos);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--will-retain\")){\n\t\t\tcfg->will_retain = true;\n\t\t}else if(!strcmp(argv[i], \"--will-topic\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: --will-topic argument given but no will topic specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){\n\t\t\t\t\tfprintf(stderr, \"Error: Malformed UTF-8 in --will-topic argument.\\n\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif(mosquitto_pub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){\n\t\t\t\t\tfprintf(stderr, \"Error: Invalid will topic '%s', does it contain '+' or '#'?\\n\", argv[i+1]);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tcfg->will_topic = strdup(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--ws\")){\n\t\t\tcfg->transport = MOSQ_T_WEBSOCKETS;\n\t\t}else if(!strcmp(argv[i], \"-x\")){\n\t\t\tif(i==argc-1){\n\t\t\t\tfprintf(stderr, \"Error: -x argument given but no session expiry interval specified.\\n\\n\");\n\t\t\t\treturn 1;\n\t\t\t}else{\n\t\t\t\tif(!strcmp(argv[i+1], \"∞\")){\n\t\t\t\t\tcfg->session_expiry_interval = UINT32_MAX;\n\t\t\t\t}else{\n\t\t\t\t\tchar *endptr = NULL;\n\t\t\t\t\tcfg->session_expiry_interval = strtol(argv[i+1], &endptr, 0);\n\t\t\t\t\tif(endptr == argv[i+1] || endptr[0] != '\\0'){\n\t\t\t\t\t\t/* Entirety of argument wasn't a number */\n\t\t\t\t\t\tfprintf(stderr, \"Error: session-expiry-interval not a number.\\n\\n\");\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\tif(cfg->session_expiry_interval > UINT32_MAX || cfg->session_expiry_interval < -1){\n\t\t\t\t\t\tfprintf(stderr, \"Error: session-expiry-interval out of range.\\n\\n\");\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\tif(cfg->session_expiry_interval == -1){\n\t\t\t\t\t\t/* Convenience value for infinity. */\n\t\t\t\t\t\tcfg->session_expiry_interval = UINT32_MAX;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcfg->protocol_version = MQTT_PROTOCOL_V5;\n\t\t\t}\n\t\t\ti++;\n\t\t}else{\n\t\t\tgoto unknown_option;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n\nunknown_option:\n\tfprintf(stderr, \"Error: Unknown option '%s'.\\n\", argv[i]);\n\treturn 1;\n}\n\n\n#ifdef WITH_TLS\n\n\nstatic int client_tls_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)\n{\n\tint rc;\n\n\tif(cfg->no_tls){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(cfg->tls_keylog){\n\t\tif(tls_ex_index_cfg == -1){\n\t\t\ttls_ex_index_cfg = SSL_CTX_get_ex_new_index(0, \"client config\", NULL, NULL, NULL);\n\t\t}\n\t\tcfg->ssl_ctx = SSL_CTX_new(TLS_client_method());\n\t\tif(!cfg->ssl_ctx){\n\t\t\terr_printf(cfg, \"Error: Unable to create SSL_CTX.\\n\");\n\t\t\treturn 1;\n\t\t}\n\t\tif(!SSL_CTX_set_ex_data(cfg->ssl_ctx, tls_ex_index_cfg, cfg)){\n\t\t\terr_printf(cfg, \"Error: Unable to set SSL_CTX ex data.\\n\");\n\t\t\treturn 1;\n\t\t}\n\t\tmosquitto_void_option(mosq, MOSQ_OPT_SSL_CTX, cfg->ssl_ctx);\n\t\tmosquitto_int_option(mosq, MOSQ_OPT_SSL_CTX_WITH_DEFAULTS, 1);\n\t\tSSL_CTX_set_keylog_callback(cfg->ssl_ctx, tls_keylog_callback);\n\t}\n\n\tif(cfg->cafile || cfg->capath){\n\t\trc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL);\n\t\tif(rc){\n\t\t\tif(rc == MOSQ_ERR_INVAL){\n\t\t\t\terr_printf(cfg, \"Error: Problem setting TLS options: File not found.\\n\");\n\t\t\t}else{\n\t\t\t\terr_printf(cfg, \"Error: Problem setting TLS options: %s.\\n\", mosquitto_strerror(rc));\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n#  ifdef FINAL_WITH_TLS_PSK\n\t}else if(cfg->psk){\n\t\tif(mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){\n\t\t\terr_printf(cfg, \"Error: Problem setting TLS-PSK options.\\n\");\n\t\t\treturn 1;\n\t\t}\n#  endif\n\t}else if(cfg->port == 8883){\n\t\tmosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);\n\t}\n\tif(cfg->tls_use_os_certs){\n\t\tmosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);\n\t}\n\n\tif(cfg->insecure && mosquitto_tls_insecure_set(mosq, true)){\n\t\terr_printf(cfg, \"Error: Problem setting TLS insecure option.\\n\");\n\t\treturn 1;\n\t}\n\tif(cfg->tls_engine && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE, cfg->tls_engine)){\n\t\terr_printf(cfg, \"Error: Problem setting TLS engine, is %s a valid engine?\\n\", cfg->tls_engine);\n\t\treturn 1;\n\t}\n\tif(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){\n\t\terr_printf(cfg, \"Error: Problem setting key form, it must be one of 'pem' or 'engine'.\\n\");\n\t\treturn 1;\n\t}\n\tif(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){\n\t\terr_printf(cfg, \"Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\\n\");\n\t\treturn 1;\n\t}\n\tif(cfg->tls_alpn && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ALPN, cfg->tls_alpn)){\n\t\terr_printf(cfg, \"Error: Problem setting TLS ALPN protocol.\\n\");\n\t\treturn 1;\n\t}\n\tif((cfg->tls_version || cfg->ciphers) && mosquitto_tls_opts_set(mosq, !cfg->insecure, cfg->tls_version, cfg->ciphers)){\n\t\terr_printf(cfg, \"Error: Problem setting TLS options, check the options are valid.\\n\");\n\t\treturn 1;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\nint client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)\n{\n#if defined(WITH_SOCKS)\n\tint rc;\n#endif\n\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, cfg->protocol_version);\n\tmosquitto_int_option(mosq, MOSQ_OPT_TRANSPORT, cfg->transport);\n\n\tif(cfg->will_topic && mosquitto_will_set_v5(mosq, cfg->will_topic,\n\t\t\tcfg->will_payloadlen, cfg->will_payload, cfg->will_qos,\n\t\t\tcfg->will_retain, cfg->will_props)){\n\n\t\terr_printf(cfg, \"Error: Problem setting will.\\n\");\n\t\treturn 1;\n\t}\n\tcfg->will_props = NULL;\n\n\tif((cfg->username || cfg->password) && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){\n\t\terr_printf(cfg, \"Error: Problem setting username and/or password.\\n\");\n\t\treturn 1;\n\t}\n#ifdef WITH_TLS\n\tif(client_tls_opts_set(mosq, cfg)){\n\t\treturn 1;\n\t}\n#endif\n\tmosquitto_max_inflight_messages_set(mosq, cfg->max_inflight);\n#ifdef WITH_SOCKS\n\tif(cfg->socks5_host){\n\t\trc = mosquitto_socks5_set(mosq, cfg->socks5_host, cfg->socks5_port, cfg->socks5_username, cfg->socks5_password);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n#endif\n\tif(cfg->tcp_nodelay){\n\t\tmosquitto_int_option(mosq, MOSQ_OPT_TCP_NODELAY, 1);\n\t}\n\n\tif(cfg->msg_count > 0 && cfg->msg_count < 20){\n\t\t/* 20 is the default \"receive maximum\"\n\t\t * If we don't set this, then we can receive > msg_count messages\n\t\t * before we quit.*/\n\t\tmosquitto_int_option(mosq, MOSQ_OPT_RECEIVE_MAXIMUM, cfg->msg_count);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint clientid_generate(struct mosq_config *cfg)\n{\n\tif(cfg->id_prefix){\n\t\tcfg->id = malloc(strlen(cfg->id_prefix)+10);\n\t\tif(!cfg->id){\n\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\treturn 1;\n\t\t}\n\t\tsnprintf(cfg->id, strlen(cfg->id_prefix)+10, \"%s%d\", cfg->id_prefix, getpid());\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint client_connect(struct mosquitto *mosq, struct mosq_config *cfg)\n{\n#ifndef WIN32\n\tchar *err;\n#else\n\tchar err[1024];\n#endif\n\tint rc;\n\tint port;\n\n#ifndef WIN32\n\tsignal(SIGPIPE, SIG_IGN);\n#endif\n\n\tif(cfg->port == PORT_UNDEFINED){\n#ifdef WITH_TLS\n\t\tif(cfg->cafile || cfg->capath\n#  ifdef FINAL_WITH_TLS_PSK\n\t\t\t\t|| cfg->psk\n#  endif\n\t\t\t\t){\n\t\t\tport = 8883;\n\t\t}else\n#endif\n\t\t{\n\t\t\tport = 1883;\n\t\t}\n\t}else{\n\t\tport = cfg->port;\n\t}\n\n#ifdef WITH_SRV\n\tif(cfg->use_srv){\n\t\trc = mosquitto_connect_srv(mosq, cfg->host, cfg->keepalive, cfg->bind_address);\n\t}else{\n\t\trc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props);\n\t}\n#else\n\trc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props);\n#endif\n\tif(rc>0){\n\t\tif(rc == MOSQ_ERR_ERRNO){\n#ifndef WIN32\n\t\t\terr = strerror(errno);\n#else\n\t\t\tFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL);\n#endif\n\t\t\terr_printf(cfg, \"Error: %s\\n\", err);\n\t\t}else{\n\t\t\terr_printf(cfg, \"Unable to connect (%s).\\n\", mosquitto_strerror(rc));\n\t\t}\n\t\tmosquitto_lib_cleanup();\n\t\treturn rc;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n#ifdef WITH_SOCKS\n\n\n/* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */\nstatic int mosquitto__urldecode(char *str)\n{\n\tsize_t i, j;\n\tsize_t len;\n\tif(!str){\n\t\treturn 0;\n\t}\n\n\tif(!strchr(str, '%')){\n\t\treturn 0;\n\t}\n\n\tlen = strlen(str);\n\tfor(i=0; i<len; i++){\n\t\tif(str[i] == '%'){\n\t\t\tif(i+2 >= len){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(str[i+1] == '2' && str[i+2] == '5'){\n\t\t\t\tstr[i] = '%';\n\t\t\t\tlen -= 2;\n\t\t\t\tfor(j=i+1; j<len; j++){\n\t\t\t\t\tstr[j] = str[j+2];\n\t\t\t\t}\n\t\t\t\tstr[j] = '\\0';\n\t\t\t}else if(str[i+1] == '3' && (str[i+2] == 'A' || str[i+2] == 'a')){\n\t\t\t\tstr[i] = ':';\n\t\t\t\tlen -= 2;\n\t\t\t\tfor(j=i+1; j<len; j++){\n\t\t\t\t\tstr[j] = str[j+2];\n\t\t\t\t}\n\t\t\t\tstr[j] = '\\0';\n\t\t\t}else if(str[i+1] == '4' && str[i+2] == '0'){\n\t\t\t\tstr[i] = ':';\n\t\t\t\tlen -= 2;\n\t\t\t\tfor(j=i+1; j<len; j++){\n\t\t\t\t\tstr[j] = str[j+2];\n\t\t\t\t}\n\t\t\t\tstr[j] = '\\0';\n\t\t\t}else{\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t}\n\treturn 0;\n}\n\n\nstatic int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url)\n{\n\tchar *str;\n\tsize_t i;\n\tchar *username = NULL, *password = NULL, *host = NULL, *port = NULL;\n\tsize_t start;\n\tsize_t len;\n\tbool in_ipv6_address = false;\n\tint port_int;\n\tchar *auth_at;\n\n\tif(!strncmp(url, \"socks5h://\", strlen(\"socks5h://\"))){\n\t\tstr = url + strlen(\"socks5h://\");\n\t}else{\n\t\terr_printf(cfg, \"Error: Unsupported proxy protocol: %s\\n\", url);\n\t\treturn 1;\n\t}\n\n\t/* socks5h://username:password@host:1883\n\t * socks5h://username:password@host\n\t * socks5h://username@host:1883\n\t * socks5h://username@host\n\t * socks5h://host:1883\n\t * socks5h://host\n\t */\n\n\t/* Parse credentials */\n\tstart = 0;\n\tauth_at = strchr(str, '@');\n\tif(auth_at){\n\t\tfor(i=0; &str[i] != auth_at; i++){\n\t\t\tif(str[i] == ':'){\n\t\t\t\tif(i == start){\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tlen = i-start;\n\t\t\t\tif(username){\n\t\t\t\t\terr_printf(cfg, \"Error: Username cannot contain ':'.\\n\");\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tusername = malloc(len + 1);\n\t\t\t\tif(!username){\n\t\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tmemcpy(username, &(str[start]), len);\n\t\t\t\tusername[len] = '\\0';\n\t\t\t\tstart = i+1;\n\t\t\t}\n\t\t}\n\t\tif(username){\n\t\t\tlen = i-start;\n\t\t\tpassword = malloc(len + 1);\n\t\t\tif(!password){\n\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tmemcpy(password, &(str[start]), len);\n\t\t\tpassword[len] = '\\0';\n\t\t}else{\n\t\t\tlen = i-start;\n\t\t\tusername = malloc(len + 1);\n\t\t\tif(!username){\n\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tmemcpy(username, &(str[start]), len);\n\t\t\tusername[len] = '\\0';\n\t\t}\n\t\tstr = auth_at + 1;\n\t}\n\n\tstart = 0;\n\tfor(i=0; i<strlen(str); i++){\n\t\tif(str[i] == '['){\n\t\t\tif(host){\n\t\t\t\terr_printf(cfg, \"Error: Duplicate IPv6 address.\\n\");\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tin_ipv6_address = true;\n\t\t\tstart = i+1;\n\t\t}else if(str[i] == ']'){\n\t\t\tif(host){\n\t\t\t\terr_printf(cfg, \"Error: Duplicate IPv6 address.\\n\");\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tin_ipv6_address = false;\n\n\t\t\tlen = i-start;\n\t\t\thost = malloc(len + 1);\n\t\t\tif(!host){\n\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tmemcpy(host, &(str[start]), len);\n\t\t\thost[len] = '\\0';\n\t\t\tif(str[i+1] == ':'){\n\t\t\t\tstart = i+2;\n\t\t\t\ti++;\n\t\t\t}else{\n\t\t\t\tstart = i+1;\n\t\t\t}\n\t\t}else if(str[i] == ':'){\n\t\t\tif(in_ipv6_address){\n\t\t\t\t/* Normal IPv6 separator */\n\t\t\t}else{\n\t\t\t\t/* host:port separator */\n\n\t\t\t\tif(host){\n\t\t\t\t\t/* Already seen a host, must be malformed. */\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tlen = i-start;\n\t\t\t\thost = malloc(len + 1);\n\t\t\t\tif(!host){\n\t\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\t\tgoto cleanup;\n\t\t\t\t}\n\t\t\t\tmemcpy(host, &(str[start]), len);\n\t\t\t\thost[len] = '\\0';\n\t\t\t\tstart = i+1;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Deal with remainder - either the port, or the host */\n\tif(i > start){\n\t\tlen = i-start;\n\t\tif(host){\n\t\t\t/* Have already seen a @ , so this must be of form\n\t\t\t * socks5h://username[:password]@host:port */\n\t\t\tport = malloc(len + 1);\n\t\t\tif(!port){\n\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tmemcpy(port, &(str[start]), len);\n\t\t\tport[len] = '\\0';\n\t\t}else{\n\t\t\thost = malloc(len + 1);\n\t\t\tif(!host){\n\t\t\t\terr_printf(cfg, \"Error: Out of memory.\\n\");\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tmemcpy(host, &(str[start]), len);\n\t\t\thost[len] = '\\0';\n\t\t}\n\t}\n\n\tif(!host){\n\t\terr_printf(cfg, \"Error: Invalid proxy.\\n\");\n\t\tgoto cleanup;\n\t}\n\n\tif(mosquitto__urldecode(username)){\n\t\tgoto cleanup;\n\t}\n\tif(mosquitto__urldecode(password)){\n\t\tgoto cleanup;\n\t}\n\tif(port){\n\t\tport_int = atoi(port);\n\t\tif(port_int < 1 || port_int > 65535){\n\t\t\terr_printf(cfg, \"Error: Invalid proxy port %d\\n\", port_int);\n\t\t\tgoto cleanup;\n\t\t}\n\t\tfree(port);\n\t}else{\n\t\tport_int = 1080;\n\t}\n\n\tcfg->socks5_username = username;\n\tcfg->socks5_password = password;\n\tcfg->socks5_host = host;\n\tcfg->socks5_port = port_int;\n\n\treturn 0;\ncleanup:\n\tfree(username);\n\tfree(password);\n\tfree(host);\n\tfree(port);\n\treturn 1;\n}\n#endif\n\n\nvoid err_printf(const struct mosq_config *cfg, const char *fmt, ...)\n{\n\tva_list va;\n\n\tif(cfg->quiet){\n\t\treturn;\n\t}\n\n\tva_start(va, fmt);\n\tvfprintf(stderr, fmt, va);\n\tva_end(va);\n}\n\n#ifdef WITH_TLS\n\n\nstatic void tls_keylog_callback(const SSL *ssl, const char *line)\n{\n\tstruct mosq_config *cfg;\n\tFILE *fptr;\n\n\tUNUSED(ssl);\n\n\tcfg = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl), tls_ex_index_cfg);\n\n\tif(cfg && cfg->tls_keylog){\n\t\tfptr = fopen(cfg->tls_keylog, \"at\");\n\t\tif(fptr){\n\t\t\tfprintf(fptr, \"%s\\n\", line);\n\t\t\tfclose(fptr);\n\t\t}\n\t}\n}\n#endif\n"
  },
  {
    "path": "client/client_shared.h",
    "content": "/*\nCopyright (c) 2014-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef CLIENT_SHARED_H\n#define CLIENT_SHARED_H\n\n#include <stdio.h>\n\n#ifdef WIN32\n#  include <winsock2.h>\n#else\n#  include <sys/time.h>\n#endif\n\n#ifdef WITH_TLS\n#  include <openssl/ssl.h>\n#endif\n\n#ifndef __GNUC__\n#define __attribute__(attrib)\n#endif\n\n/* pub_client.c modes */\n#define MSGMODE_NONE 0\n#define MSGMODE_CMD 1\n#define MSGMODE_STDIN_LINE 2\n#define MSGMODE_STDIN_FILE 3\n#define MSGMODE_FILE 4\n#define MSGMODE_NULL 5\n\n#define CLIENT_PUB 1\n#define CLIENT_SUB 2\n#define CLIENT_RR 3\n#define CLIENT_RESPONSE_TOPIC 4\n\n#define PORT_UNDEFINED -1\n#define PORT_UNIX 0\n\nstruct mosq_config {\n\tchar *id;\n\tchar *id_prefix;\n\tint protocol_version;\n\tint keepalive;\n\tchar *host;\n\tint port;\n\tint qos;\n\tbool retain;\n\tint pub_mode; /* pub, rr */\n\tchar *file_input; /* pub, rr */\n\tchar *message; /* pub, rr */\n\tint msglen; /* pub, rr */\n\tchar *topic; /* pub, rr */\n\tchar *bind_address;\n\tint repeat_count; /* pub */\n\tstruct timeval repeat_delay; /* pub */\n#ifdef WITH_SRV\n\tbool use_srv;\n#endif\n\tbool debug;\n\tbool quiet;\n\tunsigned int max_inflight;\n\tchar *username;\n\tchar *password;\n\tchar *will_topic;\n\tchar *will_payload;\n\tint will_payloadlen;\n\tint will_qos;\n\tbool will_retain;\n#ifdef WITH_TLS\n\tchar *cafile;\n\tchar *capath;\n\tchar *certfile;\n\tchar *keyfile;\n\tchar *ciphers;\n\tbool insecure;\n\tchar *tls_alpn;\n\tchar *tls_version;\n\tchar *tls_engine;\n\tchar *tls_engine_kpass_sha1;\n\tchar *keyform;\n\tbool tls_use_os_certs;\n\tchar *tls_keylog;\n\tSSL_CTX *ssl_ctx;\n#  ifdef FINAL_WITH_TLS_PSK\n\tchar *psk;\n\tchar *psk_identity;\n#  endif\n#endif\n\tbool clean_session;\n\tchar **topics; /* sub, rr */\n\tint topic_count; /* sub, rr */\n\tbool exit_after_sub; /* sub */\n\tbool no_retain; /* sub */\n\tbool retained_only; /* sub */\n\tbool remove_retained; /* sub */\n\tchar **filter_outs; /* sub */\n\tint filter_out_count; /* sub */\n\tchar **unsub_topics; /* sub */\n\tint unsub_topic_count; /* sub */\n\tbool verbose; /* sub */\n\tbool eol; /* sub */\n\tint msg_count; /* sub */\n\tchar *format; /* sub, rr */\n\tbool pretty; /* sub, rr */\n\tunsigned int timeout; /* sub */\n\tint sub_opts; /* sub */\n\tlong session_expiry_interval;\n\tint random_filter; /* sub */\n\tint transport;\n#ifndef WIN32\n\tbool watch; /* sub */\n#endif\n#ifdef WITH_SOCKS\n\tchar *socks5_host;\n\tint socks5_port;\n\tchar *socks5_username;\n\tchar *socks5_password;\n#endif\n\tmosquitto_property *connect_props;\n\tmosquitto_property *publish_props;\n\tmosquitto_property *subscribe_props;\n\tmosquitto_property *unsubscribe_props;\n\tmosquitto_property *disconnect_props;\n\tmosquitto_property *will_props;\n\tchar *response_topic; /* rr */\n\tchar *options_file;\n\tbool have_topic_alias; /* pub */\n\tbool tcp_nodelay;\n\tbool no_tls;\n\tbool message_rate; /* sub */\n\tbool measure_latency; /* rr */\n};\n\nextern const char hexseplist[32];\n\nint client_config_load(struct mosq_config *config, int pub_or_sub, int argc, char *argv[]);\nvoid client_config_cleanup(struct mosq_config *cfg);\nint client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg);\nint clientid_generate(struct mosq_config *cfg);\nint client_connect(struct mosquitto *mosq, struct mosq_config *cfg);\n\nint cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx);\n\nvoid err_printf(const struct mosq_config *cfg, const char *fmt, ...) __attribute__((format(printf, 2, 3)));\n#endif\n"
  },
  {
    "path": "client/pub_client.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#ifndef WIN32\n#include <sys/time.h>\n#include <time.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#define snprintf sprintf_s\n#endif\n\n#include <mosquitto.h>\n#include \"client_shared.h\"\n#include \"pub_shared.h\"\n\n/* Global variables for use in callbacks. See sub_client.c for an example of\n * using a struct to hold variables for use in callbacks. */\nstatic bool first_publish = true;\nstatic int last_mid = -1;\nstatic int last_mid_sent = -1;\nstatic char *line_buf = NULL;\nstatic int line_buf_len = 1024;\nstatic bool disconnect_sent = false;\nstatic int publish_count = 0;\nstatic bool ready_for_repeat = false;\nstatic volatile int status = STATUS_CONNECTING;\nstatic int connack_result = 0;\n\n#ifdef WIN32\nstatic uint64_t next_publish_tv;\n\n\nstatic void set_repeat_time(void)\n{\n\tuint64_t ticks = GetTickCount64();\n\tnext_publish_tv = ticks + cfg.repeat_delay.tv_sec*1000 + cfg.repeat_delay.tv_usec/1000;\n}\n\n\nstatic int check_repeat_time(void)\n{\n\tuint64_t ticks = GetTickCount64();\n\n\tif(ticks > next_publish_tv){\n\t\treturn 1;\n\t}else{\n\t\treturn 0;\n\t}\n}\n#else\n\nstatic struct timeval next_publish_tv;\n\n\nstatic void set_repeat_time(void)\n{\n\tgettimeofday(&next_publish_tv, NULL);\n\tnext_publish_tv.tv_sec += cfg.repeat_delay.tv_sec;\n\tnext_publish_tv.tv_usec += cfg.repeat_delay.tv_usec;\n\n\tnext_publish_tv.tv_sec += next_publish_tv.tv_usec/1000000;\n\tnext_publish_tv.tv_usec = next_publish_tv.tv_usec%1000000;\n}\n\n\nstatic int check_repeat_time(void)\n{\n\tstruct timeval tv;\n\n\tgettimeofday(&tv, NULL);\n\n\tif(tv.tv_sec > next_publish_tv.tv_sec){\n\t\treturn 1;\n\t}else if(tv.tv_sec == next_publish_tv.tv_sec\n\t\t\t&& tv.tv_usec > next_publish_tv.tv_usec){\n\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n#endif\n\n\nvoid my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(rc);\n\tUNUSED(properties);\n\n\tif(rc == 0){\n\t\tstatus = STATUS_DISCONNECTED;\n\t}\n}\n\n\nint my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain)\n{\n\tready_for_repeat = false;\n\tif(cfg.protocol_version == MQTT_PROTOCOL_V5 && cfg.have_topic_alias && first_publish == false){\n\t\treturn mosquitto_publish_v5(mosq, mid, NULL, payloadlen, payload, qos, retain, cfg.publish_props);\n\t}else{\n\t\tfirst_publish = false;\n\t\treturn mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, cfg.publish_props);\n\t}\n}\n\n\nvoid my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tUNUSED(obj);\n\tUNUSED(flags);\n\tUNUSED(properties);\n\n\tconnack_result = result;\n\n\tif(!result){\n\t\tfirst_publish = true;\n\t\tswitch(cfg.pub_mode){\n\t\t\tcase MSGMODE_CMD:\n\t\t\tcase MSGMODE_FILE:\n\t\t\tcase MSGMODE_STDIN_FILE:\n\t\t\t\trc = my_publish(mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain);\n\t\t\t\tbreak;\n\t\t\tcase MSGMODE_NULL:\n\t\t\t\trc = my_publish(mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain);\n\t\t\t\tbreak;\n\t\t\tcase MSGMODE_STDIN_LINE:\n\t\t\t\tstatus = STATUS_CONNACK_RECVD;\n\t\t\t\tbreak;\n\t\t}\n\t\tif(rc){\n\t\t\tswitch(rc){\n\t\t\t\tcase MOSQ_ERR_INVAL:\n\t\t\t\t\terr_printf(&cfg, \"Error: Invalid input. Does your topic contain '+' or '#'?\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase MOSQ_ERR_NOMEM:\n\t\t\t\t\terr_printf(&cfg, \"Error: Out of memory when trying to publish message.\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase MOSQ_ERR_NO_CONN:\n\t\t\t\t\terr_printf(&cfg, \"Error: Client not connected when trying to publish.\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase MOSQ_ERR_PROTOCOL:\n\t\t\t\t\terr_printf(&cfg, \"Error: Protocol error when communicating with broker.\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase MOSQ_ERR_PAYLOAD_SIZE:\n\t\t\t\t\terr_printf(&cfg, \"Error: Message payload is too large.\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase MOSQ_ERR_QOS_NOT_SUPPORTED:\n\t\t\t\t\terr_printf(&cfg, \"Error: Message QoS not supported on broker, try a lower QoS.\\n\");\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t\t}\n\t}else{\n\t\tif(result){\n\t\t\tif(cfg.protocol_version == MQTT_PROTOCOL_V5){\n\t\t\t\tif(result == MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION){\n\t\t\t\t\terr_printf(&cfg, \"Connection error: %s. Try connecting to an MQTT v5 broker, or use MQTT v3.x mode.\\n\", mosquitto_reason_string(result));\n\t\t\t\t}else{\n\t\t\t\t\terr_printf(&cfg, \"Connection error: %s\\n\", mosquitto_reason_string(result));\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\terr_printf(&cfg, \"Connection error: %s\\n\", mosquitto_connack_string(result));\n\t\t\t}\n\t\t\t/* let the loop know that this is an unrecoverable connection */\n\t\t\tstatus = STATUS_NOHOPE;\n\t\t}\n\t}\n}\n\n\nvoid my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties)\n{\n\tchar *reason_string = NULL;\n\tUNUSED(obj);\n\tUNUSED(properties);\n\n\tlast_mid_sent = mid;\n\tif(reason_code > 127){\n\t\terr_printf(&cfg, \"Warning: Publish %d failed: %s.\\n\", mid, mosquitto_reason_string(reason_code));\n\t\tmosquitto_property_read_string(properties, MQTT_PROP_REASON_STRING, &reason_string, false);\n\t\tif(reason_string){\n\t\t\terr_printf(&cfg, \"%s\\n\", reason_string);\n\t\t\tfree(reason_string);\n\t\t}\n\t}\n\tpublish_count++;\n\n\tif(cfg.pub_mode == MSGMODE_STDIN_LINE){\n\t\tif(mid == last_mid){\n\t\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t\t\tdisconnect_sent = true;\n\t\t}\n\t}else if(publish_count < cfg.repeat_count){\n\t\tready_for_repeat = true;\n\t\tset_repeat_time();\n\t}else if(disconnect_sent == false){\n\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t\tdisconnect_sent = true;\n\t}\n}\n\n\nint pub_shared_init(void)\n{\n\tline_buf = malloc((size_t )line_buf_len);\n\tif(!line_buf){\n\t\terr_printf(&cfg, \"Error: Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\n\nstatic int pub_stdin_line_loop(struct mosquitto *mosq)\n{\n\tchar *buf2;\n\tint buf_len_actual = 0;\n\tint pos;\n\tint rc = MOSQ_ERR_SUCCESS;\n\tint read_len;\n\tbool stdin_finished = false;\n\n\trc = mosquitto_loop_start(mosq);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\tstdin_finished = false;\n\tdo{\n\t\tif(status == STATUS_CONNECTING){\n#ifdef WIN32\n\t\t\tSleep(100);\n#else\n\t\t\tstruct timespec ts;\n\t\t\tts.tv_sec = 0;\n\t\t\tts.tv_nsec = 100000000;\n\t\t\tnanosleep(&ts, NULL);\n#endif\n\t\t}\n\n\t\tif(status == STATUS_NOHOPE){\n\t\t\treturn MOSQ_ERR_CONN_REFUSED;\n\t\t}\n\n\t\tif(status == STATUS_CONNACK_RECVD){\n\t\t\tpos = 0;\n\t\t\tread_len = line_buf_len;\n\t\t\twhile(status == STATUS_CONNACK_RECVD && fgets(&line_buf[pos], read_len, stdin)){\n\t\t\t\tbuf_len_actual = (int )strlen(line_buf);\n\t\t\t\tif(line_buf[buf_len_actual-1] == '\\n'){\n\t\t\t\t\tline_buf[buf_len_actual-1] = '\\0';\n\t\t\t\t\trc = my_publish(mosq, &mid_sent, cfg.topic, buf_len_actual-1, line_buf, cfg.qos, cfg.retain);\n\t\t\t\t\tpos = 0;\n\t\t\t\t\tif(rc != MOSQ_ERR_SUCCESS && rc != MOSQ_ERR_NO_CONN){\n\t\t\t\t\t\treturn rc;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}else{\n\t\t\t\t\tline_buf_len += 1024;\n\t\t\t\t\tpos += read_len-1;\n\t\t\t\t\tread_len = 1024;\n\t\t\t\t\tbuf2 = realloc(line_buf, (size_t )line_buf_len);\n\t\t\t\t\tif(!buf2){\n\t\t\t\t\t\terr_printf(&cfg, \"Error: Out of memory.\\n\");\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tline_buf = buf2;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(pos != 0){\n\t\t\t\trc = my_publish(mosq, &mid_sent, cfg.topic, buf_len_actual, line_buf, cfg.qos, cfg.retain);\n\t\t\t\tif(rc){\n\t\t\t\t\tif(cfg.qos>0){\n\t\t\t\t\t\treturn rc;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(feof(stdin)){\n\t\t\t\tif(mid_sent == -1){\n\t\t\t\t\t/* Empty file */\n\t\t\t\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t\t\t\t\tdisconnect_sent = true;\n\t\t\t\t\tstatus = STATUS_DISCONNECTING;\n\t\t\t\t}else{\n\t\t\t\t\tlast_mid = mid_sent;\n\t\t\t\t\tstatus = STATUS_WAITING;\n\t\t\t\t}\n\t\t\t\tstdin_finished = true;\n\t\t\t}else if(status == STATUS_DISCONNECTED){\n\t\t\t\t/* Not end of stdin, so we've lost our connection and must\n\t\t\t\t * reconnect */\n\t\t\t}\n\t\t}\n\n\t\tif(status == STATUS_WAITING){\n\t\t\tif(last_mid_sent == last_mid && disconnect_sent == false){\n\t\t\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t\t\t\tdisconnect_sent = true;\n\t\t\t}\n#ifdef WIN32\n\t\t\tSleep(100);\n#else\n\t\t\tstruct timespec ts;\n\t\t\tts.tv_sec = 0;\n\t\t\tts.tv_nsec = 100000000;\n\t\t\tnanosleep(&ts, NULL);\n#endif\n\t\t}\n\t}while(stdin_finished == false);\n\tmosquitto_loop_stop(mosq, false);\n\n\tif(status == STATUS_DISCONNECTED){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn rc;\n\t}\n}\n\n\nstatic int pub_other_loop(struct mosquitto *mosq)\n{\n\tint rc;\n\tint loop_delay = 1000;\n\n\tif(cfg.repeat_count > 1 && (cfg.repeat_delay.tv_sec == 0 || cfg.repeat_delay.tv_usec != 0)){\n\t\tloop_delay = (int )cfg.repeat_delay.tv_usec / 2000;\n\t}\n\n\tdo{\n\t\trc = mosquitto_loop(mosq, loop_delay, 1);\n\t\tif(ready_for_repeat && check_repeat_time()){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t\tswitch(cfg.pub_mode){\n\t\t\t\tcase MSGMODE_CMD:\n\t\t\t\tcase MSGMODE_FILE:\n\t\t\t\tcase MSGMODE_STDIN_FILE:\n\t\t\t\t\trc = my_publish(mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain);\n\t\t\t\t\tbreak;\n\t\t\t\tcase MSGMODE_NULL:\n\t\t\t\t\trc = my_publish(mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif(rc != MOSQ_ERR_SUCCESS && rc != MOSQ_ERR_NO_CONN){\n\t\t\t\terr_printf(&cfg, \"Error sending repeat publish: %s\", mosquitto_strerror(rc));\n\t\t\t}\n\t\t}\n\t}while(rc == MOSQ_ERR_SUCCESS && disconnect_sent == false);\n\n\tif(status == STATUS_DISCONNECTED){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn rc;\n\t}\n}\n\n\nint pub_shared_loop(struct mosquitto *mosq)\n{\n\tif(cfg.pub_mode == MSGMODE_STDIN_LINE){\n\t\treturn pub_stdin_line_loop(mosq);\n\t}else{\n\t\treturn pub_other_loop(mosq);\n\t}\n}\n\n\nvoid pub_shared_cleanup(void)\n{\n\tfree(line_buf);\n}\n\n\nstatic void print_version(void)\n{\n\tint major, minor, revision;\n\n\tmosquitto_lib_version(&major, &minor, &revision);\n\tprintf(\"mosquitto_pub version %s running on libmosquitto %d.%d.%d.\\n\", VERSION, major, minor, revision);\n}\n\n\nstatic void print_usage(void)\n{\n\tint major, minor, revision;\n\n\tmosquitto_lib_version(&major, &minor, &revision);\n\tprintf(\"mosquitto_pub is a simple mqtt client that will publish a message on a single topic and exit.\\n\");\n\tprintf(\"mosquitto_pub version %s running on libmosquitto %d.%d.%d.\\n\\n\", VERSION, major, minor, revision);\n\tprintf(\"Usage: mosquitto_pub {[-h host] [--unix path] [-p port] [-u username] [-P password] [--ws] -t topic | -L URL}\\n\");\n\tprintf(\"                     {-f file | -l | -n | -m message}\\n\");\n\tprintf(\"                     [-c] [-k keepalive] [-q qos] [-r] [--repeat N] [--repeat-delay time] [-x session-expiry]\\n\");\n#ifdef WITH_SRV\n\tprintf(\"                     [-A bind_address] [--nodelay] [-S]\\n\");\n#else\n\tprintf(\"                     [-A bind_address] [--nodelay]\\n\");\n#endif\n\tprintf(\"                     [-i id] [-I id_prefix]\\n\");\n\tprintf(\"                     [-d] [--quiet]\\n\");\n\tprintf(\"                     [-M max_inflight]\\n\");\n\tprintf(\"                     [-u username [-P password]]\\n\");\n\tprintf(\"                     [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\\n\");\n#ifdef WITH_TLS\n\tprintf(\"                     [--no-tls]\\n\");\n\tprintf(\"                     [{--cafile file | --capath dir} [--cert file] [--key file]\\n\");\n\tprintf(\"                       [--ciphers ciphers] [--insecure]\\n\");\n\tprintf(\"                       [--tls-alpn protocol]\\n\");\n\tprintf(\"                       [--tls-engine engine] [--keyform keyform] [--tls-engine-kpass-sha1]]\\n\");\n\tprintf(\"                       [--tls-use-os-certs]\\n\");\n#ifdef FINAL_WITH_TLS_PSK\n\tprintf(\"                     [--psk hex-key --psk-identity identity [--ciphers ciphers]]\\n\");\n#endif\n#endif\n#ifdef WITH_SOCKS\n\tprintf(\"                     [--proxy socks-url]\\n\");\n#endif\n\tprintf(\"                     [--property command identifier value]\\n\");\n\tprintf(\"                     [-D command identifier value]\\n\");\n\tprintf(\"                     [-o options-file]\\n\");\n\tprintf(\"       mosquitto_pub --help\\n\\n\");\n\tprintf(\" -A : bind the outgoing socket to this host/ip address. Use to control which interface\\n\");\n\tprintf(\"      the client communicates over.\\n\");\n\tprintf(\" -d : enable debug messages.\\n\");\n\tprintf(\" -c : disable clean session/enable persistent client mode\\n\");\n\tprintf(\"      When this argument is used, the broker will be instructed not to clean existing sessions\\n\");\n\tprintf(\"      for the same client id when the client connects, and sessions will never expire when the\\n\");\n\tprintf(\"      client disconnects. MQTT v5 clients can change their session expiry interval with the -x\\n\");\n\tprintf(\"      argument.\\n\");\n\tprintf(\" -D : Define MQTT v5 properties. See the documentation for more details.\\n\");\n\tprintf(\" -f : send the contents of a file as the message.\\n\");\n\tprintf(\" -h : mqtt host to connect to. Defaults to localhost.\\n\");\n\tprintf(\" -i : id to use for this client. Defaults to mosquitto_pub_ appended with the process id.\\n\");\n\tprintf(\" -I : define the client id as id_prefix appended with the process id. Useful for when the\\n\");\n\tprintf(\"      broker is using the clientid_prefixes option.\\n\");\n\tprintf(\" -k : keep alive in seconds for this client. Defaults to 60.\\n\");\n\tprintf(\" -L : specify user, password, hostname, port and topic as a URL in the form:\\n\");\n\tprintf(\"      mqtt(s)://[username[:password]@]host[:port]/topic\\n\");\n\tprintf(\"      ws(s)://[username[:password]@]host[:port]/topic\\n\");\n\tprintf(\" -l : read messages from stdin, sending a separate message for each line.\\n\");\n\tprintf(\" -m : message payload to send.\\n\");\n\tprintf(\" -M : the maximum inflight messages for QoS 1/2..\\n\");\n\tprintf(\" -n : send a null (zero length) message.\\n\");\n\tprintf(\" -o : provide options in a file rather than on the command line.\\n\");\n\tprintf(\"      See the Options section of https://mosquitto.org/man/mosquitto_pub-1.html\\n\");\n\tprintf(\" -p : network port to connect to. Defaults to 1883 for plain MQTT and 8883 for MQTT over TLS.\\n\");\n\tprintf(\" -P : provide a password\\n\");\n\tprintf(\" -q : quality of service level to use for all messages. Defaults to 0.\\n\");\n\tprintf(\" -r : message should be retained.\\n\");\n\tprintf(\" -s : read message from stdin, sending the entire input as a message.\\n\");\n#ifdef WITH_SRV\n\tprintf(\" -S : use SRV lookups to determine which host to connect to.\\n\");\n#endif\n\tprintf(\" -t : mqtt topic to publish to.\\n\");\n\tprintf(\" -u : provide a username\\n\");\n\tprintf(\" -V : specify the version of the MQTT protocol to use when connecting.\\n\");\n\tprintf(\"      Can be mqttv5, mqttv311 or mqttv31. Defaults to mqttv311.\\n\");\n\tprintf(\" -x : Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5\\n\");\n\tprintf(\"      clients only. Set to 0-4294967294 to specify the session will expire in that many\\n\");\n\tprintf(\"      seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session\\n\");\n\tprintf(\"      that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given.\\n\");\n\tprintf(\" --help : display this message.\\n\");\n\tprintf(\" --nodelay : disable Nagle's algorithm, to reduce socket sending latency at the possible\\n\");\n\tprintf(\"             expense of more packets being sent.\\n\");\n\tprintf(\" --quiet : don't print error messages.\\n\");\n\tprintf(\" --repeat : if publish mode is -f, -m, or -s, then repeat the publish N times.\\n\");\n\tprintf(\" --repeat-delay : if using --repeat, wait time seconds between publishes. Defaults to 0.\\n\");\n\tprintf(\" --unix : connect to a broker through a unix domain socket instead of a TCP socket,\\n\");\n\tprintf(\"          e.g. /tmp/mosquitto.sock\\n\");\n\tprintf(\" --will-payload : payload for the client Will, which is sent by the broker in case of\\n\");\n\tprintf(\"                  unexpected disconnection. If not given and will-topic is set, a zero\\n\");\n\tprintf(\"                  length message will be sent.\\n\");\n\tprintf(\" --will-qos : QoS level for the client Will.\\n\");\n\tprintf(\" --will-retain : if given, make the client Will retained.\\n\");\n\tprintf(\" --will-topic : the topic on which to publish the client Will.\\n\");\n\tprintf(\" --ws : connect using WebSockets.\\n\");\n#ifdef WITH_TLS\n\tprintf(\" --cafile : path to a file containing trusted CA certificates to enable encrypted\\n\");\n\tprintf(\"            communication.\\n\");\n\tprintf(\" --capath : path to a directory containing trusted CA certificates to enable encrypted\\n\");\n\tprintf(\"            communication.\\n\");\n\tprintf(\" --cert : client certificate for authentication, if required by server.\\n\");\n\tprintf(\" --key : client private key for authentication, if required by server.\\n\");\n\tprintf(\" --keyform : keyfile type, can be either \\\"pem\\\" or \\\"engine\\\".\\n\");\n\tprintf(\" --ciphers : openssl compatible list of TLS ciphers to support.\\n\");\n\tprintf(\" --tls-version : TLS protocol version, can be one of tlsv1.3 or tlsv1.2.\\n\");\n\tprintf(\"                 Defaults to tlsv1.2 if available.\\n\");\n\tprintf(\" --insecure : do not verify the the server certificate. Using this option means that\\n\");\n\tprintf(\"              you cannot be sure that the remote host is the server you wish to connect\\n\");\n\tprintf(\"              to and so is insecure.\\n\");\n\tprintf(\"              Do not use this option in a production environment.\\n\");\n\tprintf(\" --tls-engine : If set, enables the use of a TLS engine device.\\n\");\n\tprintf(\" --tls-engine-kpass-sha1 : SHA1 of the key password to be used with the selected SSL engine.\\n\");\n\tprintf(\" --tls-use-os-certs : Load and trust OS provided CA certificates.\\n\");\n#  ifdef FINAL_WITH_TLS_PSK\n\tprintf(\" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\\n\");\n\tprintf(\" --psk-identity : client identity string for TLS-PSK mode.\\n\");\n#  endif\n#endif\n#ifdef WITH_SOCKS\n\tprintf(\" --proxy : SOCKS5 proxy URL of the form:\\n\");\n\tprintf(\"           socks5h://[username[:password]@]hostname[:port]\\n\");\n\tprintf(\"           Only \\\"none\\\" and \\\"username\\\" authentication is supported.\\n\");\n#endif\n\tprintf(\"\\nSee https://mosquitto.org/ for more information.\\n\\n\");\n}\n\n\nint main(int argc, char *argv[])\n{\n\tstruct mosquitto *mosq = NULL;\n\tint rc;\n\n\tmosquitto_lib_init();\n\n\tif(pub_shared_init()){\n\t\treturn 1;\n\t}\n\n\trc = client_config_load(&cfg, CLIENT_PUB, argc, argv);\n\tif(rc){\n\t\tif(rc == 2){\n\t\t\t/* --help */\n\t\t\tprint_usage();\n\t\t}else if(rc == 3){\n\t\t\tprint_version();\n\t\t}else{\n\t\t\tfprintf(stderr, \"\\nUse 'mosquitto_pub --help' to see usage.\\n\");\n\t\t}\n\t\tgoto cleanup;\n\t}\n\n#ifndef WITH_THREADING\n\tif(cfg.pub_mode == MSGMODE_STDIN_LINE){\n\t\tfprintf(stderr, \"Error: '-l' mode not available, threading support has not been compiled in.\\n\");\n\t\tgoto cleanup;\n\t}\n#endif\n\n\tif(cfg.pub_mode == MSGMODE_STDIN_FILE){\n\t\tif(load_stdin()){\n\t\t\terr_printf(&cfg, \"Error loading input from stdin.\\n\");\n\t\t\tgoto cleanup;\n\t\t}\n\t}else if(cfg.file_input){\n\t\tif(load_file(cfg.file_input)){\n\t\t\terr_printf(&cfg, \"Error loading input file \\\"%s\\\".\\n\", cfg.file_input);\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\n\tif(!cfg.topic || cfg.pub_mode == MSGMODE_NONE){\n\t\tfprintf(stderr, \"Error: Both topic and message must be supplied.\\n\");\n\t\tprint_usage();\n\t\tgoto cleanup;\n\t}\n\n\n\tif(clientid_generate(&cfg)){\n\t\tgoto cleanup;\n\t}\n\n\tmosq = mosquitto_new(cfg.id, cfg.clean_session, NULL);\n\tif(!mosq){\n\t\tswitch(errno){\n\t\t\tcase ENOMEM:\n\t\t\t\terr_printf(&cfg, \"Error: Out of memory.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase EINVAL:\n\t\t\t\terr_printf(&cfg, \"Error: Invalid id.\\n\");\n\t\t\t\tbreak;\n\t\t}\n\t\tgoto cleanup;\n\t}\n\tif(cfg.debug){\n\t\tmosquitto_log_callback_set(mosq, my_log_callback);\n\t}\n\tmosquitto_connect_v5_callback_set(mosq, my_connect_callback);\n\tmosquitto_disconnect_v5_callback_set(mosq, my_disconnect_callback);\n\tmosquitto_publish_v5_callback_set(mosq, my_publish_callback);\n\n\tif(client_opts_set(mosq, &cfg)){\n\t\tgoto cleanup;\n\t}\n\n\trc = client_connect(mosq, &cfg);\n\tif(rc){\n\t\tgoto cleanup;\n\t}\n\n\trc = pub_shared_loop(mosq);\n\n\tif(cfg.message && cfg.pub_mode == MSGMODE_FILE){\n\t\tfree(cfg.message);\n\t\tcfg.message = NULL;\n\t}\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\tclient_config_cleanup(&cfg);\n\tpub_shared_cleanup();\n\n\tif(rc){\n\t\terr_printf(&cfg, \"Error: %s\\n\", mosquitto_strerror(rc));\n\t}\n\tif(connack_result){\n\t\treturn connack_result;\n\t}else{\n\t\treturn rc;\n\t}\n\ncleanup:\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\tclient_config_cleanup(&cfg);\n\tpub_shared_cleanup();\n\treturn 1;\n}\n"
  },
  {
    "path": "client/pub_shared.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#ifndef WIN32\n#include <time.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#define snprintf sprintf_s\n#endif\n\n#include <mosquitto.h>\n#include \"client_shared.h\"\n#include \"pub_shared.h\"\n\n/* Global variables for use in callbacks. See sub_client.c for an example of\n * using a struct to hold variables for use in callbacks. */\nint mid_sent = -1;\nstruct mosq_config cfg;\n\n\nvoid my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(level);\n\n\tprintf(\"%s\\n\", str);\n}\n\n\nint load_stdin(void)\n{\n\tsize_t pos = 0, rlen;\n\tchar buf[1024];\n\tchar *aux_message = NULL;\n\n\tcfg.pub_mode = MSGMODE_STDIN_FILE;\n\n\twhile(!feof(stdin)){\n\t\trlen = fread(buf, 1, 1024, stdin);\n\t\taux_message = mosquitto_realloc(cfg.message, pos+rlen);\n\t\tif(!aux_message){\n\t\t\terr_printf(&cfg, \"Error: Out of memory.\\n\");\n\t\t\tmosquitto_FREE(cfg.message);\n\t\t\treturn 1;\n\t\t}else{\n\t\t\tcfg.message = aux_message;\n\t\t}\n\t\tmemcpy(&(cfg.message[pos]), buf, rlen);\n\t\tpos += rlen;\n\t}\n\tif(pos > MQTT_MAX_PAYLOAD){\n\t\terr_printf(&cfg, \"Error: Message length must be less than %u bytes.\\n\\n\", MQTT_MAX_PAYLOAD);\n\t\tmosquitto_FREE(cfg.message);\n\t\treturn 1;\n\t}\n\tcfg.msglen = (int )pos;\n\n\tif(!cfg.msglen){\n\t\terr_printf(&cfg, \"Error: Zero length input.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\n\nint load_file(const char *filename)\n{\n\tsize_t buflen;\n\tchar *buf;\n\n\tcfg.pub_mode = MSGMODE_FILE;\n\n\tint rc = mosquitto_read_file(filename, false, &buf, &buflen);\n\tif(rc){\n\t\terr_printf(&cfg, \"Error: Unable to read file \\\"%s\\\": %s.\\n\", filename, mosquitto_strerror(rc));\n\t\treturn 1;\n\t}\n\n\tif(buflen > MQTT_MAX_PAYLOAD){\n\t\terr_printf(&cfg, \"Error: File must be less than %u bytes.\\n\\n\", MQTT_MAX_PAYLOAD);\n\t\tmosquitto_FREE(buf);\n\t\treturn 1;\n\t}else if(buflen == 0){\n\t\tcfg.message = NULL;\n\t\tcfg.msglen = 0;\n\t\treturn 0;\n\t}\n\n\tcfg.msglen = (int )buflen;\n\tcfg.message = buf;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "client/pub_shared.h",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef PUB_SHARED_H\n#define PUB_SHARED_H\n\n#define STATUS_CONNECTING 0\n#define STATUS_CONNACK_RECVD 1\n#define STATUS_WAITING 2\n#define STATUS_DISCONNECTING 3\n#define STATUS_DISCONNECTED 4\n#define STATUS_NOHOPE 5\n\nextern int mid_sent;\nextern struct mosq_config cfg;\n\n\nvoid my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties);\nvoid my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties);\nvoid my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties);\nvoid my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str);\nint load_stdin(void);\nint load_file(const char *filename);\n\nint my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain);\n\nint pub_shared_init(void);\nint pub_shared_loop(struct mosquitto *mosq);\nvoid pub_shared_cleanup(void);\n\n#endif\n"
  },
  {
    "path": "client/pub_test_properties",
    "content": "LD_LIBRARY_PATH=../lib ./mosquitto_pub \\\n\t\\\n\t-t asdf -V mqttv5 -m '{\"key\":\"value\"}' \\\n\t\\\n\t-D connect authentication-data password \\\n\t-D connect authentication-method something \\\n\t-D connect maximum-packet-size 0191 \\\n\t-D connect receive-maximum 1000 \\\n\t-D connect request-problem-information 1 \\\n\t-D connect request-response-information 1 \\\n\t-D connect session-expiry-interval 39 \\\n\t-D connect topic-alias-maximum 123 \\\n\t-D connect user-property connect up \\\n\t\\\n\t-D publish content-type application/json \\\n\t-D publish correlation-data some-data \\\n\t-D publish message-expiry-interval 59 \\\n\t-D publish payload-format-indicator 1 \\\n\t-D publish response-topic /dev/null \\\n\t-D publish topic-alias 4 \\\n\t-D publish user-property publish up \\\n\t\\\n\t-D disconnect reason-string \"reason\" \\\n\t-D disconnect session-expiry-interval 40 \\\n\t-D disconnect user-property disconnect up\n\n"
  },
  {
    "path": "client/rr_client.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#ifndef WIN32\n#include <unistd.h>\n#include <signal.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#define snprintf sprintf_s\n#endif\n\n#include <mosquitto.h>\n#include \"client_shared.h\"\n#include \"pub_shared.h\"\n#include \"sub_client_output.h\"\n\nenum rr__state {\n\trr_s_new,\n\trr_s_connected,\n\trr_s_subscribed,\n\trr_s_ready_to_publish,\n\trr_s_wait_for_response,\n\trr_s_disconnect,\n};\n\nstatic enum rr__state client_state = rr_s_new;\n\nbool process_messages = true;\nint msg_count = 0;\nstruct mosquitto *g_mosq = NULL;\nstatic bool timed_out = false;\nstatic int connack_result = 0;\nstatic struct timespec publish_send_time;\nstatic struct timespec publish_recv_time;\n\n#ifndef WIN32\n\n\nstatic void my_signal_handler(int signum)\n{\n\tif(signum == SIGALRM){\n\t\tprocess_messages = false;\n\t\tmosquitto_disconnect_v5(g_mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props);\n\t\ttimed_out = true;\n\t}\n}\n#endif\n\n\nint my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain)\n{\n\tmosquitto_time_ns(&publish_send_time.tv_sec, &publish_send_time.tv_nsec);\n\n\tif(cfg.protocol_version < MQTT_PROTOCOL_V5){\n\t\treturn mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, NULL);\n\t}else{\n\t\treturn mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, cfg.publish_props);\n\t}\n}\n\n\nstatic void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(properties);\n\n\tif(process_messages == false){\n\t\treturn;\n\t}\n\tif(message->retain && cfg.no_retain){\n\t\treturn;\n\t}\n\n\tmosquitto_time_ns(&publish_recv_time.tv_sec, &publish_recv_time.tv_nsec);\n\n\tprint_message(&cfg, message, properties);\n\n\tswitch(cfg.pub_mode){\n\t\tcase MSGMODE_CMD:\n\t\tcase MSGMODE_FILE:\n\t\tcase MSGMODE_STDIN_FILE:\n\t\tcase MSGMODE_NULL:\n\t\t\tclient_state = rr_s_disconnect;\n\t\t\tbreak;\n\n\t\tcase MSGMODE_STDIN_LINE:\n\t\t\tclient_state = rr_s_ready_to_publish;\n\t\t\tbreak;\n\t}\n}\n\n\nvoid my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties)\n{\n\tUNUSED(obj);\n\tUNUSED(flags);\n\tUNUSED(properties);\n\n\tconnack_result = result;\n\tif(!result){\n\t\tclient_state = rr_s_connected;\n\t\tmosquitto_subscribe_v5(mosq, NULL, cfg.response_topic, cfg.qos, cfg.sub_opts, cfg.subscribe_props);\n\t}else{\n\t\tclient_state = rr_s_disconnect;\n\t\tif(result){\n\t\t\terr_printf(&cfg, \"Connection error: %s\\n\", mosquitto_reason_string(result));\n\t\t}\n\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t}\n}\n\n\nstatic void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\tUNUSED(obj);\n\tUNUSED(mid);\n\tUNUSED(qos_count);\n\n\tif(granted_qos[0] < 128){\n\t\tclient_state = rr_s_ready_to_publish;\n\t}else{\n\t\tclient_state = rr_s_disconnect;\n\t\terr_printf(&cfg, \"%s\\n\", mosquitto_reason_string(granted_qos[0]));\n\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t}\n}\n\n\nstatic void print_version(void)\n{\n\tint major, minor, revision;\n\n\tmosquitto_lib_version(&major, &minor, &revision);\n\tprintf(\"mosquitto_rr version %s running on libmosquitto %d.%d.%d.\\n\", VERSION, major, minor, revision);\n}\n\n\nstatic void print_usage(void)\n{\n\tint major, minor, revision;\n\n\tmosquitto_lib_version(&major, &minor, &revision);\n\tprintf(\"mosquitto_rr is an mqtt client that can be used to publish a request message and wait for a response.\\n\");\n\tprintf(\"             Defaults to MQTT v5, where the Request-Response feature will be used, but v3.1.1 can also be used\\n\");\n\tprintf(\"             with v3.1.1 brokers.\\n\");\n\tprintf(\"mosquitto_rr version %s running on libmosquitto %d.%d.%d.\\n\\n\", VERSION, major, minor, revision);\n\tprintf(\"Usage: mosquitto_rr {[-h host] [--unix path] [-p port] [-u username] [-P password] -t topic | -L URL} -e response-topic\\n\");\n\tprintf(\"                    {-f file | -l | -n | -m message}\\n\");\n\tprintf(\"                    [-c] [-k keepalive] [-q qos] [-R] [-x session-expiry-interval]\\n\");\n\tprintf(\"                    [-F format]\\n\");\n#ifndef WIN32\n\tprintf(\"                    [-W timeout_secs]\\n\");\n#endif\n#ifdef WITH_SRV\n\tprintf(\"                    [-A bind_address] [--nodelay] [-S]\\n\");\n#else\n\tprintf(\"                    [-A bind_address] [--nodelay]\\n\");\n#endif\n\tprintf(\"                    [-i id] [-I id_prefix]\\n\");\n\tprintf(\"                    [-d] [-N] [--quiet] [-v]\\n\");\n\tprintf(\"                    [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\\n\");\n#ifdef WITH_TLS\n\tprintf(\"                    [--no-tls]\\n\");\n\tprintf(\"                    [{--cafile file | --capath dir} [--cert file] [--key file]\\n\");\n\tprintf(\"                      [--ciphers ciphers] [--insecure]\\n\");\n\tprintf(\"                      [--tls-alpn protocol]\\n\");\n\tprintf(\"                      [--tls-engine engine] [--keyform keyform] [--tls-engine-kpass-sha1]]\\n\");\n\tprintf(\"                      [--tls-use-os-certs]\\n\");\n#ifdef FINAL_WITH_TLS_PSK\n\tprintf(\"                     [--psk hex-key --psk-identity identity [--ciphers ciphers]]\\n\");\n#endif\n#endif\n#ifdef WITH_SOCKS\n\tprintf(\"                    [--proxy socks-url]\\n\");\n#endif\n\tprintf(\"                    [-D command identifier value]\\n\");\n\tprintf(\"                    [-o options-file]\\n\");\n\tprintf(\"       mosquitto_rr --help\\n\\n\");\n\tprintf(\" -A : bind the outgoing socket to this host/ip address. Use to control which interface\\n\");\n\tprintf(\"      the client communicates over.\\n\");\n\tprintf(\" -c : disable clean session/enable persistent client mode\\n\");\n\tprintf(\"      When this argument is used, the broker will be instructed not to clean existing sessions\\n\");\n\tprintf(\"      for the same client id when the client connects, and sessions will never expire when the\\n\");\n\tprintf(\"      client disconnects. MQTT v5 clients can change their session expiry interval with the -x\\n\");\n\tprintf(\"      argument.\\n\");\n\tprintf(\" -d : enable debug messages.\\n\");\n\tprintf(\" -D : Define MQTT v5 properties. See the documentation for more details.\\n\");\n\tprintf(\" -e : Response topic. The client will subscribe to this topic to wait for a response.\\n\");\n\tprintf(\" -F : output format.\\n\");\n\tprintf(\" -h : mqtt host to connect to. Defaults to localhost.\\n\");\n\tprintf(\" -i : id to use for this client. Defaults to mosquitto_rr_ appended with the process id.\\n\");\n\tprintf(\" -k : keep alive in seconds for this client. Defaults to 60.\\n\");\n\tprintf(\" -L : specify user, password, hostname, port and topic as a URL in the form:\\n\");\n\tprintf(\"      mqtt(s)://[username[:password]@]host[:port]/topic\\n\");\n\tprintf(\"      ws(s)://[username[:password]@]host[:port]/topic\\n\");\n\tprintf(\" -N : do not add an end of line character when printing the payload.\\n\");\n\tprintf(\" -o : provide options in a file rather than on the command line.\\n\");\n\tprintf(\"      See the Options section of https://mosquitto.org/man/mosquitto_pub-1.html\\n\");\n\tprintf(\" -p : network port to connect to. Defaults to 1883 for plain MQTT and 8883 for MQTT over TLS.\\n\");\n\tprintf(\" -P : provide a password\\n\");\n\tprintf(\" -q : quality of service level to use for communications. Defaults to 0.\\n\");\n\tprintf(\" -R : do not print stale messages (those with retain set).\\n\");\n#ifdef WITH_SRV\n\tprintf(\" -S : use SRV lookups to determine which host to connect to.\\n\");\n#endif\n\tprintf(\" -t : topic where the request message will be sent.\\n\");\n\tprintf(\" -u : provide a username\\n\");\n\tprintf(\" -v : print received messages verbosely.\\n\");\n\tprintf(\" -V : specify the version of the MQTT protocol to use when connecting.\\n\");\n\tprintf(\"      Defaults to 5.\\n\");\n#ifndef WIN32\n\tprintf(\" -W : Specifies a timeout in seconds how long to wait for a response.\\n\");\n#endif\n\tprintf(\" -x : Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5\\n\");\n\tprintf(\"      clients only. Set to 0-4294967294 to specify the session will expire in that many\\n\");\n\tprintf(\"      seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session\\n\");\n\tprintf(\"      that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given.\\n\");\n\tprintf(\" --help : display this message.\\n\");\n\tprintf(\" --nodelay : disable Nagle's algorithm, to reduce socket sending latency at the possible\\n\");\n\tprintf(\"             expense of more packets being sent.\\n\");\n\tprintf(\" --pretty : print formatted output rather than minimised output when using the\\n\");\n\tprintf(\"            JSON output format option.\\n\");\n\tprintf(\" --quiet : don't print error messages.\\n\");\n\tprintf(\" --unix : connect to a broker through a unix domain socket instead of a TCP socket,\\n\");\n\tprintf(\"          e.g. /tmp/mosquitto.sock\\n\");\n\tprintf(\" --will-payload : payload for the client Will, which is sent by the broker in case of\\n\");\n\tprintf(\"                  unexpected disconnection. If not given and will-topic is set, a zero\\n\");\n\tprintf(\"                  length message will be sent.\\n\");\n\tprintf(\" --will-qos : QoS level for the client Will.\\n\");\n\tprintf(\" --will-retain : if given, make the client Will retained.\\n\");\n\tprintf(\" --will-topic : the topic on which to publish the client Will.\\n\");\n\tprintf(\" --ws : connect using WebSockets.\\n\");\n#ifdef WITH_TLS\n\tprintf(\" --cafile : path to a file containing trusted CA certificates to enable encrypted\\n\");\n\tprintf(\"            certificate based communication.\\n\");\n\tprintf(\" --capath : path to a directory containing trusted CA certificates to enable encrypted\\n\");\n\tprintf(\"            communication.\\n\");\n\tprintf(\" --cert : client certificate for authentication, if required by server.\\n\");\n\tprintf(\" --key : client private key for authentication, if required by server.\\n\");\n\tprintf(\" --ciphers : openssl compatible list of TLS ciphers to support.\\n\");\n\tprintf(\" --tls-use-os-certs : Load and trust OS provided CA certificates.\\n\");\n\tprintf(\" --tls-version : TLS protocol version, can be one of tlsv1.3 or tlsv1.2.\\n\");\n\tprintf(\"                 Defaults to tlsv1.2 if available.\\n\");\n\tprintf(\" --insecure : do not verify the the server certificate. Using this option means that\\n\");\n\tprintf(\"              you cannot be sure that the remote host is the server you wish to connect\\n\");\n\tprintf(\"              to and so is insecure.\\n\");\n\tprintf(\"              Do not use this option in a production environment.\\n\");\n#ifdef WITH_TLS_PSK\n\tprintf(\" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\\n\");\n\tprintf(\" --psk-identity : client identity string for TLS-PSK mode.\\n\");\n#endif\n#endif\n#ifdef WITH_SOCKS\n\tprintf(\" --proxy : SOCKS5 proxy URL of the form:\\n\");\n\tprintf(\"           socks5h://[username[:password]@]hostname[:port]\\n\");\n\tprintf(\"           Only \\\"none\\\" and \\\"username\\\" authentication is supported.\\n\");\n#endif\n\tprintf(\"\\nSee https://mosquitto.org/ for more information.\\n\\n\");\n}\n\n\nstatic void report_latency(void)\n{\n\tif(cfg.measure_latency){\n\t\ttime_t s = publish_recv_time.tv_sec - publish_send_time.tv_sec;\n\t\tlong ns = publish_recv_time.tv_nsec - publish_send_time.tv_nsec;\n\n\t\tif(ns < 0){\n\t\t\ts--;\n\t\t\tns += 1000000000;\n\t\t}\n\n\t\tif(s > 0){\n\t\t\tprintf(\"Latency: %ld.%09ld\\n\", s, ns);\n\t\t}else{\n\t\t\tif(ns < 1000){\n\t\t\t\tprintf(\"Latency: %ldns\\n\", ns);\n\t\t\t}else if(ns < 1000000){\n\t\t\t\tprintf(\"Latency: %fµs\\n\", ((double)ns)/1000.0);\n\t\t\t}else{\n\t\t\t\tprintf(\"Latency: %fms\\n\", ((double)ns)/1000000.0);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n#ifndef WIN32\n\tstruct sigaction sigact;\n#endif\n\n\tmosquitto_lib_init();\n\n\trc = client_config_load(&cfg, CLIENT_RR, argc, argv);\n\tif(rc){\n\t\tif(rc == 2){\n\t\t\t/* --help */\n\t\t\tprint_usage();\n\t\t}else if(rc == 3){\n\t\t\t/* --version */\n\t\t\tprint_version();\n\t\t}else{\n\t\t\tfprintf(stderr, \"\\nUse 'mosquitto_rr --help' to see usage.\\n\");\n\t\t}\n\t\tgoto cleanup;\n\t}\n\n\tif(cfg.pub_mode == MSGMODE_STDIN_FILE){\n\t\tif(load_stdin()){\n\t\t\terr_printf(&cfg, \"Error loading input from stdin.\\n\");\n\t\t\tgoto cleanup;\n\t\t}\n\t}else if(cfg.file_input){\n\t\tif(load_file(cfg.file_input)){\n\t\t\terr_printf(&cfg, \"Error loading input file \\\"%s\\\".\\n\", cfg.file_input);\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\n\tif(!cfg.topic || cfg.pub_mode == MSGMODE_NONE || !cfg.response_topic){\n\t\tfprintf(stderr, \"Error: All of topic, message, and response topic must be supplied.\\n\");\n\t\tfprintf(stderr, \"\\nUse 'mosquitto_rr --help' to see usage.\\n\");\n\t\tgoto cleanup;\n\t}\n\trc = mosquitto_property_add_string(&cfg.publish_props, MQTT_PROP_RESPONSE_TOPIC, cfg.response_topic);\n\tif(rc){\n\t\tfprintf(stderr, \"Error adding property RESPONSE_TOPIC.\\n\");\n\t\tgoto cleanup;\n\t}\n\trc = mosquitto_property_check_all(CMD_PUBLISH, cfg.publish_props);\n\tif(rc){\n\t\terr_printf(&cfg, \"Error in PUBLISH properties: Duplicate response topic.\\n\");\n\t\tgoto cleanup;\n\t}\n\toutput_init(&cfg);\n\n\tif(clientid_generate(&cfg)){\n\t\tgoto cleanup;\n\t}\n\n\tg_mosq = mosquitto_new(cfg.id, cfg.clean_session, &cfg);\n\tif(!g_mosq){\n\t\tswitch(errno){\n\t\t\tcase ENOMEM:\n\t\t\t\terr_printf(&cfg, \"Error: Out of memory.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase EINVAL:\n\t\t\t\terr_printf(&cfg, \"Error: Invalid id and/or clean_session.\\n\");\n\t\t\t\tbreak;\n\t\t}\n\t\tgoto cleanup;\n\t}\n\tif(client_opts_set(g_mosq, &cfg)){\n\t\tgoto cleanup;\n\t}\n\tif(cfg.debug){\n\t\tmosquitto_log_callback_set(g_mosq, my_log_callback);\n\t}\n\tmosquitto_connect_v5_callback_set(g_mosq, my_connect_callback);\n\tmosquitto_subscribe_callback_set(g_mosq, my_subscribe_callback);\n\tmosquitto_message_v5_callback_set(g_mosq, my_message_callback);\n\n\trc = client_connect(g_mosq, &cfg);\n\tif(rc){\n\t\tgoto cleanup;\n\t}\n\n#ifndef WIN32\n\tsigact.sa_handler = my_signal_handler;\n\tsigemptyset(&sigact.sa_mask);\n\tsigact.sa_flags = 0;\n\n\tif(sigaction(SIGALRM, &sigact, NULL) == -1){\n\t\tperror(\"sigaction\");\n\t\tgoto cleanup;\n\t}\n\n\tif(cfg.timeout){\n\t\talarm(cfg.timeout);\n\t}\n#endif\n\n\tdo{\n\t\trc = mosquitto_loop(g_mosq, -1, 1);\n\t\tif(client_state == rr_s_ready_to_publish){\n\t\t\tclient_state = rr_s_wait_for_response;\n\t\t\tswitch(cfg.pub_mode){\n\t\t\t\tcase MSGMODE_CMD:\n\t\t\t\tcase MSGMODE_FILE:\n\t\t\t\tcase MSGMODE_STDIN_FILE:\n\t\t\t\t\trc = my_publish(g_mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain);\n\t\t\t\t\tbreak;\n\t\t\t\tcase MSGMODE_NULL:\n\t\t\t\t\trc = my_publish(g_mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain);\n\t\t\t\t\tbreak;\n\t\t\t\tcase MSGMODE_STDIN_LINE:\n\t\t\t\t\t/* FIXME */\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}while(rc == MOSQ_ERR_SUCCESS && client_state != rr_s_disconnect);\n\n\treport_latency();\n\n\tmosquitto_destroy(g_mosq);\n\tmosquitto_lib_cleanup();\n\n\tif(cfg.msg_count>0 && rc == MOSQ_ERR_NO_CONN){\n\t\trc = 0;\n\t}\n\tclient_config_cleanup(&cfg);\n\tif(timed_out){\n\t\terr_printf(&cfg, \"Timed out\\n\");\n\t\treturn MOSQ_ERR_TIMEOUT;\n\t}else if(rc){\n\t\terr_printf(&cfg, \"Error: %s\\n\", mosquitto_strerror(rc));\n\t}\n\tif(connack_result){\n\t\treturn connack_result;\n\t}else{\n\t\treturn rc;\n\t}\n\ncleanup:\n\tmosquitto_lib_cleanup();\n\tclient_config_cleanup(&cfg);\n\treturn 1;\n}\n\n"
  },
  {
    "path": "client/sub_client.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#ifndef WIN32\n#include <unistd.h>\n#include <signal.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#define snprintf sprintf_s\n#endif\n\n#include <mosquitto.h>\n#include \"client_shared.h\"\n#include \"sub_client_output.h\"\n\nstruct mosq_config cfg;\nstatic bool run = true;\nbool process_messages = true;\nint msg_count = 0;\nint message_rate_msg_count = 0;\nstruct mosquitto *g_mosq = NULL;\nint last_mid = 0;\nstatic bool timed_out = false;\nstatic int connack_result = 0;\nbool connack_received = false;\n#ifdef WIN32\nstatic HANDLE timeout_h = NULL;\n#endif\n\n#ifdef WIN32\n\n\nvoid CALLBACK timeout_cb(PVOID lpParameter, BOOLEAN TimerOrWaitFired)\n{\n\tUNUSED(lpParameter);\n\tUNUSED(TimerOrWaitFired);\n\n\tif(connack_received){\n\t\tprocess_messages = false;\n\t\tmosquitto_disconnect_v5(g_mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props);\n\t}else{\n\t\texit(-1);\n\t}\n\n\ttimed_out = true;\n\t(void)DeleteTimerQueueTimer(NULL, timeout_h, NULL);\n\ttimeout_h = NULL;\n}\n#else\n\n\nstatic void my_signal_handler(int signum)\n{\n\tif(signum == SIGALRM || signum == SIGTERM || signum == SIGINT){\n\t\tif(connack_received){\n\t\t\tprocess_messages = false;\n\t\t\tmosquitto_disconnect_v5(g_mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props);\n\t\t}else{\n\t\t\texit(-1);\n\t\t}\n\t\trun = false;\n\t}\n\tif(signum == SIGALRM){\n\t\ttimed_out = true;\n\t}\n}\n#endif\n\n\nstatic void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *properties)\n{\n\tint i;\n\tbool res;\n\n\tUNUSED(obj);\n\tUNUSED(properties);\n\n\tmessage_rate_msg_count++;\n\n\tif(process_messages == false){\n\t\treturn;\n\t}\n\n\tif(cfg.retained_only && !message->retain && process_messages){\n\t\tprocess_messages = false;\n\t\tif(last_mid == 0){\n\t\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t\t}\n\t\treturn;\n\t}\n\n\tif(message->retain && cfg.no_retain){\n\t\treturn;\n\t}\n\tif(cfg.filter_outs){\n\t\tfor(i=0; i<cfg.filter_out_count; i++){\n\t\t\tmosquitto_topic_matches_sub(cfg.filter_outs[i], message->topic, &res);\n\t\t\tif(res){\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(cfg.remove_retained && message->retain){\n\t\tmosquitto_publish(mosq, &last_mid, message->topic, 0, NULL, 1, true);\n\t}\n\n\tprint_message(&cfg, message, properties);\n\tif(ferror(stdout)){\n\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t}\n\n\tif(cfg.msg_count>0){\n\t\tmsg_count++;\n\t\tif(cfg.msg_count == msg_count){\n\t\t\tprocess_messages = false;\n\t\t\tif(last_mid == 0){\n\t\t\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties)\n{\n\tint i;\n\n\tUNUSED(obj);\n\tUNUSED(flags);\n\tUNUSED(properties);\n\n\tconnack_received = true;\n\n\tconnack_result = result;\n\tif(!result){\n\t\tmosquitto_subscribe_multiple(mosq, NULL, cfg.topic_count, cfg.topics, cfg.qos, cfg.sub_opts, cfg.subscribe_props);\n\n\t\tfor(i=0; i<cfg.unsub_topic_count; i++){\n\t\t\tmosquitto_unsubscribe_v5(mosq, NULL, cfg.unsub_topics[i], cfg.unsubscribe_props);\n\t\t}\n\t}else{\n\t\tif(result){\n\t\t\tif(cfg.protocol_version == MQTT_PROTOCOL_V5){\n\t\t\t\tif(result == MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION){\n\t\t\t\t\terr_printf(&cfg, \"Connection error: %s. Try connecting to an MQTT v5 broker, or use MQTT v3.x mode.\\n\", mosquitto_reason_string(result));\n\t\t\t\t}else{\n\t\t\t\t\terr_printf(&cfg, \"Connection error: %s\\n\", mosquitto_reason_string(result));\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\terr_printf(&cfg, \"Connection error: %s\\n\", mosquitto_connack_string(result));\n\t\t\t}\n\t\t}\n\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t}\n}\n\n\nstatic void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\tint i;\n\tbool some_sub_allowed = (granted_qos[0] < 128);\n\tbool should_print = cfg.debug && !cfg.quiet;\n\tUNUSED(obj);\n\n\tif(should_print){\n\t\tprintf(\"Subscribed (mid: %d): %d\", mid, granted_qos[0]);\n\t}\n\tfor(i=1; i<qos_count; i++){\n\t\tif(should_print){\n\t\t\tprintf(\", %d\", granted_qos[i]);\n\t\t}\n\t\tsome_sub_allowed |= (granted_qos[i] < 128);\n\t}\n\tif(should_print){\n\t\tprintf(\"\\n\");\n\t}\n\n\tif(some_sub_allowed == false){\n\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t\terr_printf(&cfg, \"All subscription requests were denied.\\n\");\n\t}\n\n\tif(cfg.exit_after_sub){\n\t\tmosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);\n\t}\n}\n\n\nstatic void my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(level);\n\n\tprintf(\"%s\\n\", str);\n}\n\n\nstatic void print_version(void)\n{\n\tint major, minor, revision;\n\n\tmosquitto_lib_version(&major, &minor, &revision);\n\tprintf(\"mosquitto_sub version %s running on libmosquitto %d.%d.%d.\\n\", VERSION, major, minor, revision);\n}\n\n\nstatic void print_usage(void)\n{\n\tint major, minor, revision;\n\n\tmosquitto_lib_version(&major, &minor, &revision);\n\tprintf(\"mosquitto_sub is a simple mqtt client that will subscribe to a set of topics and print all messages it receives.\\n\");\n\tprintf(\"mosquitto_sub version %s running on libmosquitto %d.%d.%d.\\n\\n\", VERSION, major, minor, revision);\n\tprintf(\"Usage: mosquitto_sub {[-h host] [--unix path] [-p port] [-u username] [-P password] {-t topic | -U topic} [--ws] | -L URL [-t topic]}\\n\");\n\tprintf(\"                     [-c] [-k keepalive] [-q qos] [-x session-expiry-interval]\\n\");\n\tprintf(\"                     [-C msg_count] [-E] [-R] [--retained-only] [--remove-retained] [-T filter_out]\\n\");\n\tprintf(\"                     [-F format]\\n\");\n#ifndef WIN32\n\tprintf(\"                     [-W timeout_secs]\\n\");\n#endif\n#ifdef WITH_SRV\n\tprintf(\"                     [-A bind_address] [--nodelay] [-S]\\n\");\n#else\n\tprintf(\"                     [-A bind_address] [--nodelay]\\n\");\n#endif\n\tprintf(\"                     [-i id] [-I id_prefix]\\n\");\n\tprintf(\"                     [-d] [-N] [--quiet] [-v] [-w|--watch]\\n\");\n\tprintf(\"                     [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\\n\");\n#ifdef WITH_TLS\n\tprintf(\"                     [--no-tls]\\n\");\n\tprintf(\"                     [{--cafile file | --capath dir} [--cert file] [--key file]\\n\");\n\tprintf(\"                       [--ciphers ciphers] [--insecure]\\n\");\n\tprintf(\"                       [--tls-alpn protocol]\\n\");\n\tprintf(\"                       [--tls-engine engine] [--keyform keyform] [--tls-engine-kpass-sha1]]\\n\");\n\tprintf(\"                       [--tls-use-os-certs]\\n\");\n#ifdef FINAL_WITH_TLS_PSK\n\tprintf(\"                     [--psk hex-key --psk-identity identity [--ciphers ciphers]]\\n\");\n#endif\n#endif\n#ifdef WITH_SOCKS\n\tprintf(\"                     [--proxy socks-url]\\n\");\n#endif\n\tprintf(\"                     [-D command identifier value]\\n\");\n\tprintf(\"                     [-o options-file]\\n\");\n\tprintf(\"       mosquitto_sub --help\\n\\n\");\n\tprintf(\" -A : bind the outgoing socket to this host/ip address. Use to control which interface\\n\");\n\tprintf(\"      the client communicates over.\\n\");\n\tprintf(\" -c : disable clean session/enable persistent client mode\\n\");\n\tprintf(\"      When this argument is used, the broker will be instructed not to clean existing sessions\\n\");\n\tprintf(\"      for the same client id when the client connects, and sessions will never expire when the\\n\");\n\tprintf(\"      client disconnects. MQTT v5 clients can change their session expiry interval with the -x\\n\");\n\tprintf(\"      argument.\\n\");\n\tprintf(\" -C : disconnect and exit after receiving the 'msg_count' messages.\\n\");\n\tprintf(\" -d : enable debug messages.\\n\");\n\tprintf(\" -D : Define MQTT v5 properties. See the documentation for more details.\\n\");\n\tprintf(\" -E : Exit once all subscriptions have been acknowledged by the broker.\\n\");\n\tprintf(\" -F : output format.\\n\");\n\tprintf(\" -h : mqtt host to connect to. Defaults to localhost.\\n\");\n\tprintf(\" -i : id to use for this client. Defaults to mosquitto_sub_ appended with the process id.\\n\");\n\tprintf(\" -I : define the client id as id_prefix appended with the process id. Useful for when the\\n\");\n\tprintf(\"      broker is using the clientid_prefixes option.\\n\");\n\tprintf(\" -k : keep alive in seconds for this client. Defaults to 60.\\n\");\n\tprintf(\" -L : specify user, password, hostname, port and topic as a URL in the form:\\n\");\n\tprintf(\"      mqtt(s)://[username[:password]@]host[:port]/topic\\n\");\n\tprintf(\"      ws(s)://[username[:password]@]host[:port]/topic\\n\");\n\tprintf(\" -N : do not add an end of line character when printing the payload.\\n\");\n\tprintf(\" -o : provide options in a file rather than on the command line.\\n\");\n\tprintf(\"      See the Options section of https://mosquitto.org/man/mosquitto_pub-1.html\\n\");\n\tprintf(\" -p : network port to connect to. Defaults to 1883 for plain MQTT and 8883 for MQTT over TLS.\\n\");\n\tprintf(\" -P : provide a password\\n\");\n\tprintf(\" -q : quality of service level to use for the subscription. Defaults to 0.\\n\");\n\tprintf(\" -R : do not print stale messages (those with retain set).\\n\");\n#ifdef WITH_SRV\n\tprintf(\" -S : use SRV lookups to determine which host to connect to.\\n\");\n#endif\n\tprintf(\" -t : mqtt topic to subscribe to. May be repeated multiple times.\\n\");\n\tprintf(\" -T : topic string to filter out of results. May be repeated.\\n\");\n\tprintf(\" -u : provide a username\\n\");\n\tprintf(\" -U : unsubscribe from a topic. May be repeated.\\n\");\n\tprintf(\" -v : print published messages verbosely.\\n\");\n\tprintf(\" -V : specify the version of the MQTT protocol to use when connecting.\\n\");\n\tprintf(\"      Can be mqttv5, mqttv311 or mqttv31. Defaults to mqttv311.\\n\");\n#ifndef WIN32\n\tprintf(\" -W : Specifies a timeout in seconds how long to process incoming MQTT messages.\\n\");\n#endif\n\tprintf(\" -w : messages will be printed on a fixed line number based on the topic and order in\\n\");\n\tprintf(\"      which topics are received. Useful for monitoring multiple topics that have\\n\");\n\tprintf(\"      single line payloads.\\n\");\n\tprintf(\" -x : Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5\\n\");\n\tprintf(\"      clients only. Set to 0-4294967294 to specify the session will expire in that many\\n\");\n\tprintf(\"      seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session\\n\");\n\tprintf(\"      that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given.\\n\");\n\tprintf(\" --help : display this message.\\n\");\n\tprintf(\" --nodelay : disable Nagle's algorithm, to reduce socket sending latency at the possible\\n\");\n\tprintf(\"             expense of more packets being sent.\\n\");\n\tprintf(\" --pretty : print formatted output rather than minimised output when using the\\n\");\n\tprintf(\"            JSON output format option.\\n\");\n\tprintf(\" --quiet : don't print error messages.\\n\");\n\tprintf(\" --random-filter : only print a percentage of received messages. Set to 100 to have all\\n\");\n\tprintf(\"                   messages printed, 50.0 to have half of the messages received on average\\n\");\n\tprintf(\"                   printed, and so on.\\n\");\n\tprintf(\" --retained-only : only handle messages with the retained flag set, and exit when the\\n\");\n\tprintf(\"                   first non-retained message is received.\\n\");\n\tprintf(\" --remove-retained : send a message to the server to clear any received retained messages\\n\");\n\tprintf(\"                     Use -T to filter out messages you do not want to be cleared.\\n\");\n\tprintf(\" --unix : connect to a broker through a unix domain socket instead of a TCP socket,\\n\");\n\tprintf(\"          e.g. /tmp/mosquitto.sock\\n\");\n\tprintf(\" --will-payload : payload for the client Will, which is sent by the broker in case of\\n\");\n\tprintf(\"                  unexpected disconnection. If not given and will-topic is set, a zero\\n\");\n\tprintf(\"                  length message will be sent.\\n\");\n\tprintf(\" --will-qos : QoS level for the client Will.\\n\");\n\tprintf(\" --will-retain : if given, make the client Will retained.\\n\");\n\tprintf(\" --will-topic : the topic on which to publish the client Will.\\n\");\n\tprintf(\" --ws : connect using WebSockets.\\n\");\n#ifdef WITH_TLS\n\tprintf(\" --cafile : path to a file containing trusted CA certificates to enable encrypted\\n\");\n\tprintf(\"            certificate based communication.\\n\");\n\tprintf(\" --capath : path to a directory containing trusted CA certificates to enable encrypted\\n\");\n\tprintf(\"            communication.\\n\");\n\tprintf(\" --cert : client certificate for authentication, if required by server.\\n\");\n\tprintf(\" --key : client private key for authentication, if required by server.\\n\");\n\tprintf(\" --keyform : keyfile type, can be either \\\"pem\\\" or \\\"engine\\\".\\n\");\n\tprintf(\" --ciphers : openssl compatible list of TLS ciphers to support.\\n\");\n\tprintf(\" --tls-version : TLS protocol version, can be one of tlsv1.3 or tlsv1.2.\\n\");\n\tprintf(\"                 Defaults to tlsv1.2 if available.\\n\");\n\tprintf(\" --insecure : do not verify the the server certificate. Using this option means that\\n\");\n\tprintf(\"              you cannot be sure that the remote host is the server you wish to connect\\n\");\n\tprintf(\"              to and so is insecure.\\n\");\n\tprintf(\"              Do not use this option in a production environment.\\n\");\n\tprintf(\" --tls-engine : If set, enables the use of a SSL engine device.\\n\");\n\tprintf(\" --tls-engine-kpass-sha1 : SHA1 of the key password to be used with the selected SSL engine.\\n\");\n\tprintf(\" --tls-use-os-certs : Load and trust OS provided CA certificates.\\n\");\n#ifdef FINAL_WITH_TLS_PSK\n\tprintf(\" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\\n\");\n\tprintf(\" --psk-identity : client identity string for TLS-PSK mode.\\n\");\n#endif\n#endif\n#ifdef WITH_SOCKS\n\tprintf(\" --proxy : SOCKS5 proxy URL of the form:\\n\");\n\tprintf(\"           socks5h://[username[:password]@]hostname[:port]\\n\");\n\tprintf(\"           Only \\\"none\\\" and \\\"username\\\" authentication is supported.\\n\");\n#endif\n\tprintf(\"\\nSee https://mosquitto.org/ for more information.\\n\\n\");\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n#ifndef WIN32\n\tstruct sigaction sigact;\n#endif\n\n\tmosquitto_lib_init();\n\n\trc = client_config_load(&cfg, CLIENT_SUB, argc, argv);\n\tif(rc){\n\t\tif(rc == 2){\n\t\t\t/* --help */\n\t\t\tprint_usage();\n\t\t}else if(rc == 3){\n\t\t\t/* --version */\n\t\t\tprint_version();\n\t\t}else{\n\t\t\tfprintf(stderr, \"\\nUse 'mosquitto_sub --help' to see usage.\\n\");\n\t\t}\n\t\tgoto cleanup;\n\t}\n\n\tif(cfg.no_retain && cfg.retained_only){\n\t\tfprintf(stderr, \"\\nError: Combining '-R' and '--retained-only' makes no sense.\\n\");\n\t\tgoto cleanup;\n\t}\n\n\tif(clientid_generate(&cfg)){\n\t\tgoto cleanup;\n\t}\n\n\tg_mosq = mosquitto_new(cfg.id, cfg.clean_session, &cfg);\n\tif(!g_mosq){\n\t\tswitch(errno){\n\t\t\tcase ENOMEM:\n\t\t\t\terr_printf(&cfg, \"Error: Out of memory.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase EINVAL:\n\t\t\t\terr_printf(&cfg, \"Error: Invalid id and/or clean_session.\\n\");\n\t\t\t\tbreak;\n\t\t}\n\t\tgoto cleanup;\n\t}\n\tif(client_opts_set(g_mosq, &cfg)){\n\t\tgoto cleanup;\n\t}\n\tif(cfg.debug){\n\t\tmosquitto_log_callback_set(g_mosq, my_log_callback);\n\t}\n\tif(cfg.message_rate){\n\t\tprocess_messages = false;\n#ifndef WIN32\n\t\tcfg.watch = false;\n#endif\n\t}\n\tmosquitto_subscribe_callback_set(g_mosq, my_subscribe_callback);\n\tmosquitto_connect_v5_callback_set(g_mosq, my_connect_callback);\n\tmosquitto_message_v5_callback_set(g_mosq, my_message_callback);\n\n\toutput_init(&cfg);\n\n\trc = client_connect(g_mosq, &cfg);\n\tif(rc){\n\t\tgoto cleanup;\n\t}\n\n#ifdef WIN32\n\tif(cfg.timeout){\n\t\tif(!CreateTimerQueueTimer(&timeout_h, NULL, timeout_cb, NULL, cfg.timeout*1000, 0, WT_EXECUTEDEFAULT)){\n\t\t\terr_printf(&cfg, \"Error: Unable to create timer for -W.\\n\");\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n#else\n\tsigact.sa_handler = my_signal_handler;\n\tsigemptyset(&sigact.sa_mask);\n\tsigact.sa_flags = 0;\n\n\tif(sigaction(SIGALRM, &sigact, NULL) == -1){\n\t\tperror(\"sigaction\");\n\t\tgoto cleanup;\n\t}\n\n\tif(sigaction(SIGTERM, &sigact, NULL) == -1){\n\t\tperror(\"sigaction\");\n\t\tgoto cleanup;\n\t}\n\n\tif(sigaction(SIGINT, &sigact, NULL) == -1){\n\t\tperror(\"sigaction\");\n\t\tgoto cleanup;\n\t}\n\n\tif(cfg.timeout){\n\t\talarm(cfg.timeout);\n\t}\n#endif\n\n\tif(cfg.message_rate){\n\t\trc = mosquitto_loop_start(g_mosq);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\twhile(run){\n#ifdef WIN32\n\t\t\tSleep(1000);\n#else\n\t\t\tstruct timespec ts = {1, 0};\n\t\t\tnanosleep(&ts, NULL);\n#endif\n\t\t\tint message_count = message_rate_msg_count;\n\t\t\tmessage_rate_msg_count = 0;\n\t\t\tprintf(\"%d msgs/s\\n\", message_count);\n\t\t}\n\t}else{\n\t\trc = mosquitto_loop_forever(g_mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(g_mosq);\n\tmosquitto_lib_cleanup();\n\n\tif(cfg.msg_count>0 && rc == MOSQ_ERR_NO_CONN){\n\t\trc = 0;\n\t}\n\tclient_config_cleanup(&cfg);\n\tif(timed_out){\n\t\terr_printf(&cfg, \"Timed out\\n\");\n\t\treturn MOSQ_ERR_TIMEOUT;\n\t}else if(rc){\n\t\terr_printf(&cfg, \"Error: %s\\n\", mosquitto_strerror(rc));\n\t}\n\tif(connack_result){\n\t\treturn connack_result;\n\t}else{\n\t\treturn rc;\n\t}\n\ncleanup:\n\tmosquitto_destroy(g_mosq);\n\tmosquitto_lib_cleanup();\n\tclient_config_cleanup(&cfg);\n\treturn 1;\n}\n\n"
  },
  {
    "path": "client/sub_client_output.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WIN32\n/* For rand_s on Windows */\n#  define _CRT_RAND_S\n#  include <fcntl.h>\n#  include <io.h>\n#endif\n\n#include <assert.h>\n#include <cjson/cJSON.h>\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#ifndef WIN32\n#include <unistd.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#define snprintf sprintf_s\n#endif\n\n#undef uthash_malloc\n#undef uthash_free\n#include <uthash.h>\n\n\n#ifdef __APPLE__\n#  include <sys/time.h>\n#endif\n\n#include <mosquitto.h>\n#include \"client_shared.h\"\n#include \"sub_client_output.h\"\n\nextern struct mosq_config cfg;\n\nstruct fieldoptions {\n\tint field_width;\n\tint precision;\n\tchar hexsepchar;\n\tchar align;\n\tchar pad;\n};\n\nstruct watch_topic {\n\tUT_hash_handle hh;\n\tchar *topic;\n\tint line;\n};\nstatic int watch_max = 2;\nstatic struct watch_topic *watch_items = NULL;\n\n\nstatic int get_time(struct tm **ti, long *ns)\n{\n#ifdef WIN32\n\tSYSTEMTIME st;\n#elif defined(__APPLE__)\n\tstruct timeval tv;\n#else\n\tstruct timespec ts;\n#endif\n\ttime_t s;\n\n#ifdef WIN32\n\ts = time(NULL);\n\n\tGetLocalTime(&st);\n\t*ns = st.wMilliseconds*1000000L;\n#elif defined(__APPLE__)\n\tgettimeofday(&tv, NULL);\n\ts = tv.tv_sec;\n\t*ns = tv.tv_usec*1000;\n#else\n\tif(clock_gettime(CLOCK_REALTIME, &ts) != 0){\n\t\terr_printf(&cfg, \"Error obtaining system time.\\n\");\n\t\treturn 1;\n\t}\n\ts = ts.tv_sec;\n\t*ns = ts.tv_nsec;\n#endif\n\n\t*ti = localtime(&s);\n\tif(!(*ti)){\n\t\terr_printf(&cfg, \"Error obtaining system time.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\n\nstatic const signed char nibble_to_hex[] = {\n\t'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'\n};\n\n\nstatic void hexsep(int xpos, int precision, char sepchar)\n{\n\tif(precision > 0 && xpos%precision == (precision-1)){\n\t\tputchar(sepchar);\n\t}\n}\n\n\nstatic void write_payload(const unsigned char *payload, int payloadlen, int hex, struct fieldoptions *fopts)\n{\n\tint i;\n\tint padlen;\n\n\tif(fopts->field_width > 0){\n\t\tif(payloadlen > fopts->field_width){\n\t\t\tpayloadlen = fopts->field_width;\n\t\t}\n\t\tif(hex > 0){\n\t\t\tpadlen = fopts->field_width - payloadlen*2;\n\t\t}else{\n\t\t\tpadlen = fopts->field_width - payloadlen;\n\t\t}\n\t}else{\n\t\tpadlen = fopts->field_width - payloadlen;\n\t}\n\n\tint xpos = 0;\n\tif(fopts->align != '-'){\n\t\tfor(i=0; i<padlen; i++){\n\t\t\tputchar(fopts->pad);\n\t\t\tif(hex > 0){\n\t\t\t\thexsep(xpos, fopts->precision, fopts->hexsepchar);\n\t\t\t}\n\t\t\txpos++;\n\t\t}\n\t}\n\n\tif(hex == 0){\n\t\t(void)fwrite(payload, 1, (size_t )payloadlen, stdout);\n\t}else{\n\t\tsigned char casemod = (hex == 1?0x20:0x00);\n\t\tfor(i=0; i<payloadlen; i++){\n\t\t\tputchar(nibble_to_hex[((payload[i] & 0xF0) >> 4)] | casemod);\n\t\t\thexsep(xpos, fopts->precision, fopts->hexsepchar);\n\t\t\txpos += 1;\n\n\t\t\tputchar(nibble_to_hex[(payload[i] & 0x0F)] | casemod);\n\t\t\tif(i < payloadlen-1){\n\t\t\t\thexsep(xpos, fopts->precision, fopts->hexsepchar);\n\t\t\t\txpos += 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(fopts->align == '-'){\n\t\tprintf(\"%*s\", padlen, \"\");\n\t}\n}\n\n\nstatic int json_print_properties(cJSON *root, const mosquitto_property *properties)\n{\n\tint identifier;\n\tuint8_t i8value = 0;\n\tuint16_t i16value = 0;\n\tuint32_t i32value = 0;\n\tchar *strname = NULL, *strvalue = NULL;\n\tchar *binvalue = NULL;\n\tcJSON *tmp, *prop_json, *user_props = NULL, *user_json;\n\tconst mosquitto_property *prop = NULL;\n\n\tprop_json = cJSON_CreateObject();\n\tif(prop_json == NULL){\n\t\tcJSON_Delete(prop_json);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToObject(root, \"properties\", prop_json);\n\n\tfor(prop=properties; prop != NULL; prop = mosquitto_property_next(prop)){\n\t\ttmp = NULL;\n\t\tidentifier = mosquitto_property_identifier(prop);\n\t\tswitch(identifier){\n\t\t\tcase MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:\n\t\t\t\tmosquitto_property_read_byte(prop, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, &i8value, false);\n\t\t\t\ttmp = cJSON_CreateNumber(i8value);\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:\n\t\t\t\tmosquitto_property_read_int32(prop, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &i32value, false);\n\t\t\t\ttmp = cJSON_CreateNumber(i32value);\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_CONTENT_TYPE:\n\t\t\tcase MQTT_PROP_RESPONSE_TOPIC:\n\t\t\t\tmosquitto_property_read_string(prop, identifier, &strvalue, false);\n\t\t\t\tif(strvalue == NULL){\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\ttmp = cJSON_CreateString(strvalue);\n\t\t\t\tfree(strvalue);\n\t\t\t\tstrvalue = NULL;\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_CORRELATION_DATA:\n\t\t\t\tmosquitto_property_read_binary(prop, MQTT_PROP_CORRELATION_DATA, (void **)&binvalue, &i16value, false);\n\t\t\t\tif(binvalue == NULL){\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\ttmp = cJSON_CreateString(binvalue);\n\t\t\t\tfree(binvalue);\n\t\t\t\tbinvalue = NULL;\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_SUBSCRIPTION_IDENTIFIER:\n\t\t\t\tmosquitto_property_read_varint(prop, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &i32value, false);\n\t\t\t\ttmp = cJSON_CreateNumber(i32value);\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_TOPIC_ALIAS:\n\t\t\t\tmosquitto_property_read_int16(prop, MQTT_PROP_TOPIC_ALIAS, &i16value, false);\n\t\t\t\ttmp = cJSON_CreateNumber(i16value);\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_USER_PROPERTY:\n\t\t\t\tif(user_props == NULL){\n\t\t\t\t\tuser_props = cJSON_CreateArray();\n\t\t\t\t\tif(user_props == NULL){\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tcJSON_AddItemToObject(prop_json, \"user-properties\", user_props);\n\t\t\t\t}\n\n\t\t\t\tuser_json = cJSON_CreateObject();\n\t\t\t\tif(user_json == NULL){\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\tcJSON_AddItemToArray(user_props, user_json);\n\t\t\t\tmosquitto_property_read_string_pair(prop, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, false);\n\t\t\t\tif(strname == NULL || strvalue == NULL){\n\t\t\t\t\tfree(strname);\n\t\t\t\t\tfree(strvalue);\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\n\t\t\t\ttmp = cJSON_CreateString(strvalue);\n\t\t\t\tfree(strvalue);\n\n\t\t\t\tif(tmp == NULL){\n\t\t\t\t\tfree(strname);\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\tcJSON_AddItemToObject(user_json, strname, tmp);\n\t\t\t\tfree(strname);\n\t\t\t\tstrname = NULL;\n\t\t\t\tstrvalue = NULL;\n\t\t\t\ttmp = NULL; /* Don't add this to prop_json below */\n\t\t\t\tbreak;\n\t\t}\n\t\tif(tmp != NULL){\n\t\t\tcJSON_AddItemToObject(prop_json, mosquitto_property_identifier_to_string(identifier), tmp);\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void format_time_8601(const struct tm *ti, int ns, char *buf, size_t len)\n{\n\tchar c;\n\n\tstrftime(buf, len, \"%Y-%m-%dT%H:%M:%S.000000%z\", ti);\n\tc = buf[strlen(\"2020-05-06T21:48:00.000000\")];\n\tsnprintf(&buf[strlen(\"2020-05-06T21:48:00.\")], 9, \"%06d\", ns/1000);\n\tbuf[strlen(\"2020-05-06T21:48:00.000000\")] = c;\n}\n\n\nstatic int json_print(const struct mosquitto_message *message, const mosquitto_property *properties, const struct tm *ti, int ns, bool escaped, bool pretty)\n{\n\tchar buf[100];\n\tcJSON *root;\n\tcJSON *tmp;\n\tchar *json_str;\n\tconst char *return_parse_end;\n\n\troot = cJSON_CreateObject();\n\tif(root == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tformat_time_8601(ti, ns, buf, sizeof(buf));\n\n\tif(cJSON_AddStringToObject(root, \"tst\", buf) == NULL\n\t\t\t|| cJSON_AddStringToObject(root, \"topic\", message->topic) == NULL\n\t\t\t|| cJSON_AddNumberToObject(root, \"qos\", message->qos) == NULL\n\t\t\t|| cJSON_AddBoolToObject(root, \"retain\", message->retain) == NULL\n\t\t\t|| cJSON_AddNumberToObject(root, \"payloadlen\", message->payloadlen) == NULL\n\t\t\t|| (message->qos > 0 && cJSON_AddNumberToObject(root, \"mid\", message->mid) == NULL)\n\t\t\t|| (properties && json_print_properties(root, properties))\n\t\t\t){\n\n\t\tcJSON_Delete(root);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t/* Payload */\n\tif(escaped){\n\t\tif(message->payload){\n\t\t\ttmp = cJSON_AddStringToObject(root, \"payload\", message->payload);\n\t\t}else{\n\t\t\ttmp = cJSON_AddNullToObject(root, \"payload\");\n\t\t}\n\t\tif(tmp == NULL){\n\t\t\tcJSON_Delete(root);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}else{\n\t\treturn_parse_end = NULL;\n\t\tif(message->payload){\n\t\t\ttmp = cJSON_ParseWithOpts(message->payload, &return_parse_end, true);\n\t\t\tif(tmp == NULL || return_parse_end != (char *)message->payload + message->payloadlen){\n\t\t\t\tcJSON_Delete(root);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}else{\n\t\t\ttmp = cJSON_CreateNull();\n\t\t\tif(tmp == NULL){\n\t\t\t\tcJSON_Delete(root);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}\n\t\tcJSON_AddItemToObject(root, \"payload\", tmp);\n\t}\n\n\tif(pretty){\n\t\tjson_str = cJSON_Print(root);\n\t}else{\n\t\tjson_str = cJSON_PrintUnformatted(root);\n\t}\n\tcJSON_Delete(root);\n\tif(json_str == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tfputs(json_str, stdout);\n\tfree(json_str);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void formatted_print_blank(struct fieldoptions *fopts)\n{\n\tint i;\n\tfor(i=0; i<fopts->field_width; i++){\n\t\tputchar(fopts->pad);\n\t}\n}\n\n\n#ifdef __STDC_IEC_559__\n\n\nstatic int formatted_print_float(const unsigned char *payload, int payloadlen, char format, struct fieldoptions *fopts)\n{\n\tfloat float_value;\n\tdouble value = 0.0;\n\n\tif(format == 'f'){\n\t\tif(sizeof(float_value) != payloadlen){\n\t\t\treturn -1;\n\t\t}\n\t\tmemcpy(&float_value, payload, sizeof(float_value));\n\t\tvalue = float_value;\n\t}else if(format == 'd'){\n\t\tif(sizeof(value) != payloadlen){\n\t\t\treturn -1;\n\t\t}\n\t\tmemcpy(&value, payload, sizeof(value));\n\t}\n\n\tif(fopts->field_width == 0){\n\t\tprintf(\"%.*f\", fopts->precision, value);\n\t}else{\n\t\tif(fopts->align == '-'){\n\t\t\tprintf(\"%-*.*f\", fopts->field_width, fopts->precision, value);\n\t\t}else{\n\t\t\tif(fopts->pad == '0'){\n\t\t\t\tprintf(\"%0*.*f\", fopts->field_width, fopts->precision, value);\n\t\t\t}else{\n\t\t\t\tprintf(\"%*.*f\", fopts->field_width, fopts->precision, value);\n\t\t\t}\n\t\t}\n\t}\n\treturn 0;\n}\n#endif\n\n\nstatic void formatted_print_int(int value, struct fieldoptions *fopts)\n{\n\tif(fopts->field_width == 0){\n\t\tprintf(\"%d\", value);\n\t}else{\n\t\tif(fopts->align == '-'){\n\t\t\tprintf(\"%-*d\", fopts->field_width, value);\n\t\t}else{\n\t\t\tif(fopts->pad == '0'){\n\t\t\t\tprintf(\"%0*d\", fopts->field_width, value);\n\t\t\t}else{\n\t\t\t\tprintf(\"%*d\", fopts->field_width, value);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void formatted_print_str(const char *value, struct fieldoptions *fopts)\n{\n\tif(fopts->field_width == 0 && fopts->precision == -1){\n\t\tfputs(value, stdout);\n\t}else{\n\t\tif(fopts->precision == -1){\n\t\t\tif(fopts->align == '-'){\n\t\t\t\tprintf(\"%-*s\", fopts->field_width, value);\n\t\t\t}else{\n\t\t\t\tprintf(\"%*s\", fopts->field_width, value);\n\t\t\t}\n\t\t}else if(fopts->field_width == 0){\n\t\t\tif(fopts->align == '-'){\n\t\t\t\tprintf(\"%-.*s\", fopts->precision, value);\n\t\t\t}else{\n\t\t\t\tprintf(\"%.*s\", fopts->precision, value);\n\t\t\t}\n\t\t}else{\n\t\t\tif(fopts->align == '-'){\n\t\t\t\tprintf(\"%-*.*s\", fopts->field_width, fopts->precision, value);\n\t\t\t}else{\n\t\t\t\tprintf(\"%*.*s\", fopts->field_width, fopts->precision, value);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void formatted_print_percent(const struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties, char format, struct fieldoptions *fopts)\n{\n\tstruct tm *ti = NULL;\n\tlong ns = 0;\n\tchar buf[100];\n\tint rc;\n\tuint8_t i8value;\n\tuint16_t i16value;\n\tuint32_t i32value;\n\tchar *binvalue = NULL, *strname, *strvalue;\n\tconst mosquitto_property *prop;\n\n\n\tswitch(format){\n\t\tcase '%':\n\t\t\tfputc('%', stdout);\n\t\t\tbreak;\n\n\t\tcase 'A':\n\t\t\tif(mosquitto_property_read_int16(properties, MQTT_PROP_TOPIC_ALIAS, &i16value, false)){\n\t\t\t\tformatted_print_int(i16value, fopts);\n\t\t\t}else{\n\t\t\t\tformatted_print_blank(fopts);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'C':\n\t\t\tif(mosquitto_property_read_string(properties, MQTT_PROP_CONTENT_TYPE, &strvalue, false)){\n\t\t\t\tformatted_print_str(strvalue, fopts);\n\t\t\t\tfree(strvalue);\n\t\t\t}else{\n\t\t\t\tformatted_print_blank(fopts);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'D':\n\t\t\tif(mosquitto_property_read_binary(properties, MQTT_PROP_CORRELATION_DATA, (void **)&binvalue, &i16value, false)){\n\t\t\t\tfwrite(binvalue, 1, i16value, stdout);\n\t\t\t\tfree(binvalue);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'E':\n\t\t\tif(mosquitto_property_read_int32(properties, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &i32value, false)){\n\t\t\t\tformatted_print_int((int)i32value, fopts);\n\t\t\t}else{\n\t\t\t\tformatted_print_blank(fopts);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'F':\n\t\t\tif(mosquitto_property_read_byte(properties, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, &i8value, false)){\n\t\t\t\tformatted_print_int(i8value, fopts);\n\t\t\t}else{\n\t\t\t\tformatted_print_blank(fopts);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'I':\n\t\t\tif(!ti){\n\t\t\t\tif(get_time(&ti, &ns)){\n\t\t\t\t\terr_printf(lcfg, \"Error obtaining system time.\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(strftime(buf, 100, \"%FT%T%z\", ti) != 0){\n\t\t\t\tformatted_print_str(buf, fopts);\n\t\t\t}else{\n\t\t\t\tformatted_print_blank(fopts);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'j':\n\t\t\tif(!ti){\n\t\t\t\tif(get_time(&ti, &ns)){\n\t\t\t\t\terr_printf(lcfg, \"Error obtaining system time.\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(json_print(message, properties, ti, (int)ns, true, lcfg->pretty) != MOSQ_ERR_SUCCESS){\n\t\t\t\terr_printf(lcfg, \"Error: Out of memory.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'J':\n\t\t\tif(!ti){\n\t\t\t\tif(get_time(&ti, &ns)){\n\t\t\t\t\terr_printf(lcfg, \"Error obtaining system time.\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\trc = json_print(message, properties, ti, (int)ns, false, lcfg->pretty);\n\t\t\tif(rc == MOSQ_ERR_NOMEM){\n\t\t\t\terr_printf(lcfg, \"Error: Out of memory.\\n\");\n\t\t\t\treturn;\n\t\t\t}else if(rc == MOSQ_ERR_INVAL){\n\t\t\t\terr_printf(lcfg, \"Error: Message payload is not valid JSON on topic %s.\\n\", message->topic);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'l':\n\t\t\tformatted_print_int(message->payloadlen, fopts);\n\t\t\tbreak;\n\n\t\tcase 'm':\n\t\t\tformatted_print_int(message->mid, fopts);\n\t\t\tbreak;\n\n\t\tcase 'P':\n\t\t\tstrname = NULL;\n\t\t\tstrvalue = NULL;\n\t\t\tprop = mosquitto_property_read_string_pair(properties, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, false);\n\t\t\twhile(prop){\n\t\t\t\tprintf(\"%s:%s\", strname, strvalue);\n\t\t\t\tfree(strname);\n\t\t\t\tfree(strvalue);\n\t\t\t\tstrname = NULL;\n\t\t\t\tstrvalue = NULL;\n\n\t\t\t\tprop = mosquitto_property_read_string_pair(prop, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, true);\n\t\t\t\tif(prop){\n\t\t\t\t\tfputc(' ', stdout);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfree(strname);\n\t\t\tfree(strvalue);\n\t\t\tbreak;\n\n\t\tcase 'p':\n\t\t\twrite_payload(message->payload, message->payloadlen, 0, fopts);\n\t\t\tbreak;\n\n\t\tcase 'q':\n\t\t\tfputc(message->qos + 48, stdout);\n\t\t\tbreak;\n\n\t\tcase 'R':\n\t\t\tif(mosquitto_property_read_string(properties, MQTT_PROP_RESPONSE_TOPIC, &strvalue, false)){\n\t\t\t\tformatted_print_str(strvalue, fopts);\n\t\t\t\tfree(strvalue);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'r':\n\t\t\tif(message->retain){\n\t\t\t\tfputc('1', stdout);\n\t\t\t}else{\n\t\t\t\tfputc('0', stdout);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'S':\n\t\t\tif(mosquitto_property_read_varint(properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &i32value, false)){\n\t\t\t\tformatted_print_int((int)i32value, fopts);\n\t\t\t}else{\n\t\t\t\tformatted_print_blank(fopts);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 't':\n\t\t\tformatted_print_str(message->topic, fopts);\n\t\t\tbreak;\n\n\t\tcase 'U':\n\t\t\tif(!ti){\n\t\t\t\tif(get_time(&ti, &ns)){\n\t\t\t\t\terr_printf(lcfg, \"Error obtaining system time.\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(strftime(buf, 100, \"%s\", ti) != 0){\n\t\t\t\tprintf(\"%s.%09ld\", buf, ns);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'x':\n\t\t\twrite_payload(message->payload, message->payloadlen, 1, fopts);\n\t\t\tbreak;\n\n\t\tcase 'X':\n\t\t\twrite_payload(message->payload, message->payloadlen, 2, fopts);\n\t\t\tbreak;\n\n#ifdef __STDC_IEC_559__\n\t\tcase 'f':\n\t\t\tif(formatted_print_float(message->payload, message->payloadlen, 'f', fopts)){\n\t\t\t\terr_printf(lcfg, \"requested float printing, but non-float data received\");\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'd':\n\t\t\tif(formatted_print_float(message->payload, message->payloadlen, 'd', fopts)){\n\t\t\t\terr_printf(lcfg, \"requested double printing, but non-double data received\");\n\t\t\t}\n\t\t\tbreak;\n#endif\n\t}\n}\n\n\nstatic void formatted_print(const struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties)\n{\n\tsize_t len;\n\tstruct tm *ti = NULL;\n\tlong ns = 0;\n\n\tlen = strlen(lcfg->format);\n\n\tfor(size_t i=0; i<len; i++){\n\t\tif(lcfg->format[i] == '%'){\n\t\t\tstruct fieldoptions fopts = {0, -1, ' ', '\\0', ' '};\n\t\t\tif(i < len-1){\n\t\t\t\ti++;\n\t\t\t\t/* Optional alignment */\n\t\t\t\tif(lcfg->format[i] == '-'){\n\t\t\t\t\tfopts.align = lcfg->format[i];\n\t\t\t\t\tif(i < len-1){\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t/* \"%-040p\" is allowed by this combination of checks, but isn't\n\t\t\t\t * a valid format specifier, the '0' will be ignored. */\n\t\t\t\t/* Optional zero padding */\n\t\t\t\tif(lcfg->format[i] == '0'){\n\t\t\t\t\tfopts.pad = '0';\n\t\t\t\t\tif(i < len-1){\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t/* Optional field width */\n\t\t\t\twhile(i < len-1 && lcfg->format[i] >= '0' && lcfg->format[i] <= '9'){\n\t\t\t\t\tfopts.field_width *= 10;\n\t\t\t\t\tfopts.field_width += lcfg->format[i]-'0';\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t\t/* Optional precision */\n\t\t\t\tif(lcfg->format[i] == '.'){\n\t\t\t\t\tif(i < len-1){\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tfopts.precision = 0;\n\t\t\t\t\t\twhile(i < len-1 && lcfg->format[i] >= '0' && lcfg->format[i] <= '9'){\n\t\t\t\t\t\t\tfopts.precision *= 10;\n\t\t\t\t\t\t\tfopts.precision += lcfg->format[i]-'0';\n\t\t\t\t\t\t\ti++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* Optional hex field separator character */\n\t\t\t\tfor(size_t j=0; j<sizeof(hexseplist); j++){\n\t\t\t\t\tif(lcfg->format[i] == hexseplist[j]){\n\t\t\t\t\t\tfopts.hexsepchar = hexseplist[j];\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif(i < len){\n\t\t\t\t\tformatted_print_percent(lcfg, message, properties, lcfg->format[i], &fopts);\n\t\t\t\t\t//align, pad, field_width, precision, hexsepchar);\n\t\t\t\t}\n\t\t\t}\n\t\t}else if(lcfg->format[i] == '@'){\n\t\t\tif(i < len-1){\n\t\t\t\ti++;\n\t\t\t\tif(lcfg->format[i] == '@'){\n\t\t\t\t\tfputc('@', stdout);\n\t\t\t\t}else{\n\t\t\t\t\tif(!ti){\n\t\t\t\t\t\tif(get_time(&ti, &ns)){\n\t\t\t\t\t\t\terr_printf(lcfg, \"Error obtaining system time.\\n\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tchar strf[3] = {0, 0, 0};\n\t\t\t\t\tstrf[0] = '%';\n\t\t\t\t\tstrf[1] = lcfg->format[i];\n\t\t\t\t\tstrf[2] = 0;\n\n\t\t\t\t\tif(lcfg->format[i] == 'N'){\n\t\t\t\t\t\tprintf(\"%09ld\", ns);\n\t\t\t\t\t}else{\n\t\t\t\t\t\tchar buf[100];\n\t\t\t\t\t\tif(strftime(buf, sizeof(buf), strf, ti) != 0){\n\t\t\t\t\t\t\tfputs(buf, stdout);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}else if(lcfg->format[i] == '\\\\'){\n\t\t\tif(i < len-1){\n\t\t\t\ti++;\n\t\t\t\tswitch(lcfg->format[i]){\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\tfputc('\\\\', stdout);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase '0':\n\t\t\t\t\t\tfputc('\\0', stdout);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'a':\n\t\t\t\t\t\tfputc('\\a', stdout);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'e':\n\t\t\t\t\t\tfputc('\\033', stdout);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'n':\n\t\t\t\t\t\tfputc('\\n', stdout);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'r':\n\t\t\t\t\t\tfputc('\\r', stdout);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 't':\n\t\t\t\t\t\tfputc('\\t', stdout);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'v':\n\t\t\t\t\t\tfputc('\\v', stdout);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}else{\n\t\t\tfputc(lcfg->format[i], stdout);\n\t\t}\n\t}\n\tif(lcfg->eol){\n\t\tfputc('\\n', stdout);\n\t}\n\tfflush(stdout);\n}\n\n\nstatic void rand_init(void)\n{\n#ifndef WIN32\n\tstruct tm *ti = NULL;\n\tlong ns;\n\n\tif(!get_time(&ti, &ns)){\n\t\tsrandom((unsigned int)ns);\n\t}\n#endif\n}\n\n#ifndef WIN32\n\n\nstatic void watch_print(const struct mosquitto_message *message)\n{\n\tstruct watch_topic *item = NULL;\n\n\tHASH_FIND(hh, watch_items, message->topic, strlen(message->topic), item);\n\tif(item == NULL){\n\t\titem = calloc(1, sizeof(struct watch_topic));\n\t\tif(item == NULL){\n\t\t\treturn;\n\t\t}\n\t\titem->line = watch_max++;\n\t\titem->topic = strdup(message->topic);\n\t\tif(item->topic == NULL){\n\t\t\tfree(item);\n\t\t\treturn;\n\t\t}\n\t\tHASH_ADD_KEYPTR(hh, watch_items, item->topic, strlen(item->topic), item);\n\t}\n\tprintf(\"\\e[%d;1H\", item->line);\n}\n#endif\n\n\nvoid print_message(struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties)\n{\n#ifdef WIN32\n\tunsigned int r = 0;\n#else\n\tlong r = 0;\n#endif\n\tstruct fieldoptions fopts = {0, 0, ' ', '\\0', ' '};\n\n#ifndef WIN32\n\tif(lcfg->watch){\n\t\twatch_print(message);\n\t}\n#endif\n\n\tif(lcfg->random_filter < 10000){\n#ifdef WIN32\n\t\trand_s(&r);\n#else\n\t\t/* coverity[dont_call] - we don't care about random() not being cryptographically secure here */\n\t\tr = random();\n#endif\n\t\tif((long)(r%10000) >= lcfg->random_filter){\n\t\t\treturn;\n\t\t}\n\t}\n\tif(lcfg->format){\n\t\tformatted_print(lcfg, message, properties);\n\t}else if(lcfg->verbose){\n\t\tif(message->payloadlen){\n\t\t\tprintf(\"%s \", message->topic);\n\t\t\twrite_payload(message->payload, message->payloadlen, false, &fopts);\n\t\t\tif(lcfg->eol){\n\t\t\t\tprintf(\"\\n\");\n\t\t\t}\n\t\t}else{\n\t\t\tif(lcfg->eol){\n\t\t\t\tprintf(\"%s (null)\\n\", message->topic);\n\t\t\t}\n\t\t}\n\t\tfflush(stdout);\n\t}else{\n\t\tif(message->payloadlen){\n\t\t\twrite_payload(message->payload, message->payloadlen, false, &fopts);\n\t\t\tif(lcfg->eol){\n\t\t\t\tprintf(\"\\n\");\n\t\t\t}\n\t\t\tfflush(stdout);\n\t\t}\n\t}\n#ifndef WIN32\n\tif(lcfg->watch){\n\t\tprintf(\"\\e[%d;1H\\n\", watch_max-1);\n\t}\n#endif\n}\n\n\nvoid output_init(struct mosq_config *lcfg)\n{\n\trand_init();\n#ifndef WIN32\n\tif(lcfg->watch){\n\t\tprintf(\"\\e[2J\\e[1;1H\");\n\t\tprintf(\"Broker: %s\\n\", lcfg->host);\n\t}\n#endif\n#ifdef WIN32\n\t/* Disable text translation so binary payloads aren't modified */\n\t(void)_setmode(_fileno(stdout), _O_BINARY);\n#endif\n}\n"
  },
  {
    "path": "client/sub_client_output.h",
    "content": "/*\nCopyright (c) 2019-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef SUB_CLIENT_OUTPUT_H\n#define SUB_CLIENT_OUTPUT_H\n\n#include \"mosquitto.h\"\n#include \"client_shared.h\"\n\nvoid output_init(struct mosq_config *cfg);\nvoid print_message(struct mosq_config *cfg, const struct mosquitto_message *message, const mosquitto_property *properties);\n\n#endif\n"
  },
  {
    "path": "client/sub_test_fixed_width",
    "content": "LD_LIBRARY_PATH=../lib ./mosquitto_sub \\\n\t-h test.mosquitto.org \\\n\t--retained-only \\\n\t--remove-retained \\\n\t-t FW/# \\\n\t-W 1>/dev/null 2>/dev/null\n\nLD_LIBRARY_PATH=../lib ./mosquitto_pub \\\n\t-h test.mosquitto.org \\\n\t-D publish content-type \"application/json\" \\\n\t-D publish message-expiry-interval 360000 \\\n\t-D publish payload-format-indicator 1 \\\n\t-D publish response-topic response-topic \\\n\t-m ABCDEFGHIJKLMNOPQRSTUVWXYZ \\\n\t-q 2 \\\n\t-r \\\n\t-t FW/truncate \\\n\t-V 5\n\nLD_LIBRARY_PATH=../lib ./mosquitto_pub \\\n\t-h test.mosquitto.org \\\n\t-D publish content-type \"null\" \\\n\t-D publish message-expiry-interval 3600 \\\n\t-D publish payload-format-indicator 1 \\\n\t-D publish response-topic r-t \\\n\t-m Off \\\n\t-q 2 \\\n\t-r \\\n\t-t FW/expire \\\n\t-V 5\n\nLD_LIBRARY_PATH=../lib ./mosquitto_pub \\\n\t-h test.mosquitto.org \\\n\t-D publish payload-format-indicator 1 \\\n\t-D publish response-topic rt \\\n\t-m Offline \\\n\t-q 2 \\\n\t-r \\\n\t-t FW/1 \\\n\t-V 5\n\nLD_LIBRARY_PATH=../lib ./mosquitto_sub \\\n\t-h test.mosquitto.org \\\n\t-C 3 \\\n\t-F \"| %10t | %-10t | %7x | %-7x | %08x | %7X | %-7X | %08X | %8p | %-8p | %3m | %-3m | %03m | %3l | %-3l | %03l | %2F | %-2F | %02F | %5C | %-5C | %5E | %-5E | %05E | %5A | %5R | %-5R |\" \\\n\t-q 2 \\\n\t-t 'FW/#' \\\n\t-V 5\n\necho\n\nLD_LIBRARY_PATH=../lib ./mosquitto_sub \\\n\t-h test.mosquitto.org \\\n\t-C 3 \\\n\t-F \"| %10.10t | %.5t | %-10.10t | %7x | %-7x | %08x | %7X | %-7X | %08X | %8p | %-8p | %3m | %-3m | %03m | %3l | %-3l | %03l | %2F | %-2F | %02F | %5C | %-5C | %5E | %-5E | %05E | %5.5A | %5.5R | %-5.5R |\" \\\n\t-q 2 \\\n\t-t 'FW/#' \\\n\t-V 5\n\n"
  },
  {
    "path": "client/sub_test_properties",
    "content": "LD_LIBRARY_PATH=../lib ./mosquitto_sub \\\n\t\\\n\t-V mqttv5 -C 10 -t \\$SYS/# -v -U unsub --will-topic will --will-payload '{\"key\":\"value\"}' \\\n\t\\\n\t-D connect authentication-data password \\\n\t-D connect authentication-method something \\\n\t-D connect maximum-packet-size 0191 \\\n\t-D connect receive-maximum 1000 \\\n\t-D connect request-problem-information 1 \\\n\t-D connect request-response-information 1 \\\n\t-D connect session-expiry-interval 39 \\\n\t-D connect topic-alias-maximum 123 \\\n\t-D connect user-property connect up \\\n\t\\\n\t-D will content-type application/json \\\n\t-D will correlation-data some-data \\\n\t-D will message-expiry-interval 59 \\\n\t-D will payload-format-indicator 1 \\\n\t-D will response-topic /dev/null \\\n\t-D will user-property will up \\\n\t-D will will-delay-interval 100 \\\n\t\\\n\t-D subscribe subscription-identifier 1 \\\n\t-D subscribe user-property subscribe up \\\n\t\\\n\t-D unsubscribe user-property unsubscribe up \\\n\t\\\n\t-D disconnect reason-string \"reason\" \\\n\t-D disconnect session-expiry-interval 40 \\\n\t-D disconnect user-property disconnect up\n\n"
  },
  {
    "path": "cmake/FindCUnit.cmake",
    "content": "find_package(PkgConfig)\npkg_check_modules(PC_CUnit QUIET cunit)\n\nfind_path(CUnit_INCLUDE_DIR\n  NAMES CUnit/CUnit.h\n  PATHS ${PC_CUnit_INCLUDE_DIRS}\n)\n\nfind_library(CUnit_LIBRARY\n  NAMES cunit\n  PATHS ${PC_CUnit_LIBRARY_DIRS}\n)\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(CUnit\n  FOUND_VAR CUnit_FOUND\n  REQUIRED_VARS\n    CUnit_LIBRARY\n    CUnit_INCLUDE_DIR\n  VERSION_VAR CUnit_VERSION\n)\n\nif(CUnit_FOUND)\n  set(CUnit_LIBRARIES ${CUnit_LIBRARY})\n  set(CUnit_INCLUDE_DIRS ${CUnit_INCLUDE_DIR})\n  set(CUnit_DEFINITIONS ${PC_CUnit_CFLAGS_OTHER})\nendif()\n\nif(CUnit_FOUND AND NOT TARGET CUnit::CUnit)\n  add_library(CUnit::CUnit UNKNOWN IMPORTED)\n  set_target_properties(CUnit::CUnit PROPERTIES\n    IMPORTED_LOCATION \"${CUnit_LIBRARY}\"\n    INTERFACE_COMPILE_OPTIONS \"${PC_CUnit_CFLAGS_OTHER}\"\n    INTERFACE_INCLUDE_DIRECTORIES \"${CUnit_INCLUDE_DIR}\"\n  )\nendif()\n"
  },
  {
    "path": "cmake/FindLineEditing.cmake",
    "content": "include(FindPackageHandleStandardArgs)\n\nset(FIND_PATH_OPTS \"\")\nif(APPLE)\n\tlist(APPEND FIND_PATH_OPTS\n\t\tNO_CMAKE_SYSTEM_PATH\n        NO_SYSTEM_ENVIRONMENT_PATH\n    )\nendif()\n\n# Checks an environment variable; note that the first check\n# does not require the usual CMake $-sign.\nif(DEFINED env{EDITLINE_DIR})\n\tset(EDITLINE_DIR \"$ENV{EDITLINE_DIR}\")\nendif()\n\nfind_path(\n\t\tEDITLINE_INCLUDE_DIR\n\t\teditline/readline.h\n\tHINTS\n\t\tEDITLINE_DIR\n\t${FIND_PATH_OPTS}\n)\n\nfind_library(EDITLINE_LIBRARY\n\tNAMES edit\n\tHINTS ${EDITLINE_DIR}\n)\n\nif(EDITLINE_INCLUDE_DIR AND EDITLINE_LIBRARY)\n\tset(EDITLINE_FOUND TRUE)\n\tset(LINEEDITING_FOUND TRUE)\n\tset(LINEEDITING_INCLUDE_DIRS ${EDITLINE_INCLUDE_DIR})\n\tset(LINEEDITING_LIBRARIES ${EDITLINE_LIBRARY})\n\n\tif(NOT TARGET LineEditing::LineEditing)\n\t\tadd_library(LineEditing::LineEditing UNKNOWN IMPORTED)\n\t\tset_target_properties(LineEditing::LineEditing PROPERTIES\n\t\t\tIMPORTED_LOCATION \"${EDITLINE_LIBRARY}\"\n\t\t\tINTERFACE_INCLUDE_DIRECTORIES \"${EDITLINE_INCLUDE_DIR}\"\n\t\t\tINTERFACE_COMPILE_DEFINITIONS \"WITH_EDITLINE\"\n\t\t)\n\tendif()\nelse()\n\tfind_path(\n\t\t\tREADLINE_INCLUDE_DIR\n\t\t\treadline/readline.h\n\t\tHINTS\n\t\t\tREADLINE_DIR\n\t\t${FIND_PATH_OPTS}\n\t)\n\n\tfind_library(READLINE_LIBRARY\n\t\tNAMES readline\n\t\tHINTS ${READLINE_DIR}\n\t)\n\n\tif(READLINE_INCLUDE_DIR AND READLINE_LIBRARY)\n\t\tset(LINEEDITING_FOUND TRUE)\n\t\tset(LINEEDITING_INCLUDE_DIRS ${READLINE_INCLUDE_DIR})\n\t\tset(LINEEDITING_LIBRARIES ${READLINE_LIBRARY})\n\n\t\tif(NOT TARGET LineEditing::LineEditing)\n\t\t\tadd_library(LineEditing::LineEditing UNKNOWN IMPORTED)\n\t\t\tset_target_properties(LineEditing::LineEditing PROPERTIES\n\t\t\t\tIMPORTED_LOCATION \"${READLINE_LIBRARY}\"\n\t\t\t\tINTERFACE_INCLUDE_DIRECTORIES \"${READLINE_INCLUDE_DIR}\"\n\t\t\t\tINTERFACE_COMPILE_DEFINITIONS \"WITH_READLINE\"\n\t\t\t)\n\t\tendif()\n\tendif()\nendif()\n\nfind_package_handle_standard_args(LineEditing\n\tREQUIRED_VARS LINEEDITING_LIBRARIES LINEEDITING_INCLUDE_DIRS\n\tFAIL_MESSAGE \"Could not find libedit or readline library\"\n)\n"
  },
  {
    "path": "cmake/Findargon2.cmake",
    "content": "INCLUDE( FindPackageHandleStandardArgs )\n\n# Checks an environment variable; note that the first check\n# does not require the usual CMake $-sign.\nIF( DEFINED ENV{ARGON2_DIR} )\n\tSET( ARGON2_DIR \"$ENV{ARGON2_DIR}\" )\nENDIF()\n\nFIND_PATH(\n\t\tARGON2_INCLUDE_DIR\n\t\targon2.h\n\tHINTS\n\t\tARGON2_DIR\n)\n\nFIND_LIBRARY( ARGON2_LIBRARY\n\tNAMES argon2\n\tHINTS ${ARGON2_DIR}\n)\n\nFIND_PACKAGE_HANDLE_STANDARD_ARGS( argon2 DEFAULT_MSG\n\tARGON2_INCLUDE_DIR ARGON2_LIBRARY\n)\n\nIF( ARGON2_FOUND )\n\tSET( ARGON2_INCLUDE_DIRS ${ARGON2_INCLUDE_DIR} )\n\tSET( ARGON2_LIBRARIES ${ARGON2_LIBRARY} )\n\n\tMARK_AS_ADVANCED(\n\t\tARGON2_LIBRARY\n\t\tARGON2_INCLUDE_DIR\n\t\tARGON2_DIR\n\t)\n\n\tadd_library(argon2 SHARED IMPORTED)\n\tset_target_properties(argon2\n\t\tPROPERTIES\n\t\t\tINTERFACE_INCLUDE_DIRECTORIES \"${ARGON2_INCLUDE_DIRS}\"\n\t)\n\n\tset_target_properties(argon2\n\t\tPROPERTIES\n\t\t\tIMPORTED_LOCATION \"${ARGON2_LIBRARY}\"\n\t\t\tIMPORTED_IMPLIB \"${ARGON2_LIBRARY}\"\n\t)\nELSE()\n\tSET( ARGON2_DIR \"\" CACHE STRING\n\t\t\"An optional hint to a directory for finding `argon2`\"\n\t)\nENDIF()\n"
  },
  {
    "path": "cmake/FindcJSON.cmake",
    "content": "INCLUDE( FindPackageHandleStandardArgs )\n\n# Checks an environment variable; note that the first check\n# does not require the usual CMake $-sign.\nIF( DEFINED ENV{CJSON_DIR} )\n\tSET( CJSON_DIR \"$ENV{CJSON_DIR}\" )\nENDIF()\n\nFIND_PATH(\n\t\tCJSON_INCLUDE_DIR\n\t\tcjson/cJSON.h\n\tHINTS\n\t\tCJSON_DIR\n)\n\nFIND_LIBRARY( CJSON_LIBRARY\n\tNAMES cjson\n\tHINTS ${CJSON_DIR}\n)\n\nFIND_PACKAGE_HANDLE_STANDARD_ARGS( cJSON DEFAULT_MSG\n\tCJSON_INCLUDE_DIR CJSON_LIBRARY\n)\n\nIF( CJSON_FOUND )\n\tSET( CJSON_INCLUDE_DIRS ${CJSON_INCLUDE_DIR} )\n\tSET( CJSON_LIBRARIES ${CJSON_LIBRARY} )\n\n\tMARK_AS_ADVANCED(\n\t\tCJSON_LIBRARY\n\t\tCJSON_INCLUDE_DIR\n\t\tCJSON_DIR\n\t)\n\n\tadd_library(cJSON SHARED IMPORTED)\n\tset_target_properties(cJSON\n\t\tPROPERTIES\n\t\t\tINTERFACE_INCLUDE_DIRECTORIES \"${CJSON_INCLUDE_DIRS}\"\n\t)\n\n\tset_target_properties(cJSON\n\t\tPROPERTIES\n\t\t\tIMPORTED_LOCATION \"${CJSON_LIBRARY}\"\n\t\t\tIMPORTED_IMPLIB \"${CJSON_LIBRARY}\"\n\t)\nELSE()\n\tSET( CJSON_DIR \"\" CACHE STRING\n\t\t\"An optional hint to a directory for finding `cJSON`\"\n\t)\nENDIF()\n"
  },
  {
    "path": "codecov.yml",
    "content": "codecov:\n  max_report_age: off\nignore:\n  - \"deps/picohttpparser\"\n  - \"deps/*.h\"\n  - \"plugins/examples\"\n  - \"test\"\n  - \"^.*/test/.*\"\nparsers:\n  gcov:\n    branch_detection:\n      conditional: no\n      loop: no\n"
  },
  {
    "path": "common/json_help.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdio.h>\n\n#include \"json_help.h\"\n#include \"mosquitto.h\"\n\n\nint json_get_bool(cJSON *json, const char *name, bool *value, bool optional, bool default_value)\n{\n\tcJSON *jtmp;\n\n\tif(optional == true){\n\t\t*value = default_value;\n\t}\n\n\tjtmp = cJSON_GetObjectItem(json, name);\n\tif(jtmp){\n\t\tif(cJSON_IsBool(jtmp) == false){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\t*value = cJSON_IsTrue(jtmp);\n\t}else{\n\t\tif(optional == false){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint json_get_int(cJSON *json, const char *name, int *value, bool optional, int default_value)\n{\n\tcJSON *jtmp;\n\n\tif(optional == true){\n\t\t*value = default_value;\n\t}\n\n\tjtmp = cJSON_GetObjectItem(json, name);\n\tif(jtmp){\n\t\tif(cJSON_IsNumber(jtmp) == false){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\t*value  = jtmp->valueint;\n\t}else{\n\t\tif(optional == false){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint json_get_int64(cJSON *json, const char *name, int64_t *value, bool optional, int64_t default_value)\n{\n\tcJSON *jtmp;\n\n\tif(optional == true){\n\t\t*value = default_value;\n\t}\n\n\tjtmp = cJSON_GetObjectItem(json, name);\n\tif(jtmp){\n\t\tif(cJSON_IsNumber(jtmp) == false){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\t*value  = (int64_t)jtmp->valuedouble;\n\t}else{\n\t\tif(optional == false){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint json_get_string(cJSON *json, const char *name, const char **value, bool optional)\n{\n\tcJSON *jtmp;\n\n\t*value = NULL;\n\n\tjtmp = cJSON_GetObjectItem(json, name);\n\tif(jtmp){\n\t\tif(cJSON_IsString(jtmp) == false){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(jtmp->valuestring){\n\t\t\t*value = jtmp->valuestring;\n\t\t}else{\n\t\t\t*value = \"\";\n\t\t}\n\t}else{\n\t\tif(optional == false){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\ncJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, long long number)\n{\n\tchar buf[30];\n\n\tsnprintf(buf, sizeof(buf), \"%lld\", number);\n\treturn cJSON_AddRawToObject(object, name, buf);\n}\n\n\ncJSON *cJSON_AddUIntToObject(cJSON * const object, const char * const name, unsigned long long number)\n{\n\tchar buf[30];\n\n\tsnprintf(buf, sizeof(buf), \"%llu\", number);\n\treturn cJSON_AddRawToObject(object, name, buf);\n}\n"
  },
  {
    "path": "common/json_help.h",
    "content": "#ifndef JSON_HELP_H\n#define JSON_HELP_H\n/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <cjson/cJSON.h>\n#include <stdbool.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* \"optional==false\" can also be taken to mean \"only return success if the key exists and is valid\" */\nint json_get_bool(cJSON *json, const char *name, bool *value, bool optional, bool default_value);\nint json_get_int(cJSON *json, const char *name, int *value, bool optional, int default_value);\nint json_get_int64(cJSON *json, const char *name, int64_t *value, bool optional, int64_t default_value);\nint json_get_string(cJSON *json, const char *name, const char **value, bool optional);\n\ncJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, long long number);\ncJSON *cJSON_AddUIntToObject(cJSON * const object, const char * const name, unsigned long long number);\ncJSON *cJSON_CreateInt(int num);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "common/lib_load.h",
    "content": "/*\nCopyright (c) 2012-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef LIB_LOAD_H\n#define LIB_LOAD_H\n\n#ifdef WIN32\n#   include <windows.h>\n#else\n#   include <dlfcn.h>\n#endif\n\n#ifdef WIN32\n#   define LIB_LOAD(A) LoadLibrary(A)\n#   define LIB_CLOSE(A) FreeLibrary(A)\n#   define LIB_SYM(HANDLE, SYM) GetProcAddress(HANDLE, SYM)\n#else\n#   define LIB_LOAD(A) dlopen(A, RTLD_NOW|RTLD_LOCAL)\n#   define LIB_CLOSE(A) dlclose(A)\n#   define LIB_SYM(HANDLE, SYM) dlsym(HANDLE, SYM)\n#endif\n\n#define LIB_SYM_EASY(MEMBER, HANDLE, SYM) if(!(MEMBER = LIB_SYM(HANDLE, SYM)) return 1\n#endif\n"
  },
  {
    "path": "config.h",
    "content": "#ifndef CONFIG_H\n#define CONFIG_H\n/* ============================================================\n * Platform options\n * ============================================================ */\n\n#ifdef __APPLE__\n#  define __DARWIN_C_SOURCE\n#elif defined(__FreeBSD__) || defined(__NetBSD__)\n#  define HAVE_NETINET_IN_H\n#elif defined(__QNX__)\n#  define _XOPEN_SOURCE 600\n#  define __BSD_VISIBLE 1\n#  define HAVE_NETINET_IN_H\n#elif defined(_AIX)\n#  define HAVE_NETINET_IN_H\n#endif\n\n#define OPENSSL_LOAD_CONF\n\n/* ============================================================\n * Compatibility defines\n * ============================================================ */\n#if defined(_MSC_VER) && _MSC_VER < 1900\n#  define snprintf sprintf_s\n#  define EPROTO ECONNABORTED\n#  ifndef ECONNABORTED\n#    define ECONNABORTED WSAECONNABORTED\n#  endif\n#  ifndef ENOTCONN\n#    define ENOTCONN WSAENOTCONN\n#  endif\n#  ifndef ECONNREFUSED\n#    define ECONNREFUSED WSAECONNREFUSED\n#  endif\n#endif\n\n#ifdef WIN32\n#  define strcasecmp _stricmp\n#  define strncasecmp _strnicmp\n#  define strtok_r strtok_s\n#  define strerror_r(e, b, l) strerror_s(b, l, e)\n\n#  ifdef _MSC_VER\n#    include <basetsd.h>\ntypedef SSIZE_T ssize_t;\n#  endif\n#endif\n\n\n#define uthash_malloc(sz) mosquitto_malloc(sz)\n#define uthash_free(ptr, sz) mosquitto_free(ptr)\n\n\n#ifdef WITH_TLS\n#  include <openssl/opensslconf.h>\n#  if defined(WITH_TLS_PSK) && !defined(OPENSSL_NO_PSK)\n#    define FINAL_WITH_TLS_PSK\n#  endif\n#endif\n\n\n#ifdef __COVERITY__\n#  include <stdint.h>\n/* These are \"wrong\", but we don't use them so it doesn't matter */\n#  define _Float32 uint32_t\n#  define _Float32x uint32_t\n#  define _Float64 uint64_t\n#  define _Float64x uint64_t\n#  define _Float128 uint64_t\n#endif\n\n#define UNUSED(A) (void)(A)\n\n/* Android Bionic libpthread implementation doesn't have pthread_cancel */\n#if !defined(ANDROID) && !defined(WIN32)\n#  define HAVE_PTHREAD_CANCEL\n#endif\n\n#define WS_IS_LWS 1\n#define WS_IS_BUILTIN 2\n\n#ifdef WITH_BROKER\n#  ifdef __GNUC__\n#    define BROKER_EXPORT __attribute__((__used__))\n#  else\n#    define BROKER_EXPORT\n#  endif\n#else\n#  define BROKER_EXPORT\n#endif\n\n#define TOPIC_HIERARCHY_LIMIT 200\n\n#ifdef WITH_ADNS\n#  define _GNU_SOURCE\n#endif\n#endif\n"
  },
  {
    "path": "config.mk",
    "content": "# =============================================================================\n# User configuration section.\n#\n# These options control compilation on all systems apart from Windows and Mac\n# OS X. Use CMake to compile on Windows and Mac.\n#\n# Largely, these are options that are designed to make mosquitto run more\n# easily in restrictive environments by removing features.\n#\n# Modify the variable below to enable/disable features.\n#\n# Can also be overridden at the command line, e.g.:\n#\n# make WITH_TLS=no\n# =============================================================================\n\n# Uncomment to compile the broker with tcpd/libwrap support.\n#WITH_WRAP:=yes\n\n# Comment out to disable SSL/TLS support in the broker and client.\n# Disabling this will also mean that passwords must be stored in plain text. It\n# is strongly recommended that you only disable WITH_TLS if you are not using\n# password authentication at all.\nWITH_TLS:=yes\n\n# Comment out to disable TLS/PSK support in the broker and client. Requires\n# WITH_TLS=yes.\n# This must be disabled if using openssl < 1.0.\nWITH_TLS_PSK:=yes\n\n# Comment out to disable client threading support.\nWITH_THREADING:=yes\n\n# Comment out to remove bridge support from the broker. This allow the broker\n# to connect to other brokers and subscribe/publish to topics. You probably\n# want to leave this included unless you want to save a very small amount of\n# memory size and CPU time.\nWITH_BRIDGE:=yes\n\n# Comment out to remove persistent database support from the broker. This\n# allows the broker to store retained messages and durable subscriptions to a\n# file periodically and on shutdown. This is usually desirable (and is\n# suggested by the MQTT spec), but it can be disabled if required.\nWITH_PERSISTENCE:=yes\n\n# Comment out to remove memory tracking support from the broker. If disabled,\n# mosquitto won't track heap memory usage nor export '$SYS/broker/heap/current\n# size', but will use slightly less memory and CPU time.\nWITH_MEMORY_TRACKING:=yes\n\n# Uncomment to activate a consistency check on the usage of the memory tracking\n# alloc/free function use. Any memory allocated without a tracking function,\n# but freed with the tracking function will trigger an invalid memory read in\n# memory trackers like valgrind memcheck or ASAN.\n#ALLOC_MISMATCH_INVALID_READ:=yes\n\n# Uncomment to activate consistency check on the usage of the memory tracking\n# alloc/free function use. Any memory allocated without a tracking function,\n# but freed with the tracking function will trigger an abort.\n#ALLOC_MISMATCH_ABORT:=yes\n\n# Compile with database upgrading support? If disabled, mosquitto won't\n# automatically upgrade old database versions.\n# Not currently supported.\n#WITH_DB_UPGRADE:=yes\n\n# Comment out to remove publishing of the $SYS topic hierarchy containing\n# information about the broker state.\nWITH_SYS_TREE:=yes\n\n# Build with systemd support. If enabled, mosquitto will notify systemd after\n# initialization. See README in service/systemd/ for more information.\n# Setting to yes means the libsystemd-dev or similar package will need to be\n# installed.\nWITH_SYSTEMD:=no\n\n# Build with SRV lookup support.\nWITH_SRV:=no\n\n# Build with websockets support on the broker.\n# Set to yes to build with new websockets support\n# Set to lws to build with old libwebsockets code\n# Set to no to disable\nWITH_WEBSOCKETS:=yes\n\n# Build man page documentation by default.\nWITH_DOCS:=yes\n\n# Build with client support for SOCK5 proxy.\nWITH_SOCKS:=yes\n\n# Strip executables and shared libraries on install.\nWITH_STRIP:=no\n\n# Build static libraries\nWITH_STATIC_LIBRARIES:=no\n\n# Use this variable to add extra library dependencies when building the clients\n# with the static libmosquitto library. This may be required on some systems\n# where e.g. -lz or -latomic are needed for openssl.\nCLIENT_STATIC_LDADD:=\n\n# Build shared libraries\nWITH_SHARED_LIBRARIES:=yes\n\n# Build with async dns lookup support for bridges (temporary). Requires glibc.\n#WITH_ADNS:=yes\n\n# Build with epoll support.\nWITH_EPOLL:=yes\n\n# Build with bundled uthash.h\nWITH_BUNDLED_DEPS:=yes\n\n# Build with coverage options\nWITH_COVERAGE:=no\n\n# Build with unix domain socket support\nWITH_UNIX_SOCKETS:=yes\n\n# Build mosquitto with support for the $CONTROL topics.\nWITH_CONTROL:=yes\n\n# Build the broker with the jemalloc allocator\nWITH_JEMALLOC:=no\n\n# Build with xtreport capability. This is for debugging purposes and is\n# probably of no particular interest to end users.\nWITH_XTREPORT=no\n\n# Use the old O(n) keepalive check routine, instead of the new O(1) keepalive\n# check routine. See src/keepalive.c for notes on this.\nWITH_OLD_KEEPALIVE=no\n\n# Use link time optimisation - note that enabling this currently prevents\n# broker plugins from working.\n#WITH_LTO=yes\n\n# Build with sqlite3 support - this enables the sqlite persistence plugin.\nWITH_SQLITE=yes\n\n# Use gmock for testing\nWITH_GMOCK:=yes\n\n# Build broker for fuzzing only - does not work as a normal broker. This is\n# currently only suitable for use with oss-fuzz.\nWITH_FUZZING=no\n\n# Build using clang and with address sanitiser enabled\nWITH_ASAN=no\n\n# Build with editline support to allow the mosquitto_ctrl shell\nWITH_EDITLINE=yes\n\n# Build with basic HTTP API support\nWITH_HTTP_API=yes\n\n# =============================================================================\n# End of user configuration\n# =============================================================================\n\n\n# Also bump lib/mosquitto.h, CMakeLists.txt,\n# installer/mosquitto.nsi, installer/mosquitto64.nsi\nVERSION=2.1.2\n\n# Client library SO version. Bump if incompatible API/ABI changes are made.\nSOVERSION=1\n\n# Man page generation requires xsltproc and docbook-xsl\nXSLTPROC=xsltproc --nonet\n# For html generation\nDB_HTML_XSL=man/html.xsl\n\n#MANCOUNTRIES=en_GB\n\nMAKE_ALL:=mosquitto\n\nUNAME:=$(shell uname -s)\nARCH:=$(shell uname -p)\n\nINSTALL?=install\nprefix?=/usr/local\nincdir?=${prefix}/include\nlibdir?=${prefix}/lib${LIB_SUFFIX}\nlocaledir?=${prefix}/share/locale\nmandir?=${prefix}/share/man\nSTRIP?=strip\n\nifeq ($(UNAME),SunOS)\n\tifeq ($(CC),cc)\n\t\tCFLAGS?=-O\n\telse\n\t\tCFLAGS?=-Wall -ggdb -O2\n\tendif\nelse\n\tCFLAGS?=-Wall -ggdb -O3 -Wconversion -Wextra -std=gnu99 -Werror=switch\n\tCXXFLAGS?=-Wall -ggdb -O3 -Wconversion -Wextra\nendif\n\nLOCAL_CPPFLAGS=$(CPPFLAGS)\nLOCAL_CFLAGS=$(CFLAGS)\nLOCAL_CXXFLAGS=$(CXXFLAGS)\nLOCAL_LDFLAGS=$(LDFLAGS)\nLOCAL_LIBADD=$(LIBADD)\n\nLOCAL_CPPFLAGS+=-DVERSION=\\\"\"${VERSION}\\\"\" -I${R} -I. -I${R}/include -I${R}/common\n\nifneq ($(or $(findstring $(UNAME),FreeBSD), $(findstring $(UNAME),OpenBSD), $(findstring $(UNAME),NetBSD)),)\n\tSEDINPLACE:=-i \"\"\nelse\nifeq ($(UNAME),SunOS)\n\tSEDINPLACE:=\nelse\n\tSEDINPLACE:=-i\nendif\nendif\n\nifeq ($(UNAME),QNX)\n\tLOCAL_LDADD+=-lsocket\nendif\n\nifeq ($(UNAME),SunOS)\n\tLOCAL_LDADD+=-lsocket -lnsl\n\tLOCAL_LIBADD+=-lsocket -lnsl\nendif\n\nifeq ($(WITH_FUZZING),yes)\n\tWITH_GMOCK:=no\n\tWITH_SHARED_LIBRARIES:=no\n\tWITH_STATIC_LIBRARIES:=yes\nendif\n\nifeq ($(WITH_SHARED_LIBRARIES),yes)\n\tLIBMOSQ:=${R}/lib/libmosquitto.so.${SOVERSION}\nelse\n\tLIBMOSQ:=${R}/lib/libmosquitto.a\nendif\nLIBMOSQ_COMMON:=-Wl,--whole-archive ${R}/libcommon/libmosquitto_common.a -Wl,--no-whole-archive -lcjson\n\nifeq ($(WITH_TLS),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_TLS\n\tifeq ($(WITH_TLS_PSK),yes)\n\t\tLOCAL_CPPFLAGS+=-DWITH_TLS_PSK\n\tendif\nendif\n\nifeq ($(WITH_ASAN),yes)\n\tCC:=clang\n\tCXX:=clang++\n\tLOCAL_CFLAGS+=-fsanitize=address -fno-omit-frame-pointer\n\tLOCAL_CXXFLAGS+=-fsanitize=address -fno-omit-frame-pointer\n\tLOCAL_LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer -static-libsan\nendif\n\nifeq ($(WITH_LTO),yes)\n\tLOCAL_CFLAGS+=-flto\n\tLOCAL_LDFLAGS+=-flto\nendif\n\nifeq ($(WITH_DOCS),yes)\n\tMAKE_ALL+=docs\nendif\n\nifeq ($(WITH_JEMALLOC),yes)\n\tLOCAL_LDADD+=-ljemalloc\nendif\n\nifeq ($(WITH_UNIX_SOCKETS),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_UNIX_SOCKETS\nendif\n\nifeq ($(WITH_WEBSOCKETS),yes)\n\tifeq ($(WITH_TLS),yes)\n\t\tLOCAL_CPPFLAGS+=-DWITH_WEBSOCKETS=WS_IS_BUILTIN -I${R}/deps/picohttpparser\n\tendif\nendif\n\nifeq ($(WITH_WEBSOCKETS),lws)\n\tLOCAL_CPPFLAGS+=-DWITH_WEBSOCKETS=WS_IS_LWS\n\tLOCAL_LDADD+=-lwebsockets\nendif\n\nifeq ($(WITH_STRIP),yes)\n\tSTRIP_OPTS?=-s --strip-program=${CROSS_COMPILE}${STRIP}\nendif\n\nifeq ($(WITH_BUNDLED_DEPS),yes)\n\tLOCAL_CPPFLAGS+=-I${R}/deps\nendif\n\nifeq ($(WITH_COVERAGE),yes)\n\tLOCAL_CFLAGS+=-coverage\n\tLOCAL_CXXFLAGS+=-coverage\n\tLOCAL_LDFLAGS+=-coverage\nendif\n\nifeq ($(WITH_FUZZING),yes)\n\tMAKE_ALL+=fuzzing\n\tLOCAL_CPPFLAGS+=-DWITH_FUZZING\n\tLOCAL_CFLAGS+=-fPIC\n\tLOCAL_LDFLAGS+=-shared $(LOCAL_CFLAGS)\nendif\n\nifeq ($(WITH_ARGON2),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_ARGON2\n\tLIB_ARGON2=-largon2\n\tLIBMOSQ_COMMON+=${LIB_ARGON2}\nendif\n"
  },
  {
    "path": "dashboard/README.md",
    "content": "### Simple web-based graphical user interface for Mosquitto\n\nTo develop UI locally.\n\n1) Install tailwind:\n```sh\nnpm -g install tailwindcss@3\n```\n\n2) Go into `src` and run tailwind to generate a CSS file based on tailwind classes used in `index.html`:\n\n```sh\ntailwindcss -i ./css/styles.css -o ./tailwind/styles.css\n```\n\n3) Run mosquitto http api mock\n\n4) Change mosquitto api endponits in `src/consts.js`\n\n5) Go into `src` and run a simple http server, e.g. `python3 -m http.server 3000`\n\n\nDependencies (in `src/lib` directory):\n\n* chartjs 4.3.0 (https://cdnjs.com/libraries/Chart.js/4.3.0)\n* chartjs-plugin-zoom 2.2.0 (https://cdnjs.com/libraries/chartjs-plugin-zoom)\n* hammer.js 2.0.8 (https://cdnjs.com/libraries/hammer.js)\n\n"
  },
  {
    "path": "dashboard/src/app/consts.js",
    "content": "const MAX_POINTS_IN_CHART = 5_000;\nconst KEEP_DATAPOINTS_FOR_INTERVAL =\n  1000 * // 1 sec\n  60 * // 1 minute\n  60 * // 1 hour\n  2; // 2 hours\nconst CHART_UPDATE_INTERVAL_IN_MILLISECONDS =\n  1000 * // 1 sec\n  60 * // 1 minute\n  1;\nconst SMOOTHED_CHART_UPDATE_INTERVAL_IN_MILLISECONDS =\n  1000 * // 1 sec\n  60 * // 1 minute\n  5; // 5 minutes\nconst INTERVAL_5SECS_IN_MILLISECONDS = 1000 * 5;\nconst CHARTJS_ANIMATION_DURATION_MS = 400;\nconst SYSTOPIC_ENDPOINT = \"/api/v1/systree\";\nconst LISTENERS_ENDPOINT = \"/api/v1/listeners\";\nconst CHART_DISPLAY_WINDOW = 16;\n"
  },
  {
    "path": "dashboard/src/app/dashboard.js",
    "content": "const MAIN_CHART_COLOR = \"#fd602e\";\nconst SUPPLEMENTARY_CHART_COLOR = \"#6366f1\";\n\nclass MosquittoDashboard {\n  constructor(headless = false) {\n    this.abort = new AbortController();\n    registerAbortController(this.abort, this);\n\n    this.headlessMode = !!headless;\n\n    !this.headlessMode && window.Chart.register(window.ChartZoom); // chartjs comes from lib/\n\n    this.previousDataFetchFailed = false;\n    this.brokerOnline = true;\n    this.version = \"\";\n    this.dashboardDataObject = { lastSysTopics: [] };\n    const dashboardDataObject = this.composeDashboardObject();\n    const [entitiesToUpdate] = this.getElementsToUpdate(\n      dashboardDataObject.lastSysTopics,\n    );\n\n    // if metrics were present in the session store, update them immediately\n    if (!this.headlessMode && entitiesToUpdate) {\n      Object.entries(entitiesToUpdate).forEach(([id, value]) => {\n        this.updateHtmlElementById(id, value);\n      });\n    }\n\n    this.dashboardDataObject = dashboardDataObject;\n\n    this.charts = {};\n    this.timeoutHandler = null;\n\n    !this.headlessMode && this.initializeCharts();\n    !this.headlessMode && this.addToggle();\n    this.startDataUpdates();\n  }\n\n  getChartDataFromStore(chartId) {\n    const chartDataString = sessionStorage.getItem(chartId);\n    let chartDataObject = null;\n    try {\n      if (chartDataString) {\n        chartDataObject = JSON.parse(chartDataString);\n      }\n      return chartDataObject;\n    } catch (error) {\n      const errorMsg = `Error while creating dashboards: ${error?.message}. Chart data string: \"${chartDataString}\"`;\n      console.error(errorMsg, error);\n      alert(errorMsg);\n      throw new Error(errorMsg);\n    }\n  }\n\n  createOptions() {\n    return {\n      chartDataType: \"raw\",\n    };\n  }\n\n  createChartDataObject() {\n    // we are already updating dashboardDataObject directly in updateChartInner\n    return {\n      data: {\n        rawData: [],\n        smoothedData: [],\n      },\n      labels: {\n        rawLabels: [],\n        smoothedLabels: [],\n      },\n      options: {\n        mustUpdate: false,\n      },\n    };\n  }\n\n  composeDashboardObject() {\n    const dashboardDataObject = {\n      charts: {\n        \"chart-messages-dropped\":\n          this.getChartDataFromStore(\"chart-messages-dropped\") ||\n          this.createChartDataObject(),\n        \"chart-messages-sent\":\n          this.getChartDataFromStore(\"chart-messages-sent\") ||\n          this.createChartDataObject(),\n        \"chart-messages-received\":\n          this.getChartDataFromStore(\"chart-messages-received\") ||\n          this.createChartDataObject(),\n        \"chart-messages-sent-rate\":\n          this.getChartDataFromStore(\"chart-messages-sent-rate\") ||\n          this.createChartDataObject(),\n        \"chart-messages-received-rate\":\n          this.getChartDataFromStore(\"chart-messages-received-rate\") ||\n          this.createChartDataObject(),\n        \"chart-clients-connected\":\n          this.getChartDataFromStore(\"chart-clients-connected\") ||\n          this.createChartDataObject(),\n        \"chart-clients-disconnected\":\n          this.getChartDataFromStore(\"chart-clients-disconnected\") ||\n          this.createChartDataObject(),\n      },\n      lastSysTopics: this.getChartDataFromStore(\"sysTopics\") || {},\n      lastUpdateDueToIntervalTimestamp:\n        this.getChartDataFromStore(\"updateDueToIntervalTimestamp\") || 0, // used to make sure we also always update graphs at certain intervals\n      options: this.getChartDataFromStore(\"options\") || this.createOptions(),\n    };\n    return dashboardDataObject;\n  }\n\n  setBrokerVersion() {\n    if (this.headlessMode) {\n      return;\n    }\n    this.updateHtmlElementById(\"broker-version\", this.version);\n  }\n\n  setBrokerStatus() {\n    if (this.headlessMode) {\n      return;\n    }\n    this.removeHtmlElementClass(\n      \"broker-status\",\n      this.brokerOnline ? \"broker-inactive\" : \"broker-active\",\n    );\n    this.addHtmlElementClass(\n      \"broker-status\",\n      this.brokerOnline ? \"broker-active\" : \"broker-inactive\",\n    );\n    this.updateHtmlElementById(\n      \"broker-status-text\",\n      this.brokerOnline ? \"Online\" : \"Offline\",\n    );\n  }\n\n  createLineChart(\n    canvasId,\n    label,\n    color = SUPPLEMENTARY_CHART_COLOR,\n    chartDataType,\n  ) {\n    const labelsType = chartDataType === \"raw\" ? \"rawLabels\" : \"smoothedLabels\";\n    const dataType = chartDataType === \"raw\" ? \"rawData\" : \"smoothedData\";\n    const ctx = document.getElementById(canvasId).getContext(\"2d\");\n    const totalLen =\n      this.dashboardDataObject.charts[canvasId].labels[labelsType].length;\n    const windowSize = CHART_DISPLAY_WINDOW;\n    const startIndex = Math.max(0, totalLen - windowSize);\n    const chart = new window.Chart(ctx, {\n      type: \"line\",\n      data: {\n        labels: this.dashboardDataObject.charts[canvasId].labels[labelsType],\n        datasets: [\n          {\n            label: label,\n            data: this.dashboardDataObject.charts[canvasId].data[dataType],\n            borderColor: color,\n            backgroundColor: color + \"20\",\n            borderWidth: 2,\n            fill: true,\n            tension: 0.4,\n            pointRadius: 3,\n            pointHoverRadius: 5,\n          },\n        ],\n      },\n      options: {\n        responsive: true,\n        maintainAspectRatio: false,\n        plugins: {\n          legend: {\n            display: false,\n          },\n          zoom: {\n            zoom: {\n              wheel: {\n                enabled: true,\n              },\n              pinch: {\n                enabled: true,\n              },\n              mode: \"xy\",\n            },\n            pan: {\n              enabled: true,\n              mode: \"xy\",\n              rangeMin: startIndex,\n              rangeMax: totalLen - 1,\n            },\n          },\n        },\n        scales: {\n          x: {\n            display: true,\n            grid: {\n              color: \"#f3f4f6\",\n            },\n            ticks: {\n              font: {\n                size: 10,\n              },\n              maxRotation: 45,\n            },\n            min: startIndex,\n            max: totalLen - 1,\n          },\n          y: {\n            display: true,\n            beginAtZero: true,\n            grid: {\n              color: \"#f3f4f6\",\n            },\n            ticks: {\n              font: {\n                size: 10,\n              },\n            },\n          },\n        },\n        interaction: {\n          intersect: false,\n          mode: \"index\",\n        },\n      },\n    });\n    return chart;\n  }\n\n  createSentVsReceivedChart(chartDataType) {\n    const labelsType = chartDataType === \"raw\" ? \"rawLabels\" : \"smoothedLabels\";\n    const dataType = chartDataType === \"raw\" ? \"rawData\" : \"smoothedData\";\n    const ctx = document\n      .getElementById(\"chart-message-overview\")\n      .getContext(\"2d\");\n    const totalLen =\n      this.dashboardDataObject.charts[\"chart-messages-sent\"].labels[labelsType]\n        .length;\n    const windowSize = CHART_DISPLAY_WINDOW;\n    const startIndex = Math.max(0, totalLen - windowSize);\n    const chart = new window.Chart(ctx, {\n      type: \"line\",\n      data: {\n        labels:\n          this.dashboardDataObject.charts[\"chart-messages-sent\"].labels[\n            labelsType\n          ],\n        datasets: [\n          {\n            label: \"Messages Sent\",\n            data: this.dashboardDataObject.charts[\"chart-messages-sent\"].data[\n              dataType\n            ],\n            borderColor: SUPPLEMENTARY_CHART_COLOR,\n            backgroundColor: SUPPLEMENTARY_CHART_COLOR + \"20\",\n            borderWidth: 2,\n            fill: false,\n            tension: 0.4,\n          },\n          {\n            label: \"Messages Received\",\n            data: this.dashboardDataObject.charts[\"chart-messages-received\"]\n              .data[dataType],\n            borderColor: MAIN_CHART_COLOR,\n            backgroundColor: MAIN_CHART_COLOR + \"20\",\n            borderWidth: 2,\n            fill: false,\n            tension: 0.4,\n          },\n        ],\n      },\n      options: {\n        responsive: true,\n        maintainAspectRatio: false,\n        plugins: {\n          legend: {\n            display: true,\n            position: \"top\",\n          },\n          zoom: {\n            zoom: {\n              wheel: {\n                enabled: true,\n              },\n              pinch: {\n                enabled: true,\n              },\n              mode: \"xy\",\n            },\n            pan: {\n              enabled: true,\n              mode: \"xy\",\n              rangeMin: startIndex,\n              rangeMax: totalLen - 1,\n            },\n          },\n        },\n        scales: {\n          x: {\n            display: true,\n            grid: {\n              color: \"#f3f4f6\",\n            },\n            min: startIndex,\n            max: totalLen - 1,\n          },\n          y: {\n            display: true,\n            beginAtZero: true,\n            grid: {\n              color: \"#f3f4f6\",\n            },\n          },\n        },\n        interaction: {\n          intersect: false,\n          mode: \"index\",\n        },\n      },\n    });\n\n    return chart;\n  }\n\n  createRateSentVsReceivedChart(chartDataType) {\n    const labelsType = chartDataType === \"raw\" ? \"rawLabels\" : \"smoothedLabels\";\n    const dataType = chartDataType === \"raw\" ? \"rawData\" : \"smoothedData\";\n    const ctx = document\n      .getElementById(\"chart-message-rate-overview\")\n      .getContext(\"2d\");\n    const totalLen =\n      this.dashboardDataObject.charts[\"chart-messages-sent-rate\"].labels[\n        labelsType\n      ].length;\n    const windowSize = CHART_DISPLAY_WINDOW;\n    const startIndex = Math.max(0, totalLen - windowSize);\n    const chart = new window.Chart(ctx, {\n      type: \"line\",\n      data: {\n        labels:\n          this.dashboardDataObject.charts[\"chart-messages-sent-rate\"].labels[\n            labelsType\n          ],\n        datasets: [\n          {\n            label: \"Messages Sent per Minute\",\n            data: this.dashboardDataObject.charts[\"chart-messages-sent-rate\"]\n              .data[dataType],\n            borderColor: SUPPLEMENTARY_CHART_COLOR,\n            backgroundColor: SUPPLEMENTARY_CHART_COLOR + \"20\",\n            borderWidth: 2,\n            fill: false,\n            tension: 0.4,\n          },\n          {\n            label: \"Messages Received per Minute\",\n            data: this.dashboardDataObject.charts[\n              \"chart-messages-received-rate\"\n            ].data[dataType],\n            borderColor: MAIN_CHART_COLOR,\n            backgroundColor: MAIN_CHART_COLOR + \"20\",\n            borderWidth: 2,\n            fill: false,\n            tension: 0.4,\n          },\n        ],\n      },\n      options: {\n        responsive: true,\n        maintainAspectRatio: false,\n        plugins: {\n          legend: {\n            display: true,\n            position: \"top\",\n          },\n          zoom: {\n            zoom: {\n              wheel: {\n                enabled: true,\n              },\n              pinch: {\n                enabled: true,\n              },\n              mode: \"xy\",\n            },\n            pan: {\n              enabled: true,\n              mode: \"xy\",\n              rangeMin: startIndex,\n              rangeMax: totalLen - 1,\n            },\n          },\n        },\n        scales: {\n          x: {\n            display: true,\n            grid: {\n              color: \"#f3f4f6\",\n            },\n            min: startIndex,\n            max: totalLen - 1,\n          },\n          y: {\n            display: true,\n            beginAtZero: true,\n            grid: {\n              color: \"#f3f4f6\",\n            },\n          },\n        },\n        interaction: {\n          intersect: false,\n          mode: \"index\",\n        },\n      },\n    });\n\n    return chart;\n  }\n\n  handleChartAction(chartId, action) {\n    const chart = this.charts[chartId];\n    if (!chart) {\n      const errorMsg = \"Couldn't find the chart: \" + chartId;\n      console.error(errorMsg + \". Charts:\", Object.keys(this.charts));\n      alert(errorMsg);\n    }\n\n    switch (action) {\n      case \"zoom-in\":\n        chart.zoom(1.2);\n        break;\n      case \"zoom-out\":\n        chart.zoom(0.8);\n        break;\n      case \"pan-left\":\n        chart.pan({ x: 100 });\n        break;\n      case \"pan-right\":\n        chart.pan({ x: -100 });\n        break;\n      case \"reset\":\n        const newTotalLen = chart.data.labels.length;\n        const newStart = Math.max(0, newTotalLen - CHART_DISPLAY_WINDOW);\n        chart.options.scales.x.min = newStart;\n        chart.options.scales.x.max = newTotalLen - 1;\n        chart.update();\n        //chart.update(\"none\");\n        //Object.values(chart.options.scales).forEach((axisOptions) => {\n        //  delete axisOptions.min;\n        //  delete axisOptions.max;\n        //});\n        chart.resetZoom();\n        break;\n      default:\n        const errorMsg = `Unrecognized action \"${action}\"`;\n        console.error(errorMsg);\n        alert(errorMsg);\n    }\n  }\n\n  addToggle() {\n    const toggleChartDataTypeButton = document.getElementById(\n      \"chart-data-type-global-toggle\",\n    );\n    const toggleChartDataTypeText = document.getElementById(\n      \"chart-data-type-text\",\n    );\n\n    // set to an opposite state and toggle once to refresh the button captions etc\n    if (this.dashboardDataObject.options.chartDataType === \"raw\") {\n      this.dashboardDataObject.options.chartDataType = \"smooth\";\n    } else {\n      this.dashboardDataObject.options.chartDataType = \"raw\";\n    }\n    const handleChartDataTypeToggle = () => {\n      if (this.dashboardDataObject.options.chartDataType === \"raw\") {\n        this.dashboardDataObject.options.chartDataType = \"smooth\";\n        this.destroyCharts();\n        toggleChartDataTypeText.textContent = \"Show Raw Data\";\n        this.addHtmlElementClass(\"smooth-state-svg\", \"hidden\");\n        this.removeHtmlElementClass(\"raw-state-svg\", \"hidden\");\n      } else {\n        this.dashboardDataObject.options.chartDataType = \"raw\";\n        this.destroyCharts();\n        toggleChartDataTypeText.textContent = \"Show Smoothed Data\";\n        this.addHtmlElementClass(\"raw-state-svg\", \"hidden\");\n        this.removeHtmlElementClass(\"smooth-state-svg\", \"hidden\");\n      }\n      this.initializeCharts();\n      sessionStorage.setItem(\n        \"options\",\n        JSON.stringify(this.dashboardDataObject.options),\n      );\n    };\n    toggleChartDataTypeButton.addEventListener(\"click\", () => {\n      queue.enqueue(toAsyncAndWaitAfter(handleChartDataTypeToggle));\n    });\n    queue.enqueue(toAsyncAndWaitAfter(handleChartDataTypeToggle));\n  }\n\n  initializeCharts() {\n    let id = \"\";\n\n    id = \"chart-messages-dropped\";\n    this.charts[id] = this.createLineChart(\n      id,\n      \"Dropped Messages\",\n      MAIN_CHART_COLOR,\n      this.dashboardDataObject.options.chartDataType,\n    );\n    id = \"chart-messages-sent\";\n    this.charts[id] = this.createLineChart(\n      id,\n      \"Messages Sent\",\n      MAIN_CHART_COLOR,\n      this.dashboardDataObject.options.chartDataType,\n    );\n    id = \"chart-messages-received\";\n    this.charts[id] = this.createLineChart(\n      id,\n      \"Messages Received\",\n      MAIN_CHART_COLOR,\n      this.dashboardDataObject.options.chartDataType,\n    );\n    id = \"chart-messages-sent-rate\";\n    this.charts[id] = this.createLineChart(\n      id,\n      \"Sent Rate\",\n      MAIN_CHART_COLOR,\n      this.dashboardDataObject.options.chartDataType,\n    );\n    id = \"chart-messages-received-rate\";\n    this.charts[id] = this.createLineChart(\n      id,\n      \"Received Rate\",\n      MAIN_CHART_COLOR,\n      this.dashboardDataObject.options.chartDataType,\n    );\n    id = \"chart-clients-connected\";\n    this.charts[id] = this.createLineChart(\n      id,\n      \"Connected Clients\",\n      MAIN_CHART_COLOR,\n      this.dashboardDataObject.options.chartDataType,\n    );\n    id = \"chart-clients-disconnected\";\n    this.charts[id] = this.createLineChart(\n      id,\n      \"Disconnected Persistent Clients\",\n      MAIN_CHART_COLOR,\n      this.dashboardDataObject.options.chartDataType,\n    );\n    id = \"chart-message-overview\";\n    this.charts[id] = this.createSentVsReceivedChart(\n      this.dashboardDataObject.options.chartDataType,\n    );\n    id = \"chart-message-rate-overview\";\n    this.charts[id] = this.createRateSentVsReceivedChart(\n      this.dashboardDataObject.options.chartDataType,\n    );\n\n    document.addEventListener(\"click\", (e) => {\n      if (e.target.dataset.action) {\n        const chartId = e.target.dataset.chart;\n        if (chartId) {\n          const action = e.target.dataset.action;\n          queue.enqueue(\n            toAsyncAndWaitAfter(() => this.handleChartAction(chartId, action)),\n          );\n        }\n      }\n    });\n  }\n\n  getChartDatasets(chartId) {\n    let labels, dataset, dataset2;\n    let id1, id2;\n    if (chartId === \"chart-message-overview\") {\n      id1 = \"chart-messages-sent\";\n      id2 = \"chart-messages-received\";\n      labels = this.dashboardDataObject.charts[id1].labels;\n      dataset = this.dashboardDataObject.charts[id1].data;\n      dataset2 = this.dashboardDataObject.charts[id2].data;\n      assertExistence(labels, `Labels not found for \"${id1}\"`);\n      assertExistence(labels, `Dataset not found for \"${id1}\"`);\n      assertExistence(labels, `Dataset not found for \"${id2}\"`);\n    } else if (chartId === \"chart-message-rate-overview\") {\n      id1 = \"chart-messages-sent-rate\";\n      id2 = \"chart-messages-received-rate\";\n      labels = this.dashboardDataObject.charts[id1].labels;\n      dataset = this.dashboardDataObject.charts[id1].data;\n      dataset2 = this.dashboardDataObject.charts[id2].data;\n      assertExistence(labels, `Labels not found for \"${id1}\"`);\n      assertExistence(labels, `Dataset not found for \"${id1}\"`);\n      assertExistence(labels, `Dataset not found for \"${id2}\"`);\n    } else {\n      labels = this.dashboardDataObject.charts[chartId].labels;\n      dataset = this.dashboardDataObject.charts[chartId].data;\n      assertExistence(labels, `Labels not found for chartId \"${chartId}\"`);\n      assertExistence(labels, `Dataset not found for chartId \"${chartId}\"`);\n    }\n\n    return [labels, dataset, dataset2];\n  }\n\n  getChartPositionalData(chart) {\n    const lastX = chart.data.labels.length - 1;\n    const secondToLastX = chart.data.labels.length - 2;\n    const currentViewFieldEnd = chart.scales.x.max;\n    const zoomLevel = chart.getZoomLevel();\n\n    return [zoomLevel, currentViewFieldEnd, lastX, secondToLastX];\n  }\n\n  isEndElementVisibleAndDefaultZoom(lastX, currentEnd, zoomLevel) {\n    if (currentEnd === lastX && zoomLevel === 1) {\n      return true;\n    }\n    return false;\n  }\n\n  slideChart(chart) {\n    const newTotalLen = chart.data.labels.length;\n    const newStart = Math.max(0, newTotalLen - CHART_DISPLAY_WINDOW);\n    chart.options.scales.x.min = newStart;\n    chart.options.scales.x.max = newTotalLen - 1;\n  }\n\n  destroyCharts() {\n    for (const [chartId, _] of Object.entries(this.charts)) {\n      let data;\n      let data2;\n      let labels;\n      [labels, data, data2] = this.getChartDatasets(chartId);\n      this.charts[chartId].destroy();\n    }\n  }\n\n  updateLastSysTopics(id, value) {\n    this.dashboardDataObject.lastSysTopics[id] = value;\n  }\n\n  updateMatchingChart(chartId, sysTopics, chartIdsToUpdate) {\n    const createErrorMsg = (matchingChartId, matchingChartSysTopic) =>\n      `datapoint doesn't exist in current sysTopic data for the chart \"${matchingChartId}\" matching the chart \"${chartId}\". Matching chart sys topic: ${matchingChartSysTopic}. Available sys topics: ${JSON.stringify(\n        sysTopics,\n      )}`;\n\n    if (chartId === \"chart-messages-sent\") {\n      const matchingChartId = \"chart-messages-received\";\n      const matchingChartSysTopic = \"$SYS/broker/messages/received\";\n      const datapoint = sysTopics[matchingChartSysTopic];\n      assertExistence(\n        datapoint,\n        createErrorMsg(matchingChartId, matchingChartSysTopic),\n      );\n      chartIdsToUpdate[matchingChartId] = datapoint;\n    }\n    if (chartId === \"chart-messages-received\") {\n      const matchingChartId = \"chart-messages-sent\";\n      const matchingChartSysTopic = \"$SYS/broker/messages/sent\";\n      const datapoint = sysTopics[matchingChartSysTopic];\n      assertExistence(\n        datapoint,\n        createErrorMsg(matchingChartId, matchingChartSysTopic),\n      );\n      chartIdsToUpdate[matchingChartId] = datapoint;\n    }\n    if (chartId === \"chart-messages-sent-rate\") {\n      const matchingChartId = \"chart-messages-received-rate\";\n      const matchingChartSysTopic = \"$SYS/broker/load/messages/received/1min\";\n      const datapoint = sysTopics[matchingChartSysTopic];\n      assertExistence(\n        datapoint,\n        createErrorMsg(matchingChartId, matchingChartSysTopic),\n      );\n      chartIdsToUpdate[matchingChartId] = datapoint;\n    }\n    if (chartId === \"chart-messages-received-rate\") {\n      const matchingChartId = \"chart-messages-sent-rate\";\n      const matchingChartSysTopic = \"$SYS/broker/load/messages/sent/1min\";\n      const datapoint = sysTopics[matchingChartSysTopic];\n      assertExistence(\n        datapoint,\n        createErrorMsg(matchingChartId, matchingChartSysTopic),\n      );\n      chartIdsToUpdate[matchingChartId] = datapoint;\n    }\n  }\n\n  getElementsToUpdate(sysTopics) {\n    // we only update what has actually changed\n    let htmlIdsToUpdate = {};\n    let chartIdsToUpdate = {};\n    let topic = \"\";\n\n    // sys topics object looks as follows:\n    //{\n    //  \"$SYS/broker/uptime\":\t99999,\n    //  \"$SYS/broker/clients/total\":\t0,\n    //  \"$SYS/broker/clients/maximum\":\t1,\n    //  \"$SYS/broker/clients/disconnected\":\t0,\n    //  \"$SYS/broker/clients/connected\":\t0,\n    //  \"$SYS/broker/clients/expired\":\t0,\n    //  \"$SYS/broker/messages/stored\":\t2,\n    //  \"$SYS/broker/store/messages/bytes\":\t32,\n    //  \"$SYS/broker/subscriptions/count\":\t0,\n    //  \"$SYS/broker/shared_subscriptions/count\":\t0,\n    //  \"$SYS/broker/retained messages/count\":\t2,\n    //  \"$SYS/broker/heap/current\":\t796624,\n    //  \"$SYS/broker/heap/maximum\":\t796704,\n    //  \"$SYS/broker/messages/received\":\t0,\n    //  \"$SYS/broker/messages/sent\":\t0,\n    //  \"$SYS/broker/bytes/received\":\t0,\n    //  \"$SYS/broker/bytes/sent\":\t0,\n    //  \"$SYS/broker/publish/bytes/received\":\t0,\n    //  \"$SYS/broker/publish/bytes/sent\":\t0,\n    //  \"$SYS/broker/packet/out/count\":\t0,\n    //  \"$SYS/broker/packet/out/bytes\":\t0,\n    //  \"$SYS/broker/connections/socket/count\":\t0,\n    //  \"$SYS/broker/publish/messages/dropped\":\t0,\n    //  \"$SYS/broker/publish/messages/received\":\t0,\n    //  \"$SYS/broker/publish/messages/sent\":\t0\n    //}\n    topic = \"$SYS/broker/uptime\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"broker-uptime\"] = secondsToIntervalString(\n        sysTopics[topic],\n      );\n    }\n\n    topic = \"$SYS/broker/clients/total\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"clients-total\"] = prettifyNumber(sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-clients-total\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/clients/disconnected\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"clients-disconnected\"] = prettifyNumber(\n        sysTopics[topic],\n      );\n      htmlIdsToUpdate[\"systopic-clients-disconnected\"] = sysTopics[topic];\n      chartIdsToUpdate[\"chart-clients-disconnected\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/clients/connected\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"clients-connected\"] = prettifyNumber(sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-clients-connected\"] = sysTopics[topic];\n      chartIdsToUpdate[\"chart-clients-connected\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/clients/maximum\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[\"clients-maximum\"] !==\n        sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(\"clients-disconnected\", sysTopics[topic]);\n      htmlIdsToUpdate[\"clients-maximum\"] = sysTopics[topic];\n      htmlIdsToUpdate[\"systopic-clients-max\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/clients/expired\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"clients-expired\"] = prettifyNumber(sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-clients-expired\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/subscriptions/count\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"total-subscriptions\"] = prettifyNumber(sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-total-subscriptions\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/shared_subscriptions/count\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-total-shared-subscriptions\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/heap/current\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-heap-current\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/heap/maximum\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-heap-max\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/connections/socket/count\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"connection-sockets\"] = sysTopics[topic];\n      htmlIdsToUpdate[\"systopic-connection-sockets\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/load/messages/received/1min\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      const chartId = \"chart-messages-received-rate\";\n      chartIdsToUpdate[chartId] = sysTopics[topic];\n      this.updateMatchingChart(chartId, sysTopics, chartIdsToUpdate);\n      htmlIdsToUpdate[\"systopic-messages-received-1min\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/load/messages/received/10min\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-messages-received-10min\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/load/messages/received/15min\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-messages-received-15min\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/load/messages/sent/1min\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      const chartId = \"chart-messages-sent-rate\";\n      chartIdsToUpdate[chartId] = sysTopics[topic];\n      this.updateMatchingChart(chartId, sysTopics, chartIdsToUpdate);\n      htmlIdsToUpdate[\"systopic-messages-sent-1min\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/load/messages/sent/10min\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-messages-sent-10min\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/load/messages/sent/15min\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-messages-sent-15min\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/messages/stored\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"messages-stored\"] = sysTopics[topic];\n      htmlIdsToUpdate[\"systopic-messages-stored\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/retained messages/count\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"messages-retained\"] = sysTopics[topic];\n      htmlIdsToUpdate[\"systopic-messages-retained\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/messages/received\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-messages-received\"] = sysTopics[topic];\n      const chartId = \"chart-messages-received\";\n      chartIdsToUpdate[chartId] = sysTopics[topic];\n      this.updateMatchingChart(chartId, sysTopics, chartIdsToUpdate);\n    }\n\n    topic = \"$SYS/broker/messages/sent\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-messages-sent\"] = sysTopics[topic];\n      const chartId = \"chart-messages-sent\";\n      chartIdsToUpdate[chartId] = sysTopics[topic];\n      this.updateMatchingChart(chartId, sysTopics, chartIdsToUpdate);\n    }\n\n    topic = \"$SYS/broker/store/messages/bytes\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-messages-stored-bytes\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/bytes/received\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-received-bytes\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/bytes/sent\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-sent-bytes\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/publish/bytes/received\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-publish-received-bytes\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/publish/bytes/sent\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-publish-sent-bytes\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/publish/messages/dropped\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"messages-dropped\"] = sysTopics[topic];\n      htmlIdsToUpdate[\"systopic-messages-dropped\"] = sysTopics[topic];\n      chartIdsToUpdate[\"chart-messages-dropped\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/publish/messages/received\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"messages-published-to-broker\"] = sysTopics[topic];\n      htmlIdsToUpdate[\"systopic-messages-published-to-broker\"] =\n        sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/publish/messages/sent\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"messages-published-by-broker\"] = sysTopics[topic];\n      htmlIdsToUpdate[\"systopic-messages-published-by-broker\"] =\n        sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/packet/out/count\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-out-packets\"] = sysTopics[topic];\n    }\n\n    topic = \"$SYS/broker/packet/out/bytes\";\n    if (\n      sysTopics[topic] !== undefined &&\n      this.dashboardDataObject.lastSysTopics[topic] !== sysTopics[topic]\n    ) {\n      this.updateLastSysTopics(topic, sysTopics[topic]);\n      htmlIdsToUpdate[\"systopic-out-bytes\"] = sysTopics[topic];\n    }\n\n    if (!Object.keys(htmlIdsToUpdate).length) {\n      htmlIdsToUpdate = null;\n    }\n\n    if (!Object.keys(chartIdsToUpdate).length) {\n      chartIdsToUpdate = null;\n    }\n\n    return [htmlIdsToUpdate, chartIdsToUpdate];\n  }\n\n  trimChartDataWindow(labels, dataset, timestampNow) {\n    let earliestTimestamp = 0;\n    while (\n      timestampNow - earliestTimestamp >= KEEP_DATAPOINTS_FOR_INTERVAL &&\n      labels.length &&\n      dataset.length\n    ) {\n      earliestTimestamp = timeStringToTimestamp(labels[0]);\n\n      labels.shift();\n      dataset.shift();\n    }\n  }\n\n  processChartOverflow(labels, dataset, timestamp) {\n    if (!labels.length || !dataset.length) {\n      return;\n    }\n    if (\n      labels.length > MAX_POINTS_IN_CHART &&\n      dataset.length > MAX_POINTS_IN_CHART\n    ) {\n      labels.shift();\n      dataset.shift();\n    }\n\n    if (\n      timestamp - timeStringToTimestamp(labels[0]) >=\n      KEEP_DATAPOINTS_FOR_INTERVAL\n    ) {\n      this.trimChartDataWindow(labels, dataset, timestamp);\n    }\n  }\n\n  datapointsAreSufficientlyDifferent(datapoint1, datapoint2) {\n    if (Math.abs(datapoint1 - datapoint2) / datapoint1 > 0.2) {\n      return true;\n    }\n    return false;\n  }\n\n  labelsAreFarApart(earlierTimeString, laterTimeString) {\n    const earlierTimestamp = timeStringToTimestamp(earlierTimeString);\n    const laterTimestamp = timeStringToTimestamp(laterTimeString);\n\n    if (\n      laterTimestamp - earlierTimestamp >\n      SMOOTHED_CHART_UPDATE_INTERVAL_IN_MILLISECONDS\n    ) {\n      // also works at the bound between two days because laterTimestamp - earlierTimestamp will in this case be negative, so we return false for the check happening when one timestamp (earlierTimestamp) is coming from the previous day and the current timestamp (laterTimestamp) is for the new day, just after midnight\n      return true;\n    }\n    return false;\n  }\n\n  setMustUpdateForMatchingGraph(chartId) {\n    const createAssertErrorMsg = (id) =>\n      `mustUpdate option not found for chart \"${id}\". Available options: ${JSON.stringify(\n        this.dashboardDataObject.charts[id]?.options,\n      )}. Available charts: ${Object.keys(this.dashboardDataObject.charts)}`;\n    let oppositeChartId;\n\n    if (chartId === \"chart-messages-sent\") {\n      oppositeChartId = \"chart-messages-received\";\n      assertExistence(\n        this.dashboardDataObject.charts[oppositeChartId]?.options?.mustUpdate,\n        createAssertErrorMsg(oppositeChartId),\n      );\n      this.dashboardDataObject.charts[oppositeChartId].options.mustUpdate =\n        true;\n    } else if (chartId === \"chart-messages-received\") {\n      oppositeChartId = \"chart-messages-sent\";\n      assertExistence(\n        this.dashboardDataObject.charts[oppositeChartId]?.options?.mustUpdate,\n        createAssertErrorMsg(oppositeChartId),\n      );\n      this.dashboardDataObject.charts[oppositeChartId].options.mustUpdate =\n        true;\n    } else if (chartId === \"chart-messages-sent-rate\") {\n      oppositeChartId = \"chart-messages-received-rate\";\n      assertExistence(\n        this.dashboardDataObject.charts[oppositeChartId]?.options?.mustUpdate,\n        createAssertErrorMsg(oppositeChartId),\n      );\n      this.dashboardDataObject.charts[oppositeChartId].options.mustUpdate =\n        true;\n    } else if (chartId === \"chart-messages-received-rate\") {\n      oppositeChartId = \"chart-messages-sent-rate\";\n      assertExistence(\n        this.dashboardDataObject.charts[oppositeChartId]?.options?.mustUpdate,\n        createAssertErrorMsg(oppositeChartId),\n      );\n      this.dashboardDataObject.charts[oppositeChartId].options.mustUpdate =\n        true;\n    }\n  }\n\n  addSmoothedDataPoint(\n    chartData,\n    chartLabels,\n    chartOptions,\n    datapoint,\n    timeString,\n    chartId,\n  ) {\n    const lastElement = chartData.smoothedData.pop();\n    const lastLabel = chartLabels.smoothedLabels.pop();\n\n    const smoothedDataLen = chartData.smoothedData.length;\n    const smoothedLabelsLen = chartLabels.smoothedLabels.length;\n    if (smoothedDataLen != smoothedLabelsLen) {\n      const errorMessage = `Smoothed data and label set size deviated: ${smoothedDataLen} vs ${smoothedLabelsLen}. Broken state`;\n      throw new Error(errorMessage);\n    }\n\n    if (\n      lastElement &&\n      lastLabel &&\n      (chartOptions.mustUpdate ||\n        // Compare popped datapoint and the one that is left in the array before it. We check that the differences between datapoints' values reaches a certain threshold or these datapoints have large time interval between insertions\n        (smoothedDataLen == 0 && smoothedLabelsLen == 0) ||\n        this.datapointsAreSufficientlyDifferent(\n          chartData.smoothedData[smoothedDataLen - 1],\n          lastElement,\n        ) ||\n        this.labelsAreFarApart(\n          chartLabels.smoothedLabels[smoothedLabelsLen - 1],\n          lastLabel,\n        ))\n    ) {\n      !chartOptions.mustUpdate && this.setMustUpdateForMatchingGraph(chartId); // check is needed to avoud a circular update\n      chartOptions.mustUpdate = false;\n      chartData.smoothedData.push(lastElement);\n      chartLabels.smoothedLabels.push(lastLabel);\n    }\n    // always append the latest datapoint regardless if its value is sufficiently different or not. this is to keep the graph up to date with the latest change and not make it appear stale or simply empty\n    chartData.smoothedData.push(datapoint);\n    chartLabels.smoothedLabels.push(timeString);\n  }\n\n  updateChartInner(id, datapoint, timestamp, dashboardDataObject) {\n    const chart = this.charts[id]; // note: in headless mode chart will be undefined as no chart objects are initialized\n    const chartData = dashboardDataObject.charts[id].data;\n    const chartLabels = dashboardDataObject.charts[id].labels;\n    const chartOptions = dashboardDataObject.charts[id].options;\n\n    !this.headlessMode &&\n      assertExistence(\n        chart,\n        `Chart \"${id}\" not found. Available charts: ${Object.keys(this.charts)}`,\n      );\n    assertExistence(\n      chartData,\n      `Data for the chart \"${id}\" not found. Available charts: ${Object.keys(\n        this.dashboardDataObject.charts,\n      )}`,\n    );\n    assertExistence(\n      chartLabels,\n      `Labels for the chart \"${id}\" not found. Available charts: ${Object.keys(\n        this.dashboardDataObject.charts,\n      )}`,\n    );\n    assertExistence(\n      chartOptions,\n      `Options for the chart \"${id}\" not found. Available charts: ${Object.keys(\n        this.dashboardDataObject.charts,\n      )}`,\n    );\n\n    this.processChartOverflow(\n      chartLabels.rawLabels,\n      chartData.rawData,\n      timestamp,\n    );\n\n    let zoomLevel, currentEnd, lastX;\n    if (!this.headlessMode) {\n      [zoomLevel, currentEnd, lastX] = this.getChartPositionalData(chart);\n    }\n\n    const timeString = toTimeString(new Date(timestamp));\n    chartData.rawData.push(datapoint);\n    chartLabels.rawLabels.push(timeString);\n    this.addSmoothedDataPoint(\n      chartData,\n      chartLabels,\n      chartOptions,\n      datapoint,\n      timeString,\n      id,\n    );\n\n    if (\n      !this.headlessMode &&\n      this.isEndElementVisibleAndDefaultZoom(lastX, currentEnd, zoomLevel)\n    ) {\n      this.slideChart(chart);\n    }\n\n    !this.headlessMode && chart.update(); // put 'none' for no animation on updates\n  }\n\n  getOverviewChartSubchartIds(id) {\n    if (id == \"chart-message-overview\") {\n      return [\"chart-messages-sent\", \"chart-messages-received\"];\n    } else if (id == \"chart-message-rate-overview\") {\n      return [\"chart-messages-sent-rate\", \"chart-messages-received-rate\"];\n    } else {\n      throw new Error(`No such overview chart id: ${id}`);\n    }\n  }\n\n  updateOverviewChartInner(id, firstSubChartId, secondSubChartId) {\n    if (this.headlessMode) {\n      // this function only update the chart chart view itself, no data manipulations are done as it simply consumes other line charts. So we have nothing to do in headless mode\n      return;\n    }\n    const chart = this.charts[id];\n    const firstChartData =\n      this.dashboardDataObject.charts[firstSubChartId].data;\n    const firstChartLabels =\n      this.dashboardDataObject.charts[firstSubChartId].labels;\n    const secondChartData =\n      this.dashboardDataObject.charts[secondSubChartId].data;\n    const secondChartLabels =\n      this.dashboardDataObject.charts[secondSubChartId].labels;\n\n    assertExistence(\n      chart,\n      `Chart \"${id}\" not found. Available charts: ${Object.keys(this.charts)}`,\n    );\n    assertExistence(\n      firstChartData,\n      `Data for the first sub chart with id \"${firstSubChartId}\" not found. Available charts: ${Object.keys(\n        this.dashboardDataObject.charts,\n      )}`,\n    );\n    assertExistence(\n      firstChartLabels,\n      `Labels for the first sub chart with id \"${firstSubChartId}\" not found. Available charts: ${Object.keys(\n        this.dashboardDataObject.charts,\n      )}`,\n    );\n\n    assertExistence(\n      secondChartData,\n      `Data for the second sub chart with \"${secondSubChartId}\" not found. Available charts: ${Object.keys(\n        this.dashboardDataObject.charts,\n      )}`,\n    );\n    assertExistence(\n      secondChartLabels,\n      `Labels for the second sub chart with id \"${secondSubChartId}\" not found. Available charts: ${Object.keys(\n        this.dashboardDataObject.charts,\n      )}`,\n    );\n\n    const [zoomLevel, currentEnd, lastX, secondToLastX] =\n      this.getChartPositionalData(chart);\n\n    // compare to both last and second to last x because x may have already moved forward one step since it comes from a separate graph that gets rendered before overview graphs\n    if (\n      this.isEndElementVisibleAndDefaultZoom(lastX, currentEnd, zoomLevel) ||\n      this.isEndElementVisibleAndDefaultZoom(\n        secondToLastX,\n        currentEnd,\n        zoomLevel,\n      )\n    ) {\n      this.slideChart(chart);\n    }\n    chart.update();\n  }\n\n  updateHtmlElementById(elementId, value) {\n    const element = document.getElementById(elementId);\n    if (element) {\n      element.textContent = value;\n    }\n  }\n\n  removeHtmlElementClass(elementId, className) {\n    const element = document.getElementById(elementId);\n    if (element) {\n      element.classList.remove(className);\n    }\n  }\n\n  addHtmlElementClass(elementId, className) {\n    const element = document.getElementById(elementId);\n    if (element) {\n      element.classList.add(className);\n    }\n  }\n\n  updateChart(\n    id,\n    datapoint,\n    timestampMilliseconds,\n    dashboardDataObject,\n    lastDataPoint,\n    isUpdatingAllCharts,\n  ) {\n    if (datapoint !== undefined || isUpdatingAllCharts) {\n      this.updateChartInner(\n        id,\n        datapoint !== undefined ? datapoint : lastDataPoint,\n        timestampMilliseconds,\n        dashboardDataObject,\n      );\n    }\n  }\n\n  updateOverviewChart(\n    id,\n    firstChartDatapoint,\n    secondChartDatapoint,\n    isUpdatingAllCharts,\n    firstSubChartId,\n    secondSubChartId,\n  ) {\n    if (\n      firstChartDatapoint !== undefined ||\n      secondChartDatapoint !== undefined ||\n      isUpdatingAllCharts\n    ) {\n      this.updateOverviewChartInner(id, firstSubChartId, secondSubChartId);\n    }\n  }\n\n  getLastChartsDataPoints(dashboardDataObject) {\n    const lastChartsDataPoints = {\n      \"chart-messages-dropped\":\n        dashboardDataObject.lastSysTopics[\n          \"$SYS/broker/publish/messages/dropped\"\n        ],\n      \"chart-messages-sent\":\n        dashboardDataObject.lastSysTopics[\"$SYS/broker/messages/sent\"],\n      \"chart-messages-received\":\n        dashboardDataObject.lastSysTopics[\"$SYS/broker/messages/received\"],\n      \"chart-messages-sent-rate\":\n        dashboardDataObject.lastSysTopics[\n          \"$SYS/broker/load/messages/sent/1min\"\n        ],\n      \"chart-messages-received-rate\":\n        dashboardDataObject.lastSysTopics[\n          \"$SYS/broker/load/messages/received/1min\"\n        ],\n      \"chart-clients-connected\":\n        dashboardDataObject.lastSysTopics[\"$SYS/broker/clients/connected\"],\n      \"chart-clients-disconnected\":\n        dashboardDataObject.lastSysTopics[\"$SYS/broker/clients/disconnected\"],\n    };\n    return lastChartsDataPoints;\n  }\n\n  updateCharts(\n    chartData,\n    dashboardDataObject,\n    timestampMilliseconds,\n    isUpdatingAllCharts,\n  ) {\n    const lastDataPoints = this.getLastChartsDataPoints(dashboardDataObject);\n    let id = \"\";\n\n    id = \"chart-messages-dropped\";\n    this.updateChart(\n      id,\n      chartData[id],\n      timestampMilliseconds,\n      dashboardDataObject,\n      lastDataPoints[id],\n      isUpdatingAllCharts,\n    );\n\n    id = \"chart-messages-sent\";\n    this.updateChart(\n      id,\n      chartData[id],\n      timestampMilliseconds,\n      dashboardDataObject,\n      lastDataPoints[id],\n      isUpdatingAllCharts,\n    );\n\n    id = \"chart-messages-received\";\n    this.updateChart(\n      id,\n      chartData[id],\n      timestampMilliseconds,\n      dashboardDataObject,\n      lastDataPoints[id],\n      isUpdatingAllCharts,\n    );\n\n    id = \"chart-messages-sent-rate\";\n    this.updateChart(\n      id,\n      chartData[id],\n      timestampMilliseconds,\n      dashboardDataObject,\n      lastDataPoints[id],\n      isUpdatingAllCharts,\n    );\n\n    id = \"chart-messages-received-rate\";\n    this.updateChart(\n      id,\n      chartData[id],\n      timestampMilliseconds,\n      dashboardDataObject,\n      lastDataPoints[id],\n      isUpdatingAllCharts,\n    );\n\n    id = \"chart-clients-connected\";\n    this.updateChart(\n      id,\n      chartData[id],\n      timestampMilliseconds,\n      dashboardDataObject,\n      lastDataPoints[id],\n      isUpdatingAllCharts,\n    );\n\n    id = \"chart-clients-disconnected\";\n    this.updateChart(\n      id,\n      chartData[id],\n      timestampMilliseconds,\n      dashboardDataObject,\n      lastDataPoints[id],\n      isUpdatingAllCharts,\n    );\n\n    id = \"chart-messages-sent\";\n    let id2 = \"chart-messages-received\";\n    this.updateOverviewChart(\n      \"chart-message-overview\",\n      chartData[id],\n      chartData[id2],\n      isUpdatingAllCharts,\n      id,\n      id2,\n    );\n\n    id = \"chart-messages-sent-rate\";\n    id2 = \"chart-messages-received-rate\";\n    this.updateOverviewChart(\n      \"chart-message-rate-overview\",\n      chartData[id],\n      chartData[id2],\n      isUpdatingAllCharts,\n      id,\n      id2,\n    );\n  }\n\n  mustInsertDatapointDueToInterval(\n    lastUpdateDueToIntervalTimestamp,\n    nowTimestampMilliseconds,\n  ) {\n    if (\n      lastUpdateDueToIntervalTimestamp === 0 ||\n      nowTimestampMilliseconds - lastUpdateDueToIntervalTimestamp >=\n        CHART_UPDATE_INTERVAL_IN_MILLISECONDS\n    ) {\n      return true;\n    }\n    return false;\n  }\n\n  async checkForDataUpdates() {\n    const nowTimestampMilliseconds = new Date().getTime();\n    let sysTopics = null;\n    try {\n      sysTopics = await fetchData(SYSTOPIC_ENDPOINT, {\n        signal: this.abort.signal,\n        cache: \"no-store\",\n      });\n      this.version = sysTopics?.[\"$SYS/broker/version\"];\n\n      this.previousDataFetchFailed = false;\n      this.brokerOnline = true;\n      this.setBrokerVersion();\n      this.setBrokerStatus();\n    } catch (error) {\n      const errorMsg = `Error fetching sys topics: ${error?.message}`;\n      if (this.abort.signal.aborted || error?.name === \"AbortError\") {\n        console.log(\"Fetching systopics aborted\");\n      } else {\n        console.error(errorMsg);\n        if (!this.previousDataFetchFailed) {\n          alert(errorMsg);\n        }\n      }\n      this.brokerOnline = false;\n      this.setBrokerStatus();\n      this.previousDataFetchFailed = true;\n    }\n    if (!sysTopics) {\n      return;\n    }\n\n    const [metricsToUpdate, chartsToUpdate] =\n      this.getElementsToUpdate(sysTopics);\n    this.dashboardDataObject.lastSysTopics = sysTopics;\n\n    if (!this.headlessMode && metricsToUpdate) {\n      Object.entries(metricsToUpdate).forEach(([id, value]) => {\n        this.updateHtmlElementById(id, value);\n      });\n    }\n\n    let updateAllCharts = false;\n\n    if (\n      this.mustInsertDatapointDueToInterval(\n        this.dashboardDataObject.lastUpdateDueToIntervalTimestamp,\n        nowTimestampMilliseconds,\n      )\n    ) {\n      updateAllCharts = true;\n      this.dashboardDataObject.lastUpdateDueToIntervalTimestamp =\n        nowTimestampMilliseconds;\n    }\n\n    if (chartsToUpdate || updateAllCharts) {\n      this.updateCharts(\n        chartsToUpdate || {},\n        this.dashboardDataObject,\n        nowTimestampMilliseconds,\n        updateAllCharts,\n      );\n\n      let chartsIds;\n      if (updateAllCharts) {\n        const lastDataPointsOfAllCharts = this.getLastChartsDataPoints(\n          this.dashboardDataObject,\n        );\n        // importantly this gives us ids of all charts\n        chartsIds = Object.keys(lastDataPointsOfAllCharts);\n      } else if (chartsToUpdate) {\n        chartsIds = Object.keys(chartsToUpdate);\n      } else {\n        chartsIds = []; // nothing to update\n      }\n      this.updateStore(this.dashboardDataObject, chartsIds);\n    }\n  }\n\n  updateStore(dashboardDataObject, idsOfChartsToUpdate) {\n    try {\n      sessionStorage.setItem(\n        \"options\",\n        JSON.stringify(dashboardDataObject.options),\n      );\n      sessionStorage.setItem(\n        \"sysTopics\",\n        JSON.stringify(dashboardDataObject.lastSysTopics),\n      );\n      sessionStorage.setItem(\n        \"updateDueToIntervalTimestamp\",\n        JSON.stringify(dashboardDataObject.lastUpdateDueToIntervalTimestamp),\n      );\n      for (const key of idsOfChartsToUpdate) {\n        const chartData = dashboardDataObject.charts[key];\n        if (!chartData) {\n          throw new Error(\n            `dashboardDataObject.charts does not contain key ${key}`,\n          );\n        }\n        sessionStorage.setItem(key, JSON.stringify(chartData));\n      }\n    } catch (error) {\n      const errorMsg = `Error while updating sessionStorage`;\n      console.error(errorMsg);\n      throw new Error(errorMsg + \": \" + error?.message);\n    }\n  }\n\n  async startDataUpdates() {\n    const checkForDataUpdatesWrapper = async () => {\n      try {\n        await this.checkForDataUpdates();\n      } catch (error) {\n        const errorMsg = `Error while checking for dashboard data updates ${error?.message}. Reopen the page to try again.`;\n        console.error(errorMsg);\n        alert(errorMsg);\n        throw error;\n      }\n    };\n\n    try {\n      await checkForDataUpdatesWrapper();\n    } catch (error) {\n      return;\n    }\n    // we assume that we want to perform data updates every 5 seconds\n    const timestampNow = new Date().getTime();\n    const nextTimestampDivisibleBy5Seconds =\n      INTERVAL_5SECS_IN_MILLISECONDS *\n        Math.floor(timestampNow / INTERVAL_5SECS_IN_MILLISECONDS) +\n      INTERVAL_5SECS_IN_MILLISECONDS; // integer division and then reconstruct the actual number\n\n    const interval = nextTimestampDivisibleBy5Seconds - timestampNow;\n\n    const doDataUpdate = async () => {\n      clearTimeout(this.timeoutHandler);\n\n      let startTs, endTs;\n      try {\n        startTs = Date.now();\n        await checkForDataUpdatesWrapper();\n        endTs = Date.now();\n      } catch (error) {\n        return;\n      }\n      const executionTimeMs = endTs - startTs;\n\n      this.timeoutHandler = setTimeout(\n        // don't want anything to get into a contending state while animation is running, so wait a bit after doDataUpdate returns\n        () =>\n          queue.enqueue(\n            toAsyncAndWaitAfter(\n              doDataUpdate,\n              CHARTJS_ANIMATION_DURATION_MS + 50,\n            ),\n          ),\n        INTERVAL_5SECS_IN_MILLISECONDS - executionTimeMs > 0\n          ? INTERVAL_5SECS_IN_MILLISECONDS - executionTimeMs\n          : 0,\n      );\n    };\n    this.timeoutHandler = setTimeout(\n      () => queue.enqueue(doDataUpdate),\n      interval,\n    );\n  }\n}\n"
  },
  {
    "path": "dashboard/src/app/index.js",
    "content": "document.addEventListener(\"DOMContentLoaded\", () => {\n  new Sidebar();\n  new MosquittoDashboard();\n});\n\nfunction checkNormalBannerImage(bannerImage, bannerCard) {\n  const imageSrc = \"https://mosquitto.org/banner/image\"; // no extension on the file - it can svg or png\n  const probe = new Image();\n  probe.onload = () => {\n    bannerImage.src = imageSrc;\n  };\n  probe.onerror = () => {\n    console.warn(\"Banner-image didn't loaded\");\n  };\n  probe.src = imageSrc;\n}\n\nfunction checkSvgBannerImage(bannerImage, bannerCard, bannerLink, bannerInner) {\n  // if a full fledged svg found, display it and remove the default link\n  const svgSrc = \"https://mosquitto.org/banner/image.svg\";\n  const svgProbe = new Image();\n  svgProbe.onload = () => {\n    bannerImage.src = svgSrc;\n    // Only if the svg exists (was loaded successfully) make a call to fetch it and then inline it. Requires CORS to be set on svgSrc\n    fetch(svgSrc)\n      .then((r) => r.text())\n      .then((svg) => {\n        bannerInner.innerHTML = svg;\n        bannerLink.removeAttribute(\"href\");\n      })\n      .catch((error) =>\n        console.warn(\"SVG banner-image couldn't be fetched:\", error),\n      );\n  };\n  svgProbe.onerror = () => {\n    console.warn(\"SVG Banner-image didn't loaded\");\n  };\n  svgProbe.src = svgSrc;\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n  const toggleButton = document.getElementById(\"layout-toggle\");\n  const chartsGrid = document.getElementById(\"charts-grid\");\n  const layoutText = document.getElementById(\"layout-text\");\n  const bannerImage = document.getElementById(\"banner-img\");\n  const bannerCard = document.getElementById(\"banner-card\");\n  const bannerInner = document.getElementById(\"banner-inner\");\n  const bannerLink = document.getElementById(\"banner-link\");\n\n  checkNormalBannerImage(bannerImage, bannerCard);\n\n  checkSvgBannerImage(bannerImage, bannerCard, bannerLink, bannerInner);\n\n  let isGridView = true;\n  let storedSetting = sessionStorage.getItem(\"isGridView\");\n\n  const toggleView = () => {\n    if (isGridView) {\n      // switch to single column\n      chartsGrid.className = \"grid grid-cols-1 gap-4\";\n      layoutText.textContent = \"Grid View\";\n      isGridView = false;\n    } else {\n      // switch back to grid\n      chartsGrid.className = \"grid grid-cols-1 lg:grid-cols-2 gap-4\";\n      layoutText.textContent = \"Single Column\";\n      isGridView = true;\n    }\n    sessionStorage.setItem(\"isGridView\", JSON.stringify(isGridView));\n  };\n\n  if (storedSetting) {\n    storedSetting = JSON.parse(storedSetting);\n    if (storedSetting === false) {\n      // set isGridView from the default value of true to match the \"false\" coming from the session store by calling the toggle function\n      queue.enqueue(toAsyncAndWaitAfter(toggleView));\n    }\n  }\n\n  toggleButton.addEventListener(\"click\", toggleView);\n});\n"
  },
  {
    "path": "dashboard/src/app/listeners.js",
    "content": "class Listeners {\n  constructor() {\n    this.abort = new AbortController();\n    registerAbortController(this.abort, this);\n    this.init();\n  }\n\n  async init() {\n    try {\n      const listeners = await fetchData(LISTENERS_ENDPOINT, {\n        signal: this.abort.signal,\n        cache: \"no-store\",\n      });\n      this.displayListeners(listeners);\n    } catch (error) {\n      if (\n        this.pageHiding ||\n        this.abort.signal.aborted ||\n        error?.name === \"AbortError\"\n      ) {\n        console.log(\"Fetching listeners aborted\");\n      } else {\n        console.error(\"Error fetching listeners:\", error);\n        alert(`Error loading listeners: ${error}`);\n      }\n    }\n  }\n\n  displayListeners(data) {\n    const listenersContainer = document.getElementById(\"listeners-container\");\n    const brokerAnonymListenerCnt = document.getElementById(\n      \"broker-anonym-listener-cnt\",\n    );\n    const brokerAllListenerCnt = document.getElementById(\n      \"broker-all-listener-cnt\",\n    );\n    brokerAnonymListenerCnt.innerHTML = \"\";\n    brokerAllListenerCnt.innerHTML = \"\";\n    listenersContainer.innerHTML = \"\";\n\n    if (!data || !data.listeners) {\n      listenersContainer.innerHTML =\n        '<p class=\"text-gray-500\">No listeners available</p>';\n      return;\n    }\n\n    const listeners = data.listeners;\n\n    brokerAllListenerCnt.textContent = listeners.length;\n    const anonymousCount = listeners.filter((l) => l.allow_anonymous).length;\n\n    if (anonymousCount > 0) {\n      const warningText = document.createElement(\"span\");\n      warningText.textContent = anonymousCount;\n\n      const warningBadge = document.createElement(\"span\");\n      warningBadge.className =\n        \"ml-2 px-2 py-1 text-xs font-medium rounded-full\";\n      warningBadge.style.backgroundColor = \"#fee2e2\"; // red-100\n      warningBadge.style.color = \"#dc2626\"; // red-600\n      warningBadge.textContent = \"UNSAFE\";\n\n      brokerAnonymListenerCnt.appendChild(warningText);\n      brokerAnonymListenerCnt.appendChild(warningBadge);\n    } else {\n      brokerAnonymListenerCnt.textContent = \"none\";\n    }\n\n    listeners.forEach((listener, index) => {\n      const listenerCard = this.createListenerCard(listener, index);\n      listenersContainer.appendChild(listenerCard);\n    });\n  }\n\n  createCommandSection(listener, type, addMargin = true) {\n    const commandSection = document.createElement(\"div\");\n    if (addMargin) {\n      commandSection.className = \"mt-4\";\n    }\n\n    const commandHeader = document.createElement(\"div\");\n    commandHeader.style.display = \"flex\";\n    commandHeader.style.alignItems = \"center\";\n    commandHeader.style.justifyContent = \"space-between\";\n    commandHeader.style.marginBottom = \"0.5rem\";\n\n    const commandContainer = document.createElement(\"div\");\n    commandContainer.style.display = \"flex\";\n\n    const copyButton = document.createElement(\"button\");\n    copyButton.className = \"p-2 hover:bg-c-orange transition-colors\";\n    copyButton.style.cursor = \"pointer\";\n    copyButton.style.border = \"0.1px solid #d3d3d3\";\n    copyButton.title = \"Copy\";\n\n    // create an svg copy icon\n    const copyIcon = document.createElementNS(\n      \"http://www.w3.org/2000/svg\",\n      \"svg\",\n    );\n    copyIcon.setAttribute(\"width\", \"16\");\n    copyIcon.setAttribute(\"height\", \"16\");\n    copyIcon.setAttribute(\"viewBox\", \"0 0 24 24\");\n    copyIcon.setAttribute(\"fill\", \"none\");\n    copyIcon.setAttribute(\"stroke\", \"currentColor\");\n    copyIcon.setAttribute(\"stroke-width\", \"2\");\n    copyIcon.style.color = \"#6b7280\"; // gray-500\n\n    copyButton.addEventListener(\"mouseenter\", () => {\n      copyIcon.style.stroke = \"white\";\n      checkIcon.style.stroke = \"white\";\n    });\n\n    copyButton.addEventListener(\"mouseleave\", () => {\n      copyIcon.style.stroke = \"#6b7280\"; // back to gray for copy icon and green for the checkmark\n      checkIcon.style.stroke = \"#10b981\";\n    });\n\n    const copyPath = document.createElementNS(\n      \"http://www.w3.org/2000/svg\",\n      \"path\",\n    );\n    copyPath.setAttribute(\n      \"d\",\n      \"M8 4H6a2 2 0 00-2 2v12a2 2 0 002 2h8a2 2 0 002-2V6a2 2 0 00-2-2h-2m-4-1v1m0 0V2a1 1 0 011-1h2a1 1 0 011 1v1m-4 0h4\",\n    );\n    copyPath.setAttribute(\"stroke-linecap\", \"round\");\n    copyPath.setAttribute(\"stroke-linejoin\", \"round\");\n\n    copyIcon.appendChild(copyPath);\n    copyButton.appendChild(copyIcon);\n\n    // create an svg checkmark icon for the success state after copying\n    const checkIcon = document.createElementNS(\n      \"http://www.w3.org/2000/svg\",\n      \"svg\",\n    );\n    checkIcon.setAttribute(\"width\", \"16\");\n    checkIcon.setAttribute(\"height\", \"16\");\n    checkIcon.setAttribute(\"viewBox\", \"0 0 24 24\");\n    checkIcon.setAttribute(\"fill\", \"none\");\n    checkIcon.setAttribute(\"stroke\", \"currentColor\");\n    checkIcon.setAttribute(\"stroke-width\", \"2\");\n    checkIcon.style.color = \"#10b981\"; // green-500\n    checkIcon.style.display = \"none\";\n\n    const checkPath = document.createElementNS(\n      \"http://www.w3.org/2000/svg\",\n      \"path\",\n    );\n    checkPath.setAttribute(\"d\", \"M5 13l4 4L19 7\");\n    checkPath.setAttribute(\"stroke-linecap\", \"round\");\n    checkPath.setAttribute(\"stroke-linejoin\", \"round\");\n\n    checkIcon.appendChild(checkPath);\n    copyButton.appendChild(checkIcon);\n\n    copyButton.addEventListener(\"click\", async () => {\n      try {\n        const command = this.generateConnectionCommand(listener, type);\n        copyToClipboard(command);\n\n        copyIcon.style.display = \"none\";\n        checkIcon.style.display = \"block\";\n        copyButton.title = \"Copied!\";\n\n        setTimeout(() => {\n          copyIcon.style.display = \"block\";\n          checkIcon.style.display = \"none\";\n          copyButton.title = \"Copy command\";\n        }, 2000);\n      } catch (err) {\n        console.error(\"Error when copying to clipboard:\", err);\n      }\n    });\n\n    const commandText = document.createElement(\"pre\");\n    commandText.className = \"bg-gray-100 p-2 text-sm font-mono\";\n    commandText.style.overflowX = \"auto\";\n    commandText.style.whiteSpace = \"pre-wrap\";\n    commandText.style.width = \"100%\";\n    commandText.style.wordBreak = \"break-all\";\n    commandText.textContent = this.generateConnectionCommand(listener, type);\n\n    commandContainer.appendChild(commandText);\n    commandContainer.appendChild(copyButton);\n    commandSection.appendChild(commandContainer);\n\n    return commandSection;\n  }\n\n  createListenerCard(listener, index) {\n    const card = document.createElement(\"div\");\n    card.className = \"card p-4 border border-gray-200\";\n\n    const title = document.createElement(\"h3\");\n    title.className = \"font-semibold mb-4\";\n    title.textContent = `Listener ${index + 1}`;\n\n    const details = document.createElement(\"div\");\n    details.className = \"grid gap-2 text-sm mb-4\";\n\n    if (listener.port) {\n      details.appendChild(this.createDetailRow(\"Port\", listener.port));\n    }\n    if (listener.path) {\n      details.appendChild(this.createDetailRow(\"Unix Socket\", listener.path));\n    }\n    if (listener.protocol) {\n      details.appendChild(this.createDetailRow(\"Protocol\", listener.protocol));\n    }\n    details.appendChild(\n      this.createDetailRow(\"TLS\", listener.tls ? \"Yes\" : \"No\"),\n    );\n    details.appendChild(\n      this.createDetailRow(\"mTLS\", listener.mtls ? \"Yes\" : \"No\"),\n    );\n    details.appendChild(\n      this.createDetailRow(\n        \"Allow Anonymous\",\n        listener.allow_anonymous ? \"Yes\" : \"No\",\n      ),\n    );\n\n    let commandSectionPub;\n    if (listener.protocol !== \"httpapi\") {\n      commandSectionPub = this.createCommandSection(listener, \"mosquitto_pub\");\n    }\n    card.appendChild(title);\n    card.appendChild(details);\n    commandSectionPub && card.appendChild(commandSectionPub);\n\n    return card;\n  }\n\n  createDetailRow(label, value) {\n    const row = document.createElement(\"div\");\n    row.className = \"flex items-center\";\n\n    const labelSpan = document.createElement(\"span\");\n    labelSpan.className = \"text-gray-500 mr-2\";\n    labelSpan.style.width = \"120px\";\n    labelSpan.style.display = \"inline-block\";\n    labelSpan.textContent = label + \":\";\n\n    const valueSpan = document.createElement(\"span\");\n    valueSpan.className =\n      \"px-3 py-1 text-xs font-medium rounded-full text-center\";\n    valueSpan.style.display = \"inline-block\";\n    valueSpan.style.border = \"0.1px solid #d3d3d3\";\n    valueSpan.textContent = value;\n\n    row.appendChild(labelSpan);\n    row.appendChild(valueSpan);\n\n    return row;\n  }\n  generateConnectionCommand(listener, commandType = \"mosquitto_pub\") {\n    if (listener.protocol === \"httpapi\") {\n      return \"HTTP API Listener - use REST calls instead of mosquitto_pub/sub\";\n    }\n    let command = commandType;\n\n    if (listener.path) {\n      command += ` --unix ${listener.path}`;\n    } else {\n      command += ` -h <host> -p ${listener.port}`;\n    }\n\n    if (listener.mtls) {\n      command += \" --cert <client-crt.pem> --key <client-key.pem>\";\n    }\n\n    if (listener.tls) {\n      command += \" --cafile <ca-crt.pem>\";\n    }\n\n    if (listener.protocol === \"websockets\") {\n      command += \" --ws\";\n    }\n\n    if (!listener.allow_anonymous) {\n      command += \" -u <username> -P <password>\";\n    }\n\n    command += \" -t <topic>\";\n\n    if (commandType === \"mosquitto_pub\") {\n      command += \" -m <message>\";\n    }\n\n    return command;\n  }\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n  new Sidebar();\n  new Listeners();\n  new MosquittoDashboard(true);\n});\n"
  },
  {
    "path": "dashboard/src/app/sidebar.js",
    "content": "class Sidebar {\n  constructor() {\n    this.menuToggle = document.getElementById(\"menu-toggle\");\n    this.menuClose = document.getElementById(\"menu-close\");\n    this.slidingMenu = document.getElementById(\"sliding-menu\");\n    this.menuOverlay = document.getElementById(\"menu-overlay\");\n    this.mainContent = document.getElementById(\"main-content\");\n    this.root = document.documentElement;\n    this.isOpen = sessionStorage.getItem(\"isSidebarOpen\") === \"true\";\n\n    // !isMobile() becase we don't want the sidebar to be preloaded as open on mobile - there is no space anyway\n    if (!isMobile() && this.isOpen) {\n      this.openMenu(); // the initial open of the sidebar is hanlded by the inlined preload script but calling openMenu will properly set hamburger icon to be an arrow icon\n    }\n\n    this.bindEvents();\n  }\n\n  bindEvents() {\n    this.menuToggle.addEventListener(\"click\", () => this.toggleMenu());\n    this.menuClose.addEventListener(\"click\", () => this.closeMenu());\n    this.menuOverlay.addEventListener(\"click\", () => this.closeMenu());\n\n    document.addEventListener(\"keydown\", (e) => {\n      if (e.key === \"Escape\" && this.isOpen) {\n        this.closeMenu();\n      }\n    });\n\n    window.addEventListener(\"resize\", () => {\n      this.syncUi();\n    });\n  }\n\n  toggleMenu() {\n    if (this.isOpen) {\n      this.closeMenu();\n    } else {\n      this.openMenu();\n    }\n  }\n\n  syncUi() {\n    document\n      .getElementById(\"hamburger-icon\")\n      .classList.toggle(\"hidden\", this.isOpen);\n    document\n      .getElementById(\"arrow-icon\")\n      .classList.toggle(\"hidden\", !this.isOpen);\n\n    const showOverlay = this.isOpen && isMobile();\n    document.body.classList.toggle(\"sidebar-lock-scroll\", showOverlay);\n  }\n\n  openMenu() {\n    this.isOpen = true;\n    sessionStorage.setItem(\"isSidebarOpen\", \"true\");\n    this.root.classList.add(\"sidebar-open\");\n    this.syncUi();\n  }\n\n  closeMenu() {\n    this.isOpen = false;\n    sessionStorage.setItem(\"isSidebarOpen\", \"false\");\n    this.root.classList.remove(\"sidebar-open\");\n    this.syncUi();\n  }\n}\n"
  },
  {
    "path": "dashboard/src/css/styles.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n.bg-c-orange {\n  background-color: #fd602e;\n}\n\n.hover\\:bg-c-orange:hover {\n  background-color: #fd602e;\n}\n\n#menu-overlay {\n  transition: opacity 300ms;\n  opacity: 0;\n  pointer-events: none;\n  background: rgba(0,0,0,0.45);\n}\n\n@media (min-width: 1024px) {\n    #menu-overlay {\n        display: none;\n    }\n    #sliding-menu {\n        box-shadow: none;\n    }\n    #menu-close {\n        visibility: hidden;\n    }\n    #broker-info-icon {\n        display: block;\n    }\n}\n\n#sliding-menu {\n    transform: translateX(-100%);\n    transition: transform 300ms;\n}\n.sidebar-open #sliding-menu { transform: translateX(0); }\n\n@media (max-width: 1023px) {\n    .sidebar-open #menu-overlay {\n        opacity: 1;\n        pointer-events: auto;\n    }\n}\n\n@media (min-width: 1024px) {\n    .sidebar-open #main-content {\n      margin-left: 320px;\n    }\n}\n/* lock scroll on mobile when sidebar open */\n.sidebar-lock-scroll {\n    overflow: hidden;\n}\n\n.sidebar-preload #sliding-menu {\n    transition: none;\n}\n\n@media (max-width: 1024px) {\n    #layout-toggle {\n        display: none;\n    }\n}\n\n@media (max-width: 375px) {\n    #logo-icon {\n        display: block;\n    }\n\n    #logo-text {\n        display: none\n    }\n}\n\n.broker-active {\n    background-color: rgb(34 197 94); /* green-500 */\n}\n\n.broker-inactive {\n    background-color: rgb(239 68 68); /* red-500 */\n}\n\n@layer components {\n    .card {\n        @apply bg-white rounded-lg border border-gray-200 shadow-sm;\n    }\n    .card-header {\n        @apply px-6 py-4 border-b border-gray-200;\n    }\n    .card-content {\n        @apply px-6 py-4;\n    }\n    .metric-value {\n        @apply text-3xl font-bold text-gray-900;\n    }\n    .metric-label {\n        @apply text-sm font-medium text-gray-500 mb-2;\n    }\n    .status-dot {\n        @apply w-3 h-3 rounded-full;\n    }\n    .chart-container {\n        position: relative;\n        height: 200px;\n        width: 100%;\n    }\n    .nav-btn {\n        @apply inline-flex items-center justify-center w-8 h-8 text-gray-500 bg-white border border-gray-300 rounded hover:bg-gray-50 hover:text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 transition-colors duration-150 disabled:opacity-50 disabled:cursor-not-allowed;\n    }\n    .nav-btn:hover:not(:disabled) {\n        @apply shadow-sm;\n    }\n    .nav-btn.active {\n        @apply bg-blue-50 border-blue-300 text-blue-600;\n    }\n    .nav-separator {\n        @apply w-px h-6 bg-gray-300 mx-2;\n    }\n    .nav-btn svg {\n        pointer-events: none;\n    }\n}\n"
  },
  {
    "path": "dashboard/src/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Mosquitto Broker Dashboard</title>\n    <link rel=\"stylesheet\" href=\"tailwind/styles.css\">\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"media/favicon-16x16.png\" sizes=\"16x16\">\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"media/favicon-32x32.png\" sizes=\"32x32\">\n</head>\n<body class=\"min-h-screen bg-gray-50\">\n    <div id=\"menu-overlay\" class=\"fixed inset-0 z-40 opacity-0 transition-opacity duration-300\"></div>\n\n    <!-- sliding menu panel -->\n    <div id=\"sliding-menu\" class=\"fixed top-0 left-0 h-full w-80 bg-white shadow-2xl transform -translate-x-full transition-transform duration-300 ease-in-out z-50\">\n        <div class=\"flex flex-col h-full\">\n            <!-- menu header with close button (displayed only for mobile) -->\n            <div class=\"flex items-center justify-between p-6 border-b border-gray-200\">\n                <div></div>\n                <button id=\"menu-close\" class=\"p-2 hover:bg-gray-100 rounded-md transition-colors duration-200\">\n                    <svg class=\"h-5 w-5 text-gray-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n                    </svg>\n                </button>\n            </div>\n\n            <!-- menu content -->\n            <div class=\"flex-1 overflow-y-auto p-6\">\n                <nav class=\"space-y-1 mb-8\">\n                    <a href=\".\" class=\"flex items-center gap-3 px-3 py-2 text-gray-700 hover:bg-gray-100 rounded-md transition-colors bg-gray-100\">\n                        Dashboard\n                    </a>\n                    <a href=\"listeners.html\" class=\"flex items-center gap-3 px-3 py-2 text-gray-700 hover:bg-gray-100 rounded-md transition-colors\">\n                        Listeners\n                    </a>\n                    <a href=\"https://mosquitto.org/documentation\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"flex items-center gap-3 px-3 py-2 text-gray-700 hover:bg-gray-100 rounded-md transition-colors\">\n                        Docs\n                    </a>\n                </nav>\n            </div>\n\n            <!-- menu footer -->\n            <div class=\"border-t border-gray-200 p-6\">\n                <a href=\"https://cedalo.com/link/go-pro-from-mosquitto\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"block w-full text-center bg-c-orange text-white py-2 px-4 rounded-md hover:bg-orange-700 transition-colors duration-200\">\n                    Go Pro\n                </a>\n            </div>\n        </div>\n    </div>\n    <script>\n        // script that immidiately sets siderbar into the open state (if it was open on the previous page or before refresh) and skips the animation\n        // inline to execute the script faster\n        const isSidebarOpen = sessionStorage.getItem(\"isSidebarOpen\") === \"true\";\n        const isDesktop = window.matchMedia(\"(min-width: 1024px)\").matches;\n        const openSidebar = isSidebarOpen && isDesktop;\n\n        if (openSidebar) {\n            document.documentElement.classList.add(\"sidebar-open\", \"sidebar-preload\");\n        }\n        // remove sidebar-preload class after at least one paint so that it does not block animations when we press on the \"menu\" button\n        requestAnimationFrame(() => { // rAF runs before next paint. use double rAF pattern to make sure there was a page paint (styles were applied) before removing sidebar-preload. In paractice, however, a single rAF seems to also be enought. We still leave two for safety\n            requestAnimationFrame(() => {\n              document.documentElement.classList.remove(\"sidebar-preload\");\n            });\n        });\n    </script>\n    <div id=\"main-content\" class=\"transition-all duration-300 ease-in-out\">\n        <div class=\"p-6\">\n            <div class=\"max-w-7xl mx-auto space-y-6\">\n                <!-- header -->\n                <div class=\"sticky bg-gray-50 top-0 z-10 pb-2 flex items-center justify-between\">\n                    <button id=\"menu-toggle\" class=\"inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-c-orange hover:cursor-pointer hover:text-white transition-colors duration-200\">\n                        <!-- menu icon -->\n                        <svg id=\"hamburger-icon\" class=\"h-4 w-4 transition-opacity duration-200\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"></path>\n                        </svg>\n\n                        <!-- arrow icon in place of the menu icon when the menu is open (hidden by default) -->\n                        <svg id=\"arrow-icon\" class=\"h-4 w-4 hidden transition-opacity duration-200\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"></path>\n                        </svg>\n                    </button>\n                    <div id=\"logo-text\" class=\"flex items-center gap-3 w-64\">\n                        <img src=\"media/mosquitto-logo.png\"/>\n                    </div>\n\n                    <div id=\"logo-icon\" class=\"hidden\">\n                        <svg width=\"71\" height=\"54\" viewBox=\"0 0 114 86\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n                            <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M57.0013 86L58.5908 64.5156L59.8811 47.0706C62.5402 45.9585 64.4064 43.3537 64.4064 40.3183C64.4064 36.2725 61.0902 32.9916 57.0013 32.9916C52.9121 32.9916 49.5962 36.2725 49.5962 40.3183C49.5962 43.3537 51.4625 45.9585 54.1216 47.0706L55.412 64.5156L57.0013 86Z\" fill=\"#F3771C\"/>\n                            <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M6.42676 40.291C6.42676 52.6852 11.0068 64.2874 18.9495 73.2527L14.1625 77.4988C5.1991 67.4059 0 54.2981 0 40.291C0 24.5083 6.55628 10.2381 17.1193 7.62939e-06L17.3602 0.213791L31.52 12.7736C18.2121 24.8448 15.456 44.4755 24.7152 59.5987L29.6037 55.2628C22.5455 42.6463 25.2344 26.7106 36.3333 17.0429L41.1815 21.3426L45.4936 25.168L49.9667 29.1355C48.2936 30.1731 46.8715 31.5698 45.8107 33.2165C44.4992 35.2519 43.7396 37.6684 43.7396 40.2597C43.7396 46.037 47.5151 50.9431 52.7571 52.6942L53.2087 58.7914C44.4739 57.0506 37.8941 49.4142 37.8941 40.2597C37.8941 36.1833 39.1989 32.408 41.4175 29.3198L37.1379 25.5231L37.1219 25.509C30.1062 34.73 30.5396 47.5312 38.0944 56.2719L23.7361 69.0071C9.60061 52.9899 9.19696 29.2169 22.7358 12.7492L17.9465 8.50084C11.336 16.442 7.14974 26.4328 6.51204 37.3633L6.48277 37.3959L6.50886 37.4186C6.45429 38.3692 6.42676 39.3268 6.42676 40.2912V40.291ZM61.2429 52.6942C66.4849 50.9431 70.2608 46.037 70.2608 40.2597C70.2608 37.6684 69.5008 35.2519 68.1898 33.217C67.1285 31.5703 65.7064 30.1731 64.0342 29.1355L68.5069 25.1683L72.8194 21.3426H72.8189L77.6672 17.0429C88.7656 26.7106 91.455 42.6463 84.3968 55.2628L89.2848 59.5987C98.5445 44.4755 95.7883 24.8448 82.48 12.7736L96.6408 0.212845L96.8807 7.62939e-06C107.444 10.2381 114 24.5083 114 40.291C114 54.2981 108.801 67.4059 99.838 77.4988L95.0505 73.2527C102.994 64.2874 107.573 52.6852 107.573 40.291C107.573 39.3266 107.546 38.369 107.492 37.4184L107.517 37.3958L107.488 37.3632C106.851 26.4326 102.664 16.4419 96.054 8.50068L91.2647 12.7491C104.804 29.2167 104.4 52.9897 90.2639 69.007L75.9061 56.2718C83.4609 47.531 83.8943 34.7299 76.8784 25.5088L76.8621 25.523L72.5825 29.3202C74.8011 32.4078 76.1059 36.1836 76.1059 40.2596C76.1059 49.414 69.5261 57.05 60.7918 58.7913L61.2429 52.6941V52.6942Z\" fill=\"#3C5280\"/>\n                        </svg>\n                    </div>\n                </div>\n\n                <div class=\"p-4\"></div>\n                <!-- broker information -->\n                <div class=\"card flex items-top gap-3 p-6\">\n                    <div id=\"broker-info-icon\" class=\"mr-[3%] hidden\">\n                        <svg width=\"114\" height=\"86\" viewBox=\"0 0 114 86\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n                            <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M57.0013 86L58.5908 64.5156L59.8811 47.0706C62.5402 45.9585 64.4064 43.3537 64.4064 40.3183C64.4064 36.2725 61.0902 32.9916 57.0013 32.9916C52.9121 32.9916 49.5962 36.2725 49.5962 40.3183C49.5962 43.3537 51.4625 45.9585 54.1216 47.0706L55.412 64.5156L57.0013 86Z\" fill=\"#F3771C\"/>\n                            <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M6.42676 40.291C6.42676 52.6852 11.0068 64.2874 18.9495 73.2527L14.1625 77.4988C5.1991 67.4059 0 54.2981 0 40.291C0 24.5083 6.55628 10.2381 17.1193 7.62939e-06L17.3602 0.213791L31.52 12.7736C18.2121 24.8448 15.456 44.4755 24.7152 59.5987L29.6037 55.2628C22.5455 42.6463 25.2344 26.7106 36.3333 17.0429L41.1815 21.3426L45.4936 25.168L49.9667 29.1355C48.2936 30.1731 46.8715 31.5698 45.8107 33.2165C44.4992 35.2519 43.7396 37.6684 43.7396 40.2597C43.7396 46.037 47.5151 50.9431 52.7571 52.6942L53.2087 58.7914C44.4739 57.0506 37.8941 49.4142 37.8941 40.2597C37.8941 36.1833 39.1989 32.408 41.4175 29.3198L37.1379 25.5231L37.1219 25.509C30.1062 34.73 30.5396 47.5312 38.0944 56.2719L23.7361 69.0071C9.60061 52.9899 9.19696 29.2169 22.7358 12.7492L17.9465 8.50084C11.336 16.442 7.14974 26.4328 6.51204 37.3633L6.48277 37.3959L6.50886 37.4186C6.45429 38.3692 6.42676 39.3268 6.42676 40.2912V40.291ZM61.2429 52.6942C66.4849 50.9431 70.2608 46.037 70.2608 40.2597C70.2608 37.6684 69.5008 35.2519 68.1898 33.217C67.1285 31.5703 65.7064 30.1731 64.0342 29.1355L68.5069 25.1683L72.8194 21.3426H72.8189L77.6672 17.0429C88.7656 26.7106 91.455 42.6463 84.3968 55.2628L89.2848 59.5987C98.5445 44.4755 95.7883 24.8448 82.48 12.7736L96.6408 0.212845L96.8807 7.62939e-06C107.444 10.2381 114 24.5083 114 40.291C114 54.2981 108.801 67.4059 99.838 77.4988L95.0505 73.2527C102.994 64.2874 107.573 52.6852 107.573 40.291C107.573 39.3266 107.546 38.369 107.492 37.4184L107.517 37.3958L107.488 37.3632C106.851 26.4326 102.664 16.4419 96.054 8.50068L91.2647 12.7491C104.804 29.2167 104.4 52.9897 90.2639 69.007L75.9061 56.2718C83.4609 47.531 83.8943 34.7299 76.8784 25.5088L76.8621 25.523L72.5825 29.3202C74.8011 32.4078 76.1059 36.1836 76.1059 40.2596C76.1059 49.414 69.5261 57.05 60.7918 58.7913L61.2429 52.6941V52.6942Z\" fill=\"#3C5280\"/>\n                        </svg>\n                    </div>\n                    <div>\n                        <div>\n                            <h2 class=\"text-lg font-semibold\">Broker Information</h2>\n                        </div>\n                        <div>\n                            <div class=\"grid grid-rows-3 gap-1 text-sm\">\n                                <div>\n                                    <span class=\"text-gray-500\">version:</span>\n                                    <span class=\"ml-2\" id=\"broker-version\"></span>\n                                </div>\n                                <div>\n                                    <span class=\"text-gray-500\">uptime:</span>\n                                    <span class=\"ml-2\" id=\"broker-uptime\">?</span>\n                                </div>\n                                <div>\n                                    <span class=\"text-gray-500\">status:</span>\n                                    <div class=\"inline-block w-2 h-2 bg-gray-500 rounded-full ml-2 mr-2\" id=\"broker-status\"></div> <!-- broker active/inactive status dot. depends on the class set from js code (broker-active vs broker-inactive) -->\n                                    <span class=\"text-gray-700\" id=\"broker-status-text\"></span> <!-- broker status text, set from js-->\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n\n                <!-- metrics grid -->\n                <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 gap-4\">\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">clients online</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    Number of currently connected clients. Topic: <code>$SYS/broker/clients/connected</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">clients online</div>\n                            <div class=\"metric-value\" id=\"clients-connected\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">clients total</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    The total number of online and offline client sessions currently connected and registered on the broker. Topic: <code>$SYS/broker/clients/total</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">clients total</div>\n                            <div class=\"metric-value\" id=\"clients-total\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">clients expired</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    The number of disconnected persistent clients that have been expired and removed through the <code>persistent_client_expiration</code> option. Topic: <code>$SYS/broker/clients/expired</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">clients expired</div>\n                            <div class=\"metric-value\" id=\"clients-expired\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">clients offline</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    The total number of persistent client sessions (with clean session disabled) that are registered at the broker but are currently disconnected. Topic: <code>$SYS/broker/clients/disconnected</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">clients offline</div>\n                            <div class=\"metric-value\" id=\"clients-disconnected\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">max simultaneous clients</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    The maximum number of clients that have been connected to the broker at the same time. Topic: <code>$SYS/broker/clients/maximum</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">max simultaneous clients</div>\n                            <div class=\"metric-value\" id=\"clients-maximum\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">socket connections</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    Number of open socket connections on the broker. Topic: <code>$SYS/broker/connections/socket/count</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">connection sockets</div>\n                            <div class=\"metric-value\" id=\"connection-sockets\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">total subscriptions</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    The total number of subscriptions active on the broker. Topic: <code>$SYS/broker/subscriptions/count</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">total subscriptions</div>\n                            <div class=\"metric-value\" id=\"total-subscriptions\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">messages published to the broker</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    The total number of PUBLISH messages received since the broker started. Topic: <code>$SYS/broker/publish/messages/received</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">messages published to the broker</div>\n                            <div class=\"metric-value\" id=\"messages-published-to-broker\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">messages published by the broker</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    The total number of PUBLISH messages sent since the broker started. Topic: <code>$SYS/broker/publish/messages/sent</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">messages published by the broker</div>\n                            <div class=\"metric-value\" id=\"messages-published-by-broker\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">publishes dropped</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    The total number of publish messages that have been dropped due to inflight/queuing limits. See the <code>max_inflight_messages</code> and <code>max_queued_messages</code> options in mosquitto.conf for more information. Topic: <code>$SYS/broker/publish/messages/dropped</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">publishes dropped</div>\n                            <div class=\"metric-value\" id=\"messages-dropped\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">messages retained</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    The total number of retained messages active on the broker. Topic: <code>$SYS/broker/retained messages/count</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">messages retained</div>\n                            <div class=\"metric-value\" id=\"messages-retained\">?</div>\n                        </div>\n                    </div>\n                    <div class=\"card relative\">\n                        <details class=\"group absolute top-2 right-2\">\n                            <summary\n                              class=\"flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 bg-white/80\n                                     hover:bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer\"\n                              aria-label=\"more info\"\n                            >\n                                <!-- info icon -->\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" width=\"30\" height=\"30\">\n                                    <!-- Outer circle -->\n                                    <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"none\" stroke=\"rgb(107, 114, 128, 0.8)\" stroke-width=\"16\" stroke-linecap=\"round\"/>\n                                    <!-- Dot on top of the \"i\" -->\n                                    <circle cx=\"100\" cy=\"65\" r=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                    <!-- Vertical line of the \"i\" -->\n                                    <rect x=\"88\" y=\"90\" width=\"24\" height=\"55\" rx=\"12\" fill=\"rgb(107, 114, 128, 0.8)\"/>\n                                </svg>\n                            </summary>\n\n                            <!-- popup panel -->\n                            <div class=\"group-open:block hidden absolute right-0 mt-2 w-64 rounded-xl border border-gray-200 bg-white p-3 text-sm shadow-lg z-10\">\n                                <div class=\"font-medium mb-1\">messages stored</div>\n                                <p class=\"text-gray-600 break-words\">\n                                    The number of messages currently held in the message store. This includes retained messages and messages queued for durable clients. Topic: <code>$SYS/broker/store/messages/count</code>\n                                </p>\n                            </div>\n                        </details>\n\n                        <div class=\"card-content\">\n                            <div class=\"metric-label\">messages stored</div>\n                            <div class=\"metric-value\" id=\"messages-stored\">?</div>\n                        </div>\n                    </div>\n                </div>\n\n                <!-- chart data type (smoothed or raw data) and grid layout toggles -->\n                <div class=\"mb-4 flex justify-end\">\n                    <button id=\"chart-data-type-global-toggle\" class=\"mr-2 inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 transition-colors duration-200\">\n                        <!-- for when we are on raw data state -->\n                        <svg id=\"smooth-state-svg\" class=\"h-4 w-4 mr-2\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12c2-4 4-4 6 0s4 4 6 0 4-4 6 0\"></path>\n                        </svg>\n                        <!-- for when we are on smoothed data state -->\n                        <svg id=\"raw-state-svg\" class=\"h-4 w-4 mr-2 hidden\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 15l2-3 2 3 2-6 2 4 2-2 2 1 2-3 2 2 3-1\"></path>\n                        </svg>\n                        <!-- default text: \"Smoothed data\" assumes we are on a raw data state, so we display the opposite state on a toggle -->\n                        <span id=\"chart-data-type-text\">Show Smoothed Data</span>\n                    </button>\n                    <button id=\"layout-toggle\" class=\"inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 transition-colors duration-200\">\n                        <svg class=\"h-4 w-4 mr-2\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z\"></path>\n                        </svg>\n                        <span id=\"layout-text\">Single Column</span>\n                    </button>\n                </div>\n\n                <!-- charts grid -->\n                <div id=\"charts-grid\" class=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">\n                    <div id=\"banner-card\" class=\"card\">\n                        <div id=\"banner-container\" class=\"h-[335px] overflow-hidden flex\">\n                            <div id=\"banner-inner\" class=\"h-full w-full flex items-center justify-center\">\n                                <a id=\"banner-link\" href=\"https://mosquitto.org/banner/link\" target=\"_blank\" rel=\"noopener noreferrer\">\n                                    <img id=\"banner-img\" src=\"media/banner.svg\" alt=\"banner\">\n                                </a>\n                            </div>\n                        </div>\n                    </div>\n\n                    <div class=\"card\">\n                        <div class=\"card-header\">\n                            <div class=\"metric-label\">messages dropped</div>\n                        </div>\n                        <div class=\"card-content\">\n                            <div class=\"chart-container\">\n                                <canvas id=\"chart-messages-dropped\"></canvas>\n                            </div>\n                        </div>\n\n                        <!-- chart navigation -->\n                        <div class=\"flex items-center justify-start\">\n                            <div class=\"inline-flex items-center bg-gray-50 rounded-lg p-1 border border-gray-200\">\n                                <!-- zoom controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-dropped\" data-action=\"zoom-in\" title=\"Zoom In\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-dropped\" data-action=\"zoom-out\" title=\"Zoom Out\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- pan controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-dropped\" data-action=\"pan-left\" title=\"Pan Left\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-dropped\" data-action=\"pan-right\" title=\"Pan Right\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- reset -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-dropped\" data-action=\"reset\" title=\"Reset View\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\"/>\n                                    </svg>\n                                </button>\n                            </div>\n                        </div>\n                    </div>\n\n                    <!-- mqtt clients charts -->\n                    <div class=\"card\">\n                        <div class=\"card-header\">\n                            <div class=\"metric-label\">online clients</div>\n                        </div>\n                        <div class=\"card-content\">\n                            <div class=\"chart-container\">\n                                <canvas id=\"chart-clients-connected\"></canvas>\n                            </div>\n                        </div>\n\n                        <!-- chart navigation -->\n                        <div class=\"flex items-center justify-start\">\n                            <div class=\"inline-flex items-center bg-gray-50 rounded-lg p-1 border border-gray-200\">\n                                <!-- zoom controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-clients-connected\" data-action=\"zoom-in\" title=\"Zoom In\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-clients-connected\" data-action=\"zoom-out\" title=\"Zoom Out\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- pan controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-clients-connected\" data-action=\"pan-left\" title=\"Pan Left\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-clients-connected\" data-action=\"pan-right\" title=\"Pan Right\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- reset -->\n                                <button class=\"nav-btn\" data-chart=\"chart-clients-connected\" data-action=\"reset\" title=\"Reset View\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\"/>\n                                    </svg>\n                                </button>\n                            </div>\n                        </div>\n                    </div>\n\n                    <div class=\"card\">\n                        <div class=\"card-header\">\n                            <div class=\"metric-label\">offline clients</div>\n                        </div>\n                        <div class=\"card-content\">\n                            <div class=\"chart-container\">\n                                <canvas id=\"chart-clients-disconnected\"></canvas>\n                            </div>\n                        </div>\n\n                        <!-- chart navigation -->\n                        <div class=\"flex items-center justify-start\">\n                            <div class=\"inline-flex items-center bg-gray-50 rounded-lg p-1 border border-gray-200\">\n                                <!-- zoom controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-clients-disconnected\" data-action=\"zoom-in\" title=\"Zoom In\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-clients-disconnected\" data-action=\"zoom-out\" title=\"Zoom Out\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- pan controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-clients-disconnected\" data-action=\"pan-left\" title=\"Pan Left\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-clients-disconnected\" data-action=\"pan-right\" title=\"Pan Right\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- reset -->\n                                <button class=\"nav-btn\" data-chart=\"chart-clients-disconnected\" data-action=\"reset\" title=\"Reset View\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\"/>\n                                    </svg>\n                                </button>\n                            </div>\n                        </div>\n                    </div>\n\n                    <div class=\"card\">\n                        <div class=\"card-header\">\n                            <div class=\"metric-label\">messages sent</div>\n                        </div>\n                        <div class=\"card-content\">\n                            <div class=\"chart-container\">\n                                <canvas id=\"chart-messages-sent\"></canvas>\n                            </div>\n                        </div>\n\n                        <!-- chart navigation -->\n                        <div class=\"flex items-center justify-start\">\n                            <div class=\"inline-flex items-center bg-gray-50 rounded-lg p-1 border border-gray-200\">\n                                <!-- zoom controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-sent\" data-action=\"zoom-in\" title=\"Zoom In\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-sent\" data-action=\"zoom-out\" title=\"Zoom Out\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- pan controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-sent\" data-action=\"pan-left\" title=\"Pan Left\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-sent\" data-action=\"pan-right\" title=\"Pan Right\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- reset -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-sent\" data-action=\"reset\" title=\"Reset View\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\"/>\n                                    </svg>\n                                </button>\n                            </div>\n                        </div>\n                    </div>\n\n                    <div class=\"card\">\n                        <div class=\"card-header\">\n                            <div class=\"metric-label\">messages received</div>\n                        </div>\n                        <div class=\"card-content\">\n                            <div class=\"chart-container\">\n                                <canvas id=\"chart-messages-received\"></canvas>\n                            </div>\n                        </div>\n\n                        <!-- chart navigation -->\n                        <div class=\"flex items-center justify-start\">\n                            <div class=\"inline-flex items-center bg-gray-50 rounded-lg p-1 border border-gray-200\">\n                                <!-- zoom controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-received\" data-action=\"zoom-in\" title=\"Zoom In\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-received\" data-action=\"zoom-out\" title=\"Zoom Out\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- pan controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-received\" data-action=\"pan-left\" title=\"Pan Left\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-received\" data-action=\"pan-right\" title=\"Pan Right\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- reset -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-received\" data-action=\"reset\" title=\"Reset View\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\"/>\n                                    </svg>\n                                </button>\n                            </div>\n                        </div>\n                    </div>\n\n                    <div class=\"card\">\n                        <div class=\"card-header\">\n                            <div class=\"metric-label\">messages sent per minute</div>\n                        </div>\n                        <div class=\"card-content\">\n                            <div class=\"chart-container\">\n                                <canvas id=\"chart-messages-sent-rate\"></canvas>\n                            </div>\n                        </div>\n\n                        <!-- chart navigation -->\n                        <div class=\"flex items-center justify-start\">\n                            <div class=\"inline-flex items-center bg-gray-50 rounded-lg p-1 border border-gray-200\">\n                                <!-- zoom controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-sent-rate\" data-action=\"zoom-in\" title=\"Zoom In\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-sent-rate\" data-action=\"zoom-out\" title=\"Zoom Out\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- pan controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-sent-rate\" data-action=\"pan-left\" title=\"Pan Left\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-sent-rate\" data-action=\"pan-right\" title=\"Pan Right\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- reset -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-sent-rate\" data-action=\"reset\" title=\"Reset View\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\"/>\n                                    </svg>\n                                </button>\n                            </div>\n                        </div>\n                    </div>\n\n                    <div class=\"card\">\n                        <div class=\"card-header\">\n                            <div class=\"metric-label\">messages received per minute</div>\n                        </div>\n                        <div class=\"card-content\">\n                            <div class=\"chart-container\">\n                                <canvas id=\"chart-messages-received-rate\"></canvas>\n                            </div>\n                        </div>\n\n                        <!-- chart navigation -->\n                        <div class=\"flex items-center justify-start\">\n                            <div class=\"inline-flex items-center bg-gray-50 rounded-lg p-1 border border-gray-200\">\n                                <!-- zoom controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-received-rate\" data-action=\"zoom-in\" title=\"Zoom In\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-received-rate\" data-action=\"zoom-out\" title=\"Zoom Out\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- pan controls -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-received-rate\" data-action=\"pan-left\" title=\"Pan Left\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"/>\n                                    </svg>\n                                </button>\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-received-rate\" data-action=\"pan-right\" title=\"Pan Right\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"/>\n                                    </svg>\n                                </button>\n\n                                <div class=\"nav-separator\"></div>\n\n                                <!-- reset -->\n                                <button class=\"nav-btn\" data-chart=\"chart-messages-received-rate\" data-action=\"reset\" title=\"Reset View\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\"/>\n                                    </svg>\n                                </button>\n                            </div>\n                        </div>\n                    </div>\n\n                </div>\n\n                <!-- total messages overview chart -->\n                <div class=\"card\">\n                    <div class=\"card-header\">\n                        <h3 class=\"text-base font-semibold\">Published vs Received Messages Total</h3>\n                    </div>\n                    <div class=\"card-content\">\n                        <div style=\"position: relative; height: 300px;\">\n                            <canvas id=\"chart-message-overview\"></canvas>\n                        </div>\n                    </div>\n\n                    <!-- chart navigation -->\n                    <div class=\"flex items-center justify-start\">\n                        <div class=\"inline-flex items-center bg-gray-50 rounded-lg p-1 border border-gray-200\">\n                            <!-- zoom controls -->\n                            <button class=\"nav-btn\" data-chart=\"chart-message-overview\" data-action=\"zoom-in\" title=\"Zoom In\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"/>\n                                </svg>\n                            </button>\n                            <button class=\"nav-btn\" data-chart=\"chart-message-overview\" data-action=\"zoom-out\" title=\"Zoom Out\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7\"/>\n                                </svg>\n                            </button>\n\n                            <div class=\"nav-separator\"></div>\n\n                            <!-- pan controls -->\n                            <button class=\"nav-btn\" data-chart=\"chart-message-overview\" data-action=\"pan-left\" title=\"Pan Left\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"/>\n                                </svg>\n                            </button>\n                            <button class=\"nav-btn\" data-chart=\"chart-message-overview\" data-action=\"pan-right\" title=\"Pan Right\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"/>\n                                </svg>\n                            </button>\n\n                            <div class=\"nav-separator\"></div>\n\n                            <!-- reset -->\n                            <button class=\"nav-btn\" data-chart=\"chart-message-overview\" data-action=\"reset\" title=\"Reset View\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\"/>\n                                </svg>\n                            </button>\n                        </div>\n                    </div>\n                </div>\n\n                <!-- message rate overview chart -->\n                <div class=\"card\">\n                    <div class=\"card-header\">\n                        <h3 class=\"text-base font-semibold\">Publish vs Received Messages Rates</h3>\n                    </div>\n                    <div class=\"card-content\">\n                        <div style=\"position: relative; height: 300px;\">\n                            <canvas id=\"chart-message-rate-overview\"></canvas>\n                        </div>\n                    </div>\n\n                    <!-- chart navigation -->\n                    <div class=\"flex items-center justify-start\">\n                        <div class=\"inline-flex items-center bg-gray-50 rounded-lg p-1 border border-gray-200\">\n                            <!-- zoom controls -->\n                            <button class=\"nav-btn\" data-chart=\"chart-message-rate-overview\" data-action=\"zoom-in\" title=\"Zoom In\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"/>\n                                </svg>\n                            </button>\n                            <button class=\"nav-btn\" data-chart=\"chart-message-rate-overview\" data-action=\"zoom-out\" title=\"Zoom Out\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7\"/>\n                                </svg>\n                            </button>\n\n                            <div class=\"nav-separator\"></div>\n\n                            <!-- pan controls -->\n                            <button class=\"nav-btn\" data-chart=\"chart-message-rate-overview\" data-action=\"pan-left\" title=\"Pan Left\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"/>\n                                </svg>\n                            </button>\n                            <button class=\"nav-btn\" data-chart=\"chart-message-rate-overview\" data-action=\"pan-right\" title=\"Pan Right\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"/>\n                                </svg>\n                            </button>\n\n                            <div class=\"nav-separator\"></div>\n\n                            <!-- reset -->\n                            <button class=\"nav-btn\" data-chart=\"chart-message-rate-overview\" data-action=\"reset\" title=\"Reset View\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\"/>\n                                </svg>\n                            </button>\n                        </div>\n                    </div>\n                </div>\n\n                <!-- system metrics -->\n                <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n\n                    <div class=\"card\">\n                        <div class=\"card-header\">\n                            <h3 class=\"text-base font-semibold\">System Metrics</h3>\n                        </div>\n                        <div class=\"card-content overflow-x-auto\">\n                            <div class=\"inline-block min-w-max space-y-1 text-sm font-mono text-gray-700 whitespace-nowrap\" id=\"system-metrics\">\n                                <div>$SYS/broker/clients/total <span id=\"systopic-clients-total\">?</span></div>\n                                <div>$SYS/broker/clients/disconnected <span id=\"systopic-clients-disconnected\">?</span></div>\n                                <div>$SYS/broker/clients/connected <span id=\"systopic-clients-connected\">?</span></div>\n                                <div>$SYS/broker/clients/maximum <span id=\"systopic-clients-max\">?</span></div>\n                                <div>$SYS/broker/clients/expired <span id=\"systopic-clients-expired\">?</span></div>\n                                <div>$SYS/broker/subscriptions/count <span id=\"systopic-total-subscriptions\">?</span></div>\n                                <div>$SYS/broker/shared_subscriptions/count <span id=\"systopic-total-shared-subscriptions\">?</span></div>\n                                <div>$SYS/broker/heap/current <span id=\"systopic-heap-current\">?</span></div>\n                                <div>$SYS/broker/heap/maximum <span id=\"systopic-heap-max\">?</span></div>\n                                <div>$SYS/broker/connections/socket/count <span id=\"systopic-connection-sockets\">?</span></div>\n                            </div>\n                        </div>\n                    </div>\n\n                    <div class=\"card\">\n                        <div class=\"card-header\">\n                            <h3 class=\"text-base font-semibold\">Traffic Metrics</h3>\n                        </div>\n                        <div class=\"card-content overflow-x-auto\">\n                            <div class=\"inline-block min-w-max space-y-1 text-sm font-mono text-gray-700 whitespace-nowrap pr-6\" id=\"message-metrics\">\n                                <div>$SYS/broker/load/messages/received/1min <span id=\"systopic-messages-received-1min\">?</span></div>\n                                <div>$SYS/broker/load/messages/received/10min <span id=\"systopic-messages-received-10min\">?</span></div>\n                                <div>$SYS/broker/load/messages/received/15min <span id=\"systopic-messages-received-15min\">?</span></div>\n                                <div>$SYS/broker/load/messages/sent/1min <span id=\"systopic-messages-sent-1min\">?</span></div>\n                                <div>$SYS/broker/load/messages/sent/10min <span id=\"systopic-messages-sent-10min\">?</span></div>\n                                <div>$SYS/broker/load/messages/sent/15min <span id=\"systopic-messages-sent-15min\">?</span></div>\n                                <div>$SYS/broker/messages/stored <span id=\"systopic-messages-stored\">?</span></div>\n                                <div>$SYS/broker/retained messages/count <span id=\"systopic-messages-retained\">?</span></div>\n                                <div>$SYS/broker/messages/received <span id=\"systopic-messages-received\">?</span></div>\n                                <div>$SYS/broker/messages/sent <span id=\"systopic-messages-sent\">?</span></div>\n                                <div>$SYS/broker/store/messages/bytes <span id=\"systopic-messages-stored-bytes\">?</span></div>\n                                <div>$SYS/broker/bytes/received <span id=\"systopic-received-bytes\">?</span></div>\n                                <div>$SYS/broker/bytes/sent <span id=\"systopic-sent-bytes\">?</span></div>\n                                <div>$SYS/broker/publish/bytes/received <span id=\"systopic-publish-received-bytes\">?</span></div>\n                                <div>$SYS/broker/publish/bytes/sent <span id=\"systopic-publish-sent-bytes\">?</span></div>\n                                <div>$SYS/broker/publish/messages/dropped <span id=\"systopic-messages-dropped\">?</span></div>\n                                <div>$SYS/broker/publish/messages/received <span id=\"systopic-messages-published-to-broker\">?</span></div>\n                                <div>$SYS/broker/publish/messages/sent <span id=\"systopic-messages-published-by-broker\">?</span></div>\n                                <div>$SYS/broker/packet/out/count <span id=\"systopic-out-packets\">?</span></div>\n                                <div>$SYS/broker/packet/out/bytes <span id=\"systopic-out-bytes\">?</span></div>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"flex md:justify-end justify-center\">\n                    <p class=\"text-center text-sm text-gray-500\">\n                        <a href=\"https://mosquitto.org/man/mosquitto-8.html#:~:text=not%20require%20authentication.-,Broker%20Status,-Clients%20can%20find\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-blue-700 hover:text-blue-900 hover:underline\">\n                            Learn more about system topics\n                        </a>\n                    </p>\n                </div>\n\n            </div>\n        </div>\n        <!-- footer -->\n        <div class=\"mt-[5%] pt-6 pb-6 bg-gray-100\">\n            <div class=\"flex grid grid-cols-1 md:grid-cols-5 items-center justify-evenly gap-2 text-gray-500\">\n                <div class=\"flex items-center justify-center\">\n                    <a href=\"https://github.com/eclipse-mosquitto/mosquitto\" target=\"_blank\" rel=\"noopener noreferrer\">Github</a>\n                </div>\n                <div class=\"flex items-center justify-center\">\n                    <a href=\"https://test.mosquitto.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Test Mosquitto Instance</a>\n                </div>\n                <div class=\"flex items-center justify-center\">\n                    <a href=\"https://forum.cedalo.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Mosquitto Forum</a>\n                </div>\n                <div class=\"flex items-center justify-center\">\n                    <a href=\"https://cedalo.com/link/mosquitto-pro-edition\" target=\"_blank\" rel=\"noopener noreferrer\">Pro Edition for Eclipse Mosquitto™</a>\n                </div>\n                <div class=\"flex items-center justify-center md:justify-start\">\n                    <a href=\"https://cedalo.com/link/company-behind-mosquitto\" target=\"_blank\" rel=\"noopener noreferrer\">The company behind Mosquitto</a>\n                </div>\n            </div>\n        </div>\n    </div>\n    <script src=\"lib/hammer.min.js\"></script>\n    <script src=\"lib/chart.umd.js\"></script>\n    <script src=\"lib/chartjs-plugin-zoom.min.js\"></script>\n    <script src=\"app/consts.js\"></script>\n    <script src=\"app/sidebar.js\"></script>\n    <script src=\"app/dashboard.js\"></script>\n    <script src=\"app/index.js\"></script>\n    <script src=\"utils/queue.js\"></script>\n    <script src=\"utils/assert.js\"></script>\n    <script src=\"utils/utils.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dashboard/src/lib/chart.umd.js",
    "content": "/*!\n * Chart.js v4.3.0\n * https://www.chartjs.org\n * (c) 2023 Chart.js Contributors\n * Released under the MIT License\n */\n!function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=e():\"function\"==typeof define&&define.amd?define(e):(t=\"undefined\"!=typeof globalThis?globalThis:t||self).Chart=e()}(this,(function(){\"use strict\";var t=Object.freeze({__proto__:null,get Colors(){return Ko},get Decimation(){return Jo},get Filler(){return pa},get Legend(){return _a},get SubTitle(){return wa},get Title(){return va},get Tooltip(){return Va}});function e(){}const i=(()=>{let t=0;return()=>t++})();function s(t){return null==t}function n(t){if(Array.isArray&&Array.isArray(t))return!0;const e=Object.prototype.toString.call(t);return\"[object\"===e.slice(0,7)&&\"Array]\"===e.slice(-6)}function o(t){return null!==t&&\"[object Object]\"===Object.prototype.toString.call(t)}function a(t){return(\"number\"==typeof t||t instanceof Number)&&isFinite(+t)}function r(t,e){return a(t)?t:e}function l(t,e){return void 0===t?e:t}const h=(t,e)=>\"string\"==typeof t&&t.endsWith(\"%\")?parseFloat(t)/100:+t/e,c=(t,e)=>\"string\"==typeof t&&t.endsWith(\"%\")?parseFloat(t)/100*e:+t;function d(t,e,i){if(t&&\"function\"==typeof t.call)return t.apply(i,e)}function u(t,e,i,s){let a,r,l;if(n(t))if(r=t.length,s)for(a=r-1;a>=0;a--)e.call(i,t[a],a);else for(a=0;a<r;a++)e.call(i,t[a],a);else if(o(t))for(l=Object.keys(t),r=l.length,a=0;a<r;a++)e.call(i,t[l[a]],l[a])}function f(t,e){let i,s,n,o;if(!t||!e||t.length!==e.length)return!1;for(i=0,s=t.length;i<s;++i)if(n=t[i],o=e[i],n.datasetIndex!==o.datasetIndex||n.index!==o.index)return!1;return!0}function g(t){if(n(t))return t.map(g);if(o(t)){const e=Object.create(null),i=Object.keys(t),s=i.length;let n=0;for(;n<s;++n)e[i[n]]=g(t[i[n]]);return e}return t}function p(t){return-1===[\"__proto__\",\"prototype\",\"constructor\"].indexOf(t)}function m(t,e,i,s){if(!p(t))return;const n=e[t],a=i[t];o(n)&&o(a)?b(n,a,s):e[t]=g(a)}function b(t,e,i){const s=n(e)?e:[e],a=s.length;if(!o(t))return t;const r=(i=i||{}).merger||m;let l;for(let e=0;e<a;++e){if(l=s[e],!o(l))continue;const n=Object.keys(l);for(let e=0,s=n.length;e<s;++e)r(n[e],t,l,i)}return t}function x(t,e){return b(t,e,{merger:_})}function _(t,e,i){if(!p(t))return;const s=e[t],n=i[t];o(s)&&o(n)?x(s,n):Object.prototype.hasOwnProperty.call(e,t)||(e[t]=g(n))}const y={\"\":t=>t,x:t=>t.x,y:t=>t.y};function v(t){const e=t.split(\".\"),i=[];let s=\"\";for(const t of e)s+=t,s.endsWith(\"\\\\\")?s=s.slice(0,-1)+\".\":(i.push(s),s=\"\");return i}function M(t,e){const i=y[e]||(y[e]=function(t){const e=v(t);return t=>{for(const i of e){if(\"\"===i)break;t=t&&t[i]}return t}}(e));return i(t)}function w(t){return t.charAt(0).toUpperCase()+t.slice(1)}const k=t=>void 0!==t,S=t=>\"function\"==typeof t,P=(t,e)=>{if(t.size!==e.size)return!1;for(const i of t)if(!e.has(i))return!1;return!0};function D(t){return\"mouseup\"===t.type||\"click\"===t.type||\"contextmenu\"===t.type}const C=Math.PI,O=2*C,A=O+C,T=Number.POSITIVE_INFINITY,L=C/180,E=C/2,R=C/4,I=2*C/3,z=Math.log10,F=Math.sign;function V(t,e,i){return Math.abs(t-e)<i}function B(t){const e=Math.round(t);t=V(t,e,t/1e3)?e:t;const i=Math.pow(10,Math.floor(z(t))),s=t/i;return(s<=1?1:s<=2?2:s<=5?5:10)*i}function N(t){const e=[],i=Math.sqrt(t);let s;for(s=1;s<i;s++)t%s==0&&(e.push(s),e.push(t/s));return i===(0|i)&&e.push(i),e.sort(((t,e)=>t-e)).pop(),e}function W(t){return!isNaN(parseFloat(t))&&isFinite(t)}function H(t,e){const i=Math.round(t);return i-e<=t&&i+e>=t}function j(t,e,i){let s,n,o;for(s=0,n=t.length;s<n;s++)o=t[s][i],isNaN(o)||(e.min=Math.min(e.min,o),e.max=Math.max(e.max,o))}function $(t){return t*(C/180)}function Y(t){return t*(180/C)}function U(t){if(!a(t))return;let e=1,i=0;for(;Math.round(t*e)/e!==t;)e*=10,i++;return i}function X(t,e){const i=e.x-t.x,s=e.y-t.y,n=Math.sqrt(i*i+s*s);let o=Math.atan2(s,i);return o<-.5*C&&(o+=O),{angle:o,distance:n}}function q(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function K(t,e){return(t-e+A)%O-C}function G(t){return(t%O+O)%O}function Z(t,e,i,s){const n=G(t),o=G(e),a=G(i),r=G(o-n),l=G(a-n),h=G(n-o),c=G(n-a);return n===o||n===a||s&&o===a||r>l&&h<c}function J(t,e,i){return Math.max(e,Math.min(i,t))}function Q(t){return J(t,-32768,32767)}function tt(t,e,i,s=1e-6){return t>=Math.min(e,i)-s&&t<=Math.max(e,i)+s}function et(t,e,i){i=i||(i=>t[i]<e);let s,n=t.length-1,o=0;for(;n-o>1;)s=o+n>>1,i(s)?o=s:n=s;return{lo:o,hi:n}}const it=(t,e,i,s)=>et(t,i,s?s=>{const n=t[s][e];return n<i||n===i&&t[s+1][e]===i}:s=>t[s][e]<i),st=(t,e,i)=>et(t,i,(s=>t[s][e]>=i));function nt(t,e,i){let s=0,n=t.length;for(;s<n&&t[s]<e;)s++;for(;n>s&&t[n-1]>i;)n--;return s>0||n<t.length?t.slice(s,n):t}const ot=[\"push\",\"pop\",\"shift\",\"splice\",\"unshift\"];function at(t,e){t._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,\"_chartjs\",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),ot.forEach((e=>{const i=\"_onData\"+w(e),s=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value(...e){const n=s.apply(this,e);return t._chartjs.listeners.forEach((t=>{\"function\"==typeof t[i]&&t[i](...e)})),n}})})))}function rt(t,e){const i=t._chartjs;if(!i)return;const s=i.listeners,n=s.indexOf(e);-1!==n&&s.splice(n,1),s.length>0||(ot.forEach((e=>{delete t[e]})),delete t._chartjs)}function lt(t){const e=new Set(t);return e.size===t.length?t:Array.from(e)}const ht=\"undefined\"==typeof window?function(t){return t()}:window.requestAnimationFrame;function ct(t,e){let i=[],s=!1;return function(...n){i=n,s||(s=!0,ht.call(window,(()=>{s=!1,t.apply(e,i)})))}}function dt(t,e){let i;return function(...s){return e?(clearTimeout(i),i=setTimeout(t,e,s)):t.apply(this,s),e}}const ut=t=>\"start\"===t?\"left\":\"end\"===t?\"right\":\"center\",ft=(t,e,i)=>\"start\"===t?e:\"end\"===t?i:(e+i)/2,gt=(t,e,i,s)=>t===(s?\"left\":\"right\")?i:\"center\"===t?(e+i)/2:e;function pt(t,e,i){const s=e.length;let n=0,o=s;if(t._sorted){const{iScale:a,_parsed:r}=t,l=a.axis,{min:h,max:c,minDefined:d,maxDefined:u}=a.getUserBounds();d&&(n=J(Math.min(it(r,a.axis,h).lo,i?s:it(e,l,a.getPixelForValue(h)).lo),0,s-1)),o=u?J(Math.max(it(r,a.axis,c,!0).hi+1,i?0:it(e,l,a.getPixelForValue(c),!0).hi+1),n,s)-n:s-n}return{start:n,count:o}}function mt(t){const{xScale:e,yScale:i,_scaleRanges:s}=t,n={xmin:e.min,xmax:e.max,ymin:i.min,ymax:i.max};if(!s)return t._scaleRanges=n,!0;const o=s.xmin!==e.min||s.xmax!==e.max||s.ymin!==i.min||s.ymax!==i.max;return Object.assign(s,n),o}class bt{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(t,e,i,s){const n=e.listeners[s],o=e.duration;n.forEach((s=>s({chart:t,initial:e.initial,numSteps:o,currentStep:Math.min(i-e.start,o)})))}_refresh(){this._request||(this._running=!0,this._request=ht.call(window,(()=>{this._update(),this._request=null,this._running&&this._refresh()})))}_update(t=Date.now()){let e=0;this._charts.forEach(((i,s)=>{if(!i.running||!i.items.length)return;const n=i.items;let o,a=n.length-1,r=!1;for(;a>=0;--a)o=n[a],o._active?(o._total>i.duration&&(i.duration=o._total),o.tick(t),r=!0):(n[a]=n[n.length-1],n.pop());r&&(s.draw(),this._notify(s,i,t,\"progress\")),n.length||(i.running=!1,this._notify(s,i,t,\"complete\"),i.initial=!1),e+=n.length})),this._lastDate=t,0===e&&(this._running=!1)}_getAnims(t){const e=this._charts;let i=e.get(t);return i||(i={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,i)),i}listen(t,e,i){this._getAnims(t).listeners[e].push(i)}add(t,e){e&&e.length&&this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce(((t,e)=>Math.max(t,e._duration)),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!!(e&&e.running&&e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const i=e.items;let s=i.length-1;for(;s>=0;--s)i[s].cancel();e.items=[],this._notify(t,e,Date.now(),\"complete\")}remove(t){return this._charts.delete(t)}}var xt=new bt;\n/*!\n * @kurkle/color v0.3.2\n * https://github.com/kurkle/color#readme\n * (c) 2023 Jukka Kurkela\n * Released under the MIT License\n */function _t(t){return t+.5|0}const yt=(t,e,i)=>Math.max(Math.min(t,i),e);function vt(t){return yt(_t(2.55*t),0,255)}function Mt(t){return yt(_t(255*t),0,255)}function wt(t){return yt(_t(t/2.55)/100,0,1)}function kt(t){return yt(_t(100*t),0,100)}const St={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Pt=[...\"0123456789ABCDEF\"],Dt=t=>Pt[15&t],Ct=t=>Pt[(240&t)>>4]+Pt[15&t],Ot=t=>(240&t)>>4==(15&t);function At(t){var e=(t=>Ot(t.r)&&Ot(t.g)&&Ot(t.b)&&Ot(t.a))(t)?Dt:Ct;return t?\"#\"+e(t.r)+e(t.g)+e(t.b)+((t,e)=>t<255?e(t):\"\")(t.a,e):void 0}const Tt=/^(hsla?|hwb|hsv)\\(\\s*([-+.e\\d]+)(?:deg)?[\\s,]+([-+.e\\d]+)%[\\s,]+([-+.e\\d]+)%(?:[\\s,]+([-+.e\\d]+)(%)?)?\\s*\\)$/;function Lt(t,e,i){const s=e*Math.min(i,1-i),n=(e,n=(e+t/30)%12)=>i-s*Math.max(Math.min(n-3,9-n,1),-1);return[n(0),n(8),n(4)]}function Et(t,e,i){const s=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return[s(5),s(3),s(1)]}function Rt(t,e,i){const s=Lt(t,1,.5);let n;for(e+i>1&&(n=1/(e+i),e*=n,i*=n),n=0;n<3;n++)s[n]*=1-e-i,s[n]+=e;return s}function It(t){const e=t.r/255,i=t.g/255,s=t.b/255,n=Math.max(e,i,s),o=Math.min(e,i,s),a=(n+o)/2;let r,l,h;return n!==o&&(h=n-o,l=a>.5?h/(2-n-o):h/(n+o),r=function(t,e,i,s,n){return t===n?(e-i)/s+(e<i?6:0):e===n?(i-t)/s+2:(t-e)/s+4}(e,i,s,h,n),r=60*r+.5),[0|r,l||0,a]}function zt(t,e,i,s){return(Array.isArray(e)?t(e[0],e[1],e[2]):t(e,i,s)).map(Mt)}function Ft(t,e,i){return zt(Lt,t,e,i)}function Vt(t){return(t%360+360)%360}function Bt(t){const e=Tt.exec(t);let i,s=255;if(!e)return;e[5]!==i&&(s=e[6]?vt(+e[5]):Mt(+e[5]));const n=Vt(+e[2]),o=+e[3]/100,a=+e[4]/100;return i=\"hwb\"===e[1]?function(t,e,i){return zt(Rt,t,e,i)}(n,o,a):\"hsv\"===e[1]?function(t,e,i){return zt(Et,t,e,i)}(n,o,a):Ft(n,o,a),{r:i[0],g:i[1],b:i[2],a:s}}const Nt={x:\"dark\",Z:\"light\",Y:\"re\",X:\"blu\",W:\"gr\",V:\"medium\",U:\"slate\",A:\"ee\",T:\"ol\",S:\"or\",B:\"ra\",C:\"lateg\",D:\"ights\",R:\"in\",Q:\"turquois\",E:\"hi\",P:\"ro\",O:\"al\",N:\"le\",M:\"de\",L:\"yello\",F:\"en\",K:\"ch\",G:\"arks\",H:\"ea\",I:\"ightg\",J:\"wh\"},Wt={OiceXe:\"f0f8ff\",antiquewEte:\"faebd7\",aqua:\"ffff\",aquamarRe:\"7fffd4\",azuY:\"f0ffff\",beige:\"f5f5dc\",bisque:\"ffe4c4\",black:\"0\",blanKedOmond:\"ffebcd\",Xe:\"ff\",XeviTet:\"8a2be2\",bPwn:\"a52a2a\",burlywood:\"deb887\",caMtXe:\"5f9ea0\",KartYuse:\"7fff00\",KocTate:\"d2691e\",cSO:\"ff7f50\",cSnflowerXe:\"6495ed\",cSnsilk:\"fff8dc\",crimson:\"dc143c\",cyan:\"ffff\",xXe:\"8b\",xcyan:\"8b8b\",xgTMnPd:\"b8860b\",xWay:\"a9a9a9\",xgYF:\"6400\",xgYy:\"a9a9a9\",xkhaki:\"bdb76b\",xmagFta:\"8b008b\",xTivegYF:\"556b2f\",xSange:\"ff8c00\",xScEd:\"9932cc\",xYd:\"8b0000\",xsOmon:\"e9967a\",xsHgYF:\"8fbc8f\",xUXe:\"483d8b\",xUWay:\"2f4f4f\",xUgYy:\"2f4f4f\",xQe:\"ced1\",xviTet:\"9400d3\",dAppRk:\"ff1493\",dApskyXe:\"bfff\",dimWay:\"696969\",dimgYy:\"696969\",dodgerXe:\"1e90ff\",fiYbrick:\"b22222\",flSOwEte:\"fffaf0\",foYstWAn:\"228b22\",fuKsia:\"ff00ff\",gaRsbSo:\"dcdcdc\",ghostwEte:\"f8f8ff\",gTd:\"ffd700\",gTMnPd:\"daa520\",Way:\"808080\",gYF:\"8000\",gYFLw:\"adff2f\",gYy:\"808080\",honeyMw:\"f0fff0\",hotpRk:\"ff69b4\",RdianYd:\"cd5c5c\",Rdigo:\"4b0082\",ivSy:\"fffff0\",khaki:\"f0e68c\",lavFMr:\"e6e6fa\",lavFMrXsh:\"fff0f5\",lawngYF:\"7cfc00\",NmoncEffon:\"fffacd\",ZXe:\"add8e6\",ZcSO:\"f08080\",Zcyan:\"e0ffff\",ZgTMnPdLw:\"fafad2\",ZWay:\"d3d3d3\",ZgYF:\"90ee90\",ZgYy:\"d3d3d3\",ZpRk:\"ffb6c1\",ZsOmon:\"ffa07a\",ZsHgYF:\"20b2aa\",ZskyXe:\"87cefa\",ZUWay:\"778899\",ZUgYy:\"778899\",ZstAlXe:\"b0c4de\",ZLw:\"ffffe0\",lime:\"ff00\",limegYF:\"32cd32\",lRF:\"faf0e6\",magFta:\"ff00ff\",maPon:\"800000\",VaquamarRe:\"66cdaa\",VXe:\"cd\",VScEd:\"ba55d3\",VpurpN:\"9370db\",VsHgYF:\"3cb371\",VUXe:\"7b68ee\",VsprRggYF:\"fa9a\",VQe:\"48d1cc\",VviTetYd:\"c71585\",midnightXe:\"191970\",mRtcYam:\"f5fffa\",mistyPse:\"ffe4e1\",moccasR:\"ffe4b5\",navajowEte:\"ffdead\",navy:\"80\",Tdlace:\"fdf5e6\",Tive:\"808000\",TivedBb:\"6b8e23\",Sange:\"ffa500\",SangeYd:\"ff4500\",ScEd:\"da70d6\",pOegTMnPd:\"eee8aa\",pOegYF:\"98fb98\",pOeQe:\"afeeee\",pOeviTetYd:\"db7093\",papayawEp:\"ffefd5\",pHKpuff:\"ffdab9\",peru:\"cd853f\",pRk:\"ffc0cb\",plum:\"dda0dd\",powMrXe:\"b0e0e6\",purpN:\"800080\",YbeccapurpN:\"663399\",Yd:\"ff0000\",Psybrown:\"bc8f8f\",PyOXe:\"4169e1\",saddNbPwn:\"8b4513\",sOmon:\"fa8072\",sandybPwn:\"f4a460\",sHgYF:\"2e8b57\",sHshell:\"fff5ee\",siFna:\"a0522d\",silver:\"c0c0c0\",skyXe:\"87ceeb\",UXe:\"6a5acd\",UWay:\"708090\",UgYy:\"708090\",snow:\"fffafa\",sprRggYF:\"ff7f\",stAlXe:\"4682b4\",tan:\"d2b48c\",teO:\"8080\",tEstN:\"d8bfd8\",tomato:\"ff6347\",Qe:\"40e0d0\",viTet:\"ee82ee\",JHt:\"f5deb3\",wEte:\"ffffff\",wEtesmoke:\"f5f5f5\",Lw:\"ffff00\",LwgYF:\"9acd32\"};let Ht;function jt(t){Ht||(Ht=function(){const t={},e=Object.keys(Wt),i=Object.keys(Nt);let s,n,o,a,r;for(s=0;s<e.length;s++){for(a=r=e[s],n=0;n<i.length;n++)o=i[n],r=r.replace(o,Nt[o]);o=parseInt(Wt[a],16),t[r]=[o>>16&255,o>>8&255,255&o]}return t}(),Ht.transparent=[0,0,0,0]);const e=Ht[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}const $t=/^rgba?\\(\\s*([-+.\\d]+)(%)?[\\s,]+([-+.e\\d]+)(%)?[\\s,]+([-+.e\\d]+)(%)?(?:[\\s,/]+([-+.e\\d]+)(%)?)?\\s*\\)$/;const Yt=t=>t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055,Ut=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4);function Xt(t,e,i){if(t){let s=It(t);s[e]=Math.max(0,Math.min(s[e]+s[e]*i,0===e?360:1)),s=Ft(s),t.r=s[0],t.g=s[1],t.b=s[2]}}function qt(t,e){return t?Object.assign(e||{},t):t}function Kt(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=Mt(t[3]))):(e=qt(t,{r:0,g:0,b:0,a:1})).a=Mt(e.a),e}function Gt(t){return\"r\"===t.charAt(0)?function(t){const e=$t.exec(t);let i,s,n,o=255;if(e){if(e[7]!==i){const t=+e[7];o=e[8]?vt(t):yt(255*t,0,255)}return i=+e[1],s=+e[3],n=+e[5],i=255&(e[2]?vt(i):yt(i,0,255)),s=255&(e[4]?vt(s):yt(s,0,255)),n=255&(e[6]?vt(n):yt(n,0,255)),{r:i,g:s,b:n,a:o}}}(t):Bt(t)}class Zt{constructor(t){if(t instanceof Zt)return t;const e=typeof t;let i;var s,n,o;\"object\"===e?i=Kt(t):\"string\"===e&&(o=(s=t).length,\"#\"===s[0]&&(4===o||5===o?n={r:255&17*St[s[1]],g:255&17*St[s[2]],b:255&17*St[s[3]],a:5===o?17*St[s[4]]:255}:7!==o&&9!==o||(n={r:St[s[1]]<<4|St[s[2]],g:St[s[3]]<<4|St[s[4]],b:St[s[5]]<<4|St[s[6]],a:9===o?St[s[7]]<<4|St[s[8]]:255})),i=n||jt(t)||Gt(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=qt(this._rgb);return t&&(t.a=wt(t.a)),t}set rgb(t){this._rgb=Kt(t)}rgbString(){return this._valid?(t=this._rgb)&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${wt(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`):void 0;var t}hexString(){return this._valid?At(this._rgb):void 0}hslString(){return this._valid?function(t){if(!t)return;const e=It(t),i=e[0],s=kt(e[1]),n=kt(e[2]);return t.a<255?`hsla(${i}, ${s}%, ${n}%, ${wt(t.a)})`:`hsl(${i}, ${s}%, ${n}%)`}(this._rgb):void 0}mix(t,e){if(t){const i=this.rgb,s=t.rgb;let n;const o=e===n?.5:e,a=2*o-1,r=i.a-s.a,l=((a*r==-1?a:(a+r)/(1+a*r))+1)/2;n=1-l,i.r=255&l*i.r+n*s.r+.5,i.g=255&l*i.g+n*s.g+.5,i.b=255&l*i.b+n*s.b+.5,i.a=o*i.a+(1-o)*s.a,this.rgb=i}return this}interpolate(t,e){return t&&(this._rgb=function(t,e,i){const s=Ut(wt(t.r)),n=Ut(wt(t.g)),o=Ut(wt(t.b));return{r:Mt(Yt(s+i*(Ut(wt(e.r))-s))),g:Mt(Yt(n+i*(Ut(wt(e.g))-n))),b:Mt(Yt(o+i*(Ut(wt(e.b))-o))),a:t.a+i*(e.a-t.a)}}(this._rgb,t._rgb,e)),this}clone(){return new Zt(this.rgb)}alpha(t){return this._rgb.a=Mt(t),this}clearer(t){return this._rgb.a*=1-t,this}greyscale(){const t=this._rgb,e=_t(.3*t.r+.59*t.g+.11*t.b);return t.r=t.g=t.b=e,this}opaquer(t){return this._rgb.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return Xt(this._rgb,2,t),this}darken(t){return Xt(this._rgb,2,-t),this}saturate(t){return Xt(this._rgb,1,t),this}desaturate(t){return Xt(this._rgb,1,-t),this}rotate(t){return function(t,e){var i=It(t);i[0]=Vt(i[0]+e),i=Ft(i),t.r=i[0],t.g=i[1],t.b=i[2]}(this._rgb,t),this}}function Jt(t){if(t&&\"object\"==typeof t){const e=t.toString();return\"[object CanvasPattern]\"===e||\"[object CanvasGradient]\"===e}return!1}function Qt(t){return Jt(t)?t:new Zt(t)}function te(t){return Jt(t)?t:new Zt(t).saturate(.5).darken(.1).hexString()}const ee=[\"x\",\"y\",\"borderWidth\",\"radius\",\"tension\"],ie=[\"color\",\"borderColor\",\"backgroundColor\"];const se=new Map;function ne(t,e,i){return function(t,e){e=e||{};const i=t+JSON.stringify(e);let s=se.get(i);return s||(s=new Intl.NumberFormat(t,e),se.set(i,s)),s}(e,i).format(t)}const oe={values:t=>n(t)?t:\"\"+t,numeric(t,e,i){if(0===t)return\"0\";const s=this.chart.options.locale;let n,o=t;if(i.length>1){const e=Math.max(Math.abs(i[0].value),Math.abs(i[i.length-1].value));(e<1e-4||e>1e15)&&(n=\"scientific\"),o=function(t,e){let i=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;Math.abs(i)>=1&&t!==Math.floor(t)&&(i=t-Math.floor(t));return i}(t,i)}const a=z(Math.abs(o)),r=isNaN(a)?1:Math.max(Math.min(-1*Math.floor(a),20),0),l={notation:n,minimumFractionDigits:r,maximumFractionDigits:r};return Object.assign(l,this.options.ticks.format),ne(t,s,l)},logarithmic(t,e,i){if(0===t)return\"0\";const s=i[e].significand||t/Math.pow(10,Math.floor(z(t)));return[1,2,3,5,10,15].includes(s)||e>.8*i.length?oe.numeric.call(this,t,e,i):\"\"}};var ae={formatters:oe};const re=Object.create(null),le=Object.create(null);function he(t,e){if(!e)return t;const i=e.split(\".\");for(let e=0,s=i.length;e<s;++e){const s=i[e];t=t[s]||(t[s]=Object.create(null))}return t}function ce(t,e,i){return\"string\"==typeof e?b(he(t,e),i):b(he(t,\"\"),e)}class de{constructor(t,e){this.animation=void 0,this.backgroundColor=\"rgba(0,0,0,0.1)\",this.borderColor=\"rgba(0,0,0,0.1)\",this.color=\"#666\",this.datasets={},this.devicePixelRatio=t=>t.chart.platform.getDevicePixelRatio(),this.elements={},this.events=[\"mousemove\",\"mouseout\",\"click\",\"touchstart\",\"touchmove\"],this.font={family:\"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",size:12,style:\"normal\",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(t,e)=>te(e.backgroundColor),this.hoverBorderColor=(t,e)=>te(e.borderColor),this.hoverColor=(t,e)=>te(e.color),this.indexAxis=\"x\",this.interaction={mode:\"nearest\",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ce(this,t,e)}get(t){return he(this,t)}describe(t,e){return ce(le,t,e)}override(t,e){return ce(re,t,e)}route(t,e,i,s){const n=he(this,t),a=he(this,i),r=\"_\"+e;Object.defineProperties(n,{[r]:{value:n[e],writable:!0},[e]:{enumerable:!0,get(){const t=this[r],e=a[s];return o(t)?Object.assign({},e,t):l(t,e)},set(t){this[r]=t}}})}apply(t){t.forEach((t=>t(this)))}}var ue=new de({_scriptable:t=>!t.startsWith(\"on\"),_indexable:t=>\"events\"!==t,hover:{_fallback:\"interaction\"},interaction:{_scriptable:!1,_indexable:!1}},[function(t){t.set(\"animation\",{delay:void 0,duration:1e3,easing:\"easeOutQuart\",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),t.describe(\"animation\",{_fallback:!1,_indexable:!1,_scriptable:t=>\"onProgress\"!==t&&\"onComplete\"!==t&&\"fn\"!==t}),t.set(\"animations\",{colors:{type:\"color\",properties:ie},numbers:{type:\"number\",properties:ee}}),t.describe(\"animations\",{_fallback:\"animation\"}),t.set(\"transitions\",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:\"transparent\"},visible:{type:\"boolean\",duration:0}}},hide:{animations:{colors:{to:\"transparent\"},visible:{type:\"boolean\",easing:\"linear\",fn:t=>0|t}}}})},function(t){t.set(\"layout\",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})},function(t){t.set(\"scale\",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:\"ticks\",grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:\"\",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:\"\",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:ae.formatters.values,minor:{},major:{},align:\"center\",crossAlign:\"near\",showLabelBackdrop:!1,backdropColor:\"rgba(255, 255, 255, 0.75)\",backdropPadding:2}}),t.route(\"scale.ticks\",\"color\",\"\",\"color\"),t.route(\"scale.grid\",\"color\",\"\",\"borderColor\"),t.route(\"scale.border\",\"color\",\"\",\"borderColor\"),t.route(\"scale.title\",\"color\",\"\",\"color\"),t.describe(\"scale\",{_fallback:!1,_scriptable:t=>!t.startsWith(\"before\")&&!t.startsWith(\"after\")&&\"callback\"!==t&&\"parser\"!==t,_indexable:t=>\"borderDash\"!==t&&\"tickBorderDash\"!==t&&\"dash\"!==t}),t.describe(\"scales\",{_fallback:\"scale\"}),t.describe(\"scale.ticks\",{_scriptable:t=>\"backdropPadding\"!==t&&\"callback\"!==t,_indexable:t=>\"backdropPadding\"!==t})}]);function fe(){return\"undefined\"!=typeof window&&\"undefined\"!=typeof document}function ge(t){let e=t.parentNode;return e&&\"[object ShadowRoot]\"===e.toString()&&(e=e.host),e}function pe(t,e,i){let s;return\"string\"==typeof t?(s=parseInt(t,10),-1!==t.indexOf(\"%\")&&(s=s/100*e.parentNode[i])):s=t,s}const me=t=>t.ownerDocument.defaultView.getComputedStyle(t,null);function be(t,e){return me(t).getPropertyValue(e)}const xe=[\"top\",\"right\",\"bottom\",\"left\"];function _e(t,e,i){const s={};i=i?\"-\"+i:\"\";for(let n=0;n<4;n++){const o=xe[n];s[o]=parseFloat(t[e+\"-\"+o+i])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const ye=(t,e,i)=>(t>0||e>0)&&(!i||!i.shadowRoot);function ve(t,e){if(\"native\"in t)return t;const{canvas:i,currentDevicePixelRatio:s}=e,n=me(i),o=\"border-box\"===n.boxSizing,a=_e(n,\"padding\"),r=_e(n,\"border\",\"width\"),{x:l,y:h,box:c}=function(t,e){const i=t.touches,s=i&&i.length?i[0]:t,{offsetX:n,offsetY:o}=s;let a,r,l=!1;if(ye(n,o,t.target))a=n,r=o;else{const t=e.getBoundingClientRect();a=s.clientX-t.left,r=s.clientY-t.top,l=!0}return{x:a,y:r,box:l}}(t,i),d=a.left+(c&&r.left),u=a.top+(c&&r.top);let{width:f,height:g}=e;return o&&(f-=a.width+r.width,g-=a.height+r.height),{x:Math.round((l-d)/f*i.width/s),y:Math.round((h-u)/g*i.height/s)}}const Me=t=>Math.round(10*t)/10;function we(t,e,i,s){const n=me(t),o=_e(n,\"margin\"),a=pe(n.maxWidth,t,\"clientWidth\")||T,r=pe(n.maxHeight,t,\"clientHeight\")||T,l=function(t,e,i){let s,n;if(void 0===e||void 0===i){const o=ge(t);if(o){const t=o.getBoundingClientRect(),a=me(o),r=_e(a,\"border\",\"width\"),l=_e(a,\"padding\");e=t.width-l.width-r.width,i=t.height-l.height-r.height,s=pe(a.maxWidth,o,\"clientWidth\"),n=pe(a.maxHeight,o,\"clientHeight\")}else e=t.clientWidth,i=t.clientHeight}return{width:e,height:i,maxWidth:s||T,maxHeight:n||T}}(t,e,i);let{width:h,height:c}=l;if(\"content-box\"===n.boxSizing){const t=_e(n,\"border\",\"width\"),e=_e(n,\"padding\");h-=e.width+t.width,c-=e.height+t.height}h=Math.max(0,h-o.width),c=Math.max(0,s?h/s:c-o.height),h=Me(Math.min(h,a,l.maxWidth)),c=Me(Math.min(c,r,l.maxHeight)),h&&!c&&(c=Me(h/2));return(void 0!==e||void 0!==i)&&s&&l.height&&c>l.height&&(c=l.height,h=Me(Math.floor(c*s))),{width:h,height:c}}function ke(t,e,i){const s=e||1,n=Math.floor(t.height*s),o=Math.floor(t.width*s);t.height=Math.floor(t.height),t.width=Math.floor(t.width);const a=t.canvas;return a.style&&(i||!a.style.height&&!a.style.width)&&(a.style.height=`${t.height}px`,a.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==s||a.height!==n||a.width!==o)&&(t.currentDevicePixelRatio=s,a.height=n,a.width=o,t.ctx.setTransform(s,0,0,s,0,0),!0)}const Se=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};window.addEventListener(\"test\",null,e),window.removeEventListener(\"test\",null,e)}catch(t){}return t}();function Pe(t,e){const i=be(t,e),s=i&&i.match(/^(\\d+)(\\.\\d+)?px$/);return s?+s[1]:void 0}function De(t){return!t||s(t.size)||s(t.family)?null:(t.style?t.style+\" \":\"\")+(t.weight?t.weight+\" \":\"\")+t.size+\"px \"+t.family}function Ce(t,e,i,s,n){let o=e[n];return o||(o=e[n]=t.measureText(n).width,i.push(n)),o>s&&(s=o),s}function Oe(t,e,i,s){let o=(s=s||{}).data=s.data||{},a=s.garbageCollect=s.garbageCollect||[];s.font!==e&&(o=s.data={},a=s.garbageCollect=[],s.font=e),t.save(),t.font=e;let r=0;const l=i.length;let h,c,d,u,f;for(h=0;h<l;h++)if(u=i[h],null==u||n(u)){if(n(u))for(c=0,d=u.length;c<d;c++)f=u[c],null==f||n(f)||(r=Ce(t,o,a,r,f))}else r=Ce(t,o,a,r,u);t.restore();const g=a.length/2;if(g>i.length){for(h=0;h<g;h++)delete o[a[h]];a.splice(0,g)}return r}function Ae(t,e,i){const s=t.currentDevicePixelRatio,n=0!==i?Math.max(i/2,.5):0;return Math.round((e-n)*s)/s+n}function Te(t,e){(e=e||t.getContext(\"2d\")).save(),e.resetTransform(),e.clearRect(0,0,t.width,t.height),e.restore()}function Le(t,e,i,s){Ee(t,e,i,s,null)}function Ee(t,e,i,s,n){let o,a,r,l,h,c,d,u;const f=e.pointStyle,g=e.rotation,p=e.radius;let m=(g||0)*L;if(f&&\"object\"==typeof f&&(o=f.toString(),\"[object HTMLImageElement]\"===o||\"[object HTMLCanvasElement]\"===o))return t.save(),t.translate(i,s),t.rotate(m),t.drawImage(f,-f.width/2,-f.height/2,f.width,f.height),void t.restore();if(!(isNaN(p)||p<=0)){switch(t.beginPath(),f){default:n?t.ellipse(i,s,n/2,p,0,0,O):t.arc(i,s,p,0,O),t.closePath();break;case\"triangle\":c=n?n/2:p,t.moveTo(i+Math.sin(m)*c,s-Math.cos(m)*p),m+=I,t.lineTo(i+Math.sin(m)*c,s-Math.cos(m)*p),m+=I,t.lineTo(i+Math.sin(m)*c,s-Math.cos(m)*p),t.closePath();break;case\"rectRounded\":h=.516*p,l=p-h,a=Math.cos(m+R)*l,d=Math.cos(m+R)*(n?n/2-h:l),r=Math.sin(m+R)*l,u=Math.sin(m+R)*(n?n/2-h:l),t.arc(i-d,s-r,h,m-C,m-E),t.arc(i+u,s-a,h,m-E,m),t.arc(i+d,s+r,h,m,m+E),t.arc(i-u,s+a,h,m+E,m+C),t.closePath();break;case\"rect\":if(!g){l=Math.SQRT1_2*p,c=n?n/2:l,t.rect(i-c,s-l,2*c,2*l);break}m+=R;case\"rectRot\":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+u,s-a),t.lineTo(i+d,s+r),t.lineTo(i-u,s+a),t.closePath();break;case\"crossRot\":m+=R;case\"cross\":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a);break;case\"star\":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a),m+=R,d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a);break;case\"line\":a=n?n/2:Math.cos(m)*p,r=Math.sin(m)*p,t.moveTo(i-a,s-r),t.lineTo(i+a,s+r);break;case\"dash\":t.moveTo(i,s),t.lineTo(i+Math.cos(m)*(n?n/2:p),s+Math.sin(m)*p);break;case!1:t.closePath()}t.fill(),e.borderWidth>0&&t.stroke()}}function Re(t,e,i){return i=i||.5,!e||t&&t.x>e.left-i&&t.x<e.right+i&&t.y>e.top-i&&t.y<e.bottom+i}function Ie(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()}function ze(t){t.restore()}function Fe(t,e,i,s,n){if(!e)return t.lineTo(i.x,i.y);if(\"middle\"===n){const s=(e.x+i.x)/2;t.lineTo(s,e.y),t.lineTo(s,i.y)}else\"after\"===n!=!!s?t.lineTo(e.x,i.y):t.lineTo(i.x,e.y);t.lineTo(i.x,i.y)}function Ve(t,e,i,s){if(!e)return t.lineTo(i.x,i.y);t.bezierCurveTo(s?e.cp1x:e.cp2x,s?e.cp1y:e.cp2y,s?i.cp2x:i.cp1x,s?i.cp2y:i.cp1y,i.x,i.y)}function Be(t,e,i,s,n){if(n.strikethrough||n.underline){const o=t.measureText(s),a=e-o.actualBoundingBoxLeft,r=e+o.actualBoundingBoxRight,l=i-o.actualBoundingBoxAscent,h=i+o.actualBoundingBoxDescent,c=n.strikethrough?(l+h)/2:h;t.strokeStyle=t.fillStyle,t.beginPath(),t.lineWidth=n.decorationWidth||2,t.moveTo(a,c),t.lineTo(r,c),t.stroke()}}function Ne(t,e){const i=t.fillStyle;t.fillStyle=e.color,t.fillRect(e.left,e.top,e.width,e.height),t.fillStyle=i}function We(t,e,i,o,a,r={}){const l=n(e)?e:[e],h=r.strokeWidth>0&&\"\"!==r.strokeColor;let c,d;for(t.save(),t.font=a.string,function(t,e){e.translation&&t.translate(e.translation[0],e.translation[1]),s(e.rotation)||t.rotate(e.rotation),e.color&&(t.fillStyle=e.color),e.textAlign&&(t.textAlign=e.textAlign),e.textBaseline&&(t.textBaseline=e.textBaseline)}(t,r),c=0;c<l.length;++c)d=l[c],r.backdrop&&Ne(t,r.backdrop),h&&(r.strokeColor&&(t.strokeStyle=r.strokeColor),s(r.strokeWidth)||(t.lineWidth=r.strokeWidth),t.strokeText(d,i,o,r.maxWidth)),t.fillText(d,i,o,r.maxWidth),Be(t,i,o,d,r),o+=Number(a.lineHeight);t.restore()}function He(t,e){const{x:i,y:s,w:n,h:o,radius:a}=e;t.arc(i+a.topLeft,s+a.topLeft,a.topLeft,-E,C,!0),t.lineTo(i,s+o-a.bottomLeft),t.arc(i+a.bottomLeft,s+o-a.bottomLeft,a.bottomLeft,C,E,!0),t.lineTo(i+n-a.bottomRight,s+o),t.arc(i+n-a.bottomRight,s+o-a.bottomRight,a.bottomRight,E,0,!0),t.lineTo(i+n,s+a.topRight),t.arc(i+n-a.topRight,s+a.topRight,a.topRight,0,-E,!0),t.lineTo(i+a.topLeft,s)}function je(t,e=[\"\"],i,s,n=(()=>t[0])){const o=i||t;void 0===s&&(s=ti(\"_fallback\",t));const a={[Symbol.toStringTag]:\"Object\",_cacheable:!0,_scopes:t,_rootScopes:o,_fallback:s,_getTarget:n,override:i=>je([i,...t],e,o,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete e._keys,delete t[0][i],!0),get:(i,s)=>qe(i,s,(()=>function(t,e,i,s){let n;for(const o of e)if(n=ti(Ue(o,t),i),void 0!==n)return Xe(t,n)?Je(i,s,t,n):n}(s,e,t,i))),getOwnPropertyDescriptor:(t,e)=>Reflect.getOwnPropertyDescriptor(t._scopes[0],e),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(t,e)=>ei(t).includes(e),ownKeys:t=>ei(t),set(t,e,i){const s=t._storage||(t._storage=n());return t[e]=s[e]=i,delete t._keys,!0}})}function $e(t,e,i,s){const a={_cacheable:!1,_proxy:t,_context:e,_subProxy:i,_stack:new Set,_descriptors:Ye(t,s),setContext:e=>$e(t,e,i,s),override:n=>$e(t.override(n),e,i,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete t[i],!0),get:(t,e,i)=>qe(t,e,(()=>function(t,e,i){const{_proxy:s,_context:a,_subProxy:r,_descriptors:l}=t;let h=s[e];S(h)&&l.isScriptable(e)&&(h=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=i;if(r.has(t))throw new Error(\"Recursion detected: \"+Array.from(r).join(\"->\")+\"->\"+t);r.add(t);let l=e(o,a||s);r.delete(t),Xe(t,l)&&(l=Je(n._scopes,n,t,l));return l}(e,h,t,i));n(h)&&h.length&&(h=function(t,e,i,s){const{_proxy:n,_context:a,_subProxy:r,_descriptors:l}=i;if(void 0!==a.index&&s(t))return e[a.index%e.length];if(o(e[0])){const i=e,s=n._scopes.filter((t=>t!==i));e=[];for(const o of i){const i=Je(s,n,t,o);e.push($e(i,a,r&&r[t],l))}}return e}(e,h,t,l.isIndexable));Xe(e,h)&&(h=$e(h,a,r&&r[e],l));return h}(t,e,i))),getOwnPropertyDescriptor:(e,i)=>e._descriptors.allKeys?Reflect.has(t,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,i),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(e,i)=>Reflect.has(t,i),ownKeys:()=>Reflect.ownKeys(t),set:(e,i,s)=>(t[i]=s,delete e[i],!0)})}function Ye(t,e={scriptable:!0,indexable:!0}){const{_scriptable:i=e.scriptable,_indexable:s=e.indexable,_allKeys:n=e.allKeys}=t;return{allKeys:n,scriptable:i,indexable:s,isScriptable:S(i)?i:()=>i,isIndexable:S(s)?s:()=>s}}const Ue=(t,e)=>t?t+w(e):e,Xe=(t,e)=>o(e)&&\"adapters\"!==t&&(null===Object.getPrototypeOf(e)||e.constructor===Object);function qe(t,e,i){if(Object.prototype.hasOwnProperty.call(t,e))return t[e];const s=i();return t[e]=s,s}function Ke(t,e,i){return S(t)?t(e,i):t}const Ge=(t,e)=>!0===t?e:\"string\"==typeof t?M(e,t):void 0;function Ze(t,e,i,s,n){for(const o of e){const e=Ge(i,o);if(e){t.add(e);const o=Ke(e._fallback,i,n);if(void 0!==o&&o!==i&&o!==s)return o}else if(!1===e&&void 0!==s&&i!==s)return null}return!1}function Je(t,e,i,s){const a=e._rootScopes,r=Ke(e._fallback,i,s),l=[...t,...a],h=new Set;h.add(s);let c=Qe(h,l,i,r||i,s);return null!==c&&((void 0===r||r===i||(c=Qe(h,l,r,c,s),null!==c))&&je(Array.from(h),[\"\"],a,r,(()=>function(t,e,i){const s=t._getTarget();e in s||(s[e]={});const a=s[e];if(n(a)&&o(i))return i;return a||{}}(e,i,s))))}function Qe(t,e,i,s,n){for(;i;)i=Ze(t,e,i,s,n);return i}function ti(t,e){for(const i of e){if(!i)continue;const e=i[t];if(void 0!==e)return e}}function ei(t){let e=t._keys;return e||(e=t._keys=function(t){const e=new Set;for(const i of t)for(const t of Object.keys(i).filter((t=>!t.startsWith(\"_\"))))e.add(t);return Array.from(e)}(t._scopes)),e}function ii(t,e,i,s){const{iScale:n}=t,{key:o=\"r\"}=this._parsing,a=new Array(s);let r,l,h,c;for(r=0,l=s;r<l;++r)h=r+i,c=e[h],a[r]={r:n.parse(M(c,o),h)};return a}const si=Number.EPSILON||1e-14,ni=(t,e)=>e<t.length&&!t[e].skip&&t[e],oi=t=>\"x\"===t?\"y\":\"x\";function ai(t,e,i,s){const n=t.skip?e:t,o=e,a=i.skip?e:i,r=q(o,n),l=q(a,o);let h=r/(r+l),c=l/(r+l);h=isNaN(h)?0:h,c=isNaN(c)?0:c;const d=s*h,u=s*c;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+u*(a.x-n.x),y:o.y+u*(a.y-n.y)}}}function ri(t,e=\"x\"){const i=oi(e),s=t.length,n=Array(s).fill(0),o=Array(s);let a,r,l,h=ni(t,0);for(a=0;a<s;++a)if(r=l,l=h,h=ni(t,a+1),l){if(h){const t=h[e]-l[e];n[a]=0!==t?(h[i]-l[i])/t:0}o[a]=r?h?F(n[a-1])!==F(n[a])?0:(n[a-1]+n[a])/2:n[a-1]:n[a]}!function(t,e,i){const s=t.length;let n,o,a,r,l,h=ni(t,0);for(let c=0;c<s-1;++c)l=h,h=ni(t,c+1),l&&h&&(V(e[c],0,si)?i[c]=i[c+1]=0:(n=i[c]/e[c],o=i[c+1]/e[c],r=Math.pow(n,2)+Math.pow(o,2),r<=9||(a=3/Math.sqrt(r),i[c]=n*a*e[c],i[c+1]=o*a*e[c])))}(t,n,o),function(t,e,i=\"x\"){const s=oi(i),n=t.length;let o,a,r,l=ni(t,0);for(let h=0;h<n;++h){if(a=r,r=l,l=ni(t,h+1),!r)continue;const n=r[i],c=r[s];a&&(o=(n-a[i])/3,r[`cp1${i}`]=n-o,r[`cp1${s}`]=c-o*e[h]),l&&(o=(l[i]-n)/3,r[`cp2${i}`]=n+o,r[`cp2${s}`]=c+o*e[h])}}(t,o,e)}function li(t,e,i){return Math.max(Math.min(t,i),e)}function hi(t,e,i,s,n){let o,a,r,l;if(e.spanGaps&&(t=t.filter((t=>!t.skip))),\"monotone\"===e.cubicInterpolationMode)ri(t,n);else{let i=s?t[t.length-1]:t[0];for(o=0,a=t.length;o<a;++o)r=t[o],l=ai(i,r,t[Math.min(o+1,a-(s?0:1))%a],e.tension),r.cp1x=l.previous.x,r.cp1y=l.previous.y,r.cp2x=l.next.x,r.cp2y=l.next.y,i=r}e.capBezierPoints&&function(t,e){let i,s,n,o,a,r=Re(t[0],e);for(i=0,s=t.length;i<s;++i)a=o,o=r,r=i<s-1&&Re(t[i+1],e),o&&(n=t[i],a&&(n.cp1x=li(n.cp1x,e.left,e.right),n.cp1y=li(n.cp1y,e.top,e.bottom)),r&&(n.cp2x=li(n.cp2x,e.left,e.right),n.cp2y=li(n.cp2y,e.top,e.bottom)))}(t,i)}const ci=t=>0===t||1===t,di=(t,e,i)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*O/i),ui=(t,e,i)=>Math.pow(2,-10*t)*Math.sin((t-e)*O/i)+1,fi={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*E),easeOutSine:t=>Math.sin(t*E),easeInOutSine:t=>-.5*(Math.cos(C*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>ci(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>ci(t)?t:di(t,.075,.3),easeOutElastic:t=>ci(t)?t:ui(t,.075,.3),easeInOutElastic(t){const e=.1125;return ci(t)?t:t<.5?.5*di(2*t,e,.45):.5+.5*ui(2*t-1,e,.45)},easeInBack(t){const e=1.70158;return t*t*((e+1)*t-e)},easeOutBack(t){const e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-fi.easeOutBounce(1-t),easeOutBounce(t){const e=7.5625,i=2.75;return t<1/i?e*t*t:t<2/i?e*(t-=1.5/i)*t+.75:t<2.5/i?e*(t-=2.25/i)*t+.9375:e*(t-=2.625/i)*t+.984375},easeInOutBounce:t=>t<.5?.5*fi.easeInBounce(2*t):.5*fi.easeOutBounce(2*t-1)+.5};function gi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:t.y+i*(e.y-t.y)}}function pi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:\"middle\"===s?i<.5?t.y:e.y:\"after\"===s?i<1?t.y:e.y:i>0?e.y:t.y}}function mi(t,e,i,s){const n={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},a=gi(t,n,i),r=gi(n,o,i),l=gi(o,e,i),h=gi(a,r,i),c=gi(r,l,i);return gi(h,c,i)}const bi=/^(normal|(\\d+(?:\\.\\d+)?)(px|em|%)?)$/,xi=/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;function _i(t,e){const i=(\"\"+t).match(bi);if(!i||\"normal\"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case\"px\":return t;case\"%\":t/=100}return e*t}const yi=t=>+t||0;function vi(t,e){const i={},s=o(e),n=s?Object.keys(e):e,a=o(t)?s?i=>l(t[i],t[e[i]]):e=>t[e]:()=>t;for(const t of n)i[t]=yi(a(t));return i}function Mi(t){return vi(t,{top:\"y\",right:\"x\",bottom:\"y\",left:\"x\"})}function wi(t){return vi(t,[\"topLeft\",\"topRight\",\"bottomLeft\",\"bottomRight\"])}function ki(t){const e=Mi(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function Si(t,e){t=t||{},e=e||ue.font;let i=l(t.size,e.size);\"string\"==typeof i&&(i=parseInt(i,10));let s=l(t.style,e.style);s&&!(\"\"+s).match(xi)&&(console.warn('Invalid font style specified: \"'+s+'\"'),s=void 0);const n={family:l(t.family,e.family),lineHeight:_i(l(t.lineHeight,e.lineHeight),i),size:i,style:s,weight:l(t.weight,e.weight),string:\"\"};return n.string=De(n),n}function Pi(t,e,i,s){let o,a,r,l=!0;for(o=0,a=t.length;o<a;++o)if(r=t[o],void 0!==r&&(void 0!==e&&\"function\"==typeof r&&(r=r(e),l=!1),void 0!==i&&n(r)&&(r=r[i%r.length],l=!1),void 0!==r))return s&&!l&&(s.cacheable=!1),r}function Di(t,e,i){const{min:s,max:n}=t,o=c(e,(n-s)/2),a=(t,e)=>i&&0===t?0:t+e;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function Ci(t,e){return Object.assign(Object.create(t),e)}function Oi(t,e,i){return t?function(t,e){return{x:i=>t+t+e-i,setWidth(t){e=t},textAlign:t=>\"center\"===t?t:\"right\"===t?\"left\":\"right\",xPlus:(t,e)=>t-e,leftForLtr:(t,e)=>t-e}}(e,i):{x:t=>t,setWidth(t){},textAlign:t=>t,xPlus:(t,e)=>t+e,leftForLtr:(t,e)=>t}}function Ai(t,e){let i,s;\"ltr\"!==e&&\"rtl\"!==e||(i=t.canvas.style,s=[i.getPropertyValue(\"direction\"),i.getPropertyPriority(\"direction\")],i.setProperty(\"direction\",e,\"important\"),t.prevTextDirection=s)}function Ti(t,e){void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty(\"direction\",e[0],e[1]))}function Li(t){return\"angle\"===t?{between:Z,compare:K,normalize:G}:{between:tt,compare:(t,e)=>t-e,normalize:t=>t}}function Ei({start:t,end:e,count:i,loop:s,style:n}){return{start:t%i,end:e%i,loop:s&&(e-t+1)%i==0,style:n}}function Ri(t,e,i){if(!i)return[t];const{property:s,start:n,end:o}=i,a=e.length,{compare:r,between:l,normalize:h}=Li(s),{start:c,end:d,loop:u,style:f}=function(t,e,i){const{property:s,start:n,end:o}=i,{between:a,normalize:r}=Li(s),l=e.length;let h,c,{start:d,end:u,loop:f}=t;if(f){for(d+=l,u+=l,h=0,c=l;h<c&&a(r(e[d%l][s]),n,o);++h)d--,u--;d%=l,u%=l}return u<d&&(u+=l),{start:d,end:u,loop:f,style:t.style}}(t,e,i),g=[];let p,m,b,x=!1,_=null;const y=()=>x||l(n,b,p)&&0!==r(n,b),v=()=>!x||0===r(o,p)||l(o,b,p);for(let t=c,i=c;t<=d;++t)m=e[t%a],m.skip||(p=h(m[s]),p!==b&&(x=l(p,n,o),null===_&&y()&&(_=0===r(p,n)?t:i),null!==_&&v()&&(g.push(Ei({start:_,end:t,loop:u,count:a,style:f})),_=null),i=t,b=p));return null!==_&&g.push(Ei({start:_,end:d,loop:u,count:a,style:f})),g}function Ii(t,e){const i=[],s=t.segments;for(let n=0;n<s.length;n++){const o=Ri(s[n],t.points,e);o.length&&i.push(...o)}return i}function zi(t,e){const i=t.points,s=t.options.spanGaps,n=i.length;if(!n)return[];const o=!!t._loop,{start:a,end:r}=function(t,e,i,s){let n=0,o=e-1;if(i&&!s)for(;n<e&&!t[n].skip;)n++;for(;n<e&&t[n].skip;)n++;for(n%=e,i&&(o+=n);o>n&&t[o%e].skip;)o--;return o%=e,{start:n,end:o}}(i,n,o,s);if(!0===s)return Fi(t,[{start:a,end:r,loop:o}],i,e);return Fi(t,function(t,e,i,s){const n=t.length,o=[];let a,r=e,l=t[e];for(a=e+1;a<=i;++a){const i=t[a%n];i.skip||i.stop?l.skip||(s=!1,o.push({start:e%n,end:(a-1)%n,loop:s}),e=r=i.stop?a:null):(r=a,l.skip&&(e=a)),l=i}return null!==r&&o.push({start:e%n,end:r%n,loop:s}),o}(i,a,r<a?r+n:r,!!t._fullLoop&&0===a&&r===n-1),i,e)}function Fi(t,e,i,s){return s&&s.setContext&&i?function(t,e,i,s){const n=t._chart.getContext(),o=Vi(t.options),{_datasetIndex:a,options:{spanGaps:r}}=t,l=i.length,h=[];let c=o,d=e[0].start,u=d;function f(t,e,s,n){const o=r?-1:1;if(t!==e){for(t+=l;i[t%l].skip;)t-=o;for(;i[e%l].skip;)e+=o;t%l!=e%l&&(h.push({start:t%l,end:e%l,loop:s,style:n}),c=n,d=e%l)}}for(const t of e){d=r?d:t.start;let e,o=i[d%l];for(u=d+1;u<=t.end;u++){const r=i[u%l];e=Vi(s.setContext(Ci(n,{type:\"segment\",p0:o,p1:r,p0DataIndex:(u-1)%l,p1DataIndex:u%l,datasetIndex:a}))),Bi(e,c)&&f(d,u-1,t.loop,c),o=r,c=e}d<u-1&&f(d,u-1,t.loop,c)}return h}(t,e,i,s):e}function Vi(t){return{backgroundColor:t.backgroundColor,borderCapStyle:t.borderCapStyle,borderDash:t.borderDash,borderDashOffset:t.borderDashOffset,borderJoinStyle:t.borderJoinStyle,borderWidth:t.borderWidth,borderColor:t.borderColor}}function Bi(t,e){if(!e)return!1;const i=[],s=function(t,e){return Jt(e)?(i.includes(e)||i.push(e),i.indexOf(e)):e};return JSON.stringify(t,s)!==JSON.stringify(e,s)}var Ni=Object.freeze({__proto__:null,HALF_PI:E,INFINITY:T,PI:C,PITAU:A,QUARTER_PI:R,RAD_PER_DEG:L,TAU:O,TWO_THIRDS_PI:I,_addGrace:Di,_alignPixel:Ae,_alignStartEnd:ft,_angleBetween:Z,_angleDiff:K,_arrayUnique:lt,_attachContext:$e,_bezierCurveTo:Ve,_bezierInterpolation:mi,_boundSegment:Ri,_boundSegments:Ii,_capitalize:w,_computeSegments:zi,_createResolver:je,_decimalPlaces:U,_deprecated:function(t,e,i,s){void 0!==e&&console.warn(t+': \"'+i+'\" is deprecated. Please use \"'+s+'\" instead')},_descriptors:Ye,_elementsEqual:f,_factorize:N,_filterBetween:nt,_getParentNode:ge,_getStartAndCountOfVisiblePoints:pt,_int16Range:Q,_isBetween:tt,_isClickEvent:D,_isDomSupported:fe,_isPointInArea:Re,_limitValue:J,_longestText:Oe,_lookup:et,_lookupByKey:it,_measureText:Ce,_merger:m,_mergerIf:_,_normalizeAngle:G,_parseObjectDataRadialScale:ii,_pointInLine:gi,_readValueToProps:vi,_rlookupByKey:st,_scaleRangesChanged:mt,_setMinAndMaxByKey:j,_splitKey:v,_steppedInterpolation:pi,_steppedLineTo:Fe,_textX:gt,_toLeftRightCenter:ut,_updateBezierControlPoints:hi,addRoundedRectPath:He,almostEquals:V,almostWhole:H,callback:d,clearCanvas:Te,clipArea:Ie,clone:g,color:Qt,createContext:Ci,debounce:dt,defined:k,distanceBetweenPoints:q,drawPoint:Le,drawPointLegend:Ee,each:u,easingEffects:fi,finiteOrDefault:r,fontString:function(t,e,i){return e+\" \"+t+\"px \"+i},formatNumber:ne,getAngleFromPoint:X,getHoverColor:te,getMaximumSize:we,getRelativePosition:ve,getRtlAdapter:Oi,getStyle:be,isArray:n,isFinite:a,isFunction:S,isNullOrUndef:s,isNumber:W,isObject:o,isPatternOrGradient:Jt,listenArrayEvents:at,log10:z,merge:b,mergeIf:x,niceNum:B,noop:e,overrideTextDirection:Ai,readUsedSize:Pe,renderText:We,requestAnimFrame:ht,resolve:Pi,resolveObjectKey:M,restoreTextDirection:Ti,retinaScale:ke,setsEqual:P,sign:F,splineCurve:ai,splineCurveMonotone:ri,supportsEventListenerOptions:Se,throttled:ct,toDegrees:Y,toDimension:c,toFont:Si,toFontString:De,toLineHeight:_i,toPadding:ki,toPercentage:h,toRadians:$,toTRBL:Mi,toTRBLCorners:wi,uid:i,unclipArea:ze,unlistenArrayEvents:rt,valueOrDefault:l});function Wi(t,e,i,s){const{controller:n,data:o,_sorted:a}=t,r=n._cachedMeta.iScale;if(r&&e===r.axis&&\"r\"!==e&&a&&o.length){const t=r._reversePixels?st:it;if(!s)return t(o,e,i);if(n._sharedOptions){const s=o[0],n=\"function\"==typeof s.getRange&&s.getRange(e);if(n){const s=t(o,e,i-n),a=t(o,e,i+n);return{lo:s.lo,hi:a.hi}}}}return{lo:0,hi:o.length-1}}function Hi(t,e,i,s,n){const o=t.getSortedVisibleDatasetMetas(),a=i[e];for(let t=0,i=o.length;t<i;++t){const{index:i,data:r}=o[t],{lo:l,hi:h}=Wi(o[t],e,a,n);for(let t=l;t<=h;++t){const e=r[t];e.skip||s(e,i,t)}}}function ji(t,e,i,s,n){const o=[];if(!n&&!t.isPointInArea(e))return o;return Hi(t,i,e,(function(i,a,r){(n||Re(i,t.chartArea,0))&&i.inRange(e.x,e.y,s)&&o.push({element:i,datasetIndex:a,index:r})}),!0),o}function $i(t,e,i,s,n,o){let a=[];const r=function(t){const e=-1!==t.indexOf(\"x\"),i=-1!==t.indexOf(\"y\");return function(t,s){const n=e?Math.abs(t.x-s.x):0,o=i?Math.abs(t.y-s.y):0;return Math.sqrt(Math.pow(n,2)+Math.pow(o,2))}}(i);let l=Number.POSITIVE_INFINITY;return Hi(t,i,e,(function(i,h,c){const d=i.inRange(e.x,e.y,n);if(s&&!d)return;const u=i.getCenterPoint(n);if(!(!!o||t.isPointInArea(u))&&!d)return;const f=r(e,u);f<l?(a=[{element:i,datasetIndex:h,index:c}],l=f):f===l&&a.push({element:i,datasetIndex:h,index:c})})),a}function Yi(t,e,i,s,n,o){return o||t.isPointInArea(e)?\"r\"!==i||s?$i(t,e,i,s,n,o):function(t,e,i,s){let n=[];return Hi(t,i,e,(function(t,i,o){const{startAngle:a,endAngle:r}=t.getProps([\"startAngle\",\"endAngle\"],s),{angle:l}=X(t,{x:e.x,y:e.y});Z(l,a,r)&&n.push({element:t,datasetIndex:i,index:o})})),n}(t,e,i,n):[]}function Ui(t,e,i,s,n){const o=[],a=\"x\"===i?\"inXRange\":\"inYRange\";let r=!1;return Hi(t,i,e,((t,s,l)=>{t[a](e[i],n)&&(o.push({element:t,datasetIndex:s,index:l}),r=r||t.inRange(e.x,e.y,n))})),s&&!r?[]:o}var Xi={evaluateInteractionItems:Hi,modes:{index(t,e,i,s){const n=ve(e,t),o=i.axis||\"x\",a=i.includeInvisible||!1,r=i.intersect?ji(t,n,o,s,a):Yi(t,n,o,!1,s,a),l=[];return r.length?(t.getSortedVisibleDatasetMetas().forEach((t=>{const e=r[0].index,i=t.data[e];i&&!i.skip&&l.push({element:i,datasetIndex:t.index,index:e})})),l):[]},dataset(t,e,i,s){const n=ve(e,t),o=i.axis||\"xy\",a=i.includeInvisible||!1;let r=i.intersect?ji(t,n,o,s,a):Yi(t,n,o,!1,s,a);if(r.length>0){const e=r[0].datasetIndex,i=t.getDatasetMeta(e).data;r=[];for(let t=0;t<i.length;++t)r.push({element:i[t],datasetIndex:e,index:t})}return r},point:(t,e,i,s)=>ji(t,ve(e,t),i.axis||\"xy\",s,i.includeInvisible||!1),nearest(t,e,i,s){const n=ve(e,t),o=i.axis||\"xy\",a=i.includeInvisible||!1;return Yi(t,n,o,i.intersect,s,a)},x:(t,e,i,s)=>Ui(t,ve(e,t),\"x\",i.intersect,s),y:(t,e,i,s)=>Ui(t,ve(e,t),\"y\",i.intersect,s)}};const qi=[\"left\",\"top\",\"right\",\"bottom\"];function Ki(t,e){return t.filter((t=>t.pos===e))}function Gi(t,e){return t.filter((t=>-1===qi.indexOf(t.pos)&&t.box.axis===e))}function Zi(t,e){return t.sort(((t,i)=>{const s=e?i:t,n=e?t:i;return s.weight===n.weight?s.index-n.index:s.weight-n.weight}))}function Ji(t,e){const i=function(t){const e={};for(const i of t){const{stack:t,pos:s,stackWeight:n}=i;if(!t||!qi.includes(s))continue;const o=e[t]||(e[t]={count:0,placed:0,weight:0,size:0});o.count++,o.weight+=n}return e}(t),{vBoxMaxWidth:s,hBoxMaxHeight:n}=e;let o,a,r;for(o=0,a=t.length;o<a;++o){r=t[o];const{fullSize:a}=r.box,l=i[r.stack],h=l&&r.stackWeight/l.weight;r.horizontal?(r.width=h?h*s:a&&e.availableWidth,r.height=n):(r.width=s,r.height=h?h*n:a&&e.availableHeight)}return i}function Qi(t,e,i,s){return Math.max(t[i],e[i])+Math.max(t[s],e[s])}function ts(t,e){t.top=Math.max(t.top,e.top),t.left=Math.max(t.left,e.left),t.bottom=Math.max(t.bottom,e.bottom),t.right=Math.max(t.right,e.right)}function es(t,e,i,s){const{pos:n,box:a}=i,r=t.maxPadding;if(!o(n)){i.size&&(t[n]-=i.size);const e=s[i.stack]||{size:0,count:1};e.size=Math.max(e.size,i.horizontal?a.height:a.width),i.size=e.size/e.count,t[n]+=i.size}a.getPadding&&ts(r,a.getPadding());const l=Math.max(0,e.outerWidth-Qi(r,t,\"left\",\"right\")),h=Math.max(0,e.outerHeight-Qi(r,t,\"top\",\"bottom\")),c=l!==t.w,d=h!==t.h;return t.w=l,t.h=h,i.horizontal?{same:c,other:d}:{same:d,other:c}}function is(t,e){const i=e.maxPadding;function s(t){const s={left:0,top:0,right:0,bottom:0};return t.forEach((t=>{s[t]=Math.max(e[t],i[t])})),s}return s(t?[\"left\",\"right\"]:[\"top\",\"bottom\"])}function ss(t,e,i,s){const n=[];let o,a,r,l,h,c;for(o=0,a=t.length,h=0;o<a;++o){r=t[o],l=r.box,l.update(r.width||e.w,r.height||e.h,is(r.horizontal,e));const{same:a,other:d}=es(e,i,r,s);h|=a&&n.length,c=c||d,l.fullSize||n.push(r)}return h&&ss(n,e,i,s)||c}function ns(t,e,i,s,n){t.top=i,t.left=e,t.right=e+s,t.bottom=i+n,t.width=s,t.height=n}function os(t,e,i,s){const n=i.padding;let{x:o,y:a}=e;for(const r of t){const t=r.box,l=s[r.stack]||{count:1,placed:0,weight:1},h=r.stackWeight/l.weight||1;if(r.horizontal){const s=e.w*h,o=l.size||t.height;k(l.start)&&(a=l.start),t.fullSize?ns(t,n.left,a,i.outerWidth-n.right-n.left,o):ns(t,e.left+l.placed,a,s,o),l.start=a,l.placed+=s,a=t.bottom}else{const s=e.h*h,a=l.size||t.width;k(l.start)&&(o=l.start),t.fullSize?ns(t,o,n.top,a,i.outerHeight-n.bottom-n.top):ns(t,o,e.top+l.placed,a,s),l.start=o,l.placed+=s,o=t.right}}e.x=o,e.y=a}var as={addBox(t,e){t.boxes||(t.boxes=[]),e.fullSize=e.fullSize||!1,e.position=e.position||\"top\",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw(t){e.draw(t)}}]},t.boxes.push(e)},removeBox(t,e){const i=t.boxes?t.boxes.indexOf(e):-1;-1!==i&&t.boxes.splice(i,1)},configure(t,e,i){e.fullSize=i.fullSize,e.position=i.position,e.weight=i.weight},update(t,e,i,s){if(!t)return;const n=ki(t.options.layout.padding),o=Math.max(e-n.width,0),a=Math.max(i-n.height,0),r=function(t){const e=function(t){const e=[];let i,s,n,o,a,r;for(i=0,s=(t||[]).length;i<s;++i)n=t[i],({position:o,options:{stack:a,stackWeight:r=1}}=n),e.push({index:i,box:n,pos:o,horizontal:n.isHorizontal(),weight:n.weight,stack:a&&o+a,stackWeight:r});return e}(t),i=Zi(e.filter((t=>t.box.fullSize)),!0),s=Zi(Ki(e,\"left\"),!0),n=Zi(Ki(e,\"right\")),o=Zi(Ki(e,\"top\"),!0),a=Zi(Ki(e,\"bottom\")),r=Gi(e,\"x\"),l=Gi(e,\"y\");return{fullSize:i,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:Ki(e,\"chartArea\"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}(t.boxes),l=r.vertical,h=r.horizontal;u(t.boxes,(t=>{\"function\"==typeof t.beforeLayout&&t.beforeLayout()}));const c=l.reduce(((t,e)=>e.box.options&&!1===e.box.options.display?t:t+1),0)||1,d=Object.freeze({outerWidth:e,outerHeight:i,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/c,hBoxMaxHeight:a/2}),f=Object.assign({},n);ts(f,ki(s));const g=Object.assign({maxPadding:f,w:o,h:a,x:n.left,y:n.top},n),p=Ji(l.concat(h),d);ss(r.fullSize,g,d,p),ss(l,g,d,p),ss(h,g,d,p)&&ss(l,g,d,p),function(t){const e=t.maxPadding;function i(i){const s=Math.max(e[i]-t[i],0);return t[i]+=s,s}t.y+=i(\"top\"),t.x+=i(\"left\"),i(\"right\"),i(\"bottom\")}(g),os(r.leftAndTop,g,d,p),g.x+=g.w,g.y+=g.h,os(r.rightAndBottom,g,d,p),t.chartArea={left:g.left,top:g.top,right:g.left+g.w,bottom:g.top+g.h,height:g.h,width:g.w},u(r.chartArea,(e=>{const i=e.box;Object.assign(i,t.chartArea),i.update(g.w,g.h,{left:0,top:0,right:0,bottom:0})}))}};class rs{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,i){}removeEventListener(t,e,i){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,i,s){return e=Math.max(0,e||t.width),i=i||t.height,{width:e,height:Math.max(0,s?Math.floor(e/s):i)}}isAttached(t){return!0}updateConfig(t){}}class ls extends rs{acquireContext(t){return t&&t.getContext&&t.getContext(\"2d\")||null}updateConfig(t){t.options.animation=!1}}const hs=\"$chartjs\",cs={touchstart:\"mousedown\",touchmove:\"mousemove\",touchend:\"mouseup\",pointerenter:\"mouseenter\",pointerdown:\"mousedown\",pointermove:\"mousemove\",pointerup:\"mouseup\",pointerleave:\"mouseout\",pointerout:\"mouseout\"},ds=t=>null===t||\"\"===t;const us=!!Se&&{passive:!0};function fs(t,e,i){t.canvas.removeEventListener(e,i,us)}function gs(t,e){for(const i of t)if(i===e||i.contains(e))return!0}function ps(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||gs(i.addedNodes,s),e=e&&!gs(i.removedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}function ms(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||gs(i.removedNodes,s),e=e&&!gs(i.addedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}const bs=new Map;let xs=0;function _s(){const t=window.devicePixelRatio;t!==xs&&(xs=t,bs.forEach(((e,i)=>{i.currentDevicePixelRatio!==t&&e()})))}function ys(t,e,i){const s=t.canvas,n=s&&ge(s);if(!n)return;const o=ct(((t,e)=>{const s=n.clientWidth;i(t,e),s<n.clientWidth&&i()}),window),a=new ResizeObserver((t=>{const e=t[0],i=e.contentRect.width,s=e.contentRect.height;0===i&&0===s||o(i,s)}));return a.observe(n),function(t,e){bs.size||window.addEventListener(\"resize\",_s),bs.set(t,e)}(t,o),a}function vs(t,e,i){i&&i.disconnect(),\"resize\"===e&&function(t){bs.delete(t),bs.size||window.removeEventListener(\"resize\",_s)}(t)}function Ms(t,e,i){const s=t.canvas,n=ct((e=>{null!==t.ctx&&i(function(t,e){const i=cs[t.type]||t.type,{x:s,y:n}=ve(t,e);return{type:i,chart:e,native:t,x:void 0!==s?s:null,y:void 0!==n?n:null}}(e,t))}),t);return function(t,e,i){t.addEventListener(e,i,us)}(s,e,n),n}class ws extends rs{acquireContext(t,e){const i=t&&t.getContext&&t.getContext(\"2d\");return i&&i.canvas===t?(function(t,e){const i=t.style,s=t.getAttribute(\"height\"),n=t.getAttribute(\"width\");if(t[hs]={initial:{height:s,width:n,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||\"block\",i.boxSizing=i.boxSizing||\"border-box\",ds(n)){const e=Pe(t,\"width\");void 0!==e&&(t.width=e)}if(ds(s))if(\"\"===t.style.height)t.height=t.width/(e||2);else{const e=Pe(t,\"height\");void 0!==e&&(t.height=e)}}(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e[hs])return!1;const i=e[hs].initial;[\"height\",\"width\"].forEach((t=>{const n=i[t];s(n)?e.removeAttribute(t):e.setAttribute(t,n)}));const n=i.style||{};return Object.keys(n).forEach((t=>{e.style[t]=n[t]})),e.width=e.width,delete e[hs],!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),n={attach:ps,detach:ms,resize:ys}[e]||Ms;s[e]=n(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:vs,detach:vs,resize:vs}[e]||fs)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return we(t,e,i,s)}isAttached(t){const e=ge(t);return!(!e||!e.isConnected)}}function ks(t){return!fe()||\"undefined\"!=typeof OffscreenCanvas&&t instanceof OffscreenCanvas?ls:ws}var Ss=Object.freeze({__proto__:null,BasePlatform:rs,BasicPlatform:ls,DomPlatform:ws,_detectPlatform:ks});const Ps=\"transparent\",Ds={boolean:(t,e,i)=>i>.5?e:t,color(t,e,i){const s=Qt(t||Ps),n=s.valid&&Qt(e||Ps);return n&&n.valid?n.mix(s,i).hexString():e},number:(t,e,i)=>t+(e-t)*i};class Cs{constructor(t,e,i,s){const n=e[i];s=Pi([t.to,s,n,t.from]);const o=Pi([t.from,n,s]);this._active=!0,this._fn=t.fn||Ds[t.type||typeof o],this._easing=fi[t.easing]||fi.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=o,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],n=i-this._start,o=this._duration-n;this._start=i,this._duration=Math.floor(Math.max(o,t.duration)),this._total+=n,this._loop=!!t.loop,this._to=Pi([t.to,e,s,t.from]),this._from=Pi([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,n=this._from,o=this._loop,a=this._to;let r;if(this._active=n!==a&&(o||e<i),!this._active)return this._target[s]=a,void this._notify(!0);e<0?this._target[s]=n:(r=e/i%2,r=o&&r>1?2-r:r,r=this._easing(Math.min(1,Math.max(0,r))),this._target[s]=this._fn(n,a,r))}wait(){const t=this._promises||(this._promises=[]);return new Promise(((e,i)=>{t.push({res:e,rej:i})}))}_notify(t){const e=t?\"res\":\"rej\",i=this._promises||[];for(let t=0;t<i.length;t++)i[t][e]()}}class Os{constructor(t,e){this._chart=t,this._properties=new Map,this.configure(e)}configure(t){if(!o(t))return;const e=Object.keys(ue.animation),i=this._properties;Object.getOwnPropertyNames(t).forEach((s=>{const a=t[s];if(!o(a))return;const r={};for(const t of e)r[t]=a[t];(n(a.properties)&&a.properties||[s]).forEach((t=>{t!==s&&i.has(t)||i.set(t,r)}))}))}_animateOptions(t,e){const i=e.options,s=function(t,e){if(!e)return;let i=t.options;if(!i)return void(t.options=e);i.$shared&&(t.options=i=Object.assign({},i,{$shared:!1,$animations:{}}));return i}(t,i);if(!s)return[];const n=this._createAnimations(s,i);return i.$shared&&function(t,e){const i=[],s=Object.keys(e);for(let e=0;e<s.length;e++){const n=t[s[e]];n&&n.active()&&i.push(n.wait())}return Promise.all(i)}(t.options.$animations,i).then((()=>{t.options=i}),(()=>{})),n}_createAnimations(t,e){const i=this._properties,s=[],n=t.$animations||(t.$animations={}),o=Object.keys(e),a=Date.now();let r;for(r=o.length-1;r>=0;--r){const l=o[r];if(\"$\"===l.charAt(0))continue;if(\"options\"===l){s.push(...this._animateOptions(t,e));continue}const h=e[l];let c=n[l];const d=i.get(l);if(c){if(d&&c.active()){c.update(d,h,a);continue}c.cancel()}d&&d.duration?(n[l]=c=new Cs(d,t,l,h),s.push(c)):t[l]=h}return s}update(t,e){if(0===this._properties.size)return void Object.assign(t,e);const i=this._createAnimations(t,e);return i.length?(xt.add(this._chart,i),!0):void 0}}function As(t,e){const i=t&&t.options||{},s=i.reverse,n=void 0===i.min?e:0,o=void 0===i.max?e:0;return{start:s?o:n,end:s?n:o}}function Ts(t,e){const i=[],s=t._getSortedDatasetMetas(e);let n,o;for(n=0,o=s.length;n<o;++n)i.push(s[n].index);return i}function Ls(t,e,i,s={}){const n=t.keys,o=\"single\"===s.mode;let r,l,h,c;if(null!==e){for(r=0,l=n.length;r<l;++r){if(h=+n[r],h===i){if(s.all)continue;break}c=t.values[h],a(c)&&(o||0===e||F(e)===F(c))&&(e+=c)}return e}}function Es(t,e){const i=t&&t.options.stacked;return i||void 0===i&&void 0!==e.stack}function Rs(t,e,i){const s=t[e]||(t[e]={});return s[i]||(s[i]={})}function Is(t,e,i,s){for(const n of e.getMatchingVisibleMetas(s).reverse()){const e=t[n.index];if(i&&e>0||!i&&e<0)return n.index}return null}function zs(t,e){const{chart:i,_cachedMeta:s}=t,n=i._stacks||(i._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,h=a.axis,c=function(t,e,i){return`${t.id}.${e.id}.${i.stack||i.type}`}(o,a,s),d=e.length;let u;for(let t=0;t<d;++t){const i=e[t],{[l]:o,[h]:d}=i;u=(i._stacks||(i._stacks={}))[h]=Rs(n,c,o),u[r]=d,u._top=Is(u,a,!0,s.type),u._bottom=Is(u,a,!1,s.type);(u._visualValues||(u._visualValues={}))[r]=d}}function Fs(t,e){const i=t.scales;return Object.keys(i).filter((t=>i[t].axis===e)).shift()}function Vs(t,e){const i=t.controller.index,s=t.vScale&&t.vScale.axis;if(s){e=e||t._parsed;for(const t of e){const e=t._stacks;if(!e||void 0===e[s]||void 0===e[s][i])return;delete e[s][i],void 0!==e[s]._visualValues&&void 0!==e[s]._visualValues[i]&&delete e[s]._visualValues[i]}}}const Bs=t=>\"reset\"===t||\"none\"===t,Ns=(t,e)=>e?t:Object.assign({},t);class Ws{static defaults={};static datasetElementType=null;static dataElementType=null;constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=Es(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled(\"filler\")&&console.warn(\"Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options\")}updateIndex(t){this.index!==t&&Vs(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(t,e,i,s)=>\"x\"===t?e:\"r\"===t?s:i,n=e.xAxisID=l(i.xAxisID,Fs(t,\"x\")),o=e.yAxisID=l(i.yAxisID,Fs(t,\"y\")),a=e.rAxisID=l(i.rAxisID,Fs(t,\"r\")),r=e.indexAxis,h=e.iAxisID=s(r,n,o,a),c=e.vAxisID=s(r,o,n,a);e.xScale=this.getScaleForId(n),e.yScale=this.getScaleForId(o),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(h),e.vScale=this.getScaleForId(c)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update(\"reset\")}_destroy(){const t=this._cachedMeta;this._data&&rt(this._data,this),t._stacked&&Vs(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(o(e))this._data=function(t){const e=Object.keys(t),i=new Array(e.length);let s,n,o;for(s=0,n=e.length;s<n;++s)o=e[s],i[s]={x:o,y:t[o]};return i}(e);else if(i!==e){if(i){rt(i,this);const t=this._cachedMeta;Vs(t),t._parsed=[]}e&&Object.isExtensible(e)&&at(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,i=this.getDataset();let s=!1;this._dataCheck();const n=e._stacked;e._stacked=Es(e.vScale,e),e.stack!==i.stack&&(s=!0,Vs(e),e.stack=i.stack),this._resyncElements(t),(s||n!==e._stacked)&&zs(this,e._parsed)}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),i=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(i,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:i,_data:s}=this,{iScale:a,_stacked:r}=i,l=a.axis;let h,c,d,u=0===t&&e===s.length||i._sorted,f=t>0&&i._parsed[t-1];if(!1===this._parsing)i._parsed=s,i._sorted=!0,d=s;else{d=n(s[t])?this.parseArrayData(i,s,t,e):o(s[t])?this.parseObjectData(i,s,t,e):this.parsePrimitiveData(i,s,t,e);const a=()=>null===c[l]||f&&c[l]<f[l];for(h=0;h<e;++h)i._parsed[h+t]=c=d[h],u&&(a()&&(u=!1),f=c);i._sorted=u}r&&zs(this,d)}parsePrimitiveData(t,e,i,s){const{iScale:n,vScale:o}=t,a=n.axis,r=o.axis,l=n.getLabels(),h=n===o,c=new Array(s);let d,u,f;for(d=0,u=s;d<u;++d)f=d+i,c[d]={[a]:h||n.parse(l[f],f),[r]:o.parse(e[f],f)};return c}parseArrayData(t,e,i,s){const{xScale:n,yScale:o}=t,a=new Array(s);let r,l,h,c;for(r=0,l=s;r<l;++r)h=r+i,c=e[h],a[r]={x:n.parse(c[0],h),y:o.parse(c[1],h)};return a}parseObjectData(t,e,i,s){const{xScale:n,yScale:o}=t,{xAxisKey:a=\"x\",yAxisKey:r=\"y\"}=this._parsing,l=new Array(s);let h,c,d,u;for(h=0,c=s;h<c;++h)d=h+i,u=e[d],l[h]={x:n.parse(M(u,a),d),y:o.parse(M(u,r),d)};return l}getParsed(t){return this._cachedMeta._parsed[t]}getDataElement(t){return this._cachedMeta.data[t]}applyStack(t,e,i){const s=this.chart,n=this._cachedMeta,o=e[t.axis];return Ls({keys:Ts(s,!0),values:e._stacks[t.axis]._visualValues},o,n.index,{mode:i})}updateRangeFromParsed(t,e,i,s){const n=i[e.axis];let o=null===n?NaN:n;const a=s&&i._stacks[e.axis];s&&a&&(s.values=a,o=Ls(s,n,this._cachedMeta.index)),t.min=Math.min(t.min,o),t.max=Math.max(t.max,o)}getMinMax(t,e){const i=this._cachedMeta,s=i._parsed,n=i._sorted&&t===i.iScale,o=s.length,r=this._getOtherScale(t),l=((t,e,i)=>t&&!e.hidden&&e._stacked&&{keys:Ts(i,!0),values:null})(e,i,this.chart),h={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:c,max:d}=function(t){const{min:e,max:i,minDefined:s,maxDefined:n}=t.getUserBounds();return{min:s?e:Number.NEGATIVE_INFINITY,max:n?i:Number.POSITIVE_INFINITY}}(r);let u,f;function g(){f=s[u];const e=f[r.axis];return!a(f[t.axis])||c>e||d<e}for(u=0;u<o&&(g()||(this.updateRangeFromParsed(h,t,f,l),!n));++u);if(n)for(u=o-1;u>=0;--u)if(!g()){this.updateRangeFromParsed(h,t,f,l);break}return h}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,n,o;for(s=0,n=e.length;s<n;++s)o=e[s][t.axis],a(o)&&i.push(o);return i}getMaxOverflow(){return!1}getLabelAndValue(t){const e=this._cachedMeta,i=e.iScale,s=e.vScale,n=this.getParsed(t);return{label:i?\"\"+i.getLabelForValue(n[i.axis]):\"\",value:s?\"\"+s.getLabelForValue(n[s.axis]):\"\"}}_update(t){const e=this._cachedMeta;this.update(t||\"default\"),e._clip=function(t){let e,i,s,n;return o(t)?(e=t.top,i=t.right,s=t.bottom,n=t.left):e=i=s=n=t,{top:e,right:i,bottom:s,left:n,disabled:!1===t}}(l(this.options.clip,function(t,e,i){if(!1===i)return!1;const s=As(t,i),n=As(e,i);return{top:n.end,right:s.end,bottom:n.start,left:s.start}}(e.xScale,e.yScale,this.getMaxOverflow())))}update(t){}draw(){const t=this._ctx,e=this.chart,i=this._cachedMeta,s=i.data||[],n=e.chartArea,o=[],a=this._drawStart||0,r=this._drawCount||s.length-a,l=this.options.drawActiveElementsOnTop;let h;for(i.dataset&&i.dataset.draw(t,n,a,r),h=a;h<a+r;++h){const e=s[h];e.hidden||(e.active&&l?o.push(e):e.draw(t,n))}for(h=0;h<o.length;++h)o[h].draw(t,n)}getStyle(t,e){const i=e?\"active\":\"default\";return void 0===t&&this._cachedMeta.dataset?this.resolveDatasetElementOptions(i):this.resolveDataElementOptions(t||0,i)}getContext(t,e,i){const s=this.getDataset();let n;if(t>=0&&t<this._cachedMeta.data.length){const e=this._cachedMeta.data[t];n=e.$context||(e.$context=function(t,e,i){return Ci(t,{active:!1,dataIndex:e,parsed:void 0,raw:void 0,element:i,index:e,mode:\"default\",type:\"data\"})}(this.getContext(),t,e)),n.parsed=this.getParsed(t),n.raw=s.data[t],n.index=n.dataIndex=t}else n=this.$context||(this.$context=function(t,e){return Ci(t,{active:!1,dataset:void 0,datasetIndex:e,index:e,mode:\"default\",type:\"dataset\"})}(this.chart.getContext(),this.index)),n.dataset=s,n.index=n.datasetIndex=this.index;return n.active=!!e,n.mode=i,n}resolveDatasetElementOptions(t){return this._resolveElementOptions(this.datasetElementType.id,t)}resolveDataElementOptions(t,e){return this._resolveElementOptions(this.dataElementType.id,e,t)}_resolveElementOptions(t,e=\"default\",i){const s=\"active\"===e,n=this._cachedDataOpts,o=t+\"-\"+e,a=n[o],r=this.enableOptionSharing&&k(i);if(a)return Ns(a,r);const l=this.chart.config,h=l.datasetElementScopeKeys(this._type,t),c=s?[`${t}Hover`,\"hover\",t,\"\"]:[t,\"\"],d=l.getOptionScopes(this.getDataset(),h),u=Object.keys(ue.elements[t]),f=l.resolveNamedOptions(d,u,(()=>this.getContext(i,s,e)),c);return f.$shared&&(f.$shared=r,n[o]=Object.freeze(Ns(f,r))),f}_resolveAnimations(t,e,i){const s=this.chart,n=this._cachedDataOpts,o=`animation-${e}`,a=n[o];if(a)return a;let r;if(!1!==s.options.animation){const s=this.chart.config,n=s.datasetAnimationScopeKeys(this._type,e),o=s.getOptionScopes(this.getDataset(),n);r=s.createResolver(o,this.getContext(t,i,e))}const l=new Os(s,r&&r.animations);return r&&r._cacheable&&(n[o]=Object.freeze(l)),l}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||Bs(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const i=this.resolveDataElementOptions(t,e),s=this._sharedOptions,n=this.getSharedOptions(i),o=this.includeOptions(e,n)||n!==s;return this.updateSharedOptions(n,e,i),{sharedOptions:n,includeOptions:o}}updateElement(t,e,i,s){Bs(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!Bs(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const n=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(n)||n})}removeHoverStyle(t,e,i){this._setStyle(t,i,\"active\",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,\"active\",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,\"active\",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,\"active\",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[t,e,i]of this._syncList)this[t](e,i);this._syncList=[];const s=i.length,n=e.length,o=Math.min(n,s);o&&this.parse(0,o),n>s?this._insertElements(s,n-s,t):n<s&&this._removeElements(n,s-n)}_insertElements(t,e,i=!0){const s=this._cachedMeta,n=s.data,o=t+e;let a;const r=t=>{for(t.length+=e,a=t.length-1;a>=o;a--)t[a]=t[a-e]};for(r(n),a=t;a<o;++a)n[a]=new this.dataElementType;this._parsing&&r(s._parsed),this.parse(t,e),i&&this.updateElements(n,t,e,\"reset\")}updateElements(t,e,i,s){}_removeElements(t,e){const i=this._cachedMeta;if(this._parsing){const s=i._parsed.splice(t,e);i._stacked&&Vs(i,s)}i.data.splice(t,e)}_sync(t){if(this._parsing)this._syncList.push(t);else{const[e,i,s]=t;this[e](i,s)}this.chart._dataChanges.push([this.index,...t])}_onDataPush(){const t=arguments.length;this._sync([\"_insertElements\",this.getDataset().data.length-t,t])}_onDataPop(){this._sync([\"_removeElements\",this._cachedMeta.data.length-1,1])}_onDataShift(){this._sync([\"_removeElements\",0,1])}_onDataSplice(t,e){e&&this._sync([\"_removeElements\",t,e]);const i=arguments.length-2;i&&this._sync([\"_insertElements\",t,i])}_onDataUnshift(){this._sync([\"_insertElements\",0,arguments.length])}}class Hs{static defaults={};static defaultRoutes=void 0;x;y;active=!1;options;$animations;tooltipPosition(t){const{x:e,y:i}=this.getProps([\"x\",\"y\"],t);return{x:e,y:i}}hasValue(){return W(this.x)&&W(this.y)}getProps(t,e){const i=this.$animations;if(!e||!i)return this;const s={};return t.forEach((t=>{s[t]=i[t]&&i[t].active()?i[t]._to:this[t]})),s}}function js(t,e){const i=t.options.ticks,n=function(t){const e=t.options.offset,i=t._tickSize(),s=t._length/i+(e?0:1),n=t._maxLength/i;return Math.floor(Math.min(s,n))}(t),o=Math.min(i.maxTicksLimit||n,n),a=i.major.enabled?function(t){const e=[];let i,s;for(i=0,s=t.length;i<s;i++)t[i].major&&e.push(i);return e}(e):[],r=a.length,l=a[0],h=a[r-1],c=[];if(r>o)return function(t,e,i,s){let n,o=0,a=i[0];for(s=Math.ceil(s),n=0;n<t.length;n++)n===a&&(e.push(t[n]),o++,a=i[o*s])}(e,c,a,r/o),c;const d=function(t,e,i){const s=function(t){const e=t.length;let i,s;if(e<2)return!1;for(s=t[0],i=1;i<e;++i)if(t[i]-t[i-1]!==s)return!1;return s}(t),n=e.length/i;if(!s)return Math.max(n,1);const o=N(s);for(let t=0,e=o.length-1;t<e;t++){const e=o[t];if(e>n)return e}return Math.max(n,1)}(a,e,o);if(r>0){let t,i;const n=r>1?Math.round((h-l)/(r-1)):null;for($s(e,c,d,s(n)?0:l-n,l),t=0,i=r-1;t<i;t++)$s(e,c,d,a[t],a[t+1]);return $s(e,c,d,h,s(n)?e.length:h+n),c}return $s(e,c,d),c}function $s(t,e,i,s,n){const o=l(s,0),a=Math.min(l(n,t.length),t.length);let r,h,c,d=0;for(i=Math.ceil(i),n&&(r=n-s,i=r/Math.floor(r/i)),c=o;c<0;)d++,c=Math.round(o+d*i);for(h=Math.max(o,0);h<a;h++)h===c&&(e.push(t[h]),d++,c=Math.round(o+d*i))}const Ys=(t,e,i)=>\"top\"===e||\"left\"===e?t[e]+i:t[e]-i,Us=(t,e)=>Math.min(e||t,t);function Xs(t,e){const i=[],s=t.length/e,n=t.length;let o=0;for(;o<n;o+=s)i.push(t[Math.floor(o)]);return i}function qs(t,e,i){const s=t.ticks.length,n=Math.min(e,s-1),o=t._startPixel,a=t._endPixel,r=1e-6;let l,h=t.getPixelForTick(n);if(!(i&&(l=1===s?Math.max(h-o,a-h):0===e?(t.getPixelForTick(1)-h)/2:(h-t.getPixelForTick(n-1))/2,h+=n<e?l:-l,h<o-r||h>a+r)))return h}function Ks(t){return t.drawTicks?t.tickLength:0}function Gs(t,e){if(!t.display)return 0;const i=Si(t.font,e),s=ki(t.padding);return(n(t.text)?t.text.length:1)*i.lineHeight+s.height}function Zs(t,e,i){let s=ut(t);return(i&&\"right\"!==e||!i&&\"right\"===e)&&(s=(t=>\"left\"===t?\"right\":\"right\"===t?\"left\":t)(s)),s}class Js extends Hs{constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void 0,this.ctx=t.ctx,this.chart=t.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,this._userMin=this.parse(t.min),this._userMax=this.parse(t.max),this._suggestedMin=this.parse(t.suggestedMin),this._suggestedMax=this.parse(t.suggestedMax)}parse(t,e){return t}getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:i,_suggestedMax:s}=this;return t=r(t,Number.POSITIVE_INFINITY),e=r(e,Number.NEGATIVE_INFINITY),i=r(i,Number.POSITIVE_INFINITY),s=r(s,Number.NEGATIVE_INFINITY),{min:r(t,i),max:r(e,s),minDefined:a(t),maxDefined:a(e)}}getMinMax(t){let e,{min:i,max:s,minDefined:n,maxDefined:o}=this.getUserBounds();if(n&&o)return{min:i,max:s};const a=this.getMatchingVisibleMetas();for(let r=0,l=a.length;r<l;++r)e=a[r].controller.getMinMax(this,t),n||(i=Math.min(i,e.min)),o||(s=Math.max(s,e.max));return i=o&&i>s?s:i,s=n&&i>s?i:s,{min:r(i,r(s,i)),max:r(s,r(i,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){d(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:n,ticks:o}=this.options,a=o.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Di(this,n,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const r=a<this.ticks.length;this._convertTicksToLabels(r?Xs(this.ticks,a):this.ticks),this.configure(),this.beforeCalculateLabelRotation(),this.calculateLabelRotation(),this.afterCalculateLabelRotation(),o.display&&(o.autoSkip||\"auto\"===o.source)&&(this.ticks=js(this,this.ticks),this._labelSizes=null,this.afterAutoSkip()),r&&this._convertTicksToLabels(this.ticks),this.beforeFit(),this.fit(),this.afterFit(),this.afterUpdate()}configure(){let t,e,i=this.options.reverse;this.isHorizontal()?(t=this.left,e=this.right):(t=this.top,e=this.bottom,i=!i),this._startPixel=t,this._endPixel=e,this._reversePixels=i,this._length=e-t,this._alignToPixels=this.options.alignToPixels}afterUpdate(){d(this.options.afterUpdate,[this])}beforeSetDimensions(){d(this.options.beforeSetDimensions,[this])}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=0,this.right=this.width):(this.height=this.maxHeight,this.top=0,this.bottom=this.height),this.paddingLeft=0,this.paddingTop=0,this.paddingRight=0,this.paddingBottom=0}afterSetDimensions(){d(this.options.afterSetDimensions,[this])}_callHooks(t){this.chart.notifyPlugins(t,this.getContext()),d(this.options[t],[this])}beforeDataLimits(){this._callHooks(\"beforeDataLimits\")}determineDataLimits(){}afterDataLimits(){this._callHooks(\"afterDataLimits\")}beforeBuildTicks(){this._callHooks(\"beforeBuildTicks\")}buildTicks(){return[]}afterBuildTicks(){this._callHooks(\"afterBuildTicks\")}beforeTickToLabelConversion(){d(this.options.beforeTickToLabelConversion,[this])}generateTickLabels(t){const e=this.options.ticks;let i,s,n;for(i=0,s=t.length;i<s;i++)n=t[i],n.label=d(e.callback,[n.value,i,t],this)}afterTickToLabelConversion(){d(this.options.afterTickToLabelConversion,[this])}beforeCalculateLabelRotation(){d(this.options.beforeCalculateLabelRotation,[this])}calculateLabelRotation(){const t=this.options,e=t.ticks,i=Us(this.ticks.length,t.ticks.maxTicksLimit),s=e.minRotation||0,n=e.maxRotation;let o,a,r,l=s;if(!this._isVisible()||!e.display||s>=n||i<=1||!this.isHorizontal())return void(this.labelRotation=s);const h=this._getLabelSizes(),c=h.widest.width,d=h.highest.height,u=J(this.chart.width-c,0,this.maxWidth);o=t.offset?this.maxWidth/i:u/(i-1),c+6>o&&(o=u/(i-(t.offset?.5:1)),a=this.maxHeight-Ks(t.grid)-e.padding-Gs(t.title,this.chart.options.font),r=Math.sqrt(c*c+d*d),l=Y(Math.min(Math.asin(J((h.highest.height+6)/o,-1,1)),Math.asin(J(a/r,-1,1))-Math.asin(J(d/r,-1,1)))),l=Math.max(s,Math.min(n,l))),this.labelRotation=l}afterCalculateLabelRotation(){d(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){d(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:n}}=this,o=this._isVisible(),a=this.isHorizontal();if(o){const o=Gs(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Ks(n)+o):(t.height=this.maxHeight,t.width=Ks(n)+o),i.display&&this.ticks.length){const{first:e,last:s,widest:n,highest:o}=this._getLabelSizes(),r=2*i.padding,l=$(this.labelRotation),h=Math.cos(l),c=Math.sin(l);if(a){const e=i.mirror?0:c*n.width+h*o.height;t.height=Math.min(this.maxHeight,t.height+e+r)}else{const e=i.mirror?0:h*n.width+c*o.height;t.width=Math.min(this.maxWidth,t.width+e+r)}this._calculatePadding(e,s,c,h)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:n,padding:o},position:a}=this.options,r=0!==this.labelRotation,l=\"top\"!==a&&\"x\"===this.axis;if(this.isHorizontal()){const a=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let c=0,d=0;r?l?(c=s*t.width,d=i*e.height):(c=i*t.height,d=s*e.width):\"start\"===n?d=e.width:\"end\"===n?c=t.width:\"inner\"!==n&&(c=t.width/2,d=e.width/2),this.paddingLeft=Math.max((c-a+o)*this.width/(this.width-a),0),this.paddingRight=Math.max((d-h+o)*this.width/(this.width-h),0)}else{let i=e.height/2,s=t.height/2;\"start\"===n?(i=0,s=t.height):\"end\"===n&&(i=e.height,s=0),this.paddingTop=i+o,this.paddingBottom=s+o}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){d(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return\"top\"===e||\"bottom\"===e||\"x\"===t}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){let e,i;for(this.beforeTickToLabelConversion(),this.generateTickLabels(t),e=0,i=t.length;e<i;e++)s(t[e].label)&&(t.splice(e,1),i--,e--);this.afterTickToLabelConversion()}_getLabelSizes(){let t=this._labelSizes;if(!t){const e=this.options.ticks.sampleSize;let i=this.ticks;e<i.length&&(i=Xs(i,e)),this._labelSizes=t=this._computeLabelSizes(i,i.length,this.options.ticks.maxTicksLimit)}return t}_computeLabelSizes(t,e,i){const{ctx:o,_longestTextCache:a}=this,r=[],l=[],h=Math.floor(e/Us(e,i));let c,d,f,g,p,m,b,x,_,y,v,M=0,w=0;for(c=0;c<e;c+=h){if(g=t[c].label,p=this._resolveTickFontOptions(c),o.font=m=p.string,b=a[m]=a[m]||{data:{},gc:[]},x=p.lineHeight,_=y=0,s(g)||n(g)){if(n(g))for(d=0,f=g.length;d<f;++d)v=g[d],s(v)||n(v)||(_=Ce(o,b.data,b.gc,_,v),y+=x)}else _=Ce(o,b.data,b.gc,_,g),y=x;r.push(_),l.push(y),M=Math.max(_,M),w=Math.max(y,w)}!function(t,e){u(t,(t=>{const i=t.gc,s=i.length/2;let n;if(s>e){for(n=0;n<s;++n)delete t.data[i[n]];i.splice(0,s)}}))}(a,e);const k=r.indexOf(M),S=l.indexOf(w),P=t=>({width:r[t]||0,height:l[t]||0});return{first:P(0),last:P(e-1),widest:P(k),highest:P(S),widths:r,heights:l}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return Q(this._alignToPixels?Ae(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&t<e.length){const i=e[t];return i.$context||(i.$context=function(t,e,i){return Ci(t,{tick:i,index:e,type:\"tick\"})}(this.getContext(),t,i))}return this.$context||(this.$context=Ci(this.chart.getContext(),{scale:this,type:\"scale\"}))}_tickSize(){const t=this.options.ticks,e=$(this.labelRotation),i=Math.abs(Math.cos(e)),s=Math.abs(Math.sin(e)),n=this._getLabelSizes(),o=t.autoSkipPadding||0,a=n?n.widest.width+o:0,r=n?n.highest.height+o:0;return this.isHorizontal()?r*i>a*s?a/i:r/s:r*s<a*i?r/i:a/s}_isVisible(){const t=this.options.display;return\"auto\"!==t?!!t:this.getMatchingVisibleMetas().length>0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:n,position:a,border:r}=s,h=n.offset,c=this.isHorizontal(),d=this.ticks.length+(h?1:0),u=Ks(n),f=[],g=r.setContext(this.getContext()),p=g.display?g.width:0,m=p/2,b=function(t){return Ae(i,t,p)};let x,_,y,v,M,w,k,S,P,D,C,O;if(\"top\"===a)x=b(this.bottom),w=this.bottom-u,S=x-m,D=b(t.top)+m,O=t.bottom;else if(\"bottom\"===a)x=b(this.top),D=t.top,O=b(t.bottom)-m,w=x+m,S=this.top+u;else if(\"left\"===a)x=b(this.right),M=this.right-u,k=x-m,P=b(t.left)+m,C=t.right;else if(\"right\"===a)x=b(this.left),P=t.left,C=b(t.right)-m,M=x+m,k=this.left+u;else if(\"x\"===e){if(\"center\"===a)x=b((t.top+t.bottom)/2+.5);else if(o(a)){const t=Object.keys(a)[0],e=a[t];x=b(this.chart.scales[t].getPixelForValue(e))}D=t.top,O=t.bottom,w=x+m,S=w+u}else if(\"y\"===e){if(\"center\"===a)x=b((t.left+t.right)/2);else if(o(a)){const t=Object.keys(a)[0],e=a[t];x=b(this.chart.scales[t].getPixelForValue(e))}M=x-m,k=M-u,P=t.left,C=t.right}const A=l(s.ticks.maxTicksLimit,d),T=Math.max(1,Math.ceil(d/A));for(_=0;_<d;_+=T){const t=this.getContext(_),e=n.setContext(t),s=r.setContext(t),o=e.lineWidth,a=e.color,l=s.dash||[],d=s.dashOffset,u=e.tickWidth,g=e.tickColor,p=e.tickBorderDash||[],m=e.tickBorderDashOffset;y=qs(this,_,h),void 0!==y&&(v=Ae(i,y,o),c?M=k=P=C=v:w=S=D=O=v,f.push({tx1:M,ty1:w,tx2:k,ty2:S,x1:P,y1:D,x2:C,y2:O,width:o,color:a,borderDash:l,borderDashOffset:d,tickWidth:u,tickColor:g,tickBorderDash:p,tickBorderDashOffset:m}))}return this._ticksLength=d,this._borderValue=x,f}_computeLabelItems(t){const e=this.axis,i=this.options,{position:s,ticks:a}=i,r=this.isHorizontal(),l=this.ticks,{align:h,crossAlign:c,padding:d,mirror:u}=a,f=Ks(i.grid),g=f+d,p=u?-d:g,m=-$(this.labelRotation),b=[];let x,_,y,v,M,w,k,S,P,D,C,O,A=\"middle\";if(\"top\"===s)w=this.bottom-p,k=this._getXAxisLabelAlignment();else if(\"bottom\"===s)w=this.top+p,k=this._getXAxisLabelAlignment();else if(\"left\"===s){const t=this._getYAxisLabelAlignment(f);k=t.textAlign,M=t.x}else if(\"right\"===s){const t=this._getYAxisLabelAlignment(f);k=t.textAlign,M=t.x}else if(\"x\"===e){if(\"center\"===s)w=(t.top+t.bottom)/2+g;else if(o(s)){const t=Object.keys(s)[0],e=s[t];w=this.chart.scales[t].getPixelForValue(e)+g}k=this._getXAxisLabelAlignment()}else if(\"y\"===e){if(\"center\"===s)M=(t.left+t.right)/2-g;else if(o(s)){const t=Object.keys(s)[0],e=s[t];M=this.chart.scales[t].getPixelForValue(e)}k=this._getYAxisLabelAlignment(f).textAlign}\"y\"===e&&(\"start\"===h?A=\"top\":\"end\"===h&&(A=\"bottom\"));const T=this._getLabelSizes();for(x=0,_=l.length;x<_;++x){y=l[x],v=y.label;const t=a.setContext(this.getContext(x));S=this.getPixelForTick(x)+a.labelOffset,P=this._resolveTickFontOptions(x),D=P.lineHeight,C=n(v)?v.length:1;const e=C/2,i=t.color,o=t.textStrokeColor,h=t.textStrokeWidth;let d,f=k;if(r?(M=S,\"inner\"===k&&(f=x===_-1?this.options.reverse?\"left\":\"right\":0===x?this.options.reverse?\"right\":\"left\":\"center\"),O=\"top\"===s?\"near\"===c||0!==m?-C*D+D/2:\"center\"===c?-T.highest.height/2-e*D+D:-T.highest.height+D/2:\"near\"===c||0!==m?D/2:\"center\"===c?T.highest.height/2-e*D:T.highest.height-C*D,u&&(O*=-1),0===m||t.showLabelBackdrop||(M+=D/2*Math.sin(m))):(w=S,O=(1-C)*D/2),t.showLabelBackdrop){const e=ki(t.backdropPadding),i=T.heights[x],s=T.widths[x];let n=O-e.top,o=0-e.left;switch(A){case\"middle\":n-=i/2;break;case\"bottom\":n-=i}switch(k){case\"center\":o-=s/2;break;case\"right\":o-=s}d={left:o,top:n,width:s+e.width,height:i+e.height,color:t.backdropColor}}b.push({label:v,font:P,textOffset:O,options:{rotation:m,color:i,strokeColor:o,strokeWidth:h,textAlign:f,textBaseline:A,translation:[M,w],backdrop:d}})}return b}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-$(this.labelRotation))return\"top\"===t?\"left\":\"right\";let i=\"center\";return\"start\"===e.align?i=\"left\":\"end\"===e.align?i=\"right\":\"inner\"===e.align&&(i=\"inner\"),i}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:i,mirror:s,padding:n}}=this.options,o=t+n,a=this._getLabelSizes().widest.width;let r,l;return\"left\"===e?s?(l=this.right+n,\"near\"===i?r=\"left\":\"center\"===i?(r=\"center\",l+=a/2):(r=\"right\",l+=a)):(l=this.right-o,\"near\"===i?r=\"right\":\"center\"===i?(r=\"center\",l-=a/2):(r=\"left\",l=this.left)):\"right\"===e?s?(l=this.left+n,\"near\"===i?r=\"right\":\"center\"===i?(r=\"center\",l-=a/2):(r=\"left\",l-=a)):(l=this.left+o,\"near\"===i?r=\"left\":\"center\"===i?(r=\"center\",l+=a/2):(r=\"right\",l=this.right)):r=\"right\",{textAlign:r,x:l}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;return\"left\"===e||\"right\"===e?{top:0,left:this.left,bottom:t.height,right:this.right}:\"top\"===e||\"bottom\"===e?{top:this.top,left:0,bottom:this.bottom,right:t.width}:void 0}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:i,top:s,width:n,height:o}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(i,s,n,o),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const i=this.ticks.findIndex((e=>e.value===t));if(i>=0){return e.setContext(this.getContext(i)).lineWidth}return 0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let n,o;const a=(t,e,s)=>{s.width&&s.color&&(i.save(),i.lineWidth=s.width,i.strokeStyle=s.color,i.setLineDash(s.borderDash||[]),i.lineDashOffset=s.borderDashOffset,i.beginPath(),i.moveTo(t.x,t.y),i.lineTo(e.x,e.y),i.stroke(),i.restore())};if(e.display)for(n=0,o=s.length;n<o;++n){const t=s[n];e.drawOnChartArea&&a({x:t.x1,y:t.y1},{x:t.x2,y:t.y2},t),e.drawTicks&&a({x:t.tx1,y:t.ty1},{x:t.tx2,y:t.ty2},{color:t.tickColor,width:t.tickWidth,borderDash:t.tickBorderDash,borderDashOffset:t.tickBorderDashOffset})}}drawBorder(){const{chart:t,ctx:e,options:{border:i,grid:s}}=this,n=i.setContext(this.getContext()),o=i.display?n.width:0;if(!o)return;const a=s.setContext(this.getContext(0)).lineWidth,r=this._borderValue;let l,h,c,d;this.isHorizontal()?(l=Ae(t,this.left,o)-o/2,h=Ae(t,this.right,a)+a/2,c=d=r):(c=Ae(t,this.top,o)-o/2,d=Ae(t,this.bottom,a)+a/2,l=h=r),e.save(),e.lineWidth=n.width,e.strokeStyle=n.color,e.beginPath(),e.moveTo(l,c),e.lineTo(h,d),e.stroke(),e.restore()}drawLabels(t){if(!this.options.ticks.display)return;const e=this.ctx,i=this._computeLabelArea();i&&Ie(e,i);const s=this.getLabelItems(t);for(const t of s){const i=t.options,s=t.font;We(e,t.label,0,t.textOffset,s,i)}i&&ze(e)}drawTitle(){const{ctx:t,options:{position:e,title:i,reverse:s}}=this;if(!i.display)return;const a=Si(i.font),r=ki(i.padding),l=i.align;let h=a.lineHeight/2;\"bottom\"===e||\"center\"===e||o(e)?(h+=r.bottom,n(i.text)&&(h+=a.lineHeight*(i.text.length-1))):h+=r.top;const{titleX:c,titleY:d,maxWidth:u,rotation:f}=function(t,e,i,s){const{top:n,left:a,bottom:r,right:l,chart:h}=t,{chartArea:c,scales:d}=h;let u,f,g,p=0;const m=r-n,b=l-a;if(t.isHorizontal()){if(f=ft(s,a,l),o(i)){const t=Object.keys(i)[0],s=i[t];g=d[t].getPixelForValue(s)+m-e}else g=\"center\"===i?(c.bottom+c.top)/2+m-e:Ys(t,i,e);u=l-a}else{if(o(i)){const t=Object.keys(i)[0],s=i[t];f=d[t].getPixelForValue(s)-b+e}else f=\"center\"===i?(c.left+c.right)/2-b+e:Ys(t,i,e);g=ft(s,r,n),p=\"left\"===i?-E:E}return{titleX:f,titleY:g,maxWidth:u,rotation:p}}(this,h,e,l);We(t,i.text,0,0,a,{color:i.color,maxWidth:u,rotation:f,textAlign:Zs(l,e,s),textBaseline:\"middle\",translation:[c,d]})}draw(t){this._isVisible()&&(this.drawBackground(),this.drawGrid(t),this.drawBorder(),this.drawTitle(),this.drawLabels(t))}_layers(){const t=this.options,e=t.ticks&&t.ticks.z||0,i=l(t.grid&&t.grid.z,-1),s=l(t.border&&t.border.z,0);return this._isVisible()&&this.draw===Js.prototype.draw?[{z:i,draw:t=>{this.drawBackground(),this.drawGrid(t),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:e,draw:t=>{this.drawLabels(t)}}]:[{z:e,draw:t=>{this.draw(t)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+\"AxisID\",s=[];let n,o;for(n=0,o=e.length;n<o;++n){const o=e[n];o[i]!==this.id||t&&o.type!==t||s.push(o)}return s}_resolveTickFontOptions(t){return Si(this.options.ticks.setContext(this.getContext(t)).font)}_maxDigits(){const t=this._resolveTickFontOptions(0).lineHeight;return(this.isHorizontal()?this.width:this.height)/t}}class Qs{constructor(t,e,i){this.type=t,this.scope=e,this.override=i,this.items=Object.create(null)}isForType(t){return Object.prototype.isPrototypeOf.call(this.type.prototype,t.prototype)}register(t){const e=Object.getPrototypeOf(t);let i;(function(t){return\"id\"in t&&\"defaults\"in t})(e)&&(i=this.register(e));const s=this.items,n=t.id,o=this.scope+\".\"+n;if(!n)throw new Error(\"class does not have id: \"+t);return n in s||(s[n]=t,function(t,e,i){const s=b(Object.create(null),[i?ue.get(i):{},ue.get(e),t.defaults]);ue.set(e,s),t.defaultRoutes&&function(t,e){Object.keys(e).forEach((i=>{const s=i.split(\".\"),n=s.pop(),o=[t].concat(s).join(\".\"),a=e[i].split(\".\"),r=a.pop(),l=a.join(\".\");ue.route(o,n,l,r)}))}(e,t.defaultRoutes);t.descriptors&&ue.describe(e,t.descriptors)}(t,o,i),this.override&&ue.override(t.id,t.overrides)),o}get(t){return this.items[t]}unregister(t){const e=this.items,i=t.id,s=this.scope;i in e&&delete e[i],s&&i in ue[s]&&(delete ue[s][i],this.override&&delete re[i])}}class tn{constructor(){this.controllers=new Qs(Ws,\"datasets\",!0),this.elements=new Qs(Hs,\"elements\"),this.plugins=new Qs(Object,\"plugins\"),this.scales=new Qs(Js,\"scales\"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each(\"register\",t)}remove(...t){this._each(\"unregister\",t)}addControllers(...t){this._each(\"register\",t,this.controllers)}addElements(...t){this._each(\"register\",t,this.elements)}addPlugins(...t){this._each(\"register\",t,this.plugins)}addScales(...t){this._each(\"register\",t,this.scales)}getController(t){return this._get(t,this.controllers,\"controller\")}getElement(t){return this._get(t,this.elements,\"element\")}getPlugin(t){return this._get(t,this.plugins,\"plugin\")}getScale(t){return this._get(t,this.scales,\"scale\")}removeControllers(...t){this._each(\"unregister\",t,this.controllers)}removeElements(...t){this._each(\"unregister\",t,this.elements)}removePlugins(...t){this._each(\"unregister\",t,this.plugins)}removeScales(...t){this._each(\"unregister\",t,this.scales)}_each(t,e,i){[...e].forEach((e=>{const s=i||this._getRegistryForType(e);i||s.isForType(e)||s===this.plugins&&e.id?this._exec(t,s,e):u(e,(e=>{const s=i||this._getRegistryForType(e);this._exec(t,s,e)}))}))}_exec(t,e,i){const s=w(t);d(i[\"before\"+s],[],i),e[t](i),d(i[\"after\"+s],[],i)}_getRegistryForType(t){for(let e=0;e<this._typedRegistries.length;e++){const i=this._typedRegistries[e];if(i.isForType(t))return i}return this.plugins}_get(t,e,i){const s=e.get(t);if(void 0===s)throw new Error('\"'+t+'\" is not a registered '+i+\".\");return s}}var en=new tn;class sn{constructor(){this._init=[]}notify(t,e,i,s){\"beforeInit\"===e&&(this._init=this._createDescriptors(t,!0),this._notify(this._init,t,\"install\"));const n=s?this._descriptors(t).filter(s):this._descriptors(t),o=this._notify(n,t,e,i);return\"afterDestroy\"===e&&(this._notify(n,t,\"stop\"),this._notify(this._init,t,\"uninstall\")),o}_notify(t,e,i,s){s=s||{};for(const n of t){const t=n.plugin;if(!1===d(t[i],[e,s,n.options],t)&&s.cancelable)return!1}return!0}invalidate(){s(this._cache)||(this._oldCache=this._cache,this._cache=void 0)}_descriptors(t){if(this._cache)return this._cache;const e=this._cache=this._createDescriptors(t);return this._notifyStateChanges(t),e}_createDescriptors(t,e){const i=t&&t.config,s=l(i.options&&i.options.plugins,{}),n=function(t){const e={},i=[],s=Object.keys(en.plugins.items);for(let t=0;t<s.length;t++)i.push(en.getPlugin(s[t]));const n=t.plugins||[];for(let t=0;t<n.length;t++){const s=n[t];-1===i.indexOf(s)&&(i.push(s),e[s.id]=!0)}return{plugins:i,localIds:e}}(i);return!1!==s||e?function(t,{plugins:e,localIds:i},s,n){const o=[],a=t.getContext();for(const r of e){const e=r.id,l=nn(s[e],n);null!==l&&o.push({plugin:r,options:on(t.config,{plugin:r,local:i[e]},l,a)})}return o}(t,n,s,e):[]}_notifyStateChanges(t){const e=this._oldCache||[],i=this._cache,s=(t,e)=>t.filter((t=>!e.some((e=>t.plugin.id===e.plugin.id))));this._notify(s(e,i),t,\"stop\"),this._notify(s(i,e),t,\"start\")}}function nn(t,e){return e||!1!==t?!0===t?{}:t:null}function on(t,{plugin:e,local:i},s,n){const o=t.pluginScopeKeys(e),a=t.getOptionScopes(s,o);return i&&e.defaults&&a.push(e.defaults),t.createResolver(a,n,[\"\"],{scriptable:!1,indexable:!1,allKeys:!0})}function an(t,e){const i=ue.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||i.indexAxis||\"x\"}function rn(t){if(\"x\"===t||\"y\"===t||\"r\"===t)return t}function ln(t,...e){if(rn(t))return t;for(const s of e){const e=s.axis||(\"top\"===(i=s.position)||\"bottom\"===i?\"x\":\"left\"===i||\"right\"===i?\"y\":void 0)||t.length>1&&rn(t[0].toLowerCase());if(e)return e}var i;throw new Error(`Cannot determine type of '${t}' axis. Please provide 'axis' or 'position' option.`)}function hn(t,e,i){if(i[e+\"AxisID\"]===t)return{axis:e}}function cn(t,e){const i=re[t.type]||{scales:{}},s=e.scales||{},n=an(t.type,e),a=Object.create(null);return Object.keys(s).forEach((e=>{const r=s[e];if(!o(r))return console.error(`Invalid scale configuration for scale: ${e}`);if(r._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${e}`);const l=ln(e,r,function(t,e){if(e.data&&e.data.datasets){const i=e.data.datasets.filter((e=>e.xAxisID===t||e.yAxisID===t));if(i.length)return hn(t,\"x\",i[0])||hn(t,\"y\",i[0])}return{}}(e,t),ue.scales[r.type]),h=function(t,e){return t===e?\"_index_\":\"_value_\"}(l,n),c=i.scales||{};a[e]=x(Object.create(null),[{axis:l},r,c[l],c[h]])})),t.data.datasets.forEach((i=>{const n=i.type||t.type,o=i.indexAxis||an(n,e),r=(re[n]||{}).scales||{};Object.keys(r).forEach((t=>{const e=function(t,e){let i=t;return\"_index_\"===t?i=e:\"_value_\"===t&&(i=\"x\"===e?\"y\":\"x\"),i}(t,o),n=i[e+\"AxisID\"]||e;a[n]=a[n]||Object.create(null),x(a[n],[{axis:e},s[n],r[t]])}))})),Object.keys(a).forEach((t=>{const e=a[t];x(e,[ue.scales[e.type],ue.scale])})),a}function dn(t){const e=t.options||(t.options={});e.plugins=l(e.plugins,{}),e.scales=cn(t,e)}function un(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const fn=new Map,gn=new Set;function pn(t,e){let i=fn.get(t);return i||(i=e(),fn.set(t,i),gn.add(i)),i}const mn=(t,e,i)=>{const s=M(e,i);void 0!==s&&t.add(s)};class bn{constructor(t){this._config=function(t){return(t=t||{}).data=un(t.data),dn(t),t}(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=un(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),dn(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return pn(t,(()=>[[`datasets.${t}`,\"\"]]))}datasetAnimationScopeKeys(t,e){return pn(`${t}.transition.${e}`,(()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,\"\"]]))}datasetElementScopeKeys(t,e){return pn(`${t}-${e}`,(()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,\"\"]]))}pluginScopeKeys(t){const e=t.id;return pn(`${this.type}-plugin-${e}`,(()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]]))}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return s&&!e||(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:n}=this,o=this._cachedScopes(t,i),a=o.get(e);if(a)return a;const r=new Set;e.forEach((e=>{t&&(r.add(t),e.forEach((e=>mn(r,t,e)))),e.forEach((t=>mn(r,s,t))),e.forEach((t=>mn(r,re[n]||{},t))),e.forEach((t=>mn(r,ue,t))),e.forEach((t=>mn(r,le,t)))}));const l=Array.from(r);return 0===l.length&&l.push(Object.create(null)),gn.has(e)&&o.set(e,l),l}chartOptionScopes(){const{options:t,type:e}=this;return[t,re[e]||{},ue.datasets[e]||{},{type:e},ue,le]}resolveNamedOptions(t,e,i,s=[\"\"]){const o={$shared:!0},{resolver:a,subPrefixes:r}=xn(this._resolverCache,t,s);let l=a;if(function(t,e){const{isScriptable:i,isIndexable:s}=Ye(t);for(const o of e){const e=i(o),a=s(o),r=(a||e)&&t[o];if(e&&(S(r)||_n(r))||a&&n(r))return!0}return!1}(a,e)){o.$shared=!1;l=$e(a,i=S(i)?i():i,this.createResolver(t,i,r))}for(const t of e)o[t]=l[t];return o}createResolver(t,e,i=[\"\"],s){const{resolver:n}=xn(this._resolverCache,t,i);return o(e)?$e(n,e,void 0,s):n}}function xn(t,e,i){let s=t.get(e);s||(s=new Map,t.set(e,s));const n=i.join();let o=s.get(n);if(!o){o={resolver:je(e,i),subPrefixes:i.filter((t=>!t.toLowerCase().includes(\"hover\")))},s.set(n,o)}return o}const _n=t=>o(t)&&Object.getOwnPropertyNames(t).reduce(((e,i)=>e||S(t[i])),!1);const yn=[\"top\",\"bottom\",\"left\",\"right\",\"chartArea\"];function vn(t,e){return\"top\"===t||\"bottom\"===t||-1===yn.indexOf(t)&&\"x\"===e}function Mn(t,e){return function(i,s){return i[t]===s[t]?i[e]-s[e]:i[t]-s[t]}}function wn(t){const e=t.chart,i=e.options.animation;e.notifyPlugins(\"afterRender\"),d(i&&i.onComplete,[t],e)}function kn(t){const e=t.chart,i=e.options.animation;d(i&&i.onProgress,[t],e)}function Sn(t){return fe()&&\"string\"==typeof t?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const Pn={},Dn=t=>{const e=Sn(t);return Object.values(Pn).filter((t=>t.canvas===e)).pop()};function Cn(t,e,i){const s=Object.keys(t);for(const n of s){const s=+n;if(s>=e){const o=t[n];delete t[n],(i>0||s>e)&&(t[s+i]=o)}}}class On{static defaults=ue;static instances=Pn;static overrides=re;static registry=en;static version=\"4.3.0\";static getChart=Dn;static register(...t){en.add(...t),An()}static unregister(...t){en.remove(...t),An()}constructor(t,e){const s=this.config=new bn(e),n=Sn(t),o=Dn(n);if(o)throw new Error(\"Canvas is already in use. Chart with ID '\"+o.id+\"' must be destroyed before the canvas with ID '\"+o.canvas.id+\"' can be reused.\");const a=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||ks(n)),this.platform.updateConfig(s);const r=this.platform.acquireContext(n,a.aspectRatio),l=r&&r.canvas,h=l&&l.height,c=l&&l.width;this.id=i(),this.ctx=r,this.canvas=l,this.width=c,this.height=h,this._options=a,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new sn,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=dt((t=>this.update(t)),a.resizeDelay||0),this._dataChanges=[],Pn[this.id]=this,r&&l?(xt.listen(this,\"complete\",wn),xt.listen(this,\"progress\",kn),this._initialize(),this.attached&&this.update()):console.error(\"Failed to create chart: can't acquire context from the given item\")}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:n,_aspectRatio:o}=this;return s(t)?e&&o?o:n?i/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return en}_initialize(){return this.notifyPlugins(\"beforeInit\"),this.options.responsive?this.resize():ke(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins(\"afterInit\"),this}clear(){return Te(this.canvas,this.ctx),this}stop(){return xt.stop(this),this}resize(t,e){xt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,n=i.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(s,t,e,n),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),r=this.width?\"resize\":\"attach\";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,ke(this,a,!0)&&(this.notifyPlugins(\"resize\",{size:o}),d(i.onResize,[this,o],this),this.attached&&this._doResize(r)&&this.render())}ensureScalesHaveIDs(){u(this.options.scales||{},((t,e)=>{t.id=e}))}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce(((t,e)=>(t[e]=!1,t)),{});let n=[];e&&(n=n.concat(Object.keys(e).map((t=>{const i=e[t],s=ln(t,i),n=\"r\"===s,o=\"x\"===s;return{options:i,dposition:n?\"chartArea\":o?\"bottom\":\"left\",dtype:n?\"radialLinear\":o?\"category\":\"linear\"}})))),u(n,(e=>{const n=e.options,o=n.id,a=ln(o,n),r=l(n.type,e.dtype);void 0!==n.position&&vn(n.position,a)===vn(e.dposition)||(n.position=e.dposition),s[o]=!0;let h=null;if(o in i&&i[o].type===r)h=i[o];else{h=new(en.getScale(r))({id:o,type:r,ctx:this.ctx,chart:this}),i[h.id]=h}h.init(n,t)})),u(s,((t,e)=>{t||delete i[e]})),u(i,(t=>{as.configure(this,t,t.options),as.addBox(this,t)}))}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort(((t,e)=>t.index-e.index)),i>e){for(let t=e;t<i;++t)this._destroyDatasetMeta(t);t.splice(e,i-e)}this._sortedMetasets=t.slice(0).sort(Mn(\"order\",\"index\"))}_removeUnreferencedMetasets(){const{_metasets:t,data:{datasets:e}}=this;t.length>e.length&&delete this._stacks,t.forEach(((t,i)=>{0===e.filter((e=>e===t._dataset)).length&&this._destroyDatasetMeta(i)}))}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i<s;i++){const s=e[i];let n=this.getDatasetMeta(i);const o=s.type||this.config.type;if(n.type&&n.type!==o&&(this._destroyDatasetMeta(i),n=this.getDatasetMeta(i)),n.type=o,n.indexAxis=s.indexAxis||an(o,this.options),n.order=s.order||0,n.index=i,n.label=\"\"+s.label,n.visible=this.isDatasetVisible(i),n.controller)n.controller.updateIndex(i),n.controller.linkScales();else{const e=en.getController(o),{datasetElementType:s,dataElementType:a}=ue.datasets[o];Object.assign(e,{dataElementType:en.getElement(a),datasetElementType:s&&en.getElement(s)}),n.controller=new e(this,i),t.push(n.controller)}}return this._updateMetasets(),t}_resetElements(){u(this.data.datasets,((t,e)=>{this.getDatasetMeta(e).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins(\"reset\")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins(\"beforeUpdate\",{mode:t,cancelable:!0}))return;const n=this.buildOrUpdateControllers();this.notifyPlugins(\"beforeElementsUpdate\");let o=0;for(let t=0,e=this.data.datasets.length;t<e;t++){const{controller:e}=this.getDatasetMeta(t),i=!s&&-1===n.indexOf(e);e.buildOrUpdateElements(i),o=Math.max(+e.getMaxOverflow(),o)}o=this._minPadding=i.layout.autoPadding?o:0,this._updateLayout(o),s||u(n,(t=>{t.reset()})),this._updateDatasets(t),this.notifyPlugins(\"afterUpdate\",{mode:t}),this._layers.sort(Mn(\"z\",\"_idx\"));const{_active:a,_lastEvent:r}=this;r?this._eventHandler(r,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){u(this.scales,(t=>{as.removeBox(this,t)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);P(e,i)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:n}of e){Cn(t,s,\"_removeElements\"===i?-n:n)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=e=>new Set(t.filter((t=>t[0]===e)).map(((t,e)=>e+\",\"+t.splice(1).join(\",\")))),s=i(0);for(let t=1;t<e;t++)if(!P(s,i(t)))return;return Array.from(s).map((t=>t.split(\",\"))).map((t=>({method:t[1],start:+t[2],count:+t[3]})))}_updateLayout(t){if(!1===this.notifyPlugins(\"beforeLayout\",{cancelable:!0}))return;as.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],u(this.boxes,(t=>{i&&\"chartArea\"===t.position||(t.configure&&t.configure(),this._layers.push(...t._layers()))}),this),this._layers.forEach(((t,e)=>{t._idx=e})),this.notifyPlugins(\"afterLayout\")}_updateDatasets(t){if(!1!==this.notifyPlugins(\"beforeDatasetsUpdate\",{mode:t,cancelable:!0})){for(let t=0,e=this.data.datasets.length;t<e;++t)this.getDatasetMeta(t).controller.configure();for(let e=0,i=this.data.datasets.length;e<i;++e)this._updateDataset(e,S(t)?t({datasetIndex:e}):t);this.notifyPlugins(\"afterDatasetsUpdate\",{mode:t})}}_updateDataset(t,e){const i=this.getDatasetMeta(t),s={meta:i,index:t,mode:e,cancelable:!0};!1!==this.notifyPlugins(\"beforeDatasetUpdate\",s)&&(i.controller._update(e),s.cancelable=!1,this.notifyPlugins(\"afterDatasetUpdate\",s))}render(){!1!==this.notifyPlugins(\"beforeRender\",{cancelable:!0})&&(xt.has(this)?this.attached&&!xt.running(this)&&xt.start(this):(this.draw(),wn({chart:this})))}draw(){let t;if(this._resizeBeforeDraw){const{width:t,height:e}=this._resizeBeforeDraw;this._resize(t,e),this._resizeBeforeDraw=null}if(this.clear(),this.width<=0||this.height<=0)return;if(!1===this.notifyPlugins(\"beforeDraw\",{cancelable:!0}))return;const e=this._layers;for(t=0;t<e.length&&e[t].z<=0;++t)e[t].draw(this.chartArea);for(this._drawDatasets();t<e.length;++t)e[t].draw(this.chartArea);this.notifyPlugins(\"afterDraw\")}_getSortedDatasetMetas(t){const e=this._sortedMetasets,i=[];let s,n;for(s=0,n=e.length;s<n;++s){const n=e[s];t&&!n.visible||i.push(n)}return i}getSortedVisibleDatasetMetas(){return this._getSortedDatasetMetas(!0)}_drawDatasets(){if(!1===this.notifyPlugins(\"beforeDatasetsDraw\",{cancelable:!0}))return;const t=this.getSortedVisibleDatasetMetas();for(let e=t.length-1;e>=0;--e)this._drawDataset(t[e]);this.notifyPlugins(\"afterDatasetsDraw\")}_drawDataset(t){const e=this.ctx,i=t._clip,s=!i.disabled,n=function(t){const{xScale:e,yScale:i}=t;if(e&&i)return{left:e.left,right:e.right,top:i.top,bottom:i.bottom}}(t)||this.chartArea,o={meta:t,index:t.index,cancelable:!0};!1!==this.notifyPlugins(\"beforeDatasetDraw\",o)&&(s&&Ie(e,{left:!1===i.left?0:n.left-i.left,right:!1===i.right?this.width:n.right+i.right,top:!1===i.top?0:n.top-i.top,bottom:!1===i.bottom?this.height:n.bottom+i.bottom}),t.controller.draw(),s&&ze(e),o.cancelable=!1,this.notifyPlugins(\"afterDatasetDraw\",o))}isPointInArea(t){return Re(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,i,s){const n=Xi.modes[e];return\"function\"==typeof n?n(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter((t=>t&&t._dataset===e)).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=Ci(null,{chart:this,type:\"chart\"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return\"boolean\"==typeof i.hidden?!i.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?\"show\":\"hide\",n=this.getDatasetMeta(t),o=n.controller._resolveAnimations(void 0,s);k(e)?(n.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),o.update(n,{visible:i}),this.update((e=>e.datasetIndex===t?s:void 0)))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),xt.remove(this),t=0,e=this.data.datasets.length;t<e;++t)this._destroyDatasetMeta(t)}destroy(){this.notifyPlugins(\"beforeDestroy\");const{canvas:t,ctx:e}=this;this._stop(),this.config.clearCache(),t&&(this.unbindEvents(),Te(t,e),this.platform.releaseContext(e),this.canvas=null,this.ctx=null),delete Pn[this.id],this.notifyPlugins(\"afterDestroy\")}toBase64Image(...t){return this.canvas.toDataURL(...t)}bindEvents(){this.bindUserEvents(),this.options.responsive?this.bindResponsiveEvents():this.attached=!0}bindUserEvents(){const t=this._listeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(t,e,i)=>{t.offsetX=e,t.offsetY=i,this._eventHandler(t)};u(this.options.events,(t=>i(t,s)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(i,s)=>{t[i]&&(e.removeEventListener(this,i,s),delete t[i])},n=(t,e)=>{this.canvas&&this.resize(t,e)};let o;const a=()=>{s(\"attach\",a),this.attached=!0,this.resize(),i(\"resize\",n),i(\"detach\",o)};o=()=>{this.attached=!1,s(\"resize\",n),this._stop(),this._resize(0,0),i(\"attach\",a)},e.isAttached(this.canvas)?a():o()}unbindEvents(){u(this._listeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._listeners={},u(this._responsiveListeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?\"set\":\"remove\";let n,o,a,r;for(\"dataset\"===e&&(n=this.getDatasetMeta(t[0].datasetIndex),n.controller[\"_\"+s+\"DatasetHoverStyle\"]()),a=0,r=t.length;a<r;++a){o=t[a];const e=o&&this.getDatasetMeta(o.datasetIndex).controller;e&&e[s+\"HoverStyle\"](o.element,o.datasetIndex,o.index)}}getActiveElements(){return this._active||[]}setActiveElements(t){const e=this._active||[],i=t.map((({datasetIndex:t,index:e})=>{const i=this.getDatasetMeta(t);if(!i)throw new Error(\"No dataset found at index \"+t);return{datasetIndex:t,element:i.data[e],index:e}}));!f(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}isPluginEnabled(t){return 1===this._plugins._cache.filter((e=>e.plugin.id===t)).length}_updateHoverStyles(t,e,i){const s=this.options.hover,n=(t,e)=>t.filter((t=>!e.some((e=>t.datasetIndex===e.datasetIndex&&t.index===e.index)))),o=n(e,t),a=i?t:n(t,e);o.length&&this.updateHoverStyle(o,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},s=e=>(e.options.events||this.options.events).includes(t.native.type);if(!1===this.notifyPlugins(\"beforeEvent\",i,s))return;const n=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins(\"afterEvent\",i,s),(n||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:n}=this,o=e,a=this._getActiveElements(t,s,i,o),r=D(t),l=function(t,e,i,s){return i&&\"mouseout\"!==t.type?s?e:t:null}(t,this._lastEvent,i,r);i&&(this._lastEvent=null,d(n.onHover,[t,a,this],this),r&&d(n.onClick,[t,a,this],this));const h=!f(a,s);return(h||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=l,h}_getActiveElements(t,e,i,s){if(\"mouseout\"===t.type)return[];if(!i)return e;const n=this.options.hover;return this.getElementsAtEventForMode(t,n.mode,n,s)}}function An(){return u(On.instances,(t=>t._plugins.invalidate()))}function Tn(){throw new Error(\"This method is not implemented: Check that a complete date adapter is provided.\")}class Ln{static override(t){Object.assign(Ln.prototype,t)}options;constructor(t){this.options=t||{}}init(){}formats(){return Tn()}parse(){return Tn()}format(){return Tn()}add(){return Tn()}diff(){return Tn()}startOf(){return Tn()}endOf(){return Tn()}}var En={_date:Ln};function Rn(t){const e=t.iScale,i=function(t,e){if(!t._cache.$bar){const i=t.getMatchingVisibleMetas(e);let s=[];for(let e=0,n=i.length;e<n;e++)s=s.concat(i[e].controller.getAllParsedValues(t));t._cache.$bar=lt(s.sort(((t,e)=>t-e)))}return t._cache.$bar}(e,t.type);let s,n,o,a,r=e._length;const l=()=>{32767!==o&&-32768!==o&&(k(a)&&(r=Math.min(r,Math.abs(o-a)||r)),a=o)};for(s=0,n=i.length;s<n;++s)o=e.getPixelForValue(i[s]),l();for(a=void 0,s=0,n=e.ticks.length;s<n;++s)o=e.getPixelForTick(s),l();return r}function In(t,e,i,s){return n(t)?function(t,e,i,s){const n=i.parse(t[0],s),o=i.parse(t[1],s),a=Math.min(n,o),r=Math.max(n,o);let l=a,h=r;Math.abs(a)>Math.abs(r)&&(l=r,h=a),e[i.axis]=h,e._custom={barStart:l,barEnd:h,start:n,end:o,min:a,max:r}}(t,e,i,s):e[i.axis]=i.parse(t,s),e}function zn(t,e,i,s){const n=t.iScale,o=t.vScale,a=n.getLabels(),r=n===o,l=[];let h,c,d,u;for(h=i,c=i+s;h<c;++h)u=e[h],d={},d[n.axis]=r||n.parse(a[h],h),l.push(In(u,d,o,h));return l}function Fn(t){return t&&void 0!==t.barStart&&void 0!==t.barEnd}function Vn(t,e,i,s){let n=e.borderSkipped;const o={};if(!n)return void(t.borderSkipped=o);if(!0===n)return void(t.borderSkipped={top:!0,right:!0,bottom:!0,left:!0});const{start:a,end:r,reverse:l,top:h,bottom:c}=function(t){let e,i,s,n,o;return t.horizontal?(e=t.base>t.x,i=\"left\",s=\"right\"):(e=t.base<t.y,i=\"bottom\",s=\"top\"),e?(n=\"end\",o=\"start\"):(n=\"start\",o=\"end\"),{start:i,end:s,reverse:e,top:n,bottom:o}}(t);\"middle\"===n&&i&&(t.enableBorderRadius=!0,(i._top||0)===s?n=h:(i._bottom||0)===s?n=c:(o[Bn(c,a,r,l)]=!0,n=h)),o[Bn(n,a,r,l)]=!0,t.borderSkipped=o}function Bn(t,e,i,s){var n,o,a;return s?(a=i,t=Nn(t=(n=t)===(o=e)?a:n===a?o:n,i,e)):t=Nn(t,e,i),t}function Nn(t,e,i){return\"start\"===t?e:\"end\"===t?i:t}function Wn(t,{inflateAmount:e},i){t.inflateAmount=\"auto\"===e?1===i?.33:0:e}class Hn extends Ws{static id=\"doughnut\";static defaults={datasetElementType:!1,dataElementType:\"arc\",animation:{animateRotate:!0,animateScale:!1},animations:{numbers:{type:\"number\",properties:[\"circumference\",\"endAngle\",\"innerRadius\",\"outerRadius\",\"startAngle\",\"x\",\"y\",\"offset\",\"borderWidth\",\"spacing\"]}},cutout:\"50%\",rotation:0,circumference:360,radius:\"100%\",spacing:0,indexAxis:\"r\"};static descriptors={_scriptable:t=>\"spacing\"!==t,_indexable:t=>\"spacing\"!==t&&!t.startsWith(\"borderDash\")&&!t.startsWith(\"hoverBorderDash\")};static overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map(((e,n)=>{const o=t.getDatasetMeta(0).controller.getStyle(n);return{text:e,fillStyle:o.backgroundColor,strokeStyle:o.borderColor,fontColor:s,lineWidth:o.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(n),index:n}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}}};constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(!1===this._parsing)s._parsed=i;else{let n,a,r=t=>+i[t];if(o(i[t])){const{key:t=\"value\"}=this._parsing;r=e=>+M(i[e],t)}for(n=t,a=t+e;n<a;++n)s._parsed[n]=r(n)}}_getRotation(){return $(this.options.rotation-90)}_getCircumference(){return $(this.options.circumference)}_getRotationExtents(){let t=O,e=-O;for(let i=0;i<this.chart.data.datasets.length;++i)if(this.chart.isDatasetVisible(i)&&this.chart.getDatasetMeta(i).type===this._type){const s=this.chart.getDatasetMeta(i).controller,n=s._getRotation(),o=s._getCircumference();t=Math.min(t,n),e=Math.max(e,n+o)}return{rotation:t,circumference:e-t}}update(t){const e=this.chart,{chartArea:i}=e,s=this._cachedMeta,n=s.data,o=this.getMaxBorderWidth()+this.getMaxOffset(n)+this.options.spacing,a=Math.max((Math.min(i.width,i.height)-o)/2,0),r=Math.min(h(this.options.cutout,a),1),l=this._getRingWeight(this.index),{circumference:d,rotation:u}=this._getRotationExtents(),{ratioX:f,ratioY:g,offsetX:p,offsetY:m}=function(t,e,i){let s=1,n=1,o=0,a=0;if(e<O){const r=t,l=r+e,h=Math.cos(r),c=Math.sin(r),d=Math.cos(l),u=Math.sin(l),f=(t,e,s)=>Z(t,r,l,!0)?1:Math.max(e,e*i,s,s*i),g=(t,e,s)=>Z(t,r,l,!0)?-1:Math.min(e,e*i,s,s*i),p=f(0,h,d),m=f(E,c,u),b=g(C,h,d),x=g(C+E,c,u);s=(p-b)/2,n=(m-x)/2,o=-(p+b)/2,a=-(m+x)/2}return{ratioX:s,ratioY:n,offsetX:o,offsetY:a}}(u,d,r),b=(i.width-o)/f,x=(i.height-o)/g,_=Math.max(Math.min(b,x)/2,0),y=c(this.options.radius,_),v=(y-Math.max(y*r,0))/this._getVisibleDatasetWeightTotal();this.offsetX=p*y,this.offsetY=m*y,s.total=this.calculateTotal(),this.outerRadius=y-v*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-v*l,0),this.updateElements(n,0,n.length,t)}_circumference(t,e){const i=this.options,s=this._cachedMeta,n=this._getCircumference();return e&&i.animation.animateRotate||!this.chart.getDataVisibility(t)||null===s._parsed[t]||s.data[t].hidden?0:this.calculateCircumference(s._parsed[t]*n/O)}updateElements(t,e,i,s){const n=\"reset\"===s,o=this.chart,a=o.chartArea,r=o.options.animation,l=(a.left+a.right)/2,h=(a.top+a.bottom)/2,c=n&&r.animateScale,d=c?0:this.innerRadius,u=c?0:this.outerRadius,{sharedOptions:f,includeOptions:g}=this._getSharedOptions(e,s);let p,m=this._getRotation();for(p=0;p<e;++p)m+=this._circumference(p,n);for(p=e;p<e+i;++p){const e=this._circumference(p,n),i=t[p],o={x:l+this.offsetX,y:h+this.offsetY,startAngle:m,endAngle:m+e,circumference:e,outerRadius:u,innerRadius:d};g&&(o.options=f||this.resolveDataElementOptions(p,i.active?\"active\":s)),m+=e,this.updateElement(i,p,o,s)}}calculateTotal(){const t=this._cachedMeta,e=t.data;let i,s=0;for(i=0;i<e.length;i++){const n=t._parsed[i];null===n||isNaN(n)||!this.chart.getDataVisibility(i)||e[i].hidden||(s+=Math.abs(n))}return s}calculateCircumference(t){const e=this._cachedMeta.total;return e>0&&!isNaN(t)?O*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t],i.options.locale);return{label:s[t]||\"\",value:n}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,n,o,a,r;if(!t)for(s=0,n=i.data.datasets.length;s<n;++s)if(i.isDatasetVisible(s)){o=i.getDatasetMeta(s),t=o.data,a=o.controller;break}if(!t)return 0;for(s=0,n=t.length;s<n;++s)r=a.resolveDataElementOptions(s),\"inner\"!==r.borderAlign&&(e=Math.max(e,r.borderWidth||0,r.hoverBorderWidth||0));return e}getMaxOffset(t){let e=0;for(let i=0,s=t.length;i<s;++i){const t=this.resolveDataElementOptions(i);e=Math.max(e,t.offset||0,t.hoverOffset||0)}return e}_getRingWeightOffset(t){let e=0;for(let i=0;i<t;++i)this.chart.isDatasetVisible(i)&&(e+=this._getRingWeight(i));return e}_getRingWeight(t){return Math.max(l(this.chart.data.datasets[t].weight,1),0)}_getVisibleDatasetWeightTotal(){return this._getRingWeightOffset(this.chart.data.datasets.length)||1}}class jn extends Ws{static id=\"polarArea\";static defaults={dataElementType:\"arc\",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:\"number\",properties:[\"x\",\"y\",\"startAngle\",\"endAngle\",\"innerRadius\",\"outerRadius\"]}},indexAxis:\"r\",startAngle:0};static overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map(((e,n)=>{const o=t.getDatasetMeta(0).controller.getStyle(n);return{text:e,fillStyle:o.backgroundColor,strokeStyle:o.borderColor,fontColor:s,lineWidth:o.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(n),index:n}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}},scales:{r:{type:\"radialLinear\",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t].r,i.options.locale);return{label:s[t]||\"\",value:n}}parseObjectData(t,e,i,s){return ii.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach(((t,i)=>{const s=this.getParsed(i).r;!isNaN(s)&&this.chart.getDataVisibility(i)&&(s<e.min&&(e.min=s),s>e.max&&(e.max=s))})),e}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),n=Math.max(s/2,0),o=(n-Math.max(i.cutoutPercentage?n/100*i.cutoutPercentage:1,0))/t.getVisibleDatasetCount();this.outerRadius=n-o*this.index,this.innerRadius=this.outerRadius-o}updateElements(t,e,i,s){const n=\"reset\"===s,o=this.chart,a=o.options.animation,r=this._cachedMeta.rScale,l=r.xCenter,h=r.yCenter,c=r.getIndexAngle(0)-.5*C;let d,u=c;const f=360/this.countVisibleElements();for(d=0;d<e;++d)u+=this._computeAngle(d,s,f);for(d=e;d<e+i;d++){const e=t[d];let i=u,g=u+this._computeAngle(d,s,f),p=o.getDataVisibility(d)?r.getDistanceFromCenterForValue(this.getParsed(d).r):0;u=g,n&&(a.animateScale&&(p=0),a.animateRotate&&(i=g=c));const m={x:l,y:h,innerRadius:0,outerRadius:p,startAngle:i,endAngle:g,options:this.resolveDataElementOptions(d,e.active?\"active\":s)};this.updateElement(e,d,m,s)}}countVisibleElements(){const t=this._cachedMeta;let e=0;return t.data.forEach(((t,i)=>{!isNaN(this.getParsed(i).r)&&this.chart.getDataVisibility(i)&&e++})),e}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?$(this.resolveDataElementOptions(t,e).angle||i):0}}var $n=Object.freeze({__proto__:null,BarController:class extends Ws{static id=\"bar\";static defaults={datasetElementType:!1,dataElementType:\"bar\",categoryPercentage:.8,barPercentage:.9,grouped:!0,animations:{numbers:{type:\"number\",properties:[\"x\",\"y\",\"base\",\"width\",\"height\"]}}};static overrides={scales:{_index_:{type:\"category\",offset:!0,grid:{offset:!0}},_value_:{type:\"linear\",beginAtZero:!0}}};parsePrimitiveData(t,e,i,s){return zn(t,e,i,s)}parseArrayData(t,e,i,s){return zn(t,e,i,s)}parseObjectData(t,e,i,s){const{iScale:n,vScale:o}=t,{xAxisKey:a=\"x\",yAxisKey:r=\"y\"}=this._parsing,l=\"x\"===n.axis?a:r,h=\"x\"===o.axis?a:r,c=[];let d,u,f,g;for(d=i,u=i+s;d<u;++d)g=e[d],f={},f[n.axis]=n.parse(M(g,l),d),c.push(In(M(g,h),f,o,d));return c}updateRangeFromParsed(t,e,i,s){super.updateRangeFromParsed(t,e,i,s);const n=i._custom;n&&e===this._cachedMeta.vScale&&(t.min=Math.min(t.min,n.min),t.max=Math.max(t.max,n.max))}getMaxOverflow(){return 0}getLabelAndValue(t){const e=this._cachedMeta,{iScale:i,vScale:s}=e,n=this.getParsed(t),o=n._custom,a=Fn(o)?\"[\"+o.start+\", \"+o.end+\"]\":\"\"+s.getLabelForValue(n[s.axis]);return{label:\"\"+i.getLabelForValue(n[i.axis]),value:a}}initialize(){this.enableOptionSharing=!0,super.initialize();this._cachedMeta.stack=this.getDataset().stack}update(t){const e=this._cachedMeta;this.updateElements(e.data,0,e.data.length,t)}updateElements(t,e,i,n){const o=\"reset\"===n,{index:a,_cachedMeta:{vScale:r}}=this,l=r.getBasePixel(),h=r.isHorizontal(),c=this._getRuler(),{sharedOptions:d,includeOptions:u}=this._getSharedOptions(e,n);for(let f=e;f<e+i;f++){const e=this.getParsed(f),i=o||s(e[r.axis])?{base:l,head:l}:this._calculateBarValuePixels(f),g=this._calculateBarIndexPixels(f,c),p=(e._stacks||{})[r.axis],m={horizontal:h,base:i.base,enableBorderRadius:!p||Fn(e._custom)||a===p._top||a===p._bottom,x:h?i.head:g.center,y:h?g.center:i.head,height:h?g.size:Math.abs(i.size),width:h?Math.abs(i.size):g.size};u&&(m.options=d||this.resolveDataElementOptions(f,t[f].active?\"active\":n));const b=m.options||t[f].options;Vn(m,b,p,a),Wn(m,b,c.ratio),this.updateElement(t[f],f,m,n)}}_getStacks(t,e){const{iScale:i}=this._cachedMeta,n=i.getMatchingVisibleMetas(this._type).filter((t=>t.controller.options.grouped)),o=i.options.stacked,a=[],r=t=>{const i=t.controller.getParsed(e),n=i&&i[t.vScale.axis];if(s(n)||isNaN(n))return!0};for(const i of n)if((void 0===e||!r(i))&&((!1===o||-1===a.indexOf(i.stack)||void 0===o&&void 0===i.stack)&&a.push(i.stack),i.index===t))break;return a.length||a.push(void 0),a}_getStackCount(t){return this._getStacks(void 0,t).length}_getStackIndex(t,e,i){const s=this._getStacks(t,i),n=void 0!==e?s.indexOf(e):-1;return-1===n?s.length-1:n}_getRuler(){const t=this.options,e=this._cachedMeta,i=e.iScale,s=[];let n,o;for(n=0,o=e.data.length;n<o;++n)s.push(i.getPixelForValue(this.getParsed(n)[i.axis],n));const a=t.barThickness;return{min:a||Rn(e),pixels:s,start:i._startPixel,end:i._endPixel,stackCount:this._getStackCount(),scale:i,grouped:t.grouped,ratio:a?1:t.categoryPercentage*t.barPercentage}}_calculateBarValuePixels(t){const{_cachedMeta:{vScale:e,_stacked:i,index:n},options:{base:o,minBarLength:a}}=this,r=o||0,l=this.getParsed(t),h=l._custom,c=Fn(h);let d,u,f=l[e.axis],g=0,p=i?this.applyStack(e,l,i):f;p!==f&&(g=p-f,p=f),c&&(f=h.barStart,p=h.barEnd-h.barStart,0!==f&&F(f)!==F(h.barEnd)&&(g=0),g+=f);const m=s(o)||c?g:o;let b=e.getPixelForValue(m);if(d=this.chart.getDataVisibility(t)?e.getPixelForValue(g+p):b,u=d-b,Math.abs(u)<a){u=function(t,e,i){return 0!==t?F(t):(e.isHorizontal()?1:-1)*(e.min>=i?1:-1)}(u,e,r)*a,f===r&&(b-=u/2);const t=e.getPixelForDecimal(0),s=e.getPixelForDecimal(1),o=Math.min(t,s),h=Math.max(t,s);b=Math.max(Math.min(b,h),o),d=b+u,i&&!c&&(l._stacks[e.axis]._visualValues[n]=e.getValueForPixel(d)-e.getValueForPixel(b))}if(b===e.getPixelForValue(r)){const t=F(u)*e.getLineWidthForValue(r)/2;b+=t,u-=t}return{size:u,base:b,head:d,center:d+u/2}}_calculateBarIndexPixels(t,e){const i=e.scale,n=this.options,o=n.skipNull,a=l(n.maxBarThickness,1/0);let r,h;if(e.grouped){const i=o?this._getStackCount(t):e.stackCount,l=\"flex\"===n.barThickness?function(t,e,i,s){const n=e.pixels,o=n[t];let a=t>0?n[t-1]:null,r=t<n.length-1?n[t+1]:null;const l=i.categoryPercentage;null===a&&(a=o-(null===r?e.end-e.start:r-o)),null===r&&(r=o+o-a);const h=o-(o-Math.min(a,r))/2*l;return{chunk:Math.abs(r-a)/2*l/s,ratio:i.barPercentage,start:h}}(t,e,n,i):function(t,e,i,n){const o=i.barThickness;let a,r;return s(o)?(a=e.min*i.categoryPercentage,r=i.barPercentage):(a=o*n,r=1),{chunk:a/n,ratio:r,start:e.pixels[t]-a/2}}(t,e,n,i),c=this._getStackIndex(this.index,this._cachedMeta.stack,o?t:void 0);r=l.start+l.chunk*c+l.chunk/2,h=Math.min(a,l.chunk*l.ratio)}else r=i.getPixelForValue(this.getParsed(t)[i.axis],t),h=Math.min(a,e.min*e.ratio);return{base:r-h/2,head:r+h/2,center:r,size:h}}draw(){const t=this._cachedMeta,e=t.vScale,i=t.data,s=i.length;let n=0;for(;n<s;++n)null!==this.getParsed(n)[e.axis]&&i[n].draw(this._ctx)}},BubbleController:class extends Ws{static id=\"bubble\";static defaults={datasetElementType:!1,dataElementType:\"point\",animations:{numbers:{type:\"number\",properties:[\"x\",\"y\",\"borderWidth\",\"radius\"]}}};static overrides={scales:{x:{type:\"linear\"},y:{type:\"linear\"}}};initialize(){this.enableOptionSharing=!0,super.initialize()}parsePrimitiveData(t,e,i,s){const n=super.parsePrimitiveData(t,e,i,s);for(let t=0;t<n.length;t++)n[t]._custom=this.resolveDataElementOptions(t+i).radius;return n}parseArrayData(t,e,i,s){const n=super.parseArrayData(t,e,i,s);for(let t=0;t<n.length;t++){const s=e[i+t];n[t]._custom=l(s[2],this.resolveDataElementOptions(t+i).radius)}return n}parseObjectData(t,e,i,s){const n=super.parseObjectData(t,e,i,s);for(let t=0;t<n.length;t++){const s=e[i+t];n[t]._custom=l(s&&s.r&&+s.r,this.resolveDataElementOptions(t+i).radius)}return n}getMaxOverflow(){const t=this._cachedMeta.data;let e=0;for(let i=t.length-1;i>=0;--i)e=Math.max(e,t[i].size(this.resolveDataElementOptions(i))/2);return e>0&&e}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart.data.labels||[],{xScale:s,yScale:n}=e,o=this.getParsed(t),a=s.getLabelForValue(o.x),r=n.getLabelForValue(o.y),l=o._custom;return{label:i[t]||\"\",value:\"(\"+a+\", \"+r+(l?\", \"+l:\"\")+\")\"}}update(t){const e=this._cachedMeta.data;this.updateElements(e,0,e.length,t)}updateElements(t,e,i,s){const n=\"reset\"===s,{iScale:o,vScale:a}=this._cachedMeta,{sharedOptions:r,includeOptions:l}=this._getSharedOptions(e,s),h=o.axis,c=a.axis;for(let d=e;d<e+i;d++){const e=t[d],i=!n&&this.getParsed(d),u={},f=u[h]=n?o.getPixelForDecimal(.5):o.getPixelForValue(i[h]),g=u[c]=n?a.getBasePixel():a.getPixelForValue(i[c]);u.skip=isNaN(f)||isNaN(g),l&&(u.options=r||this.resolveDataElementOptions(d,e.active?\"active\":s),n&&(u.options.radius=0)),this.updateElement(e,d,u,s)}}resolveDataElementOptions(t,e){const i=this.getParsed(t);let s=super.resolveDataElementOptions(t,e);s.$shared&&(s=Object.assign({},s,{$shared:!1}));const n=s.radius;return\"active\"!==e&&(s.radius=0),s.radius+=l(i&&i._custom,n),s}},DoughnutController:Hn,LineController:class extends Ws{static id=\"line\";static defaults={datasetElementType:\"line\",dataElementType:\"point\",showLine:!0,spanGaps:!1};static overrides={scales:{_index_:{type:\"category\"},_value_:{type:\"linear\"}}};initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:n}=e,o=this.chart._animationsDisabled;let{start:a,count:r}=pt(e,s,o);this._drawStart=a,this._drawCount=r,mt(e)&&(a=0,r=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!n._decimated,i.points=s;const l=this.resolveDatasetElementOptions(t);this.options.showLine||(l.borderWidth=0),l.segment=this.options.segment,this.updateElement(i,void 0,{animated:!o,options:l},t),this.updateElements(s,a,r,t)}updateElements(t,e,i,n){const o=\"reset\"===n,{iScale:a,vScale:r,_stacked:l,_dataset:h}=this._cachedMeta,{sharedOptions:c,includeOptions:d}=this._getSharedOptions(e,n),u=a.axis,f=r.axis,{spanGaps:g,segment:p}=this.options,m=W(g)?g:Number.POSITIVE_INFINITY,b=this.chart._animationsDisabled||o||\"none\"===n,x=e+i,_=t.length;let y=e>0&&this.getParsed(e-1);for(let i=0;i<_;++i){const g=t[i],_=b?g:{};if(i<e||i>=x){_.skip=!0;continue}const v=this.getParsed(i),M=s(v[f]),w=_[u]=a.getPixelForValue(v[u],i),k=_[f]=o||M?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,v,l):v[f],i);_.skip=isNaN(w)||isNaN(k)||M,_.stop=i>0&&Math.abs(v[u]-y[u])>m,p&&(_.parsed=v,_.raw=h.data[i]),d&&(_.options=c||this.resolveDataElementOptions(i,g.active?\"active\":n)),b||this.updateElement(g,i,_,n),y=v}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const n=s[0].size(this.resolveDataElementOptions(0)),o=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,n,o)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}},PieController:class extends Hn{static id=\"pie\";static defaults={cutout:0,rotation:0,circumference:360,radius:\"100%\"}},PolarAreaController:jn,RadarController:class extends Ws{static id=\"radar\";static defaults={datasetElementType:\"line\",dataElementType:\"point\",indexAxis:\"r\",showLine:!0,elements:{line:{fill:\"start\"}}};static overrides={aspectRatio:1,scales:{r:{type:\"radialLinear\"}}};getLabelAndValue(t){const e=this._cachedMeta.vScale,i=this.getParsed(t);return{label:e.getLabels()[t],value:\"\"+e.getLabelForValue(i[e.axis])}}parseObjectData(t,e,i,s){return ii.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta,i=e.dataset,s=e.data||[],n=e.iScale.getLabels();if(i.points=s,\"resize\"!==t){const e=this.resolveDatasetElementOptions(t);this.options.showLine||(e.borderWidth=0);const o={_loop:!0,_fullLoop:n.length===s.length,options:e};this.updateElement(i,void 0,o,t)}this.updateElements(s,0,s.length,t)}updateElements(t,e,i,s){const n=this._cachedMeta.rScale,o=\"reset\"===s;for(let a=e;a<e+i;a++){const e=t[a],i=this.resolveDataElementOptions(a,e.active?\"active\":s),r=n.getPointPositionForValue(a,this.getParsed(a).r),l=o?n.xCenter:r.x,h=o?n.yCenter:r.y,c={x:l,y:h,angle:r.angle,skip:isNaN(l)||isNaN(h),options:i};this.updateElement(e,a,c,s)}}},ScatterController:class extends Ws{static id=\"scatter\";static defaults={datasetElementType:!1,dataElementType:\"point\",showLine:!1,fill:!1};static overrides={interaction:{mode:\"point\"},scales:{x:{type:\"linear\"},y:{type:\"linear\"}}};getLabelAndValue(t){const e=this._cachedMeta,i=this.chart.data.labels||[],{xScale:s,yScale:n}=e,o=this.getParsed(t),a=s.getLabelForValue(o.x),r=n.getLabelForValue(o.y);return{label:i[t]||\"\",value:\"(\"+a+\", \"+r+\")\"}}update(t){const e=this._cachedMeta,{data:i=[]}=e,s=this.chart._animationsDisabled;let{start:n,count:o}=pt(e,i,s);if(this._drawStart=n,this._drawCount=o,mt(e)&&(n=0,o=i.length),this.options.showLine){const{dataset:n,_dataset:o}=e;n._chart=this.chart,n._datasetIndex=this.index,n._decimated=!!o._decimated,n.points=i;const a=this.resolveDatasetElementOptions(t);a.segment=this.options.segment,this.updateElement(n,void 0,{animated:!s,options:a},t)}this.updateElements(i,n,o,t)}addElements(){const{showLine:t}=this.options;!this.datasetElementType&&t&&(this.datasetElementType=this.chart.registry.getElement(\"line\")),super.addElements()}updateElements(t,e,i,n){const o=\"reset\"===n,{iScale:a,vScale:r,_stacked:l,_dataset:h}=this._cachedMeta,c=this.resolveDataElementOptions(e,n),d=this.getSharedOptions(c),u=this.includeOptions(n,d),f=a.axis,g=r.axis,{spanGaps:p,segment:m}=this.options,b=W(p)?p:Number.POSITIVE_INFINITY,x=this.chart._animationsDisabled||o||\"none\"===n;let _=e>0&&this.getParsed(e-1);for(let c=e;c<e+i;++c){const e=t[c],i=this.getParsed(c),p=x?e:{},y=s(i[g]),v=p[f]=a.getPixelForValue(i[f],c),M=p[g]=o||y?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,i,l):i[g],c);p.skip=isNaN(v)||isNaN(M)||y,p.stop=c>0&&Math.abs(i[f]-_[f])>b,m&&(p.parsed=i,p.raw=h.data[c]),u&&(p.options=d||this.resolveDataElementOptions(c,e.active?\"active\":n)),x||this.updateElement(e,c,p,n),_=i}this.updateSharedOptions(d,n,c)}getMaxOverflow(){const t=this._cachedMeta,e=t.data||[];if(!this.options.showLine){let t=0;for(let i=e.length-1;i>=0;--i)t=Math.max(t,e[i].size(this.resolveDataElementOptions(i))/2);return t>0&&t}const i=t.dataset,s=i.options&&i.options.borderWidth||0;if(!e.length)return s;const n=e[0].size(this.resolveDataElementOptions(0)),o=e[e.length-1].size(this.resolveDataElementOptions(e.length-1));return Math.max(s,n,o)/2}}});function Yn(t,e,i,s){const n=vi(t.options.borderRadius,[\"outerStart\",\"outerEnd\",\"innerStart\",\"innerEnd\"]);const o=(i-e)/2,a=Math.min(o,s*e/2),r=t=>{const e=(i-Math.min(o,t))*s/2;return J(t,0,Math.min(o,e))};return{outerStart:r(n.outerStart),outerEnd:r(n.outerEnd),innerStart:J(n.innerStart,0,a),innerEnd:J(n.innerEnd,0,a)}}function Un(t,e,i,s){return{x:i+t*Math.cos(e),y:s+t*Math.sin(e)}}function Xn(t,e,i,s,n,o){const{x:a,y:r,startAngle:l,pixelMargin:h,innerRadius:c}=e,d=Math.max(e.outerRadius+s+i-h,0),u=c>0?c+s+i+h:0;let f=0;const g=n-l;if(s){const t=((c>0?c-s:0)+(d>0?d-s:0))/2;f=(g-(0!==t?g*t/(t+s):g))/2}const p=(g-Math.max(.001,g*d-i/C)/d)/2,m=l+p+f,b=n-p-f,{outerStart:x,outerEnd:_,innerStart:y,innerEnd:v}=Yn(e,u,d,b-m),M=d-x,w=d-_,k=m+x/M,S=b-_/w,P=u+y,D=u+v,O=m+y/P,A=b-v/D;if(t.beginPath(),o){const e=(k+S)/2;if(t.arc(a,r,d,k,e),t.arc(a,r,d,e,S),_>0){const e=Un(w,S,a,r);t.arc(e.x,e.y,_,S,b+E)}const i=Un(D,b,a,r);if(t.lineTo(i.x,i.y),v>0){const e=Un(D,A,a,r);t.arc(e.x,e.y,v,b+E,A+Math.PI)}const s=(b-v/u+(m+y/u))/2;if(t.arc(a,r,u,b-v/u,s,!0),t.arc(a,r,u,s,m+y/u,!0),y>0){const e=Un(P,O,a,r);t.arc(e.x,e.y,y,O+Math.PI,m-E)}const n=Un(M,m,a,r);if(t.lineTo(n.x,n.y),x>0){const e=Un(M,k,a,r);t.arc(e.x,e.y,x,m-E,k)}}else{t.moveTo(a,r);const e=Math.cos(k)*d+a,i=Math.sin(k)*d+r;t.lineTo(e,i);const s=Math.cos(S)*d+a,n=Math.sin(S)*d+r;t.lineTo(s,n)}t.closePath()}function qn(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r,options:l}=e,{borderWidth:h,borderJoinStyle:c,borderDash:d,borderDashOffset:u}=l,f=\"inner\"===l.borderAlign;if(!h)return;t.setLineDash(d||[]),t.lineDashOffset=u,f?(t.lineWidth=2*h,t.lineJoin=c||\"round\"):(t.lineWidth=h,t.lineJoin=c||\"bevel\");let g=e.endAngle;if(o){Xn(t,e,i,s,g,n);for(let e=0;e<o;++e)t.stroke();isNaN(r)||(g=a+(r%O||O))}f&&function(t,e,i){const{startAngle:s,pixelMargin:n,x:o,y:a,outerRadius:r,innerRadius:l}=e;let h=n/r;t.beginPath(),t.arc(o,a,r,s-h,i+h),l>n?(h=n/l,t.arc(o,a,l,i+h,s-h,!0)):t.arc(o,a,n,i+E,s-E),t.closePath(),t.clip()}(t,e,g),o||(Xn(t,e,i,s,g,n),t.stroke())}function Kn(t,e,i=e){t.lineCap=l(i.borderCapStyle,e.borderCapStyle),t.setLineDash(l(i.borderDash,e.borderDash)),t.lineDashOffset=l(i.borderDashOffset,e.borderDashOffset),t.lineJoin=l(i.borderJoinStyle,e.borderJoinStyle),t.lineWidth=l(i.borderWidth,e.borderWidth),t.strokeStyle=l(i.borderColor,e.borderColor)}function Gn(t,e,i){t.lineTo(i.x,i.y)}function Zn(t,e,i={}){const s=t.length,{start:n=0,end:o=s-1}=i,{start:a,end:r}=e,l=Math.max(n,a),h=Math.min(o,r),c=n<a&&o<a||n>r&&o>r;return{count:s,start:l,loop:e.loop,ilen:h<l&&!c?s+h-l:h-l}}function Jn(t,e,i,s){const{points:n,options:o}=e,{count:a,start:r,loop:l,ilen:h}=Zn(n,i,s),c=function(t){return t.stepped?Fe:t.tension||\"monotone\"===t.cubicInterpolationMode?Ve:Gn}(o);let d,u,f,{move:g=!0,reverse:p}=s||{};for(d=0;d<=h;++d)u=n[(r+(p?h-d:d))%a],u.skip||(g?(t.moveTo(u.x,u.y),g=!1):c(t,f,u,p,o.stepped),f=u);return l&&(u=n[(r+(p?h:0))%a],c(t,f,u,p,o.stepped)),!!l}function Qn(t,e,i,s){const n=e.points,{count:o,start:a,ilen:r}=Zn(n,i,s),{move:l=!0,reverse:h}=s||{};let c,d,u,f,g,p,m=0,b=0;const x=t=>(a+(h?r-t:t))%o,_=()=>{f!==g&&(t.lineTo(m,g),t.lineTo(m,f),t.lineTo(m,p))};for(l&&(d=n[x(0)],t.moveTo(d.x,d.y)),c=0;c<=r;++c){if(d=n[x(c)],d.skip)continue;const e=d.x,i=d.y,s=0|e;s===u?(i<f?f=i:i>g&&(g=i),m=(b*m+e)/++b):(_(),t.lineTo(e,i),u=s,b=0,f=g=i),p=i}_()}function to(t){const e=t.options,i=e.borderDash&&e.borderDash.length;return!(t._decimated||t._loop||e.tension||\"monotone\"===e.cubicInterpolationMode||e.stepped||i)?Qn:Jn}const eo=\"function\"==typeof Path2D;function io(t,e,i,s){eo&&!e.options.segment?function(t,e,i,s){let n=e._path;n||(n=e._path=new Path2D,e.path(n,i,s)&&n.closePath()),Kn(t,e.options),t.stroke(n)}(t,e,i,s):function(t,e,i,s){const{segments:n,options:o}=e,a=to(e);for(const r of n)Kn(t,o,r.style),t.beginPath(),a(t,e,r,{start:i,end:i+s-1})&&t.closePath(),t.stroke()}(t,e,i,s)}class so extends Hs{static id=\"line\";static defaults={borderCapStyle:\"butt\",borderDash:[],borderDashOffset:0,borderJoinStyle:\"miter\",borderWidth:3,capBezierPoints:!0,cubicInterpolationMode:\"default\",fill:!1,spanGaps:!1,stepped:!1,tension:0};static defaultRoutes={backgroundColor:\"backgroundColor\",borderColor:\"borderColor\"};static descriptors={_scriptable:!0,_indexable:t=>\"borderDash\"!==t&&\"fill\"!==t};constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||\"monotone\"===i.cubicInterpolationMode)&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;hi(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=zi(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],n=this.points,o=Ii(this,{property:e,start:s,end:s});if(!o.length)return;const a=[],r=function(t){return t.stepped?pi:t.tension||\"monotone\"===t.cubicInterpolationMode?mi:gi}(i);let l,h;for(l=0,h=o.length;l<h;++l){const{start:h,end:c}=o[l],d=n[h],u=n[c];if(d===u){a.push(d);continue}const f=r(d,u,Math.abs((s-d[e])/(u[e]-d[e])),i.stepped);f[e]=t[e],a.push(f)}return 1===a.length?a[0]:a}pathSegment(t,e,i){return to(this)(t,this,e,i)}path(t,e,i){const s=this.segments,n=to(this);let o=this._loop;e=e||0,i=i||this.points.length-e;for(const a of s)o&=n(t,this,a,{start:e,end:e+i-1});return!!o}draw(t,e,i,s){const n=this.options||{};(this.points||[]).length&&n.borderWidth&&(t.save(),io(t,this,i,s),t.restore()),this.animated&&(this._pointsUpdated=!1,this._path=void 0)}}function no(t,e,i,s){const n=t.options,{[i]:o}=t.getProps([i],s);return Math.abs(e-o)<n.radius+n.hitRadius}function oo(t,e){const{x:i,y:s,base:n,width:o,height:a}=t.getProps([\"x\",\"y\",\"base\",\"width\",\"height\"],e);let r,l,h,c,d;return t.horizontal?(d=a/2,r=Math.min(i,n),l=Math.max(i,n),h=s-d,c=s+d):(d=o/2,r=i-d,l=i+d,h=Math.min(s,n),c=Math.max(s,n)),{left:r,top:h,right:l,bottom:c}}function ao(t,e,i,s){return t?0:J(e,i,s)}function ro(t){const e=oo(t),i=e.right-e.left,s=e.bottom-e.top,n=function(t,e,i){const s=t.options.borderWidth,n=t.borderSkipped,o=Mi(s);return{t:ao(n.top,o.top,0,i),r:ao(n.right,o.right,0,e),b:ao(n.bottom,o.bottom,0,i),l:ao(n.left,o.left,0,e)}}(t,i/2,s/2),a=function(t,e,i){const{enableBorderRadius:s}=t.getProps([\"enableBorderRadius\"]),n=t.options.borderRadius,a=wi(n),r=Math.min(e,i),l=t.borderSkipped,h=s||o(n);return{topLeft:ao(!h||l.top||l.left,a.topLeft,0,r),topRight:ao(!h||l.top||l.right,a.topRight,0,r),bottomLeft:ao(!h||l.bottom||l.left,a.bottomLeft,0,r),bottomRight:ao(!h||l.bottom||l.right,a.bottomRight,0,r)}}(t,i/2,s/2);return{outer:{x:e.left,y:e.top,w:i,h:s,radius:a},inner:{x:e.left+n.l,y:e.top+n.t,w:i-n.l-n.r,h:s-n.t-n.b,radius:{topLeft:Math.max(0,a.topLeft-Math.max(n.t,n.l)),topRight:Math.max(0,a.topRight-Math.max(n.t,n.r)),bottomLeft:Math.max(0,a.bottomLeft-Math.max(n.b,n.l)),bottomRight:Math.max(0,a.bottomRight-Math.max(n.b,n.r))}}}}function lo(t,e,i,s){const n=null===e,o=null===i,a=t&&!(n&&o)&&oo(t,s);return a&&(n||tt(e,a.left,a.right))&&(o||tt(i,a.top,a.bottom))}function ho(t,e){t.rect(e.x,e.y,e.w,e.h)}function co(t,e,i={}){const s=t.x!==i.x?-e:0,n=t.y!==i.y?-e:0,o=(t.x+t.w!==i.x+i.w?e:0)-s,a=(t.y+t.h!==i.y+i.h?e:0)-n;return{x:t.x+s,y:t.y+n,w:t.w+o,h:t.h+a,radius:t.radius}}var uo=Object.freeze({__proto__:null,ArcElement:class extends Hs{static id=\"arc\";static defaults={borderAlign:\"center\",borderColor:\"#fff\",borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0};static defaultRoutes={backgroundColor:\"backgroundColor\"};static descriptors={_scriptable:!0,_indexable:t=>\"borderDash\"!==t};circumference;endAngle;fullCircles;innerRadius;outerRadius;pixelMargin;startAngle;constructor(t){super(),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.getProps([\"x\",\"y\"],i),{angle:n,distance:o}=X(s,{x:t,y:e}),{startAngle:a,endAngle:r,innerRadius:h,outerRadius:c,circumference:d}=this.getProps([\"startAngle\",\"endAngle\",\"innerRadius\",\"outerRadius\",\"circumference\"],i),u=(this.options.spacing+this.options.borderWidth)/2,f=l(d,r-a)>=O||Z(n,a,r),g=tt(o,h+u,c+u);return f&&g}getCenterPoint(t){const{x:e,y:i,startAngle:s,endAngle:n,innerRadius:o,outerRadius:a}=this.getProps([\"x\",\"y\",\"startAngle\",\"endAngle\",\"innerRadius\",\"outerRadius\"],t),{offset:r,spacing:l}=this.options,h=(s+n)/2,c=(o+a+l+r)/2;return{x:e+Math.cos(h)*c,y:i+Math.sin(h)*c}}tooltipPosition(t){return this.getCenterPoint(t)}draw(t){const{options:e,circumference:i}=this,s=(e.offset||0)/4,n=(e.spacing||0)/2,o=e.circular;if(this.pixelMargin=\"inner\"===e.borderAlign?.33:0,this.fullCircles=i>O?Math.floor(i/O):0,0===i||this.innerRadius<0||this.outerRadius<0)return;t.save();const a=(this.startAngle+this.endAngle)/2;t.translate(Math.cos(a)*s,Math.sin(a)*s);const r=s*(1-Math.sin(Math.min(C,i||0)));t.fillStyle=e.backgroundColor,t.strokeStyle=e.borderColor,function(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r}=e;let l=e.endAngle;if(o){Xn(t,e,i,s,l,n);for(let e=0;e<o;++e)t.fill();isNaN(r)||(l=a+(r%O||O))}Xn(t,e,i,s,l,n),t.fill()}(t,this,r,n,o),qn(t,this,r,n,o),t.restore()}},BarElement:class extends Hs{static id=\"bar\";static defaults={borderSkipped:\"start\",borderWidth:0,borderRadius:0,inflateAmount:\"auto\",pointStyle:void 0};static defaultRoutes={backgroundColor:\"backgroundColor\",borderColor:\"borderColor\"};constructor(t){super(),this.options=void 0,this.horizontal=void 0,this.base=void 0,this.width=void 0,this.height=void 0,this.inflateAmount=void 0,t&&Object.assign(this,t)}draw(t){const{inflateAmount:e,options:{borderColor:i,backgroundColor:s}}=this,{inner:n,outer:o}=ro(this),a=(r=o.radius).topLeft||r.topRight||r.bottomLeft||r.bottomRight?He:ho;var r;t.save(),o.w===n.w&&o.h===n.h||(t.beginPath(),a(t,co(o,e,n)),t.clip(),a(t,co(n,-e,o)),t.fillStyle=i,t.fill(\"evenodd\")),t.beginPath(),a(t,co(n,e)),t.fillStyle=s,t.fill(),t.restore()}inRange(t,e,i){return lo(this,t,e,i)}inXRange(t,e){return lo(this,t,null,e)}inYRange(t,e){return lo(this,null,t,e)}getCenterPoint(t){const{x:e,y:i,base:s,horizontal:n}=this.getProps([\"x\",\"y\",\"base\",\"horizontal\"],t);return{x:n?(e+s)/2:e,y:n?i:(i+s)/2}}getRange(t){return\"x\"===t?this.width/2:this.height/2}},LineElement:so,PointElement:class extends Hs{static id=\"point\";parsed;skip;stop;static defaults={borderWidth:1,hitRadius:1,hoverBorderWidth:1,hoverRadius:4,pointStyle:\"circle\",radius:3,rotation:0};static defaultRoutes={backgroundColor:\"backgroundColor\",borderColor:\"borderColor\"};constructor(t){super(),this.options=void 0,this.parsed=void 0,this.skip=void 0,this.stop=void 0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.options,{x:n,y:o}=this.getProps([\"x\",\"y\"],i);return Math.pow(t-n,2)+Math.pow(e-o,2)<Math.pow(s.hitRadius+s.radius,2)}inXRange(t,e){return no(this,t,\"x\",e)}inYRange(t,e){return no(this,t,\"y\",e)}getCenterPoint(t){const{x:e,y:i}=this.getProps([\"x\",\"y\"],t);return{x:e,y:i}}size(t){let e=(t=t||this.options||{}).radius||0;e=Math.max(e,e&&t.hoverRadius||0);return 2*(e+(e&&t.borderWidth||0))}draw(t,e){const i=this.options;this.skip||i.radius<.1||!Re(this,e,this.size(i)/2)||(t.strokeStyle=i.borderColor,t.lineWidth=i.borderWidth,t.fillStyle=i.backgroundColor,Le(t,i,this.x,this.y))}getRange(){const t=this.options||{};return t.radius+t.hitRadius}}});function fo(t,e,i,s){const n=t.indexOf(e);if(-1===n)return((t,e,i,s)=>(\"string\"==typeof e?(i=t.push(e)-1,s.unshift({index:i,label:e})):isNaN(e)&&(i=null),i))(t,e,i,s);return n!==t.lastIndexOf(e)?i:n}function go(t){const e=this.getLabels();return t>=0&&t<e.length?e[t]:t}function po(t,e,{horizontal:i,minRotation:s}){const n=$(s),o=(i?Math.sin(n):Math.cos(n))||.001,a=.75*e*(\"\"+t).length;return Math.min(e/o,a)}class mo extends Js{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return s(t)||(\"number\"==typeof t||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:n}=this;const o=t=>s=e?s:t,a=t=>n=i?n:t;if(t){const t=F(s),e=F(n);t<0&&e<0?a(0):t>0&&e>0&&o(0)}if(s===n){let e=0===n?1:Math.abs(.05*n);a(n+e),t||o(s-e)}this.min=s,this.max=n}getTickLimit(){const t=this.options.ticks;let e,{maxTicksLimit:i,stepSize:s}=t;return s?(e=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,e>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.`),e=1e3)):(e=this.computeTickLimit(),i=i||11),i&&(e=Math.min(i,e)),e}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const n=function(t,e){const i=[],{bounds:n,step:o,min:a,max:r,precision:l,count:h,maxTicks:c,maxDigits:d,includeBounds:u}=t,f=o||1,g=c-1,{min:p,max:m}=e,b=!s(a),x=!s(r),_=!s(h),y=(m-p)/(d+1);let v,M,w,k,S=B((m-p)/g/f)*f;if(S<1e-14&&!b&&!x)return[{value:p},{value:m}];k=Math.ceil(m/S)-Math.floor(p/S),k>g&&(S=B(k*S/g/f)*f),s(l)||(v=Math.pow(10,l),S=Math.ceil(S*v)/v),\"ticks\"===n?(M=Math.floor(p/S)*S,w=Math.ceil(m/S)*S):(M=p,w=m),b&&x&&o&&H((r-a)/o,S/1e3)?(k=Math.round(Math.min((r-a)/S,c)),S=(r-a)/k,M=a,w=r):_?(M=b?a:M,w=x?r:w,k=h-1,S=(w-M)/k):(k=(w-M)/S,k=V(k,Math.round(k),S/1e3)?Math.round(k):Math.ceil(k));const P=Math.max(U(S),U(M));v=Math.pow(10,s(l)?P:l),M=Math.round(M*v)/v,w=Math.round(w*v)/v;let D=0;for(b&&(u&&M!==a?(i.push({value:a}),M<a&&D++,V(Math.round((M+D*S)*v)/v,a,po(a,y,t))&&D++):M<a&&D++);D<k;++D){const t=Math.round((M+D*S)*v)/v;if(x&&t>r)break;i.push({value:t})}return x&&u&&w!==r?i.length&&V(i[i.length-1].value,r,po(r,y,t))?i[i.length-1].value=r:i.push({value:r}):x&&w!==r||i.push({value:w}),i}({maxTicks:i,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:!1!==e.includeBounds},this._range||this);return\"ticks\"===t.bounds&&j(n,this,\"value\"),t.reverse?(n.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),n}configure(){const t=this.ticks;let e=this.min,i=this.max;if(super.configure(),this.options.offset&&t.length){const s=(i-e)/Math.max(t.length-1,1)/2;e-=s,i+=s}this._startValue=e,this._endValue=i,this._valueRange=i-e}getLabelForValue(t){return ne(t,this.chart.options.locale,this.options.ticks.format)}}class bo extends mo{static id=\"linear\";static defaults={ticks:{callback:ae.formatters.numeric}};determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?t:0,this.max=a(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,i=$(this.options.ticks.minRotation),s=(t?Math.sin(i):Math.cos(i))||.001,n=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,n.lineHeight/s))}getPixelForValue(t){return null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}const xo=t=>Math.floor(z(t)),_o=(t,e)=>Math.pow(10,xo(t)+e);function yo(t){return 1===t/Math.pow(10,xo(t))}function vo(t,e,i){const s=Math.pow(10,i),n=Math.floor(t/s);return Math.ceil(e/s)-n}function Mo(t,{min:e,max:i}){e=r(t.min,e);const s=[],n=xo(e);let o=function(t,e){let i=xo(e-t);for(;vo(t,e,i)>10;)i++;for(;vo(t,e,i)<10;)i--;return Math.min(i,xo(t))}(e,i),a=o<0?Math.pow(10,Math.abs(o)):1;const l=Math.pow(10,o),h=n>o?Math.pow(10,n):0,c=Math.round((e-h)*a)/a,d=Math.floor((e-h)/l/10)*l*10;let u=Math.floor((c-d)/Math.pow(10,o)),f=r(t.min,Math.round((h+d+u*Math.pow(10,o))*a)/a);for(;f<i;)s.push({value:f,major:yo(f),significand:u}),u>=10?u=u<15?15:20:u++,u>=20&&(o++,u=2,a=o>=0?1:a),f=Math.round((h+d+u*Math.pow(10,o))*a)/a;const g=r(t.max,f);return s.push({value:g,major:yo(g),significand:u}),s}class wo extends Js{static id=\"logarithmic\";static defaults={ticks:{callback:ae.formatters.logarithmic,major:{enabled:!0}}};constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const i=mo.prototype.parse.apply(this,[t,e]);if(0!==i)return a(i)&&i>0?i:null;this._zero=!0}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?Math.max(0,t):null,this.max=a(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!a(this._userMin)&&(this.min=t===_o(this.min,0)?_o(this.min,-1):_o(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const n=e=>i=t?i:e,o=t=>s=e?s:t;i===s&&(i<=0?(n(1),o(10)):(n(_o(i,-1)),o(_o(s,1)))),i<=0&&n(_o(s,-1)),s<=0&&o(_o(i,1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e=Mo({min:this._userMin,max:this._userMax},this);return\"ticks\"===t.bounds&&j(e,this,\"value\"),t.reverse?(e.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),e}getLabelForValue(t){return void 0===t?\"0\":ne(t,this.chart.options.locale,this.options.ticks.format)}configure(){const t=this.min;super.configure(),this._startValue=z(t),this._valueRange=z(this.max)-z(t)}getPixelForValue(t){return void 0!==t&&0!==t||(t=this.min),null===t||isNaN(t)?NaN:this.getPixelForDecimal(t===this.min?0:(z(t)-this._startValue)/this._valueRange)}getValueForPixel(t){const e=this.getDecimalForPixel(t);return Math.pow(10,this._startValue+e*this._valueRange)}}function ko(t){const e=t.ticks;if(e.display&&t.display){const t=ki(e.backdropPadding);return l(e.font&&e.font.size,ue.font.size)+t.height}return 0}function So(t,e,i,s,n){return t===s||t===n?{start:e-i/2,end:e+i/2}:t<s||t>n?{start:e-i,end:e}:{start:e,end:e+i}}function Po(t){const e={l:t.left+t._padding.left,r:t.right-t._padding.right,t:t.top+t._padding.top,b:t.bottom-t._padding.bottom},i=Object.assign({},e),s=[],o=[],a=t._pointLabels.length,r=t.options.pointLabels,l=r.centerPointLabels?C/a:0;for(let u=0;u<a;u++){const a=r.setContext(t.getPointLabelContext(u));o[u]=a.padding;const f=t.getPointPosition(u,t.drawingArea+o[u],l),g=Si(a.font),p=(h=t.ctx,c=g,d=n(d=t._pointLabels[u])?d:[d],{w:Oe(h,c.string,d),h:d.length*c.lineHeight});s[u]=p;const m=G(t.getIndexAngle(u)+l),b=Math.round(Y(m));Do(i,e,m,So(b,f.x,p.w,0,180),So(b,f.y,p.h,90,270))}var h,c,d;t.setCenterPoint(e.l-i.l,i.r-e.r,e.t-i.t,i.b-e.b),t._pointLabelItems=function(t,e,i){const s=[],n=t._pointLabels.length,o=t.options,{centerPointLabels:a,display:r}=o.pointLabels,l={extra:ko(o)/2,additionalAngle:a?C/n:0};let h;for(let o=0;o<n;o++){l.padding=i[o],l.size=e[o];const n=Co(t,o,l);s.push(n),\"auto\"===r&&(n.visible=Oo(n,h),n.visible&&(h=n))}return s}(t,s,o)}function Do(t,e,i,s,n){const o=Math.abs(Math.sin(i)),a=Math.abs(Math.cos(i));let r=0,l=0;s.start<e.l?(r=(e.l-s.start)/o,t.l=Math.min(t.l,e.l-r)):s.end>e.r&&(r=(s.end-e.r)/o,t.r=Math.max(t.r,e.r+r)),n.start<e.t?(l=(e.t-n.start)/a,t.t=Math.min(t.t,e.t-l)):n.end>e.b&&(l=(n.end-e.b)/a,t.b=Math.max(t.b,e.b+l))}function Co(t,e,i){const s=t.drawingArea,{extra:n,additionalAngle:o,padding:a,size:r}=i,l=t.getPointPosition(e,s+n+a,o),h=Math.round(Y(G(l.angle+E))),c=function(t,e,i){90===i||270===i?t-=e/2:(i>270||i<90)&&(t-=e);return t}(l.y,r.h,h),d=function(t){if(0===t||180===t)return\"center\";if(t<180)return\"left\";return\"right\"}(h),u=function(t,e,i){\"right\"===i?t-=e:\"center\"===i&&(t-=e/2);return t}(l.x,r.w,d);return{visible:!0,x:l.x,y:c,textAlign:d,left:u,top:c,right:u+r.w,bottom:c+r.h}}function Oo(t,e){if(!e)return!0;const{left:i,top:s,right:n,bottom:o}=t;return!(Re({x:i,y:s},e)||Re({x:i,y:o},e)||Re({x:n,y:s},e)||Re({x:n,y:o},e))}function Ao(t,e,i){const{left:n,top:o,right:a,bottom:r}=i,{backdropColor:l}=e;if(!s(l)){const i=wi(e.borderRadius),s=ki(e.backdropPadding);t.fillStyle=l;const h=n-s.left,c=o-s.top,d=a-n+s.width,u=r-o+s.height;Object.values(i).some((t=>0!==t))?(t.beginPath(),He(t,{x:h,y:c,w:d,h:u,radius:i}),t.fill()):t.fillRect(h,c,d,u)}}function To(t,e,i,s){const{ctx:n}=t;if(i)n.arc(t.xCenter,t.yCenter,e,0,O);else{let i=t.getPointPosition(0,e);n.moveTo(i.x,i.y);for(let o=1;o<s;o++)i=t.getPointPosition(o,e),n.lineTo(i.x,i.y)}}class Lo extends mo{static id=\"radialLinear\";static defaults={display:!0,animate:!0,position:\"chartArea\",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:ae.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback:t=>t,padding:5,centerPointLabels:!1}};static defaultRoutes={\"angleLines.color\":\"borderColor\",\"pointLabels.color\":\"color\",\"ticks.color\":\"color\"};static descriptors={angleLines:{_fallback:\"grid\"}};constructor(t){super(t),this.xCenter=void 0,this.yCenter=void 0,this.drawingArea=void 0,this._pointLabels=[],this._pointLabelItems=[]}setDimensions(){const t=this._padding=ki(ko(this.options)/2),e=this.width=this.maxWidth-t.width,i=this.height=this.maxHeight-t.height;this.xCenter=Math.floor(this.left+e/2+t.left),this.yCenter=Math.floor(this.top+i/2+t.top),this.drawingArea=Math.floor(Math.min(e,i)/2)}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!1);this.min=a(t)&&!isNaN(t)?t:0,this.max=a(e)&&!isNaN(e)?e:0,this.handleTickRangeOptions()}computeTickLimit(){return Math.ceil(this.drawingArea/ko(this.options))}generateTickLabels(t){mo.prototype.generateTickLabels.call(this,t),this._pointLabels=this.getLabels().map(((t,e)=>{const i=d(this.options.pointLabels.callback,[t,e],this);return i||0===i?i:\"\"})).filter(((t,e)=>this.chart.getDataVisibility(e)))}fit(){const t=this.options;t.display&&t.pointLabels.display?Po(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){return G(t*(O/(this._pointLabels.length||1))+$(this.options.startAngle||0))}getDistanceFromCenterForValue(t){if(s(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(s(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t<e.length){const i=e[t];return function(t,e,i){return Ci(t,{label:i,index:e,type:\"pointLabel\"})}(this.getContext(),t,i)}}getPointPosition(t,e,i=0){const s=this.getIndexAngle(t)-E+i;return{x:Math.cos(s)*e+this.xCenter,y:Math.sin(s)*e+this.yCenter,angle:s}}getPointPositionForValue(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))}getBasePosition(t){return this.getPointPositionForValue(t||0,this.getBaseValue())}getPointLabelPosition(t){const{left:e,top:i,right:s,bottom:n}=this._pointLabelItems[t];return{left:e,top:i,right:s,bottom:n}}drawBackground(){const{backgroundColor:t,grid:{circular:e}}=this.options;if(t){const i=this.ctx;i.save(),i.beginPath(),To(this,this.getDistanceFromCenterForValue(this._endValue),e,this._pointLabels.length),i.closePath(),i.fillStyle=t,i.fill(),i.restore()}}drawGrid(){const t=this.ctx,e=this.options,{angleLines:i,grid:s,border:n}=e,o=this._pointLabels.length;let a,r,l;if(e.pointLabels.display&&function(t,e){const{ctx:i,options:{pointLabels:s}}=t;for(let n=e-1;n>=0;n--){const e=t._pointLabelItems[n];if(!e.visible)continue;const o=s.setContext(t.getPointLabelContext(n));Ao(i,o,e);const a=Si(o.font),{x:r,y:l,textAlign:h}=e;We(i,t._pointLabels[n],r,l+a.lineHeight/2,a,{color:o.color,textAlign:h,textBaseline:\"middle\"})}}(this,o),s.display&&this.ticks.forEach(((t,e)=>{if(0!==e){r=this.getDistanceFromCenterForValue(t.value);const i=this.getContext(e),a=s.setContext(i),l=n.setContext(i);!function(t,e,i,s,n){const o=t.ctx,a=e.circular,{color:r,lineWidth:l}=e;!a&&!s||!r||!l||i<0||(o.save(),o.strokeStyle=r,o.lineWidth=l,o.setLineDash(n.dash),o.lineDashOffset=n.dashOffset,o.beginPath(),To(t,i,a,s),o.closePath(),o.stroke(),o.restore())}(this,a,r,o,l)}})),i.display){for(t.save(),a=o-1;a>=0;a--){const s=i.setContext(this.getPointLabelContext(a)),{color:n,lineWidth:o}=s;o&&n&&(t.lineWidth=o,t.strokeStyle=n,t.setLineDash(s.borderDash),t.lineDashOffset=s.borderDashOffset,r=this.getDistanceFromCenterForValue(e.ticks.reverse?this.min:this.max),l=this.getPointPosition(a,r),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(l.x,l.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let n,o;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign=\"center\",t.textBaseline=\"middle\",this.ticks.forEach(((s,a)=>{if(0===a&&!e.reverse)return;const r=i.setContext(this.getContext(a)),l=Si(r.font);if(n=this.getDistanceFromCenterForValue(this.ticks[a].value),r.showLabelBackdrop){t.font=l.string,o=t.measureText(s.label).width,t.fillStyle=r.backdropColor;const e=ki(r.backdropPadding);t.fillRect(-o/2-e.left,-n-l.size/2-e.top,o+e.width,l.size+e.height)}We(t,s.label,0,-n,l,{color:r.color})})),t.restore()}drawTitle(){}}const Eo={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},Ro=Object.keys(Eo);function Io(t,e){return t-e}function zo(t,e){if(s(e))return null;const i=t._adapter,{parser:n,round:o,isoWeekday:r}=t._parseOpts;let l=e;return\"function\"==typeof n&&(l=n(l)),a(l)||(l=\"string\"==typeof n?i.parse(l,n):i.parse(l)),null===l?null:(o&&(l=\"week\"!==o||!W(r)&&!0!==r?i.startOf(l,o):i.startOf(l,\"isoWeek\",r)),+l)}function Fo(t,e,i,s){const n=Ro.length;for(let o=Ro.indexOf(t);o<n-1;++o){const t=Eo[Ro[o]],n=t.steps?t.steps:Number.MAX_SAFE_INTEGER;if(t.common&&Math.ceil((i-e)/(n*t.size))<=s)return Ro[o]}return Ro[n-1]}function Vo(t,e,i){if(i){if(i.length){const{lo:s,hi:n}=et(i,e);t[i[s]>=e?i[s]:i[n]]=!0}}else t[e]=!0}function Bo(t,e,i){const s=[],n={},o=e.length;let a,r;for(a=0;a<o;++a)r=e[a],n[r]=a,s.push({value:r,major:!1});return 0!==o&&i?function(t,e,i,s){const n=t._adapter,o=+n.startOf(e[0].value,s),a=e[e.length-1].value;let r,l;for(r=o;r<=a;r=+n.add(r,1,s))l=i[r],l>=0&&(e[l].major=!0);return e}(t,s,n,i):s}class No extends Js{static id=\"time\";static defaults={bounds:\"data\",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:\"millisecond\",displayFormats:{}},ticks:{source:\"auto\",callback:!1,major:{enabled:!1}}};constructor(t){super(t),this._cache={data:[],labels:[],all:[]},this._unit=\"day\",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(t,e={}){const i=t.time||(t.time={}),s=this._adapter=new En._date(t.adapters.date);s.init(e),x(i.displayFormats,s.formats()),this._parseOpts={parser:i.parser,round:i.round,isoWeekday:i.isoWeekday},super.init(t),this._normalized=e.normalized}parse(t,e){return void 0===t?null:zo(this,t)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const t=this.options,e=this._adapter,i=t.time.unit||\"day\";let{min:s,max:n,minDefined:o,maxDefined:r}=this.getUserBounds();function l(t){o||isNaN(t.min)||(s=Math.min(s,t.min)),r||isNaN(t.max)||(n=Math.max(n,t.max))}o&&r||(l(this._getLabelBounds()),\"ticks\"===t.bounds&&\"labels\"===t.ticks.source||l(this.getMinMax(!1))),s=a(s)&&!isNaN(s)?s:+e.startOf(Date.now(),i),n=a(n)&&!isNaN(n)?n:+e.endOf(Date.now(),i)+1,this.min=Math.min(s,n-1),this.max=Math.max(s+1,n)}_getLabelBounds(){const t=this.getLabelTimestamps();let e=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;return t.length&&(e=t[0],i=t[t.length-1]),{min:e,max:i}}buildTicks(){const t=this.options,e=t.time,i=t.ticks,s=\"labels\"===i.source?this.getLabelTimestamps():this._generate();\"ticks\"===t.bounds&&s.length&&(this.min=this._userMin||s[0],this.max=this._userMax||s[s.length-1]);const n=this.min,o=nt(s,n,this.max);return this._unit=e.unit||(i.autoSkip?Fo(e.minUnit,this.min,this.max,this._getLabelCapacity(n)):function(t,e,i,s,n){for(let o=Ro.length-1;o>=Ro.indexOf(i);o--){const i=Ro[o];if(Eo[i].common&&t._adapter.diff(n,s,i)>=e-1)return i}return Ro[i?Ro.indexOf(i):0]}(this,o.length,e.minUnit,this.min,this.max)),this._majorUnit=i.major.enabled&&\"year\"!==this._unit?function(t){for(let e=Ro.indexOf(t)+1,i=Ro.length;e<i;++e)if(Eo[Ro[e]].common)return Ro[e]}(this._unit):void 0,this.initOffsets(s),t.reverse&&o.reverse(),Bo(this,o,this._majorUnit)}afterAutoSkip(){this.options.offsetAfterAutoskip&&this.initOffsets(this.ticks.map((t=>+t.value)))}initOffsets(t=[]){let e,i,s=0,n=0;this.options.offset&&t.length&&(e=this.getDecimalForValue(t[0]),s=1===t.length?1-e:(this.getDecimalForValue(t[1])-e)/2,i=this.getDecimalForValue(t[t.length-1]),n=1===t.length?i:(i-this.getDecimalForValue(t[t.length-2]))/2);const o=t.length<3?.5:.25;s=J(s,0,o),n=J(n,0,o),this._offsets={start:s,end:n,factor:1/(s+1+n)}}_generate(){const t=this._adapter,e=this.min,i=this.max,s=this.options,n=s.time,o=n.unit||Fo(n.minUnit,e,i,this._getLabelCapacity(e)),a=l(s.ticks.stepSize,1),r=\"week\"===o&&n.isoWeekday,h=W(r)||!0===r,c={};let d,u,f=e;if(h&&(f=+t.startOf(f,\"isoWeek\",r)),f=+t.startOf(f,h?\"day\":o),t.diff(i,e,o)>1e5*a)throw new Error(e+\" and \"+i+\" are too far apart with stepSize of \"+a+\" \"+o);const g=\"data\"===s.ticks.source&&this.getDataTimestamps();for(d=f,u=0;d<i;d=+t.add(d,a,o),u++)Vo(c,d,g);return d!==i&&\"ticks\"!==s.bounds&&1!==u||Vo(c,d,g),Object.keys(c).sort(((t,e)=>t-e)).map((t=>+t))}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}format(t,e){const i=this.options.time.displayFormats,s=this._unit,n=e||i[s];return this._adapter.format(t,n)}_tickFormatFunction(t,e,i,s){const n=this.options,o=n.ticks.callback;if(o)return d(o,[t,e,i],this);const a=n.time.displayFormats,r=this._unit,l=this._majorUnit,h=r&&a[r],c=l&&a[l],u=i[e],f=l&&c&&u&&u.major;return this._adapter.format(t,s||(f?c:h))}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e<i;++e)s=t[e],s.label=this._tickFormatFunction(s.value,e,t)}getDecimalForValue(t){return null===t?NaN:(t-this.min)/(this.max-this.min)}getPixelForValue(t){const e=this._offsets,i=this.getDecimalForValue(t);return this.getPixelForDecimal((e.start+i)*e.factor)}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return this.min+i*(this.max-this.min)}_getLabelSize(t){const e=this.options.ticks,i=this.ctx.measureText(t).width,s=$(this.isHorizontal()?e.maxRotation:e.minRotation),n=Math.cos(s),o=Math.sin(s),a=this._resolveTickFontOptions(0).size;return{w:i*n+a*o,h:i*o+a*n}}_getLabelCapacity(t){const e=this.options.time,i=e.displayFormats,s=i[e.unit]||i.millisecond,n=this._tickFormatFunction(t,0,Bo(this,[t],this._majorUnit),s),o=this._getLabelSize(n),a=Math.floor(this.isHorizontal()?this.width/o.w:this.height/o.h)-1;return a>0?a:1}getDataTimestamps(){let t,e,i=this._cache.data||[];if(i.length)return i;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(t=0,e=s.length;t<e;++t)i=i.concat(s[t].controller.getAllParsedValues(this));return this._cache.data=this.normalize(i)}getLabelTimestamps(){const t=this._cache.labels||[];let e,i;if(t.length)return t;const s=this.getLabels();for(e=0,i=s.length;e<i;++e)t.push(zo(this,s[e]));return this._cache.labels=this._normalized?t:this.normalize(t)}normalize(t){return lt(t.sort(Io))}}function Wo(t,e,i){let s,n,o,a,r=0,l=t.length-1;i?(e>=t[r].pos&&e<=t[l].pos&&({lo:r,hi:l}=it(t,\"pos\",e)),({pos:s,time:o}=t[r]),({pos:n,time:a}=t[l])):(e>=t[r].time&&e<=t[l].time&&({lo:r,hi:l}=it(t,\"time\",e)),({time:s,pos:o}=t[r]),({time:n,pos:a}=t[l]));const h=n-s;return h?o+(a-o)*(e-s)/h:o}var Ho=Object.freeze({__proto__:null,CategoryScale:class extends Js{static id=\"category\";static defaults={ticks:{callback:go}};constructor(t){super(t),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(t){const e=this._addedLabels;if(e.length){const t=this.getLabels();for(const{index:i,label:s}of e)t[i]===s&&t.splice(i,1);this._addedLabels=[]}super.init(t)}parse(t,e){if(s(t))return null;const i=this.getLabels();return((t,e)=>null===t?null:J(Math.round(t),0,e))(e=isFinite(e)&&i[e]===t?e:fo(i,t,l(e,t),this._addedLabels),i.length-1)}determineDataLimits(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let{min:i,max:s}=this.getMinMax(!0);\"ticks\"===this.options.bounds&&(t||(i=0),e||(s=this.getLabels().length-1)),this.min=i,this.max=s}buildTicks(){const t=this.min,e=this.max,i=this.options.offset,s=[];let n=this.getLabels();n=0===t&&e===n.length-1?n:n.slice(t,e+1),this._valueRange=Math.max(n.length-(i?0:1),1),this._startValue=this.min-(i?.5:0);for(let i=t;i<=e;i++)s.push({value:i});return s}getLabelForValue(t){return go.call(this,t)}configure(){super.configure(),this.isHorizontal()||(this._reversePixels=!this._reversePixels)}getPixelForValue(t){return\"number\"!=typeof t&&(t=this.parse(t)),null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}},LinearScale:bo,LogarithmicScale:wo,RadialLinearScale:Lo,TimeScale:No,TimeSeriesScale:class extends No{static id=\"timeseries\";static defaults=No.defaults;constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Wo(e,this.min),this._tableRange=Wo(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],n=[];let o,a,r,l,h;for(o=0,a=t.length;o<a;++o)l=t[o],l>=e&&l<=i&&s.push(l);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(o=0,a=s.length;o<a;++o)h=s[o+1],r=s[o-1],l=s[o],Math.round((h+r)/2)!==l&&n.push({time:l,pos:o/(a-1)});return n}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),i=this.getLabelTimestamps();return t=e.length&&i.length?this.normalize(e.concat(i)):e.length?e:i,t=this._cache.all=t,t}getDecimalForValue(t){return(Wo(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return Wo(this._table,i*this._tableRange+this._minPos,!0)}}});const jo=[\"rgb(54, 162, 235)\",\"rgb(255, 99, 132)\",\"rgb(255, 159, 64)\",\"rgb(255, 205, 86)\",\"rgb(75, 192, 192)\",\"rgb(153, 102, 255)\",\"rgb(201, 203, 207)\"],$o=jo.map((t=>t.replace(\"rgb(\",\"rgba(\").replace(\")\",\", 0.5)\")));function Yo(t){return jo[t%jo.length]}function Uo(t){return $o[t%$o.length]}function Xo(t){let e=0;return(i,s)=>{const n=t.getDatasetMeta(s).controller;n instanceof Hn?e=function(t,e){return t.backgroundColor=t.data.map((()=>Yo(e++))),e}(i,e):n instanceof jn?e=function(t,e){return t.backgroundColor=t.data.map((()=>Uo(e++))),e}(i,e):n&&(e=function(t,e){return t.borderColor=Yo(e),t.backgroundColor=Uo(e),++e}(i,e))}}function qo(t){let e;for(e in t)if(t[e].borderColor||t[e].backgroundColor)return!0;return!1}var Ko={id:\"colors\",defaults:{enabled:!0,forceOverride:!1},beforeLayout(t,e,i){if(!i.enabled)return;const{data:{datasets:s},options:n}=t.config,{elements:o}=n;if(!i.forceOverride&&(qo(s)||(a=n)&&(a.borderColor||a.backgroundColor)||o&&qo(o)))return;var a;const r=Xo(t);s.forEach(r)}};function Go(t){if(t._decimated){const e=t._data;delete t._decimated,delete t._data,Object.defineProperty(t,\"data\",{configurable:!0,enumerable:!0,writable:!0,value:e})}}function Zo(t){t.data.datasets.forEach((t=>{Go(t)}))}var Jo={id:\"decimation\",defaults:{algorithm:\"min-max\",enabled:!1},beforeElementsUpdate:(t,e,i)=>{if(!i.enabled)return void Zo(t);const n=t.width;t.data.datasets.forEach(((e,o)=>{const{_data:a,indexAxis:r}=e,l=t.getDatasetMeta(o),h=a||e.data;if(\"y\"===Pi([r,t.options.indexAxis]))return;if(!l.controller.supportsDecimation)return;const c=t.scales[l.xAxisID];if(\"linear\"!==c.type&&\"time\"!==c.type)return;if(t.options.parsing)return;let{start:d,count:u}=function(t,e){const i=e.length;let s,n=0;const{iScale:o}=t,{min:a,max:r,minDefined:l,maxDefined:h}=o.getUserBounds();return l&&(n=J(it(e,o.axis,a).lo,0,i-1)),s=h?J(it(e,o.axis,r).hi+1,n,i)-n:i-n,{start:n,count:s}}(l,h);if(u<=(i.threshold||4*n))return void Go(e);let f;switch(s(a)&&(e._data=h,delete e.data,Object.defineProperty(e,\"data\",{configurable:!0,enumerable:!0,get:function(){return this._decimated},set:function(t){this._data=t}})),i.algorithm){case\"lttb\":f=function(t,e,i,s,n){const o=n.samples||s;if(o>=i)return t.slice(e,e+i);const a=[],r=(i-2)/(o-2);let l=0;const h=e+i-1;let c,d,u,f,g,p=e;for(a[l++]=t[p],c=0;c<o-2;c++){let s,n=0,o=0;const h=Math.floor((c+1)*r)+1+e,m=Math.min(Math.floor((c+2)*r)+1,i)+e,b=m-h;for(s=h;s<m;s++)n+=t[s].x,o+=t[s].y;n/=b,o/=b;const x=Math.floor(c*r)+1+e,_=Math.min(Math.floor((c+1)*r)+1,i)+e,{x:y,y:v}=t[p];for(u=f=-1,s=x;s<_;s++)f=.5*Math.abs((y-n)*(t[s].y-v)-(y-t[s].x)*(o-v)),f>u&&(u=f,d=t[s],g=s);a[l++]=d,p=g}return a[l++]=t[h],a}(h,d,u,n,i);break;case\"min-max\":f=function(t,e,i,n){let o,a,r,l,h,c,d,u,f,g,p=0,m=0;const b=[],x=e+i-1,_=t[e].x,y=t[x].x-_;for(o=e;o<e+i;++o){a=t[o],r=(a.x-_)/y*n,l=a.y;const e=0|r;if(e===h)l<f?(f=l,c=o):l>g&&(g=l,d=o),p=(m*p+a.x)/++m;else{const i=o-1;if(!s(c)&&!s(d)){const e=Math.min(c,d),s=Math.max(c,d);e!==u&&e!==i&&b.push({...t[e],x:p}),s!==u&&s!==i&&b.push({...t[s],x:p})}o>0&&i!==u&&b.push(t[i]),b.push(a),h=e,m=0,f=g=l,c=d=u=o}}return b}(h,d,u,n);break;default:throw new Error(`Unsupported decimation algorithm '${i.algorithm}'`)}e._decimated=f}))},destroy(t){Zo(t)}};function Qo(t,e,i,s){if(s)return;let n=e[t],o=i[t];return\"angle\"===t&&(n=G(n),o=G(o)),{property:t,start:n,end:o}}function ta(t,e,i){for(;e>t;e--){const t=i[e];if(!isNaN(t.x)&&!isNaN(t.y))break}return e}function ea(t,e,i,s){return t&&e?s(t[i],e[i]):t?t[i]:e?e[i]:0}function ia(t,e){let i=[],s=!1;return n(t)?(s=!0,i=t):i=function(t,e){const{x:i=null,y:s=null}=t||{},n=e.points,o=[];return e.segments.forEach((({start:t,end:e})=>{e=ta(t,e,n);const a=n[t],r=n[e];null!==s?(o.push({x:a.x,y:s}),o.push({x:r.x,y:s})):null!==i&&(o.push({x:i,y:a.y}),o.push({x:i,y:r.y}))})),o}(t,e),i.length?new so({points:i,options:{tension:0},_loop:s,_fullLoop:s}):null}function sa(t){return t&&!1!==t.fill}function na(t,e,i){let s=t[e].fill;const n=[e];let o;if(!i)return s;for(;!1!==s&&-1===n.indexOf(s);){if(!a(s))return s;if(o=t[s],!o)return!1;if(o.visible)return s;n.push(s),s=o.fill}return!1}function oa(t,e,i){const s=function(t){const e=t.options,i=e.fill;let s=l(i&&i.target,i);void 0===s&&(s=!!e.backgroundColor);if(!1===s||null===s)return!1;if(!0===s)return\"origin\";return s}(t);if(o(s))return!isNaN(s.value)&&s;let n=parseFloat(s);return a(n)&&Math.floor(n)===n?function(t,e,i,s){\"-\"!==t&&\"+\"!==t||(i=e+i);if(i===e||i<0||i>=s)return!1;return i}(s[0],e,n,i):[\"origin\",\"start\",\"end\",\"stack\",\"shape\"].indexOf(s)>=0&&s}function aa(t,e,i){const s=[];for(let n=0;n<i.length;n++){const o=i[n],{first:a,last:r,point:l}=ra(o,e,\"x\");if(!(!l||a&&r))if(a)s.unshift(l);else if(t.push(l),!r)break}t.push(...s)}function ra(t,e,i){const s=t.interpolate(e,i);if(!s)return{};const n=s[i],o=t.segments,a=t.points;let r=!1,l=!1;for(let t=0;t<o.length;t++){const e=o[t],s=a[e.start][i],h=a[e.end][i];if(tt(n,s,h)){r=n===s,l=n===h;break}}return{first:r,last:l,point:s}}class la{constructor(t){this.x=t.x,this.y=t.y,this.radius=t.radius}pathSegment(t,e,i){const{x:s,y:n,radius:o}=this;return e=e||{start:0,end:O},t.arc(s,n,o,e.end,e.start,!0),!i.bounds}interpolate(t){const{x:e,y:i,radius:s}=this,n=t.angle;return{x:e+Math.cos(n)*s,y:i+Math.sin(n)*s,angle:n}}}function ha(t){const{chart:e,fill:i,line:s}=t;if(a(i))return function(t,e){const i=t.getDatasetMeta(e),s=i&&t.isDatasetVisible(e);return s?i.dataset:null}(e,i);if(\"stack\"===i)return function(t){const{scale:e,index:i,line:s}=t,n=[],o=s.segments,a=s.points,r=function(t,e){const i=[],s=t.getMatchingVisibleMetas(\"line\");for(let t=0;t<s.length;t++){const n=s[t];if(n.index===e)break;n.hidden||i.unshift(n.dataset)}return i}(e,i);r.push(ia({x:null,y:e.bottom},s));for(let t=0;t<o.length;t++){const e=o[t];for(let t=e.start;t<=e.end;t++)aa(n,a[t],r)}return new so({points:n,options:{}})}(t);if(\"shape\"===i)return!0;const n=function(t){const e=t.scale||{};if(e.getPointPositionForValue)return function(t){const{scale:e,fill:i}=t,s=e.options,n=e.getLabels().length,a=s.reverse?e.max:e.min,r=function(t,e,i){let s;return s=\"start\"===t?i:\"end\"===t?e.options.reverse?e.min:e.max:o(t)?t.value:e.getBaseValue(),s}(i,e,a),l=[];if(s.grid.circular){const t=e.getPointPositionForValue(0,a);return new la({x:t.x,y:t.y,radius:e.getDistanceFromCenterForValue(r)})}for(let t=0;t<n;++t)l.push(e.getPointPositionForValue(t,r));return l}(t);return function(t){const{scale:e={},fill:i}=t,s=function(t,e){let i=null;return\"start\"===t?i=e.bottom:\"end\"===t?i=e.top:o(t)?i=e.getPixelForValue(t.value):e.getBasePixel&&(i=e.getBasePixel()),i}(i,e);if(a(s)){const t=e.isHorizontal();return{x:t?s:null,y:t?null:s}}return null}(t)}(t);return n instanceof la?n:ia(n,s)}function ca(t,e,i){const s=ha(e),{line:n,scale:o,axis:a}=e,r=n.options,l=r.fill,h=r.backgroundColor,{above:c=h,below:d=h}=l||{};s&&n.points.length&&(Ie(t,i),function(t,e){const{line:i,target:s,above:n,below:o,area:a,scale:r}=e,l=i._loop?\"angle\":e.axis;t.save(),\"x\"===l&&o!==n&&(da(t,s,a.top),ua(t,{line:i,target:s,color:n,scale:r,property:l}),t.restore(),t.save(),da(t,s,a.bottom));ua(t,{line:i,target:s,color:o,scale:r,property:l}),t.restore()}(t,{line:n,target:s,above:c,below:d,area:i,scale:o,axis:a}),ze(t))}function da(t,e,i){const{segments:s,points:n}=e;let o=!0,a=!1;t.beginPath();for(const r of s){const{start:s,end:l}=r,h=n[s],c=n[ta(s,l,n)];o?(t.moveTo(h.x,h.y),o=!1):(t.lineTo(h.x,i),t.lineTo(h.x,h.y)),a=!!e.pathSegment(t,r,{move:a}),a?t.closePath():t.lineTo(c.x,i)}t.lineTo(e.first().x,i),t.closePath(),t.clip()}function ua(t,e){const{line:i,target:s,property:n,color:o,scale:a}=e,r=function(t,e,i){const s=t.segments,n=t.points,o=e.points,a=[];for(const t of s){let{start:s,end:r}=t;r=ta(s,r,n);const l=Qo(i,n[s],n[r],t.loop);if(!e.segments){a.push({source:t,target:l,start:n[s],end:n[r]});continue}const h=Ii(e,l);for(const e of h){const s=Qo(i,o[e.start],o[e.end],e.loop),r=Ri(t,n,s);for(const t of r)a.push({source:t,target:e,start:{[i]:ea(l,s,\"start\",Math.max)},end:{[i]:ea(l,s,\"end\",Math.min)}})}}return a}(i,s,n);for(const{source:e,target:l,start:h,end:c}of r){const{style:{backgroundColor:r=o}={}}=e,d=!0!==s;t.save(),t.fillStyle=r,fa(t,a,d&&Qo(n,h,c)),t.beginPath();const u=!!i.pathSegment(t,e);let f;if(d){u?t.closePath():ga(t,s,c,n);const e=!!s.pathSegment(t,l,{move:u,reverse:!0});f=u&&e,f||ga(t,s,h,n)}t.closePath(),t.fill(f?\"evenodd\":\"nonzero\"),t.restore()}}function fa(t,e,i){const{top:s,bottom:n}=e.chart.chartArea,{property:o,start:a,end:r}=i||{};\"x\"===o&&(t.beginPath(),t.rect(a,s,r-a,n-s),t.clip())}function ga(t,e,i,s){const n=e.interpolate(i,s);n&&t.lineTo(n.x,n.y)}var pa={id:\"filler\",afterDatasetsUpdate(t,e,i){const s=(t.data.datasets||[]).length,n=[];let o,a,r,l;for(a=0;a<s;++a)o=t.getDatasetMeta(a),r=o.dataset,l=null,r&&r.options&&r instanceof so&&(l={visible:t.isDatasetVisible(a),index:a,fill:oa(r,a,s),chart:t,axis:o.controller.options.indexAxis,scale:o.vScale,line:r}),o.$filler=l,n.push(l);for(a=0;a<s;++a)l=n[a],l&&!1!==l.fill&&(l.fill=na(n,a,i.propagate))},beforeDraw(t,e,i){const s=\"beforeDraw\"===i.drawTime,n=t.getSortedVisibleDatasetMetas(),o=t.chartArea;for(let e=n.length-1;e>=0;--e){const i=n[e].$filler;i&&(i.line.updateControlPoints(o,i.axis),s&&i.fill&&ca(t.ctx,i,o))}},beforeDatasetsDraw(t,e,i){if(\"beforeDatasetsDraw\"!==i.drawTime)return;const s=t.getSortedVisibleDatasetMetas();for(let e=s.length-1;e>=0;--e){const i=s[e].$filler;sa(i)&&ca(t.ctx,i,t.chartArea)}},beforeDatasetDraw(t,e,i){const s=e.meta.$filler;sa(s)&&\"beforeDatasetDraw\"===i.drawTime&&ca(t.ctx,s,t.chartArea)},defaults:{propagate:!0,drawTime:\"beforeDatasetDraw\"}};const ma=(t,e)=>{let{boxHeight:i=e,boxWidth:s=e}=t;return t.usePointStyle&&(i=Math.min(i,e),s=t.pointStyleWidth||Math.min(s,e)),{boxWidth:s,boxHeight:i,itemHeight:Math.max(e,i)}};class ba extends Hs{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=d(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter((e=>t.filter(e,this.chart.data)))),t.sort&&(e=e.sort(((e,i)=>t.sort(e,i,this.chart.data)))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display)return void(this.width=this.height=0);const i=t.labels,s=Si(i.font),n=s.size,o=this._computeTitleHeight(),{boxWidth:a,itemHeight:r}=ma(i,n);let l,h;e.font=s.string,this.isHorizontal()?(l=this.maxWidth,h=this._fitRows(o,n,a,r)+10):(h=this.maxHeight,l=this._fitCols(o,s,a,r)+10),this.width=Math.min(l,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:n,maxWidth:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.lineWidths=[0],h=s+a;let c=t;n.textAlign=\"left\",n.textBaseline=\"middle\";let d=-1,u=-h;return this.legendItems.forEach(((t,f)=>{const g=i+e/2+n.measureText(t.text).width;(0===f||l[l.length-1]+g+2*a>o)&&(c+=h,l[l.length-(f>0?0:1)]=0,u+=h,d++),r[f]={left:0,top:u,row:d,width:g,height:s},l[l.length-1]+=g+a})),c}_fitCols(t,e,i,s){const{ctx:n,maxHeight:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.columnSizes=[],h=o-t;let c=a,d=0,u=0,f=0,g=0;return this.legendItems.forEach(((t,o)=>{const{itemWidth:p,itemHeight:m}=function(t,e,i,s,n){const o=function(t,e,i,s){let n=t.text;n&&\"string\"!=typeof n&&(n=n.reduce(((t,e)=>t.length>e.length?t:e)));return e+i.size/2+s.measureText(n).width}(s,t,e,i),a=function(t,e,i){let s=t;\"string\"!=typeof e.text&&(s=xa(e,i));return s}(n,s,e.lineHeight);return{itemWidth:o,itemHeight:a}}(i,e,n,t,s);o>0&&u+m+2*a>h&&(c+=d+a,l.push({width:d,height:u}),f+=d+a,g++,d=u=0),r[o]={left:f,top:u,col:g,width:p,height:m},d=Math.max(d,p),u+=m+a})),c+=d,l.push({width:d,height:u}),c}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:n}}=this,o=Oi(n,this.left,this.width);if(this.isHorizontal()){let n=0,a=ft(i,this.left+s,this.right-this.lineWidths[n]);for(const r of e)n!==r.row&&(n=r.row,a=ft(i,this.left+s,this.right-this.lineWidths[n])),r.top+=this.top+t+s,r.left=o.leftForLtr(o.x(a),r.width),a+=r.width+s}else{let n=0,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height);for(const r of e)r.col!==n&&(n=r.col,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height)),r.top=a,r.left+=this.left+s,r.left=o.leftForLtr(o.x(r.left),r.width),a+=r.height+s}}isHorizontal(){return\"top\"===this.options.position||\"bottom\"===this.options.position}draw(){if(this.options.display){const t=this.ctx;Ie(t,this),this._draw(),ze(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:n,labels:o}=t,a=ue.color,r=Oi(t.rtl,this.left,this.width),h=Si(o.font),{padding:c}=o,d=h.size,u=d/2;let f;this.drawTitle(),s.textAlign=r.textAlign(\"left\"),s.textBaseline=\"middle\",s.lineWidth=.5,s.font=h.string;const{boxWidth:g,boxHeight:p,itemHeight:m}=ma(o,d),b=this.isHorizontal(),x=this._computeTitleHeight();f=b?{x:ft(n,this.left+c,this.right-i[0]),y:this.top+c+x,line:0}:{x:this.left+c,y:ft(n,this.top+x+c,this.bottom-e[0].height),line:0},Ai(this.ctx,t.textDirection);const _=m+c;this.legendItems.forEach(((y,v)=>{s.strokeStyle=y.fontColor,s.fillStyle=y.fontColor;const M=s.measureText(y.text).width,w=r.textAlign(y.textAlign||(y.textAlign=o.textAlign)),k=g+u+M;let S=f.x,P=f.y;r.setWidth(this.width),b?v>0&&S+k+c>this.right&&(P=f.y+=_,f.line++,S=f.x=ft(n,this.left+c,this.right-i[f.line])):v>0&&P+_>this.bottom&&(S=f.x=S+e[f.line].width+c,f.line++,P=f.y=ft(n,this.top+x+c,this.bottom-e[f.line].height));if(function(t,e,i){if(isNaN(g)||g<=0||isNaN(p)||p<0)return;s.save();const n=l(i.lineWidth,1);if(s.fillStyle=l(i.fillStyle,a),s.lineCap=l(i.lineCap,\"butt\"),s.lineDashOffset=l(i.lineDashOffset,0),s.lineJoin=l(i.lineJoin,\"miter\"),s.lineWidth=n,s.strokeStyle=l(i.strokeStyle,a),s.setLineDash(l(i.lineDash,[])),o.usePointStyle){const a={radius:p*Math.SQRT2/2,pointStyle:i.pointStyle,rotation:i.rotation,borderWidth:n},l=r.xPlus(t,g/2);Ee(s,a,l,e+u,o.pointStyleWidth&&g)}else{const o=e+Math.max((d-p)/2,0),a=r.leftForLtr(t,g),l=wi(i.borderRadius);s.beginPath(),Object.values(l).some((t=>0!==t))?He(s,{x:a,y:o,w:g,h:p,radius:l}):s.rect(a,o,g,p),s.fill(),0!==n&&s.stroke()}s.restore()}(r.x(S),P,y),S=gt(w,S+g+u,b?S+k:this.right,t.rtl),function(t,e,i){We(s,i.text,t,e+m/2,h,{strikethrough:i.hidden,textAlign:r.textAlign(i.textAlign)})}(r.x(S),P,y),b)f.x+=k+c;else if(\"string\"!=typeof y.text){const t=h.lineHeight;f.y+=xa(y,t)}else f.y+=_})),Ti(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=Si(e.font),s=ki(e.padding);if(!e.display)return;const n=Oi(t.rtl,this.left,this.width),o=this.ctx,a=e.position,r=i.size/2,l=s.top+r;let h,c=this.left,d=this.width;if(this.isHorizontal())d=Math.max(...this.lineWidths),h=this.top+l,c=ft(t.align,c,this.right-d);else{const e=this.columnSizes.reduce(((t,e)=>Math.max(t,e.height)),0);h=l+ft(t.align,this.top,this.bottom-e-t.labels.padding-this._computeTitleHeight())}const u=ft(a,c,c+d);o.textAlign=n.textAlign(ut(a)),o.textBaseline=\"middle\",o.strokeStyle=e.color,o.fillStyle=e.color,o.font=i.string,We(o,e.text,u,h,i)}_computeTitleHeight(){const t=this.options.title,e=Si(t.font),i=ki(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,n;if(tt(t,this.left,this.right)&&tt(e,this.top,this.bottom))for(n=this.legendHitBoxes,i=0;i<n.length;++i)if(s=n[i],tt(t,s.left,s.left+s.width)&&tt(e,s.top,s.top+s.height))return this.legendItems[i];return null}handleEvent(t){const e=this.options;if(!function(t,e){if((\"mousemove\"===t||\"mouseout\"===t)&&(e.onHover||e.onLeave))return!0;if(e.onClick&&(\"click\"===t||\"mouseup\"===t))return!0;return!1}(t.type,e))return;const i=this._getLegendItemAt(t.x,t.y);if(\"mousemove\"===t.type||\"mouseout\"===t.type){const o=this._hoveredItem,a=(n=i,null!==(s=o)&&null!==n&&s.datasetIndex===n.datasetIndex&&s.index===n.index);o&&!a&&d(e.onLeave,[t,o,this],this),this._hoveredItem=i,i&&!a&&d(e.onHover,[t,i,this],this)}else i&&d(e.onClick,[t,i,this],this);var s,n}}function xa(t,e){return e*(t.text?t.text.length+.5:0)}var _a={id:\"legend\",_element:ba,start(t,e,i){const s=t.legend=new ba({ctx:t.ctx,options:i,chart:t});as.configure(t,s,i),as.addBox(t,s)},stop(t){as.removeBox(t,t.legend),delete t.legend},beforeUpdate(t,e,i){const s=t.legend;as.configure(t,s,i),s.options=i},afterUpdate(t){const e=t.legend;e.buildLabels(),e.adjustHitBoxes()},afterEvent(t,e){e.replay||t.legend.handleEvent(e.event)},defaults:{display:!0,position:\"top\",align:\"center\",fullSize:!0,reverse:!1,weight:1e3,onClick(t,e,i){const s=e.datasetIndex,n=i.chart;n.isDatasetVisible(s)?(n.hide(s),e.hidden=!0):(n.show(s),e.hidden=!1)},onHover:null,onLeave:null,labels:{color:t=>t.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:i,pointStyle:s,textAlign:n,color:o,useBorderRadius:a,borderRadius:r}}=t.legend.options;return t._getSortedDatasetMetas().map((t=>{const l=t.controller.getStyle(i?0:void 0),h=ki(l.borderWidth);return{text:e[t.index].label,fillStyle:l.backgroundColor,fontColor:o,hidden:!t.visible,lineCap:l.borderCapStyle,lineDash:l.borderDash,lineDashOffset:l.borderDashOffset,lineJoin:l.borderJoinStyle,lineWidth:(h.width+h.height)/4,strokeStyle:l.borderColor,pointStyle:s||l.pointStyle,rotation:l.rotation,textAlign:n||l.textAlign,borderRadius:a&&(r||l.borderRadius),datasetIndex:t.index}}),this)}},title:{color:t=>t.chart.options.color,display:!1,position:\"center\",text:\"\"}},descriptors:{_scriptable:t=>!t.startsWith(\"on\"),labels:{_scriptable:t=>![\"generateLabels\",\"filter\",\"sort\"].includes(t)}}};class ya extends Hs{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e){const i=this.options;if(this.left=0,this.top=0,!i.display)return void(this.width=this.height=this.right=this.bottom=0);this.width=this.right=t,this.height=this.bottom=e;const s=n(i.text)?i.text.length:1;this._padding=ki(i.padding);const o=s*Si(i.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=o:this.width=o}isHorizontal(){const t=this.options.position;return\"top\"===t||\"bottom\"===t}_drawArgs(t){const{top:e,left:i,bottom:s,right:n,options:o}=this,a=o.align;let r,l,h,c=0;return this.isHorizontal()?(l=ft(a,i,n),h=e+t,r=n-i):(\"left\"===o.position?(l=i+t,h=ft(a,s,e),c=-.5*C):(l=n-t,h=ft(a,e,s),c=.5*C),r=s-e),{titleX:l,titleY:h,maxWidth:r,rotation:c}}draw(){const t=this.ctx,e=this.options;if(!e.display)return;const i=Si(e.font),s=i.lineHeight/2+this._padding.top,{titleX:n,titleY:o,maxWidth:a,rotation:r}=this._drawArgs(s);We(t,e.text,0,0,i,{color:e.color,maxWidth:a,rotation:r,textAlign:ut(e.align),textBaseline:\"middle\",translation:[n,o]})}}var va={id:\"title\",_element:ya,start(t,e,i){!function(t,e){const i=new ya({ctx:t.ctx,options:e,chart:t});as.configure(t,i,e),as.addBox(t,i),t.titleBlock=i}(t,i)},stop(t){const e=t.titleBlock;as.removeBox(t,e),delete t.titleBlock},beforeUpdate(t,e,i){const s=t.titleBlock;as.configure(t,s,i),s.options=i},defaults:{align:\"center\",display:!1,font:{weight:\"bold\"},fullSize:!0,padding:10,position:\"top\",text:\"\",weight:2e3},defaultRoutes:{color:\"color\"},descriptors:{_scriptable:!0,_indexable:!1}};const Ma=new WeakMap;var wa={id:\"subtitle\",start(t,e,i){const s=new ya({ctx:t.ctx,options:i,chart:t});as.configure(t,s,i),as.addBox(t,s),Ma.set(t,s)},stop(t){as.removeBox(t,Ma.get(t)),Ma.delete(t)},beforeUpdate(t,e,i){const s=Ma.get(t);as.configure(t,s,i),s.options=i},defaults:{align:\"center\",display:!1,font:{weight:\"normal\"},fullSize:!0,padding:0,position:\"top\",text:\"\",weight:1500},defaultRoutes:{color:\"color\"},descriptors:{_scriptable:!0,_indexable:!1}};const ka={average(t){if(!t.length)return!1;let e,i,s=0,n=0,o=0;for(e=0,i=t.length;e<i;++e){const i=t[e].element;if(i&&i.hasValue()){const t=i.tooltipPosition();s+=t.x,n+=t.y,++o}}return{x:s/o,y:n/o}},nearest(t,e){if(!t.length)return!1;let i,s,n,o=e.x,a=e.y,r=Number.POSITIVE_INFINITY;for(i=0,s=t.length;i<s;++i){const s=t[i].element;if(s&&s.hasValue()){const t=q(e,s.getCenterPoint());t<r&&(r=t,n=s)}}if(n){const t=n.tooltipPosition();o=t.x,a=t.y}return{x:o,y:a}}};function Sa(t,e){return e&&(n(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function Pa(t){return(\"string\"==typeof t||t instanceof String)&&t.indexOf(\"\\n\")>-1?t.split(\"\\n\"):t}function Da(t,e){const{element:i,datasetIndex:s,index:n}=e,o=t.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:t,label:a,parsed:o.getParsed(n),raw:t.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:i}}function Ca(t,e){const i=t.chart.ctx,{body:s,footer:n,title:o}=t,{boxWidth:a,boxHeight:r}=e,l=Si(e.bodyFont),h=Si(e.titleFont),c=Si(e.footerFont),d=o.length,f=n.length,g=s.length,p=ki(e.padding);let m=p.height,b=0,x=s.reduce(((t,e)=>t+e.before.length+e.lines.length+e.after.length),0);if(x+=t.beforeBody.length+t.afterBody.length,d&&(m+=d*h.lineHeight+(d-1)*e.titleSpacing+e.titleMarginBottom),x){m+=g*(e.displayColors?Math.max(r,l.lineHeight):l.lineHeight)+(x-g)*l.lineHeight+(x-1)*e.bodySpacing}f&&(m+=e.footerMarginTop+f*c.lineHeight+(f-1)*e.footerSpacing);let _=0;const y=function(t){b=Math.max(b,i.measureText(t).width+_)};return i.save(),i.font=h.string,u(t.title,y),i.font=l.string,u(t.beforeBody.concat(t.afterBody),y),_=e.displayColors?a+2+e.boxPadding:0,u(s,(t=>{u(t.before,y),u(t.lines,y),u(t.after,y)})),_=0,i.font=c.string,u(t.footer,y),i.restore(),b+=p.width,{width:b,height:m}}function Oa(t,e,i,s){const{x:n,width:o}=i,{width:a,chartArea:{left:r,right:l}}=t;let h=\"center\";return\"center\"===s?h=n<=(r+l)/2?\"left\":\"right\":n<=o/2?h=\"left\":n>=a-o/2&&(h=\"right\"),function(t,e,i,s){const{x:n,width:o}=s,a=i.caretSize+i.caretPadding;return\"left\"===t&&n+o+a>e.width||\"right\"===t&&n-o-a<0||void 0}(h,t,e,i)&&(h=\"center\"),h}function Aa(t,e,i){const s=i.yAlign||e.yAlign||function(t,e){const{y:i,height:s}=e;return i<s/2?\"top\":i>t.height-s/2?\"bottom\":\"center\"}(t,i);return{xAlign:i.xAlign||e.xAlign||Oa(t,e,i,s),yAlign:s}}function Ta(t,e,i,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=t,{xAlign:r,yAlign:l}=i,h=n+o,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=wi(a);let g=function(t,e){let{x:i,width:s}=t;return\"right\"===e?i-=s:\"center\"===e&&(i-=s/2),i}(e,r);const p=function(t,e,i){let{y:s,height:n}=t;return\"top\"===e?s+=i:s-=\"bottom\"===e?n+i:n/2,s}(e,l,h);return\"center\"===l?\"left\"===r?g+=h:\"right\"===r&&(g-=h):\"left\"===r?g-=Math.max(c,u)+n:\"right\"===r&&(g+=Math.max(d,f)+n),{x:J(g,0,s.width-e.width),y:J(p,0,s.height-e.height)}}function La(t,e,i){const s=ki(i.padding);return\"center\"===e?t.x+t.width/2:\"right\"===e?t.x+t.width-s.right:t.x+s.left}function Ea(t){return Sa([],Pa(t))}function Ra(t,e){const i=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return i?t.override(i):t}const Ia={beforeTitle:e,title(t){if(t.length>0){const e=t[0],i=e.chart.data.labels,s=i?i.length:0;if(this&&this.options&&\"dataset\"===this.options.mode)return e.dataset.label||\"\";if(e.label)return e.label;if(s>0&&e.dataIndex<s)return i[e.dataIndex]}return\"\"},afterTitle:e,beforeBody:e,beforeLabel:e,label(t){if(this&&this.options&&\"dataset\"===this.options.mode)return t.label+\": \"+t.formattedValue||t.formattedValue;let e=t.dataset.label||\"\";e&&(e+=\": \");const i=t.formattedValue;return s(i)||(e+=i),e},labelColor(t){const e=t.chart.getDatasetMeta(t.datasetIndex).controller.getStyle(t.dataIndex);return{borderColor:e.borderColor,backgroundColor:e.backgroundColor,borderWidth:e.borderWidth,borderDash:e.borderDash,borderDashOffset:e.borderDashOffset,borderRadius:0}},labelTextColor(){return this.options.bodyColor},labelPointStyle(t){const e=t.chart.getDatasetMeta(t.datasetIndex).controller.getStyle(t.dataIndex);return{pointStyle:e.pointStyle,rotation:e.rotation}},afterLabel:e,afterBody:e,beforeFooter:e,footer:e,afterFooter:e};function za(t,e,i,s){const n=t[e].call(i,s);return void 0===n?Ia[e].call(i,s):n}class Fa extends Hs{static positioners=ka;constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,n=new Os(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(n)),n}getContext(){return this.$context||(this.$context=(t=this.chart.getContext(),e=this,i=this._tooltipItems,Ci(t,{tooltip:e,tooltipItems:i,type:\"tooltip\"})));var t,e,i}getTitle(t,e){const{callbacks:i}=e,s=za(i,\"beforeTitle\",this,t),n=za(i,\"title\",this,t),o=za(i,\"afterTitle\",this,t);let a=[];return a=Sa(a,Pa(s)),a=Sa(a,Pa(n)),a=Sa(a,Pa(o)),a}getBeforeBody(t,e){return Ea(za(e.callbacks,\"beforeBody\",this,t))}getBody(t,e){const{callbacks:i}=e,s=[];return u(t,(t=>{const e={before:[],lines:[],after:[]},n=Ra(i,t);Sa(e.before,Pa(za(n,\"beforeLabel\",this,t))),Sa(e.lines,za(n,\"label\",this,t)),Sa(e.after,Pa(za(n,\"afterLabel\",this,t))),s.push(e)})),s}getAfterBody(t,e){return Ea(za(e.callbacks,\"afterBody\",this,t))}getFooter(t,e){const{callbacks:i}=e,s=za(i,\"beforeFooter\",this,t),n=za(i,\"footer\",this,t),o=za(i,\"afterFooter\",this,t);let a=[];return a=Sa(a,Pa(s)),a=Sa(a,Pa(n)),a=Sa(a,Pa(o)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],n=[],o=[];let a,r,l=[];for(a=0,r=e.length;a<r;++a)l.push(Da(this.chart,e[a]));return t.filter&&(l=l.filter(((e,s,n)=>t.filter(e,s,n,i)))),t.itemSort&&(l=l.sort(((e,s)=>t.itemSort(e,s,i)))),u(l,(e=>{const i=Ra(t.callbacks,e);s.push(za(i,\"labelColor\",this,e)),n.push(za(i,\"labelPointStyle\",this,e)),o.push(za(i,\"labelTextColor\",this,e))})),this.labelColors=s,this.labelPointStyles=n,this.labelTextColors=o,this.dataPoints=l,l}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let n,o=[];if(s.length){const t=ka[i.position].call(this,s,this._eventPosition);o=this._createItems(i),this.title=this.getTitle(o,i),this.beforeBody=this.getBeforeBody(o,i),this.body=this.getBody(o,i),this.afterBody=this.getAfterBody(o,i),this.footer=this.getFooter(o,i);const e=this._size=Ca(this,i),a=Object.assign({},t,e),r=Aa(this.chart,i,a),l=Ta(i,a,r,this.chart);this.xAlign=r.xAlign,this.yAlign=r.yAlign,n={opacity:1,x:l.x,y:l.y,width:e.width,height:e.height,caretX:t.x,caretY:t.y}}else 0!==this.opacity&&(n={opacity:0});this._tooltipItems=o,this.$context=void 0,n&&this._resolveAnimations().update(this,n),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const n=this.getCaretPosition(t,i,s);e.lineTo(n.x1,n.y1),e.lineTo(n.x2,n.y2),e.lineTo(n.x3,n.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:n}=this,{caretSize:o,cornerRadius:a}=i,{topLeft:r,topRight:l,bottomLeft:h,bottomRight:c}=wi(a),{x:d,y:u}=t,{width:f,height:g}=e;let p,m,b,x,_,y;return\"center\"===n?(_=u+g/2,\"left\"===s?(p=d,m=p-o,x=_+o,y=_-o):(p=d+f,m=p+o,x=_-o,y=_+o),b=p):(m=\"left\"===s?d+Math.max(r,h)+o:\"right\"===s?d+f-Math.max(l,c)-o:this.caretX,\"top\"===n?(x=u,_=x-o,p=m-o,b=m+o):(x=u+g,_=x+o,p=m+o,b=m-o),y=x),{x1:p,x2:m,x3:b,y1:x,y2:_,y3:y}}drawTitle(t,e,i){const s=this.title,n=s.length;let o,a,r;if(n){const l=Oi(i.rtl,this.x,this.width);for(t.x=La(this,i.titleAlign,i),e.textAlign=l.textAlign(i.titleAlign),e.textBaseline=\"middle\",o=Si(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=o.string,r=0;r<n;++r)e.fillText(s[r],l.x(t.x),t.y+o.lineHeight/2),t.y+=o.lineHeight+a,r+1===n&&(t.y+=i.titleMarginBottom-a)}}_drawColorBox(t,e,i,s,n){const a=this.labelColors[i],r=this.labelPointStyles[i],{boxHeight:l,boxWidth:h}=n,c=Si(n.bodyFont),d=La(this,\"left\",n),u=s.x(d),f=l<c.lineHeight?(c.lineHeight-l)/2:0,g=e.y+f;if(n.usePointStyle){const e={radius:Math.min(h,l)/2,pointStyle:r.pointStyle,rotation:r.rotation,borderWidth:1},i=s.leftForLtr(u,h)+h/2,o=g+l/2;t.strokeStyle=n.multiKeyBackground,t.fillStyle=n.multiKeyBackground,Le(t,e,i,o),t.strokeStyle=a.borderColor,t.fillStyle=a.backgroundColor,Le(t,e,i,o)}else{t.lineWidth=o(a.borderWidth)?Math.max(...Object.values(a.borderWidth)):a.borderWidth||1,t.strokeStyle=a.borderColor,t.setLineDash(a.borderDash||[]),t.lineDashOffset=a.borderDashOffset||0;const e=s.leftForLtr(u,h),i=s.leftForLtr(s.xPlus(u,1),h-2),r=wi(a.borderRadius);Object.values(r).some((t=>0!==t))?(t.beginPath(),t.fillStyle=n.multiKeyBackground,He(t,{x:e,y:g,w:h,h:l,radius:r}),t.fill(),t.stroke(),t.fillStyle=a.backgroundColor,t.beginPath(),He(t,{x:i,y:g+1,w:h-2,h:l-2,radius:r}),t.fill()):(t.fillStyle=n.multiKeyBackground,t.fillRect(e,g,h,l),t.strokeRect(e,g,h,l),t.fillStyle=a.backgroundColor,t.fillRect(i,g+1,h-2,l-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:n,bodyAlign:o,displayColors:a,boxHeight:r,boxWidth:l,boxPadding:h}=i,c=Si(i.bodyFont);let d=c.lineHeight,f=0;const g=Oi(i.rtl,this.x,this.width),p=function(i){e.fillText(i,g.x(t.x+f),t.y+d/2),t.y+=d+n},m=g.textAlign(o);let b,x,_,y,v,M,w;for(e.textAlign=o,e.textBaseline=\"middle\",e.font=c.string,t.x=La(this,m,i),e.fillStyle=i.bodyColor,u(this.beforeBody,p),f=a&&\"right\"!==m?\"center\"===o?l/2+h:l+2+h:0,y=0,M=s.length;y<M;++y){for(b=s[y],x=this.labelTextColors[y],e.fillStyle=x,u(b.before,p),_=b.lines,a&&_.length&&(this._drawColorBox(e,t,y,g,i),d=Math.max(c.lineHeight,r)),v=0,w=_.length;v<w;++v)p(_[v]),d=c.lineHeight;u(b.after,p)}f=0,d=c.lineHeight,u(this.afterBody,p),t.y-=n}drawFooter(t,e,i){const s=this.footer,n=s.length;let o,a;if(n){const r=Oi(i.rtl,this.x,this.width);for(t.x=La(this,i.footerAlign,i),t.y+=i.footerMarginTop,e.textAlign=r.textAlign(i.footerAlign),e.textBaseline=\"middle\",o=Si(i.footerFont),e.fillStyle=i.footerColor,e.font=o.string,a=0;a<n;++a)e.fillText(s[a],r.x(t.x),t.y+o.lineHeight/2),t.y+=o.lineHeight+i.footerSpacing}}drawBackground(t,e,i,s){const{xAlign:n,yAlign:o}=this,{x:a,y:r}=t,{width:l,height:h}=i,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=wi(s.cornerRadius);e.fillStyle=s.backgroundColor,e.strokeStyle=s.borderColor,e.lineWidth=s.borderWidth,e.beginPath(),e.moveTo(a+c,r),\"top\"===o&&this.drawCaret(t,e,i,s),e.lineTo(a+l-d,r),e.quadraticCurveTo(a+l,r,a+l,r+d),\"center\"===o&&\"right\"===n&&this.drawCaret(t,e,i,s),e.lineTo(a+l,r+h-f),e.quadraticCurveTo(a+l,r+h,a+l-f,r+h),\"bottom\"===o&&this.drawCaret(t,e,i,s),e.lineTo(a+u,r+h),e.quadraticCurveTo(a,r+h,a,r+h-u),\"center\"===o&&\"left\"===n&&this.drawCaret(t,e,i,s),e.lineTo(a,r+c),e.quadraticCurveTo(a,r,a+c,r),e.closePath(),e.fill(),s.borderWidth>0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,n=i&&i.y;if(s||n){const i=ka[t.position].call(this,this._active,this._eventPosition);if(!i)return;const o=this._size=Ca(this,t),a=Object.assign({},i,this._size),r=Aa(e,t,a),l=Ta(t,a,r,e);s._to===l.x&&n._to===l.y||(this.xAlign=r.xAlign,this.yAlign=r.yAlign,this.width=o.width,this.height=o.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,l))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},n={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const o=ki(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(n,t,s,e),Ai(t,e.textDirection),n.y+=o.top,this.drawTitle(n,t,e),this.drawBody(n,t,e),this.drawFooter(n,t,e),Ti(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map((({datasetIndex:t,index:e})=>{const i=this.chart.getDatasetMeta(t);if(!i)throw new Error(\"Cannot find a dataset at index \"+t);return{datasetIndex:t,element:i.data[e],index:e}})),n=!f(i,s),o=this._positionChanged(s,e);(n||o)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,n=this._active||[],o=this._getActiveElements(t,n,e,i),a=this._positionChanged(o,t),r=e||!f(o,n)||a;return r&&(this._active=o,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),r}_getActiveElements(t,e,i,s){const n=this.options;if(\"mouseout\"===t.type)return[];if(!s)return e;const o=this.chart.getElementsAtEventForMode(t,n.mode,n,i);return n.reverse&&o.reverse(),o}_positionChanged(t,e){const{caretX:i,caretY:s,options:n}=this,o=ka[n.position].call(this,t,e);return!1!==o&&(i!==o.x||s!==o.y)}}var Va={id:\"tooltip\",_element:Fa,positioners:ka,afterInit(t,e,i){i&&(t.tooltip=new Fa({chart:t,options:i}))},beforeUpdate(t,e,i){t.tooltip&&t.tooltip.initialize(i)},reset(t,e,i){t.tooltip&&t.tooltip.initialize(i)},afterDraw(t){const e=t.tooltip;if(e&&e._willRender()){const i={tooltip:e};if(!1===t.notifyPlugins(\"beforeTooltipDraw\",{...i,cancelable:!0}))return;e.draw(t.ctx),t.notifyPlugins(\"afterTooltipDraw\",i)}},afterEvent(t,e){if(t.tooltip){const i=e.replay;t.tooltip.handleEvent(e.event,i,e.inChartArea)&&(e.changed=!0)}},defaults:{enabled:!0,external:null,position:\"average\",backgroundColor:\"rgba(0,0,0,0.8)\",titleColor:\"#fff\",titleFont:{weight:\"bold\"},titleSpacing:2,titleMarginBottom:6,titleAlign:\"left\",bodyColor:\"#fff\",bodySpacing:2,bodyFont:{},bodyAlign:\"left\",footerColor:\"#fff\",footerSpacing:2,footerMarginTop:6,footerFont:{weight:\"bold\"},footerAlign:\"left\",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(t,e)=>e.bodyFont.size,boxWidth:(t,e)=>e.bodyFont.size,multiKeyBackground:\"#fff\",displayColors:!0,boxPadding:0,borderColor:\"rgba(0,0,0,0)\",borderWidth:0,animation:{duration:400,easing:\"easeOutQuart\"},animations:{numbers:{type:\"number\",properties:[\"x\",\"y\",\"width\",\"height\",\"caretX\",\"caretY\"]},opacity:{easing:\"linear\",duration:200}},callbacks:Ia},defaultRoutes:{bodyFont:\"font\",footerFont:\"font\",titleFont:\"font\"},descriptors:{_scriptable:t=>\"filter\"!==t&&\"itemSort\"!==t&&\"external\"!==t,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:\"animation\"}},additionalOptionScopes:[\"interaction\"]};return On.register($n,Ho,uo,t),On.helpers={...Ni},On._adapters=En,On.Animation=Cs,On.Animations=Os,On.animator=xt,On.controllers=en.controllers.items,On.DatasetController=Ws,On.Element=Hs,On.elements=uo,On.Interaction=Xi,On.layouts=as,On.platforms=Ss,On.Scale=Js,On.Ticks=ae,Object.assign(On,$n,Ho,uo,t,Ss),On.Chart=On,\"undefined\"!=typeof window&&(window.Chart=On),On}));\n//# sourceMappingURL=chart.umd.js.map\n"
  },
  {
    "path": "dashboard/src/listeners.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Mosquitto Broker Dashboard</title>\n    <link rel=\"stylesheet\" href=\"tailwind/styles.css\">\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"media/favicon-16x16.png\" sizes=\"16x16\">\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"media/favicon-32x32.png\" sizes=\"32x32\">\n</head>\n<body class=\"min-h-screen bg-gray-50\">\n    <div id=\"menu-overlay\" class=\"fixed inset-0 z-40 opacity-0 transition-opacity duration-300\"></div>\n\n    <!-- sliding menu panel -->\n    <div id=\"sliding-menu\" class=\"fixed top-0 left-0 h-full w-80 bg-white shadow-2xl transform -translate-x-full transition-transform duration-300 ease-in-out z-50\">\n        <div class=\"flex flex-col h-full\">\n            <!-- menu header with close button (displayed only for mobile) -->\n            <div class=\"flex items-center justify-between p-6 border-b border-gray-200\">\n                <div></div>\n                <button id=\"menu-close\" class=\"p-2 hover:bg-gray-100 rounded-md transition-colors duration-200\">\n                    <svg class=\"h-5 w-5 text-gray-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n                    </svg>\n                </button>\n            </div>\n\n            <!-- menu content -->\n            <div class=\"flex-1 overflow-y-auto p-6\">\n                <nav class=\"space-y-1 mb-8\">\n                    <a href=\".\" class=\"flex items-center gap-3 px-3 py-2 text-gray-700 hover:bg-gray-100 rounded-md transition-colors\">\n                        Dashboard\n                    </a>\n                    <a href=\"listeners.html\" class=\"flex items-center gap-3 px-3 py-2 text-gray-700 hover:bg-gray-100 rounded-md transition-colors bg-gray-100\">\n                        Listeners\n                    </a>\n                    <a href=\"https://mosquitto.org/documentation\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"flex items-center gap-3 px-3 py-2 text-gray-700 hover:bg-gray-100 rounded-md transition-colors\">\n                        Docs\n                    </a>\n                </nav>\n            </div>\n\n            <!-- menu footer -->\n            <div class=\"border-t border-gray-200 p-6\">\n                <a href=\"https://cedalo.com/link/go-pro-from-mosquitto\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"block w-full text-center bg-c-orange text-white py-2 px-4 rounded-md hover:bg-orange-700 transition-colors duration-200\">\n                    Go Pro\n                </a>\n            </div>\n        </div>\n    </div>\n    <script>\n        const isSidebarOpen = sessionStorage.getItem(\"isSidebarOpen\") === \"true\";\n        const isDesktop = window.matchMedia(\"(min-width: 1024px)\").matches;\n        const openSidebar = isSidebarOpen && isDesktop;\n\n        if (openSidebar) {\n            document.documentElement.classList.add(\"sidebar-open\", \"sidebar-preload\");\n        }\n\n        // remove sidebar-preload class after at least one paint so that it does not block animations when we press on the \"menu\" button\n        requestAnimationFrame(() => {\n            requestAnimationFrame(() => {\n              document.documentElement.classList.remove(\"sidebar-preload\");\n            });\n        });\n    </script>\n    <div id=\"main-content\" class=\"transition-all duration-300 ease-in-out\">\n        <div class=\"p-6\">\n            <div class=\"max-w-7xl mx-auto space-y-6\">\n                <!-- header -->\n                <div class=\"sticky bg-gray-50 top-0 z-10 pb-2 flex items-center justify-between\">\n                    <button id=\"menu-toggle\" class=\"inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-c-orange hover:cursor-pointer hover:text-white transition-colors duration-200\">\n                        <!-- menu icon -->\n                        <svg id=\"hamburger-icon\" class=\"h-4 w-4 transition-opacity duration-200\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"></path>\n                        </svg>\n\n                        <!-- arrow icon in place of the menu icon when the menu is open (hidden by default) -->\n                        <svg id=\"arrow-icon\" class=\"h-4 w-4 hidden transition-opacity duration-200\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"></path>\n                        </svg>\n                    </button>\n                    <div id=\"logo-text\" class=\"flex items-center gap-3 w-64\">\n                        <img src=\"media/mosquitto-logo.png\"/>\n                    </div>\n\n                    <div id=\"logo-icon\" class=\"hidden\">\n                        <svg width=\"71\" height=\"54\" viewBox=\"0 0 114 86\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n                            <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M57.0013 86L58.5908 64.5156L59.8811 47.0706C62.5402 45.9585 64.4064 43.3537 64.4064 40.3183C64.4064 36.2725 61.0902 32.9916 57.0013 32.9916C52.9121 32.9916 49.5962 36.2725 49.5962 40.3183C49.5962 43.3537 51.4625 45.9585 54.1216 47.0706L55.412 64.5156L57.0013 86Z\" fill=\"#F3771C\"/>\n                            <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M6.42676 40.291C6.42676 52.6852 11.0068 64.2874 18.9495 73.2527L14.1625 77.4988C5.1991 67.4059 0 54.2981 0 40.291C0 24.5083 6.55628 10.2381 17.1193 7.62939e-06L17.3602 0.213791L31.52 12.7736C18.2121 24.8448 15.456 44.4755 24.7152 59.5987L29.6037 55.2628C22.5455 42.6463 25.2344 26.7106 36.3333 17.0429L41.1815 21.3426L45.4936 25.168L49.9667 29.1355C48.2936 30.1731 46.8715 31.5698 45.8107 33.2165C44.4992 35.2519 43.7396 37.6684 43.7396 40.2597C43.7396 46.037 47.5151 50.9431 52.7571 52.6942L53.2087 58.7914C44.4739 57.0506 37.8941 49.4142 37.8941 40.2597C37.8941 36.1833 39.1989 32.408 41.4175 29.3198L37.1379 25.5231L37.1219 25.509C30.1062 34.73 30.5396 47.5312 38.0944 56.2719L23.7361 69.0071C9.60061 52.9899 9.19696 29.2169 22.7358 12.7492L17.9465 8.50084C11.336 16.442 7.14974 26.4328 6.51204 37.3633L6.48277 37.3959L6.50886 37.4186C6.45429 38.3692 6.42676 39.3268 6.42676 40.2912V40.291ZM61.2429 52.6942C66.4849 50.9431 70.2608 46.037 70.2608 40.2597C70.2608 37.6684 69.5008 35.2519 68.1898 33.217C67.1285 31.5703 65.7064 30.1731 64.0342 29.1355L68.5069 25.1683L72.8194 21.3426H72.8189L77.6672 17.0429C88.7656 26.7106 91.455 42.6463 84.3968 55.2628L89.2848 59.5987C98.5445 44.4755 95.7883 24.8448 82.48 12.7736L96.6408 0.212845L96.8807 7.62939e-06C107.444 10.2381 114 24.5083 114 40.291C114 54.2981 108.801 67.4059 99.838 77.4988L95.0505 73.2527C102.994 64.2874 107.573 52.6852 107.573 40.291C107.573 39.3266 107.546 38.369 107.492 37.4184L107.517 37.3958L107.488 37.3632C106.851 26.4326 102.664 16.4419 96.054 8.50068L91.2647 12.7491C104.804 29.2167 104.4 52.9897 90.2639 69.007L75.9061 56.2718C83.4609 47.531 83.8943 34.7299 76.8784 25.5088L76.8621 25.523L72.5825 29.3202C74.8011 32.4078 76.1059 36.1836 76.1059 40.2596C76.1059 49.414 69.5261 57.05 60.7918 58.7913L61.2429 52.6941V52.6942Z\" fill=\"#3C5280\"/>\n                        </svg>\n                    </div>\n                </div>\n\n                <div class=\"p-4\"></div>\n\n                <!-- listener information header card -->\n                <div class=\"card flex items-top gap-3 p-6\">\n                    <div id=\"broker-info-icon\" class=\"mr-[3%] hidden\">\n                        <svg width=\"114\" height=\"86\" viewBox=\"0 0 114 86\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n                            <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M57.0013 86L58.5908 64.5156L59.8811 47.0706C62.5402 45.9585 64.4064 43.3537 64.4064 40.3183C64.4064 36.2725 61.0902 32.9916 57.0013 32.9916C52.9121 32.9916 49.5962 36.2725 49.5962 40.3183C49.5962 43.3537 51.4625 45.9585 54.1216 47.0706L55.412 64.5156L57.0013 86Z\" fill=\"#F3771C\"/>\n                            <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M6.42676 40.291C6.42676 52.6852 11.0068 64.2874 18.9495 73.2527L14.1625 77.4988C5.1991 67.4059 0 54.2981 0 40.291C0 24.5083 6.55628 10.2381 17.1193 7.62939e-06L17.3602 0.213791L31.52 12.7736C18.2121 24.8448 15.456 44.4755 24.7152 59.5987L29.6037 55.2628C22.5455 42.6463 25.2344 26.7106 36.3333 17.0429L41.1815 21.3426L45.4936 25.168L49.9667 29.1355C48.2936 30.1731 46.8715 31.5698 45.8107 33.2165C44.4992 35.2519 43.7396 37.6684 43.7396 40.2597C43.7396 46.037 47.5151 50.9431 52.7571 52.6942L53.2087 58.7914C44.4739 57.0506 37.8941 49.4142 37.8941 40.2597C37.8941 36.1833 39.1989 32.408 41.4175 29.3198L37.1379 25.5231L37.1219 25.509C30.1062 34.73 30.5396 47.5312 38.0944 56.2719L23.7361 69.0071C9.60061 52.9899 9.19696 29.2169 22.7358 12.7492L17.9465 8.50084C11.336 16.442 7.14974 26.4328 6.51204 37.3633L6.48277 37.3959L6.50886 37.4186C6.45429 38.3692 6.42676 39.3268 6.42676 40.2912V40.291ZM61.2429 52.6942C66.4849 50.9431 70.2608 46.037 70.2608 40.2597C70.2608 37.6684 69.5008 35.2519 68.1898 33.217C67.1285 31.5703 65.7064 30.1731 64.0342 29.1355L68.5069 25.1683L72.8194 21.3426H72.8189L77.6672 17.0429C88.7656 26.7106 91.455 42.6463 84.3968 55.2628L89.2848 59.5987C98.5445 44.4755 95.7883 24.8448 82.48 12.7736L96.6408 0.212845L96.8807 7.62939e-06C107.444 10.2381 114 24.5083 114 40.291C114 54.2981 108.801 67.4059 99.838 77.4988L95.0505 73.2527C102.994 64.2874 107.573 52.6852 107.573 40.291C107.573 39.3266 107.546 38.369 107.492 37.4184L107.517 37.3958L107.488 37.3632C106.851 26.4326 102.664 16.4419 96.054 8.50068L91.2647 12.7491C104.804 29.2167 104.4 52.9897 90.2639 69.007L75.9061 56.2718C83.4609 47.531 83.8943 34.7299 76.8784 25.5088L76.8621 25.523L72.5825 29.3202C74.8011 32.4078 76.1059 36.1836 76.1059 40.2596C76.1059 49.414 69.5261 57.05 60.7918 58.7913L61.2429 52.6941V52.6942Z\" fill=\"#3C5280\"/>\n                        </svg>\n                    </div>\n                    <div>\n                    <div>\n                        <h2 class=\"text-lg font-semibold\">Listener Information</h2>\n                    </div>\n                    <div>\n                        <div class=\"grid grid-rows-3 gap-1 text-sm\">\n                            <div>\n                                <span class=\"text-gray-500\">Number of listeners:</span>\n                                <span class=\"ml-2\" id=\"broker-all-listener-cnt\">?</span>\n                            </div>\n                            <div>\n                                <span class=\"text-gray-500\">Anonymous listeners:</span>\n                                <span class=\"ml-2\" id=\"broker-anonym-listener-cnt\">none</span>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n\n            <div id=\"listeners-container\" class=\"space-y-4\">\n                <!-- listeners will be populated here from javascript -->\n\n                <div id=\"listeners-skeleton\" class=\"space-y-4\">\n                    <!-- skeleton to display something while listeners load -->\n                    <div class=\"card p-4 border border-gray-200 animate-pulse\">\n                        <div class=\"h-4 w-32 bg-gray-200 rounded mb-4\"></div>\n\n                        <div class=\"grid gap-2\">\n                            <div class=\"flex items-center\">\n                                <div class=\"h-3 w-24 bg-gray-200 rounded mr-2\"></div>\n                                <div class=\"h-5 w-16 bg-gray-200 rounded-full\"></div>\n                            </div>\n                            <div class=\"flex items-center\">\n                                <div class=\"h-3 w-24 bg-gray-200 rounded mr-2\"></div>\n                                <div class=\"h-5 w-24 bg-gray-200 rounded-full\"></div>\n                            </div>\n                            <div class=\"flex items-center\">\n                                <div class=\"h-3 w-24 bg-gray-200 rounded mr-2\"></div>\n                                <div class=\"h-5 w-20 bg-gray-200 rounded-full\"></div>\n                            </div>\n                            <div class=\"flex items-center\">\n                                <div class=\"h-3 w-24 bg-gray-200 rounded mr-2\"></div>\n                                <div class=\"h-5 w-20 bg-gray-200 rounded-full\"></div>\n                            </div>\n                        </div>\n\n                        <div class=\"mt-4\">\n                            <div class=\"h-20 bg-gray-200 rounded\"></div>\n                        </div>\n                    </div>\n\n                    <div class=\"card p-4 border border-gray-200 animate-pulse\">\n                        <div class=\"h-4 w-32 bg-gray-200 rounded mb-4\"></div>\n\n                        <div class=\"grid gap-2\">\n                            <div class=\"flex items-center\">\n                                <div class=\"h-3 w-24 bg-gray-200 rounded mr-2\"></div>\n                                <div class=\"h-5 w-16 bg-gray-200 rounded-full\"></div>\n                            </div>\n                            <div class=\"flex items-center\">\n                                <div class=\"h-3 w-24 bg-gray-200 rounded mr-2\"></div>\n                                <div class=\"h-5 w-24 bg-gray-200 rounded-full\"></div>\n                            </div>\n                            <div class=\"flex items-center\">\n                                <div class=\"h-3 w-24 bg-gray-200 rounded mr-2\"></div>\n                                <div class=\"h-5 w-20 bg-gray-200 rounded-full\"></div>\n                            </div>\n                            <div class=\"flex items-center\">\n                                <div class=\"h-3 w-24 bg-gray-200 rounded mr-2\"></div>\n                                <div class=\"h-5 w-20 bg-gray-200 rounded-full\"></div>\n                            </div>\n                        </div>\n\n                        <div class=\"mt-4\">\n                            <div class=\"h-20 bg-gray-200 rounded\"></div>\n                        </div>\n                    </div>\n\n                    <div class=\"card p-4 border border-gray-200 animate-pulse\">\n                        <div class=\"h-4 w-32 bg-gray-200 rounded mb-4\"></div>\n                        <div class=\"h-24 bg-gray-200 rounded\"></div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <!-- footer -->\n    <div class=\"mt-[5%] pt-6 pb-6 bg-gray-100\">\n        <div class=\"flex grid grid-cols-1 md:grid-cols-5 items-center justify-evenly gap-2 text-gray-500\">\n            <div class=\"flex items-center justify-center\">\n                <a href=\"https://github.com/eclipse-mosquitto/mosquitto\" target=\"_blank\" rel=\"noopener noreferrer\">Github</a>\n            </div>\n            <div class=\"flex items-center justify-center\">\n                <a href=\"https://test.mosquitto.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Test Mosquitto Instance</a>\n            </div>\n            <div class=\"flex items-center justify-center\">\n                <a href=\"https://forum.cedalo.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Mosquitto Forum</a>\n            </div>\n            <div class=\"flex items-center justify-center\">\n                <a href=\"https://cedalo.com/link/mosquitto-pro-edition\" target=\"_blank\" rel=\"noopener noreferrer\">Pro Edition for Eclipse Mosquitto™</a>\n            </div>\n            <div class=\"flex items-center justify-center md:justify-start\">\n                <a href=\"https://cedalo.com/link/company-behind-mosquitto\" target=\"_blank\" rel=\"noopener noreferrer\">The company behind Mosquitto</a>\n            </div>\n        </div>\n    </div>\n    <script src=\"app/consts.js\"></script>\n    <script src=\"app/sidebar.js\"></script>\n    <script src=\"app/dashboard.js\"></script>\n    <script src=\"app/listeners.js\"></script>\n    <script src=\"utils/queue.js\"></script>\n    <script src=\"utils/assert.js\"></script>\n    <script src=\"utils/utils.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dashboard/src/tailwind/styles.css",
    "content": "*, ::before, ::after {\n  --tw-border-spacing-x: 0;\n  --tw-border-spacing-y: 0;\n  --tw-translate-x: 0;\n  --tw-translate-y: 0;\n  --tw-rotate: 0;\n  --tw-skew-x: 0;\n  --tw-skew-y: 0;\n  --tw-scale-x: 1;\n  --tw-scale-y: 1;\n  --tw-pan-x:  ;\n  --tw-pan-y:  ;\n  --tw-pinch-zoom:  ;\n  --tw-scroll-snap-strictness: proximity;\n  --tw-gradient-from-position:  ;\n  --tw-gradient-via-position:  ;\n  --tw-gradient-to-position:  ;\n  --tw-ordinal:  ;\n  --tw-slashed-zero:  ;\n  --tw-numeric-figure:  ;\n  --tw-numeric-spacing:  ;\n  --tw-numeric-fraction:  ;\n  --tw-ring-inset:  ;\n  --tw-ring-offset-width: 0px;\n  --tw-ring-offset-color: #fff;\n  --tw-ring-color: rgb(59 130 246 / 0.5);\n  --tw-ring-offset-shadow: 0 0 #0000;\n  --tw-ring-shadow: 0 0 #0000;\n  --tw-shadow: 0 0 #0000;\n  --tw-shadow-colored: 0 0 #0000;\n  --tw-blur:  ;\n  --tw-brightness:  ;\n  --tw-contrast:  ;\n  --tw-grayscale:  ;\n  --tw-hue-rotate:  ;\n  --tw-invert:  ;\n  --tw-saturate:  ;\n  --tw-sepia:  ;\n  --tw-drop-shadow:  ;\n  --tw-backdrop-blur:  ;\n  --tw-backdrop-brightness:  ;\n  --tw-backdrop-contrast:  ;\n  --tw-backdrop-grayscale:  ;\n  --tw-backdrop-hue-rotate:  ;\n  --tw-backdrop-invert:  ;\n  --tw-backdrop-opacity:  ;\n  --tw-backdrop-saturate:  ;\n  --tw-backdrop-sepia:  ;\n  --tw-contain-size:  ;\n  --tw-contain-layout:  ;\n  --tw-contain-paint:  ;\n  --tw-contain-style:  ;\n}\n\n::backdrop {\n  --tw-border-spacing-x: 0;\n  --tw-border-spacing-y: 0;\n  --tw-translate-x: 0;\n  --tw-translate-y: 0;\n  --tw-rotate: 0;\n  --tw-skew-x: 0;\n  --tw-skew-y: 0;\n  --tw-scale-x: 1;\n  --tw-scale-y: 1;\n  --tw-pan-x:  ;\n  --tw-pan-y:  ;\n  --tw-pinch-zoom:  ;\n  --tw-scroll-snap-strictness: proximity;\n  --tw-gradient-from-position:  ;\n  --tw-gradient-via-position:  ;\n  --tw-gradient-to-position:  ;\n  --tw-ordinal:  ;\n  --tw-slashed-zero:  ;\n  --tw-numeric-figure:  ;\n  --tw-numeric-spacing:  ;\n  --tw-numeric-fraction:  ;\n  --tw-ring-inset:  ;\n  --tw-ring-offset-width: 0px;\n  --tw-ring-offset-color: #fff;\n  --tw-ring-color: rgb(59 130 246 / 0.5);\n  --tw-ring-offset-shadow: 0 0 #0000;\n  --tw-ring-shadow: 0 0 #0000;\n  --tw-shadow: 0 0 #0000;\n  --tw-shadow-colored: 0 0 #0000;\n  --tw-blur:  ;\n  --tw-brightness:  ;\n  --tw-contrast:  ;\n  --tw-grayscale:  ;\n  --tw-hue-rotate:  ;\n  --tw-invert:  ;\n  --tw-saturate:  ;\n  --tw-sepia:  ;\n  --tw-drop-shadow:  ;\n  --tw-backdrop-blur:  ;\n  --tw-backdrop-brightness:  ;\n  --tw-backdrop-contrast:  ;\n  --tw-backdrop-grayscale:  ;\n  --tw-backdrop-hue-rotate:  ;\n  --tw-backdrop-invert:  ;\n  --tw-backdrop-opacity:  ;\n  --tw-backdrop-saturate:  ;\n  --tw-backdrop-sepia:  ;\n  --tw-contain-size:  ;\n  --tw-contain-layout:  ;\n  --tw-contain-paint:  ;\n  --tw-contain-style:  ;\n}\n\n/*\n! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com\n*/\n\n/*\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\n*/\n\n*,\n::before,\n::after {\n  box-sizing: border-box;\n  /* 1 */\n  border-width: 0;\n  /* 2 */\n  border-style: solid;\n  /* 2 */\n  border-color: #e5e7eb;\n  /* 2 */\n}\n\n::before,\n::after {\n  --tw-content: '';\n}\n\n/*\n1. Use a consistent sensible line-height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n3. Use a more readable tab size.\n4. Use the user's configured `sans` font-family by default.\n5. Use the user's configured `sans` font-feature-settings by default.\n6. Use the user's configured `sans` font-variation-settings by default.\n7. Disable tap highlights on iOS\n*/\n\nhtml,\n:host {\n  line-height: 1.5;\n  /* 1 */\n  -webkit-text-size-adjust: 100%;\n  /* 2 */\n  -moz-tab-size: 4;\n  /* 3 */\n  -o-tab-size: 4;\n     tab-size: 4;\n  /* 3 */\n  font-family: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  /* 4 */\n  font-feature-settings: normal;\n  /* 5 */\n  font-variation-settings: normal;\n  /* 6 */\n  -webkit-tap-highlight-color: transparent;\n  /* 7 */\n}\n\n/*\n1. Remove the margin in all browsers.\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\n*/\n\nbody {\n  margin: 0;\n  /* 1 */\n  line-height: inherit;\n  /* 2 */\n}\n\n/*\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n3. Ensure horizontal rules are visible by default.\n*/\n\nhr {\n  height: 0;\n  /* 1 */\n  color: inherit;\n  /* 2 */\n  border-top-width: 1px;\n  /* 3 */\n}\n\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr:where([title]) {\n  -webkit-text-decoration: underline dotted;\n          text-decoration: underline dotted;\n}\n\n/*\nRemove the default font size and weight for headings.\n*/\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  font-size: inherit;\n  font-weight: inherit;\n}\n\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\n\na {\n  color: inherit;\n  text-decoration: inherit;\n}\n\n/*\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n  font-weight: bolder;\n}\n\n/*\n1. Use the user's configured `mono` font-family by default.\n2. Use the user's configured `mono` font-feature-settings by default.\n3. Use the user's configured `mono` font-variation-settings by default.\n4. Correct the odd `em` font sizing in all browsers.\n*/\n\ncode,\nkbd,\nsamp,\npre {\n  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  /* 1 */\n  font-feature-settings: normal;\n  /* 2 */\n  font-variation-settings: normal;\n  /* 3 */\n  font-size: 1em;\n  /* 4 */\n}\n\n/*\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n  font-size: 80%;\n}\n\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\nsup {\n  top: -0.5em;\n}\n\n/*\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n3. Remove gaps between table borders by default.\n*/\n\ntable {\n  text-indent: 0;\n  /* 1 */\n  border-color: inherit;\n  /* 2 */\n  border-collapse: collapse;\n  /* 3 */\n}\n\n/*\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n3. Remove default padding in all browsers.\n*/\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  font-family: inherit;\n  /* 1 */\n  font-feature-settings: inherit;\n  /* 1 */\n  font-variation-settings: inherit;\n  /* 1 */\n  font-size: 100%;\n  /* 1 */\n  font-weight: inherit;\n  /* 1 */\n  line-height: inherit;\n  /* 1 */\n  letter-spacing: inherit;\n  /* 1 */\n  color: inherit;\n  /* 1 */\n  margin: 0;\n  /* 2 */\n  padding: 0;\n  /* 3 */\n}\n\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\n\nbutton,\ninput:where([type='button']),\ninput:where([type='reset']),\ninput:where([type='submit']) {\n  -webkit-appearance: button;\n  /* 1 */\n  background-color: transparent;\n  /* 2 */\n  background-image: none;\n  /* 2 */\n}\n\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n\n:-moz-focusring {\n  outline: auto;\n}\n\n/*\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\n*/\n\n:-moz-ui-invalid {\n  box-shadow: none;\n}\n\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n  vertical-align: baseline;\n}\n\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n  height: auto;\n}\n\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n  -webkit-appearance: textfield;\n  /* 1 */\n  outline-offset: -2px;\n  /* 2 */\n}\n\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n\n::-webkit-file-upload-button {\n  -webkit-appearance: button;\n  /* 1 */\n  font: inherit;\n  /* 2 */\n}\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n  display: list-item;\n}\n\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n  margin: 0;\n}\n\nfieldset {\n  margin: 0;\n  padding: 0;\n}\n\nlegend {\n  padding: 0;\n}\n\nol,\nul,\nmenu {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\n/*\nReset default styling for dialogs.\n*/\n\ndialog {\n  padding: 0;\n}\n\n/*\nPrevent resizing textareas horizontally by default.\n*/\n\ntextarea {\n  resize: vertical;\n}\n\n/*\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\n2. Set the default placeholder color to the user's configured gray 400 color.\n*/\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n  opacity: 1;\n  /* 1 */\n  color: #9ca3af;\n  /* 2 */\n}\n\ninput::placeholder,\ntextarea::placeholder {\n  opacity: 1;\n  /* 1 */\n  color: #9ca3af;\n  /* 2 */\n}\n\n/*\nSet the default cursor for buttons.\n*/\n\nbutton,\n[role=\"button\"] {\n  cursor: pointer;\n}\n\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n\n:disabled {\n  cursor: default;\n}\n\n/*\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\n   This can trigger a poorly considered lint error in some tools but is included by design.\n*/\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n  display: block;\n  /* 1 */\n  vertical-align: middle;\n  /* 2 */\n}\n\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\n\nimg,\nvideo {\n  max-width: 100%;\n  height: auto;\n}\n\n/* Make elements with the HTML hidden attribute stay hidden by default */\n\n[hidden]:where(:not([hidden=\"until-found\"])) {\n  display: none;\n}\n\n.card {\n  border-radius: 0.5rem;\n  border-width: 1px;\n  --tw-border-opacity: 1;\n  border-color: rgb(229 231 235 / var(--tw-border-opacity, 1));\n  --tw-bg-opacity: 1;\n  background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));\n  --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n  --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);\n  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n\n.card-header {\n  border-bottom-width: 1px;\n  --tw-border-opacity: 1;\n  border-color: rgb(229 231 235 / var(--tw-border-opacity, 1));\n  padding-left: 1.5rem;\n  padding-right: 1.5rem;\n  padding-top: 1rem;\n  padding-bottom: 1rem;\n}\n\n.card-content {\n  padding-left: 1.5rem;\n  padding-right: 1.5rem;\n  padding-top: 1rem;\n  padding-bottom: 1rem;\n}\n\n.metric-value {\n  font-size: 1.875rem;\n  line-height: 2.25rem;\n  font-weight: 700;\n  --tw-text-opacity: 1;\n  color: rgb(17 24 39 / var(--tw-text-opacity, 1));\n}\n\n.metric-label {\n  margin-bottom: 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.25rem;\n  font-weight: 500;\n  --tw-text-opacity: 1;\n  color: rgb(107 114 128 / var(--tw-text-opacity, 1));\n}\n\n.chart-container {\n  position: relative;\n  height: 200px;\n  width: 100%;\n}\n\n.nav-btn {\n  display: inline-flex;\n  height: 2rem;\n  width: 2rem;\n  align-items: center;\n  justify-content: center;\n  border-radius: 0.25rem;\n  border-width: 1px;\n  --tw-border-opacity: 1;\n  border-color: rgb(209 213 219 / var(--tw-border-opacity, 1));\n  --tw-bg-opacity: 1;\n  background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));\n  --tw-text-opacity: 1;\n  color: rgb(107 114 128 / var(--tw-text-opacity, 1));\n  transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;\n  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n  transition-duration: 150ms;\n}\n\n.nav-btn:hover {\n  --tw-bg-opacity: 1;\n  background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));\n  --tw-text-opacity: 1;\n  color: rgb(55 65 81 / var(--tw-text-opacity, 1));\n}\n\n.nav-btn:focus {\n  outline: 2px solid transparent;\n  outline-offset: 2px;\n  --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);\n  --tw-ring-opacity: 1;\n  --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1));\n  --tw-ring-offset-width: 1px;\n}\n\n.nav-btn:disabled {\n  cursor: not-allowed;\n  opacity: 0.5;\n}\n\n.nav-btn:hover:not(:disabled) {\n  --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n  --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);\n  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n\n.nav-btn.active {\n  --tw-border-opacity: 1;\n  border-color: rgb(147 197 253 / var(--tw-border-opacity, 1));\n  --tw-bg-opacity: 1;\n  background-color: rgb(239 246 255 / var(--tw-bg-opacity, 1));\n  --tw-text-opacity: 1;\n  color: rgb(37 99 235 / var(--tw-text-opacity, 1));\n}\n\n.nav-separator {\n  margin-left: 0.5rem;\n  margin-right: 0.5rem;\n  height: 1.5rem;\n  width: 1px;\n  --tw-bg-opacity: 1;\n  background-color: rgb(209 213 219 / var(--tw-bg-opacity, 1));\n}\n\n.nav-btn svg {\n  pointer-events: none;\n}\n\n.fixed {\n  position: fixed;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.relative {\n  position: relative;\n}\n\n.sticky {\n  position: sticky;\n}\n\n.inset-0 {\n  inset: 0px;\n}\n\n.left-0 {\n  left: 0px;\n}\n\n.right-0 {\n  right: 0px;\n}\n\n.right-2 {\n  right: 0.5rem;\n}\n\n.top-0 {\n  top: 0px;\n}\n\n.top-2 {\n  top: 0.5rem;\n}\n\n.z-10 {\n  z-index: 10;\n}\n\n.z-40 {\n  z-index: 40;\n}\n\n.z-50 {\n  z-index: 50;\n}\n\n.mx-auto {\n  margin-left: auto;\n  margin-right: auto;\n}\n\n.mb-1 {\n  margin-bottom: 0.25rem;\n}\n\n.mb-4 {\n  margin-bottom: 1rem;\n}\n\n.mb-8 {\n  margin-bottom: 2rem;\n}\n\n.ml-2 {\n  margin-left: 0.5rem;\n}\n\n.mr-2 {\n  margin-right: 0.5rem;\n}\n\n.mr-\\[3\\%\\] {\n  margin-right: 3%;\n}\n\n.mt-2 {\n  margin-top: 0.5rem;\n}\n\n.mt-4 {\n  margin-top: 1rem;\n}\n\n.mt-\\[5\\%\\] {\n  margin-top: 5%;\n}\n\n.block {\n  display: block;\n}\n\n.inline-block {\n  display: inline-block;\n}\n\n.inline {\n  display: inline;\n}\n\n.flex {\n  display: flex;\n}\n\n.inline-flex {\n  display: inline-flex;\n}\n\n.grid {\n  display: grid;\n}\n\n.hidden {\n  display: none;\n}\n\n.h-2 {\n  height: 0.5rem;\n}\n\n.h-20 {\n  height: 5rem;\n}\n\n.h-24 {\n  height: 6rem;\n}\n\n.h-3 {\n  height: 0.75rem;\n}\n\n.h-4 {\n  height: 1rem;\n}\n\n.h-5 {\n  height: 1.25rem;\n}\n\n.h-8 {\n  height: 2rem;\n}\n\n.h-\\[335px\\] {\n  height: 335px;\n}\n\n.h-full {\n  height: 100%;\n}\n\n.min-h-screen {\n  min-height: 100vh;\n}\n\n.w-16 {\n  width: 4rem;\n}\n\n.w-2 {\n  width: 0.5rem;\n}\n\n.w-20 {\n  width: 5rem;\n}\n\n.w-24 {\n  width: 6rem;\n}\n\n.w-32 {\n  width: 8rem;\n}\n\n.w-4 {\n  width: 1rem;\n}\n\n.w-5 {\n  width: 1.25rem;\n}\n\n.w-64 {\n  width: 16rem;\n}\n\n.w-8 {\n  width: 2rem;\n}\n\n.w-80 {\n  width: 20rem;\n}\n\n.w-full {\n  width: 100%;\n}\n\n.min-w-max {\n  min-width: -moz-max-content;\n  min-width: max-content;\n}\n\n.max-w-7xl {\n  max-width: 80rem;\n}\n\n.flex-1 {\n  flex: 1 1 0%;\n}\n\n.-translate-x-full {\n  --tw-translate-x: -100%;\n  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n\n.transform {\n  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n\n@keyframes pulse {\n  50% {\n    opacity: .5;\n  }\n}\n\n.animate-pulse {\n  animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n.cursor-pointer {\n  cursor: pointer;\n}\n\n.grid-cols-1 {\n  grid-template-columns: repeat(1, minmax(0, 1fr));\n}\n\n.grid-rows-3 {\n  grid-template-rows: repeat(3, minmax(0, 1fr));\n}\n\n.flex-col {\n  flex-direction: column;\n}\n\n.items-center {\n  align-items: center;\n}\n\n.justify-start {\n  justify-content: flex-start;\n}\n\n.justify-end {\n  justify-content: flex-end;\n}\n\n.justify-center {\n  justify-content: center;\n}\n\n.justify-between {\n  justify-content: space-between;\n}\n\n.justify-evenly {\n  justify-content: space-evenly;\n}\n\n.gap-1 {\n  gap: 0.25rem;\n}\n\n.gap-2 {\n  gap: 0.5rem;\n}\n\n.gap-3 {\n  gap: 0.75rem;\n}\n\n.gap-4 {\n  gap: 1rem;\n}\n\n.gap-6 {\n  gap: 1.5rem;\n}\n\n.space-y-1 > :not([hidden]) ~ :not([hidden]) {\n  --tw-space-y-reverse: 0;\n  margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));\n  margin-bottom: calc(0.25rem * var(--tw-space-y-reverse));\n}\n\n.space-y-4 > :not([hidden]) ~ :not([hidden]) {\n  --tw-space-y-reverse: 0;\n  margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));\n  margin-bottom: calc(1rem * var(--tw-space-y-reverse));\n}\n\n.space-y-6 > :not([hidden]) ~ :not([hidden]) {\n  --tw-space-y-reverse: 0;\n  margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));\n  margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));\n}\n\n.overflow-hidden {\n  overflow: hidden;\n}\n\n.overflow-x-auto {\n  overflow-x: auto;\n}\n\n.overflow-y-auto {\n  overflow-y: auto;\n}\n\n.whitespace-nowrap {\n  white-space: nowrap;\n}\n\n.break-words {\n  overflow-wrap: break-word;\n}\n\n.rounded {\n  border-radius: 0.25rem;\n}\n\n.rounded-full {\n  border-radius: 9999px;\n}\n\n.rounded-lg {\n  border-radius: 0.5rem;\n}\n\n.rounded-md {\n  border-radius: 0.375rem;\n}\n\n.rounded-xl {\n  border-radius: 0.75rem;\n}\n\n.border {\n  border-width: 1px;\n}\n\n.border-b {\n  border-bottom-width: 1px;\n}\n\n.border-t {\n  border-top-width: 1px;\n}\n\n.border-gray-200 {\n  --tw-border-opacity: 1;\n  border-color: rgb(229 231 235 / var(--tw-border-opacity, 1));\n}\n\n.border-gray-300 {\n  --tw-border-opacity: 1;\n  border-color: rgb(209 213 219 / var(--tw-border-opacity, 1));\n}\n\n.bg-gray-100 {\n  --tw-bg-opacity: 1;\n  background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));\n}\n\n.bg-gray-200 {\n  --tw-bg-opacity: 1;\n  background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1));\n}\n\n.bg-gray-50 {\n  --tw-bg-opacity: 1;\n  background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));\n}\n\n.bg-gray-500 {\n  --tw-bg-opacity: 1;\n  background-color: rgb(107 114 128 / var(--tw-bg-opacity, 1));\n}\n\n.bg-white {\n  --tw-bg-opacity: 1;\n  background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));\n}\n\n.bg-white\\/80 {\n  background-color: rgb(255 255 255 / 0.8);\n}\n\n.p-1 {\n  padding: 0.25rem;\n}\n\n.p-2 {\n  padding: 0.5rem;\n}\n\n.p-3 {\n  padding: 0.75rem;\n}\n\n.p-4 {\n  padding: 1rem;\n}\n\n.p-6 {\n  padding: 1.5rem;\n}\n\n.px-3 {\n  padding-left: 0.75rem;\n  padding-right: 0.75rem;\n}\n\n.px-4 {\n  padding-left: 1rem;\n  padding-right: 1rem;\n}\n\n.py-2 {\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n}\n\n.pb-2 {\n  padding-bottom: 0.5rem;\n}\n\n.pb-6 {\n  padding-bottom: 1.5rem;\n}\n\n.pr-6 {\n  padding-right: 1.5rem;\n}\n\n.pt-6 {\n  padding-top: 1.5rem;\n}\n\n.text-center {\n  text-align: center;\n}\n\n.font-mono {\n  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n.text-base {\n  font-size: 1rem;\n  line-height: 1.5rem;\n}\n\n.text-lg {\n  font-size: 1.125rem;\n  line-height: 1.75rem;\n}\n\n.text-sm {\n  font-size: 0.875rem;\n  line-height: 1.25rem;\n}\n\n.font-medium {\n  font-weight: 500;\n}\n\n.font-semibold {\n  font-weight: 600;\n}\n\n.text-blue-700 {\n  --tw-text-opacity: 1;\n  color: rgb(29 78 216 / var(--tw-text-opacity, 1));\n}\n\n.text-gray-500 {\n  --tw-text-opacity: 1;\n  color: rgb(107 114 128 / var(--tw-text-opacity, 1));\n}\n\n.text-gray-600 {\n  --tw-text-opacity: 1;\n  color: rgb(75 85 99 / var(--tw-text-opacity, 1));\n}\n\n.text-gray-700 {\n  --tw-text-opacity: 1;\n  color: rgb(55 65 81 / var(--tw-text-opacity, 1));\n}\n\n.text-white {\n  --tw-text-opacity: 1;\n  color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\n\n.opacity-0 {\n  opacity: 0;\n}\n\n.shadow-2xl {\n  --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);\n  --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);\n  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n\n.shadow-lg {\n  --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);\n  --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);\n  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n\n.transition-all {\n  transition-property: all;\n  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n  transition-duration: 150ms;\n}\n\n.transition-colors {\n  transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;\n  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n  transition-duration: 150ms;\n}\n\n.transition-opacity {\n  transition-property: opacity;\n  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n  transition-duration: 150ms;\n}\n\n.transition-transform {\n  transition-property: transform;\n  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n  transition-duration: 150ms;\n}\n\n.duration-200 {\n  transition-duration: 200ms;\n}\n\n.duration-300 {\n  transition-duration: 300ms;\n}\n\n.ease-in-out {\n  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.bg-c-orange {\n  background-color: #fd602e;\n}\n\n.hover\\:bg-c-orange:hover {\n  background-color: #fd602e;\n}\n\n#menu-overlay {\n  transition: opacity 300ms;\n  opacity: 0;\n  pointer-events: none;\n  background: rgba(0,0,0,0.45);\n}\n\n@media (min-width: 1024px) {\n  #menu-overlay {\n    display: none;\n  }\n\n  #sliding-menu {\n    box-shadow: none;\n  }\n\n  #menu-close {\n    visibility: hidden;\n  }\n\n  #broker-info-icon {\n    display: block;\n  }\n}\n\n#sliding-menu {\n  transform: translateX(-100%);\n  transition: transform 300ms;\n}\n\n.sidebar-open #sliding-menu {\n  transform: translateX(0);\n}\n\n@media (max-width: 1023px) {\n  .sidebar-open #menu-overlay {\n    opacity: 1;\n    pointer-events: auto;\n  }\n}\n\n@media (min-width: 1024px) {\n  .sidebar-open #main-content {\n    margin-left: 320px;\n  }\n}\n\n/* lock scroll on mobile when sidebar open */\n\n.sidebar-lock-scroll {\n  overflow: hidden;\n}\n\n.sidebar-preload #sliding-menu {\n  transition: none;\n}\n\n@media (max-width: 1024px) {\n  #layout-toggle {\n    display: none;\n  }\n}\n\n@media (max-width: 375px) {\n  #logo-icon {\n    display: block;\n  }\n\n  #logo-text {\n    display: none\n  }\n}\n\n.broker-active {\n  background-color: rgb(34 197 94);\n  /* green-500 */\n}\n\n.broker-inactive {\n  background-color: rgb(239 68 68);\n  /* red-500 */\n}\n\n.hover\\:cursor-pointer:hover {\n  cursor: pointer;\n}\n\n.hover\\:bg-gray-100:hover {\n  --tw-bg-opacity: 1;\n  background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));\n}\n\n.hover\\:bg-gray-50:hover {\n  --tw-bg-opacity: 1;\n  background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));\n}\n\n.hover\\:bg-orange-700:hover {\n  --tw-bg-opacity: 1;\n  background-color: rgb(194 65 12 / var(--tw-bg-opacity, 1));\n}\n\n.hover\\:bg-white:hover {\n  --tw-bg-opacity: 1;\n  background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));\n}\n\n.hover\\:text-blue-900:hover {\n  --tw-text-opacity: 1;\n  color: rgb(30 58 138 / var(--tw-text-opacity, 1));\n}\n\n.hover\\:text-white:hover {\n  --tw-text-opacity: 1;\n  color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\n\n.hover\\:underline:hover {\n  text-decoration-line: underline;\n}\n\n.focus\\:outline-none:focus {\n  outline: 2px solid transparent;\n  outline-offset: 2px;\n}\n\n.focus\\:ring-2:focus {\n  --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);\n}\n\n.focus\\:ring-blue-500:focus {\n  --tw-ring-opacity: 1;\n  --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1));\n}\n\n.group[open] .group-open\\:block {\n  display: block;\n}\n\n@media (min-width: 768px) {\n  .md\\:grid-cols-2 {\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n  }\n\n  .md\\:grid-cols-5 {\n    grid-template-columns: repeat(5, minmax(0, 1fr));\n  }\n\n  .md\\:justify-start {\n    justify-content: flex-start;\n  }\n\n  .md\\:justify-end {\n    justify-content: flex-end;\n  }\n}\n\n@media (min-width: 1024px) {\n  .lg\\:grid-cols-2 {\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n  }\n\n  .lg\\:grid-cols-3 {\n    grid-template-columns: repeat(3, minmax(0, 1fr));\n  }\n}\n"
  },
  {
    "path": "dashboard/src/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: [\"./*.html\", \"./*.js\"],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n};\n"
  },
  {
    "path": "dashboard/src/utils/assert.js",
    "content": "function assertExistence(value, error) {\n  if (value === undefined) {\n    throw new Error(error);\n  }\n}\n\n// assertValue(value, expected, error) {}\n"
  },
  {
    "path": "dashboard/src/utils/queue.js",
    "content": "class Queue {\n  constructor() {\n    this.tasks = [];\n    this.active = false;\n  }\n\n  enqueue(task) {\n    this.tasks.push(task);\n    this.#dequeue();\n  }\n\n  async #dequeue() {\n    if (this.active) {\n      return;\n    }\n    this.active = true;\n    while (this.tasks.length) {\n      const task = this.tasks.shift();\n      try {\n        await task();\n      } catch (err) {\n        console.error(\"Error in queue:\", err);\n      }\n    }\n    this.active = false;\n  }\n}\nconst queue = new Queue();\n"
  },
  {
    "path": "dashboard/src/utils/utils.js",
    "content": "function toAsyncAndWaitAfter(task, delay = 0) {\n  return () => {\n    const promise = new Promise((resolve, reject) => {\n      let result;\n      try {\n        result = task();\n      } catch (err) {\n        return reject(err);\n      }\n      if (delay) {\n        setTimeout(() => {\n          resolve(result);\n        }, delay);\n      } else {\n        resolve(result);\n      }\n    });\n    return promise;\n  };\n}\n\nasync function fetchData(endpoint, opts = {}) {\n  if (!endpoint) {\n    throw new Error(\"No endpoint provided to fetch data function\");\n  }\n\n  let data;\n  const res = await fetch(endpoint, {\n    ...opts,\n    headers: { Accept: \"application/json\" },\n  });\n  if (res.ok) {\n    data = await res.json();\n  } else {\n    throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`);\n  }\n\n  return data;\n}\n\nfunction toTimeString(date = new Date()) {\n  const d = new Date(date);\n\n  const hours = String(d.getHours()).padStart(2, \"0\");\n  const minutes = String(d.getMinutes()).padStart(2, \"0\");\n  const seconds = String(d.getSeconds()).padStart(2, \"0\");\n\n  return `${hours}:${minutes}:${seconds}`;\n}\n\nfunction timeStringToTimestamp(timeString) {\n  const regex = /^(\\d{2}):(\\d{2}):(\\d{2})$/;\n  const match = timeString.match(regex);\n  if (!match) {\n    throw new Error(`Invalid format. Expected HH:mm:ss, got: ${timeString}`);\n  }\n\n  const [, hours, minutes, seconds] = match;\n  const now = new Date();\n  const [year, month, day] = [now.getFullYear(), now.getMonth(), now.getDate()];\n  const date = new Date(\n    year,\n    month,\n    day,\n    Number(hours),\n    Number(minutes),\n    Number(seconds),\n  );\n  return date.getTime();\n}\n\nfunction prettifyNumber(number) {\n  if (number > Number.MAX_SAFE_INTEGER) {\n    return \">\" + String(Number.MAX_SAFE_INTEGER);\n  }\n  let strNumber = String(number);\n  let prettifiedNumber = \"\";\n  if (strNumber.length - 1 > 3) {\n    let i = strNumber.length - 3;\n\n    for (; i > 0; i -= 3) {\n      prettifiedNumber = \",\" + strNumber.substring(i, i + 3) + prettifiedNumber;\n    }\n    prettifiedNumber = strNumber.substring(0, i + 3) + prettifiedNumber;\n  } else {\n    prettifiedNumber = strNumber;\n  }\n\n  return prettifiedNumber;\n}\n\nfunction secondsToIntervalString(number) {\n  const minuteInSeconds = 60;\n  const hourInSeconds = minuteInSeconds * 60;\n  const dayInSeconds = hourInSeconds * 24;\n  const yearInSeconds = dayInSeconds * 365;\n\n  if (typeof number !== \"number\") {\n    throw new Error(\n      `Invalid datatype for converting into interval string. Expected: number. Got: ${typeof number}`,\n    );\n  }\n  if (number < 0) {\n    throw new Error(\n      `Invalid value for converting into interval string. Received negative number: ${number}`,\n    );\n  }\n\n  let intervalString = \"\";\n\n  const years = Math.floor(number / yearInSeconds);\n  number = number % yearInSeconds;\n  if (years) {\n    intervalString += years === 1 ? \"1 year \" : `${years} years `;\n  }\n\n  const days = Math.floor(number / dayInSeconds);\n  number = number % dayInSeconds;\n  if (days) {\n    intervalString += days === 1 ? \"1 day \" : `${days} days `;\n  }\n\n  const hours = Math.floor(number / hourInSeconds);\n  number = number % hourInSeconds;\n  if (hours) {\n    intervalString += hours === 1 ? \"1 hour \" : `${hours} hours `;\n  }\n\n  const minutes = Math.floor(number / minuteInSeconds);\n  number = number % minuteInSeconds;\n  if (minutes) {\n    intervalString += minutes === 1 ? \"1 minute \" : `${minutes} minutes `;\n  }\n\n  const seconds = number;\n  if (seconds) {\n    intervalString += seconds === 1 ? \"1 second \" : `${seconds} seconds `;\n  }\n\n  if (!intervalString) {\n    return \"0 seconds\"; // This would be strange if this happened. Maybe better to throw an error\n  }\n\n  return intervalString;\n}\n\nasync function copyToClipboard(textToCopy) {\n  if (navigator.clipboard) {\n    return await navigator.clipboard.writeText(textToCopy);\n  }\n\n  const dummyTextArea = document.createElement(\"textarea\");\n  dummyTextArea.value = textToCopy;\n\n  document.body.appendChild(dummyTextArea);\n  dummyTextArea.focus({ preventScroll: true });\n  dummyTextArea.select();\n\n  try {\n    document.execCommand(\"copy\");\n  } catch (err) {\n    throw new Error(\"Copy command failed: \" + err?.message);\n  } finally {\n    dummyTextArea.remove();\n  }\n}\n\nfunction isMobile() {\n  return window.innerWidth < 1024;\n}\n\nfunction registerAbortController(abortController) {\n  // in firefox the below doesn't help unfortunately: a general netrowk error is being thrown even before the below callback is executed. A proper implementation would require aborying in-flight requets right before the navigation but it's not worth the effort. Currently you will see an alert for a quick moment when spam-clicking onto the \"listern\" tab in the sidebar on firefox\n  const abortCallback = () => {\n    abortController.abort();\n  };\n  window.addEventListener(\"pagehide\", abortCallback, { once: true });\n}\n"
  },
  {
    "path": "deps/picohttpparser/README.md",
    "content": "PicoHTTPParser\n=============\n\nCopyright (c) 2009-2014 [Kazuho Oku](https://github.com/kazuho), [Tokuhiro Matsuno](https://github.com/tokuhirom), [Daisuke Murase](https://github.com/typester), [Shigeo Mitsunari](https://github.com/herumi)\n\nPicoHTTPParser is a tiny, primitive, fast HTTP request/response parser.\n\nUnlike most parsers, it is stateless and does not allocate memory by itself.\nAll it does is accept pointer to buffer and the output structure, and setups the pointers in the latter to point at the necessary portions of the buffer.\n\nThe code is widely deployed within Perl applications through popular modules that use it, including [Plack](https://metacpan.org/pod/Plack), [Starman](https://metacpan.org/pod/Starman), [Starlet](https://metacpan.org/pod/Starlet), [Furl](https://metacpan.org/pod/Furl).  It is also the HTTP/1 parser of [H2O](https://github.com/h2o/h2o).\n\nCheck out [test.c] to find out how to use the parser.\n\nThe software is dual-licensed under the Perl License or the MIT License.\n\nUsage\n-----\n\nThe library exposes four functions: `phr_parse_request`, `phr_parse_response`, `phr_parse_headers`, `phr_decode_chunked`.\n\n### phr_parse_request\n\nThe example below reads an HTTP request from socket `sock` using `read(2)`, parses it using `phr_parse_request`, and prints the details.\n\n```c\nchar buf[4096], *method, *path;\nint pret, minor_version;\nstruct phr_header headers[100];\nsize_t buflen = 0, prevbuflen = 0, method_len, path_len, num_headers;\nssize_t rret;\n\nwhile (1) {\n    /* read the request */\n    while ((rret = read(sock, buf + buflen, sizeof(buf) - buflen)) == -1 && errno == EINTR)\n        ;\n    if (rret <= 0)\n        return IOError;\n    prevbuflen = buflen;\n    buflen += rret;\n    /* parse the request */\n    num_headers = sizeof(headers) / sizeof(headers[0]);\n    pret = phr_parse_request(buf, buflen, &method, &method_len, &path, &path_len,\n                             &minor_version, headers, &num_headers, prevbuflen);\n    if (pret > 0)\n        break; /* successfully parsed the request */\n    else if (pret == -1)\n        return ParseError;\n    /* request is incomplete, continue the loop */\n    assert(pret == -2);\n    if (buflen == sizeof(buf))\n        return RequestIsTooLongError;\n}\n\nprintf(\"request is %d bytes long\\n\", pret);\nprintf(\"method is %.*s\\n\", (int)method_len, method);\nprintf(\"path is %.*s\\n\", (int)path_len, path);\nprintf(\"HTTP version is 1.%d\\n\", minor_version);\nprintf(\"headers:\\n\");\nfor (i = 0; i != num_headers; ++i) {\n    printf(\"%.*s: %.*s\\n\", (int)headers[i].name_len, headers[i].name,\n           (int)headers[i].value_len, headers[i].value);\n}\n```\n\n### phr_parse_response, phr_parse_headers\n\n`phr_parse_response` and `phr_parse_headers` provide similar interfaces as `phr_parse_request`.  `phr_parse_response` parses an HTTP response, and `phr_parse_headers` parses the headers only.\n\n### phr_decode_chunked\n\nThe example below decodes incoming data in chunked-encoding.  The data is decoded in-place.\n\n```c\nstruct phr_chunked_decoder decoder = {}; /* zero-clear */\nchar *buf = malloc(4096);\nsize_t size = 0, capacity = 4096, rsize;\nssize_t rret, pret;\n\n/* set consume_trailer to 1 to discard the trailing header, or the application\n * should call phr_parse_headers to parse the trailing header */\ndecoder.consume_trailer = 1;\n\ndo {\n    /* expand the buffer if necessary */\n    if (size == capacity) {\n        capacity *= 2;\n        buf = realloc(buf, capacity);\n        assert(buf != NULL);\n    }\n    /* read */\n    while ((rret = read(sock, buf + size, capacity - size)) == -1 && errno == EINTR)\n        ;\n    if (rret <= 0)\n        return IOError;\n    /* decode */\n    rsize = rret;\n    pret = phr_decode_chunked(&decoder, buf + size, &rsize);\n    if (pret == -1)\n        return ParseError;\n    size += rsize;\n} while (pret == -2);\n\n/* successfully decoded the chunked data */\nassert(pret >= 0);\nprintf(\"decoded data is at %p (%zu bytes)\\n\", buf, size);\n```\n\nBenchmark\n---------\n\n![benchmark results](http://i.gyazo.com/a85c18d3162dfb46b485bb41e0ad443a.png)\n\nThe benchmark code is from [fukamachi/fast-http@6b91103](https://github.com/fukamachi/fast-http/tree/6b9110347c7a3407310c08979aefd65078518478).\n\nThe internals of picohttpparser has been described to some extent in [my blog entry]( http://blog.kazuhooku.com/2014/11/the-internals-h2o-or-how-to-write-fast.html).\n"
  },
  {
    "path": "deps/picohttpparser/picohttpparser.c",
    "content": "/*\n * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,\n *                         Shigeo Mitsunari\n *\n * The software is licensed under either the MIT License (below) or the Perl\n * license.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <assert.h>\n#include <stddef.h>\n#include <string.h>\n#ifdef __SSE4_2__\n#ifdef _MSC_VER\n#include <nmmintrin.h>\n#else\n#include <x86intrin.h>\n#endif\n#endif\n#include \"picohttpparser.h\"\n\n#if __GNUC__ >= 3\n#define likely(x) __builtin_expect(!!(x), 1)\n#define unlikely(x) __builtin_expect(!!(x), 0)\n#else\n#define likely(x) (x)\n#define unlikely(x) (x)\n#endif\n\n#ifdef _MSC_VER\n#define ALIGNED(n) _declspec(align(n))\n#else\n#define ALIGNED(n) __attribute__((aligned(n)))\n#endif\n\n#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)\n\n#define CHECK_EOF()                                                                                                                \\\n    if (buf == buf_end) {                                                                                                          \\\n        *ret = -2;                                                                                                                 \\\n        return NULL;                                                                                                               \\\n    }\n\n#define EXPECT_CHAR_NO_CHECK(ch)                                                                                                   \\\n    if (*buf++ != ch) {                                                                                                            \\\n        *ret = -1;                                                                                                                 \\\n        return NULL;                                                                                                               \\\n    }\n\n#define EXPECT_CHAR(ch)                                                                                                            \\\n    CHECK_EOF();                                                                                                                   \\\n    EXPECT_CHAR_NO_CHECK(ch);\n\n#define ADVANCE_TOKEN(tok, toklen)                                                                                                 \\\n    do {                                                                                                                           \\\n        const char *tok_start = buf;                                                                                               \\\n        static const char ALIGNED(16) ranges2[16] = \"\\000\\040\\177\\177\";                                                            \\\n        int found2;                                                                                                                \\\n        buf = findchar_fast(buf, buf_end, ranges2, 4, &found2);                                                                    \\\n        if (!found2) {                                                                                                             \\\n            CHECK_EOF();                                                                                                           \\\n        }                                                                                                                          \\\n        while (1) {                                                                                                                \\\n            if (*buf == ' ') {                                                                                                     \\\n                break;                                                                                                             \\\n            } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {                                                                      \\\n                if ((unsigned char)*buf < '\\040' || *buf == '\\177') {                                                              \\\n                    *ret = -1;                                                                                                     \\\n                    return NULL;                                                                                                   \\\n                }                                                                                                                  \\\n            }                                                                                                                      \\\n            ++buf;                                                                                                                 \\\n            CHECK_EOF();                                                                                                           \\\n        }                                                                                                                          \\\n        tok = tok_start;                                                                                                           \\\n        toklen = (size_t)(buf - tok_start);\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t                                                           \\\n    } while (0)\n\nstatic const char *token_char_map = \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n                                    \"\\0\\1\\0\\1\\1\\1\\1\\1\\0\\0\\1\\1\\0\\1\\1\\0\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\0\\0\\0\\0\\0\\0\"\n                                    \"\\0\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\0\\0\\0\\1\\1\"\n                                    \"\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\1\\0\\1\\0\\1\\0\"\n                                    \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n                                    \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n                                    \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n                                    \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\";\n\nstatic const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found)\n{\n    *found = 0;\n#if __SSE4_2__\n    if (likely(buf_end - buf >= 16)) {\n        __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);\n\n        size_t left = (buf_end - buf) & ~15;\n        do {\n            __m128i b16 = _mm_loadu_si128((const __m128i *)buf);\n            int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);\n            if (unlikely(r != 16)) {\n                buf += r;\n                *found = 1;\n                break;\n            }\n            buf += 16;\n            left -= 16;\n        } while (likely(left != 0));\n    }\n#else\n    /* suppress unused parameter warning */\n    (void)buf_end;\n    (void)ranges;\n    (void)ranges_size;\n#endif\n    return buf;\n}\n\nstatic const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret)\n{\n    const char *token_start = buf;\n\n#ifdef __SSE4_2__\n    static const char ALIGNED(16) ranges1[16] = \"\\0\\010\"    /* allow HT */\n                                                \"\\012\\037\"  /* allow SP and up to but not including DEL */\n                                                \"\\177\\177\"; /* allow chars w. MSB set */\n    int found;\n    buf = findchar_fast(buf, buf_end, ranges1, 6, &found);\n    if (found)\n        goto FOUND_CTL;\n#else\n    /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */\n    while (likely(buf_end - buf >= 8)) {\n#define DOIT()                                                                                                                     \\\n    do {                                                                                                                           \\\n        if (unlikely(!IS_PRINTABLE_ASCII(*buf)))                                                                                   \\\n            goto NonPrintable;                                                                                                     \\\n        ++buf;                                                                                                                     \\\n    } while (0)\n        DOIT();\n        DOIT();\n        DOIT();\n        DOIT();\n        DOIT();\n        DOIT();\n        DOIT();\n        DOIT();\n#undef DOIT\n        continue;\n    NonPrintable:\n        if ((likely((unsigned char)*buf < '\\040') && likely(*buf != '\\011')) || unlikely(*buf == '\\177')) {\n            goto FOUND_CTL;\n        }\n        ++buf;\n    }\n#endif\n    for (;; ++buf) {\n        CHECK_EOF();\n        if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {\n            if ((likely((unsigned char)*buf < '\\040') && likely(*buf != '\\011')) || unlikely(*buf == '\\177')) {\n                goto FOUND_CTL;\n            }\n        }\n    }\nFOUND_CTL:\n    if (likely(*buf == '\\015')) {\n        ++buf;\n        EXPECT_CHAR('\\012');\n        *token_len = (size_t)(buf - 2 - token_start);\n    } else if (*buf == '\\012') {\n\t\t\t*token_len = (size_t)(buf - token_start);\n        ++buf;\n    } else {\n        *ret = -1;\n        return NULL;\n    }\n    *token = token_start;\n\n    return buf;\n}\n\nstatic const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret)\n{\n    int ret_cnt = 0;\n    buf = last_len < 3 ? buf : buf + last_len - 3;\n\n    while (1) {\n        CHECK_EOF();\n        if (*buf == '\\015') {\n            ++buf;\n            CHECK_EOF();\n            EXPECT_CHAR('\\012');\n            ++ret_cnt;\n        } else if (*buf == '\\012') {\n            ++buf;\n            ++ret_cnt;\n        } else {\n            ++buf;\n            ret_cnt = 0;\n        }\n        if (ret_cnt == 2) {\n            return buf;\n        }\n    }\n}\n\n#define PARSE_INT(valp_, mul_)                                                                                                     \\\n    if (*buf < '0' || '9' < *buf) {                                                                                                \\\n        buf++;                                                                                                                     \\\n        *ret = -1;                                                                                                                 \\\n        return NULL;                                                                                                               \\\n    }                                                                                                                              \\\n    *(valp_) = (mul_) * (*buf++ - '0');\n\n#define PARSE_INT_3(valp_)                                                                                                         \\\n    do {                                                                                                                           \\\n        int res_ = 0;                                                                                                              \\\n        PARSE_INT(&res_, 100)                                                                                                      \\\n        *valp_ = res_;                                                                                                             \\\n        PARSE_INT(&res_, 10)                                                                                                       \\\n        *valp_ += res_;                                                                                                            \\\n        PARSE_INT(&res_, 1)                                                                                                        \\\n        *valp_ += res_;                                                                                                            \\\n    } while (0)\n\n/* returned pointer is always within [buf, buf_end), or null */\nstatic const char *parse_token(const char *buf, const char *buf_end, const char **token, size_t *token_len, char next_char,\n                               int *ret)\n{\n    /* We use pcmpestri to detect non-token characters. This instruction can take no more than eight character ranges (8*2*8=128\n     * bits that is the size of a SSE register). Due to this restriction, characters `|` and `~` are handled in the slow loop. */\n    static const char ALIGNED(16) ranges[] = \"\\x00 \"  /* control chars and up to SP */\n                                             \"\\\"\\\"\"   /* 0x22 */\n                                             \"()\"     /* 0x28,0x29 */\n                                             \",,\"     /* 0x2c */\n                                             \"//\"     /* 0x2f */\n                                             \":@\"     /* 0x3a-0x40 */\n                                             \"[]\"     /* 0x5b-0x5d */\n                                             \"{\\xff\"; /* 0x7b-0xff */\n    const char *buf_start = buf;\n    int found;\n    buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found);\n    if (!found) {\n        CHECK_EOF();\n    }\n    while (1) {\n        if (*buf == next_char) {\n            break;\n        } else if (!token_char_map[(unsigned char)*buf]) {\n            *ret = -1;\n            return NULL;\n        }\n        ++buf;\n        CHECK_EOF();\n    }\n    *token = buf_start;\n    *token_len = (size_t)(buf - buf_start);\n    return buf;\n}\n\n/* returned pointer is always within [buf, buf_end), or null */\nstatic const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)\n{\n    /* we want at least [HTTP/1.<two chars>] to try to parse */\n    if (buf_end - buf < 9) {\n        *ret = -2;\n        return NULL;\n    }\n    EXPECT_CHAR_NO_CHECK('H');\n    EXPECT_CHAR_NO_CHECK('T');\n    EXPECT_CHAR_NO_CHECK('T');\n    EXPECT_CHAR_NO_CHECK('P');\n    EXPECT_CHAR_NO_CHECK('/');\n    EXPECT_CHAR_NO_CHECK('1');\n    EXPECT_CHAR_NO_CHECK('.');\n    PARSE_INT(minor_version, 1);\n    return buf;\n}\n\nstatic const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers,\n                                 size_t max_headers, int *ret)\n{\n    for (;; ++*num_headers) {\n        CHECK_EOF();\n        if (*buf == '\\015') {\n            ++buf;\n            EXPECT_CHAR('\\012');\n            break;\n        } else if (*buf == '\\012') {\n            ++buf;\n            break;\n        }\n        if (*num_headers == max_headers) {\n            *ret = -1;\n            return NULL;\n        }\n        if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\\t'))) {\n            /* parsing name, but do not discard SP before colon, see\n             * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */\n            if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, &headers[*num_headers].name_len, ':', ret)) == NULL) {\n                return NULL;\n            }\n            if (headers[*num_headers].name_len == 0) {\n                *ret = -1;\n                return NULL;\n            }\n            ++buf;\n            for (;; ++buf) {\n                CHECK_EOF();\n                if (!(*buf == ' ' || *buf == '\\t')) {\n                    break;\n                }\n            }\n        } else {\n            headers[*num_headers].name = NULL;\n            headers[*num_headers].name_len = 0;\n        }\n        const char *value;\n        size_t value_len;\n        if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) {\n            return NULL;\n        }\n        /* remove trailing SPs and HTABs */\n        const char *value_end = value + value_len;\n        for (; value_end != value; --value_end) {\n            const char c = *(value_end - 1);\n            if (!(c == ' ' || c == '\\t')) {\n                break;\n            }\n        }\n        headers[*num_headers].value = value;\n        headers[*num_headers].value_len = (size_t)(value_end - value);\n    }\n    return buf;\n}\n\nstatic const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path,\n                                 size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers,\n                                 size_t max_headers, int *ret)\n{\n    /* skip first empty line (some clients add CRLF after POST content) */\n    CHECK_EOF();\n    if (*buf == '\\015') {\n        ++buf;\n        EXPECT_CHAR('\\012');\n    } else if (*buf == '\\012') {\n        ++buf;\n    }\n\n    /* parse request line */\n    if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) {\n        return NULL;\n    }\n    do {\n        ++buf;\n        CHECK_EOF();\n    } while (*buf == ' ');\n    ADVANCE_TOKEN(*path, *path_len);\n    do {\n        ++buf;\n        CHECK_EOF();\n    } while (*buf == ' ');\n    if (*method_len == 0 || *path_len == 0) {\n        *ret = -1;\n        return NULL;\n    }\n    if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {\n        return NULL;\n    }\n    if (*buf == '\\015') {\n        ++buf;\n        EXPECT_CHAR('\\012');\n    } else if (*buf == '\\012') {\n        ++buf;\n    } else {\n        *ret = -1;\n        return NULL;\n    }\n\n    return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);\n}\n\nint phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path,\n                      size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len)\n{\n    const char *buf = buf_start, *buf_end = buf_start + len;\n    size_t max_headers = *num_headers;\n    int r;\n\n    *method = NULL;\n    *method_len = 0;\n    *path = NULL;\n    *path_len = 0;\n    *minor_version = -1;\n    *num_headers = 0;\n\n    /* if last_len != 0, check if the request is complete (a fast countermeasure\n       againt slowloris */\n    if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {\n        return r;\n    }\n\n    if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers,\n                             &r)) == NULL) {\n        return r;\n    }\n\n    return (int)(buf - buf_start);\n}\n\nstatic const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg,\n                                  size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)\n{\n    /* parse \"HTTP/1.x\" */\n    if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {\n        return NULL;\n    }\n    /* skip space */\n    if (*buf != ' ') {\n        *ret = -1;\n        return NULL;\n    }\n    do {\n        ++buf;\n        CHECK_EOF();\n    } while (*buf == ' ');\n    /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */\n    if (buf_end - buf < 4) {\n        *ret = -2;\n        return NULL;\n    }\n    PARSE_INT_3(status);\n\n    /* get message including preceding space */\n    if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {\n        return NULL;\n    }\n    if (*msg_len == 0) {\n        /* ok */\n    } else if (**msg == ' ') {\n        /* Remove preceding space. Successful return from `get_token_to_eol` guarantees that we would hit something other than SP\n         * before running past the end of the given buffer. */\n        do {\n            ++*msg;\n            --*msg_len;\n        } while (**msg == ' ');\n    } else {\n        /* garbage found after status code */\n        *ret = -1;\n        return NULL;\n    }\n\n    return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);\n}\n\nint phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,\n                       struct phr_header *headers, size_t *num_headers, size_t last_len)\n{\n    const char *buf = buf_start, *buf_end = buf + len;\n    size_t max_headers = *num_headers;\n    int r;\n\n    *minor_version = -1;\n    *status = 0;\n    *msg = NULL;\n    *msg_len = 0;\n    *num_headers = 0;\n\n    /* if last_len != 0, check if the response is complete (a fast countermeasure\n       against slowloris */\n    if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {\n        return r;\n    }\n\n    if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) {\n        return r;\n    }\n\n    return (int)(buf - buf_start);\n}\n\nint phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len)\n{\n    const char *buf = buf_start, *buf_end = buf + len;\n    size_t max_headers = *num_headers;\n    int r;\n\n    *num_headers = 0;\n\n    /* if last_len != 0, check if the response is complete (a fast countermeasure\n       against slowloris */\n    if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {\n        return r;\n    }\n\n    if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {\n        return r;\n    }\n\n    return (int)(buf - buf_start);\n}\n\nenum {\n    CHUNKED_IN_CHUNK_SIZE,\n    CHUNKED_IN_CHUNK_EXT,\n    CHUNKED_IN_CHUNK_DATA,\n    CHUNKED_IN_CHUNK_CRLF,\n    CHUNKED_IN_TRAILERS_LINE_HEAD,\n    CHUNKED_IN_TRAILERS_LINE_MIDDLE\n};\n\nstatic int decode_hex(int ch)\n{\n    if ('0' <= ch && ch <= '9') {\n        return ch - '0';\n    } else if ('A' <= ch && ch <= 'F') {\n        return ch - 'A' + 0xa;\n    } else if ('a' <= ch && ch <= 'f') {\n        return ch - 'a' + 0xa;\n    } else {\n        return -1;\n    }\n}\n\nssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz)\n{\n    size_t dst = 0, src = 0, bufsz = *_bufsz;\n    ssize_t ret = -2; /* incomplete */\n\n    while (1) {\n        switch (decoder->_state) {\n        case CHUNKED_IN_CHUNK_SIZE:\n            for (;; ++src) {\n                int v;\n                if (src == bufsz)\n                    goto Exit;\n                if ((v = decode_hex(buf[src])) == -1) {\n                    if (decoder->_hex_count == 0) {\n                        ret = -1;\n                        goto Exit;\n                    }\n                    break;\n                }\n                if (decoder->_hex_count == sizeof(size_t) * 2) {\n                    ret = -1;\n                    goto Exit;\n                }\n                decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + (size_t)v;\n                ++decoder->_hex_count;\n            }\n            decoder->_hex_count = 0;\n            decoder->_state = CHUNKED_IN_CHUNK_EXT;\n        /* fallthru */\n        case CHUNKED_IN_CHUNK_EXT:\n            /* RFC 7230 A.2 \"Line folding in chunk extensions is disallowed\" */\n            for (;; ++src) {\n                if (src == bufsz)\n                    goto Exit;\n                if (buf[src] == '\\012')\n                    break;\n            }\n            ++src;\n            if (decoder->bytes_left_in_chunk == 0) {\n                if (decoder->consume_trailer) {\n                    decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;\n                    break;\n                } else {\n                    goto Complete;\n                }\n            }\n            decoder->_state = CHUNKED_IN_CHUNK_DATA;\n        /* fallthru */\n        case CHUNKED_IN_CHUNK_DATA: {\n            size_t avail = bufsz - src;\n            if (avail < decoder->bytes_left_in_chunk) {\n                if (dst != src)\n                    memmove(buf + dst, buf + src, avail);\n                src += avail;\n                dst += avail;\n                decoder->bytes_left_in_chunk -= avail;\n                goto Exit;\n            }\n            if (dst != src)\n                memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);\n            src += decoder->bytes_left_in_chunk;\n            dst += decoder->bytes_left_in_chunk;\n            decoder->bytes_left_in_chunk = 0;\n            decoder->_state = CHUNKED_IN_CHUNK_CRLF;\n        }\n        /* fallthru */\n        case CHUNKED_IN_CHUNK_CRLF:\n            for (;; ++src) {\n                if (src == bufsz)\n                    goto Exit;\n                if (buf[src] != '\\015')\n                    break;\n            }\n            if (buf[src] != '\\012') {\n                ret = -1;\n                goto Exit;\n            }\n            ++src;\n            decoder->_state = CHUNKED_IN_CHUNK_SIZE;\n            break;\n        case CHUNKED_IN_TRAILERS_LINE_HEAD:\n            for (;; ++src) {\n                if (src == bufsz)\n                    goto Exit;\n                if (buf[src] != '\\015')\n                    break;\n            }\n            if (buf[src++] == '\\012')\n                goto Complete;\n            decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;\n        /* fallthru */\n        case CHUNKED_IN_TRAILERS_LINE_MIDDLE:\n            for (;; ++src) {\n                if (src == bufsz)\n                    goto Exit;\n                if (buf[src] == '\\012')\n                    break;\n            }\n            ++src;\n            decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;\n            break;\n        default:\n            assert(0 /*\"decoder is corrupt\"*/);\n        }\n    }\n\nComplete:\n    ret = (ssize_t)(bufsz - src);\nExit:\n    if (dst != src)\n        memmove(buf + dst, buf + src, bufsz - src);\n    *_bufsz = dst;\n    return ret;\n}\n\nint phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder)\n{\n    return decoder->_state == CHUNKED_IN_CHUNK_DATA;\n}\n\n#undef CHECK_EOF\n#undef EXPECT_CHAR\n#undef ADVANCE_TOKEN\n"
  },
  {
    "path": "deps/picohttpparser/picohttpparser.h",
    "content": "/*\n * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,\n *                         Shigeo Mitsunari\n *\n * The software is licensed under either the MIT License (below) or the Perl\n * license.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#ifndef picohttpparser_h\n#define picohttpparser_h\n\n#include <sys/types.h>\n\n#ifdef _MSC_VER\n#define ssize_t intptr_t\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* contains name and value of a header (name == NULL if is a continuing line\n * of a multiline header */\nstruct phr_header {\n    const char *name;\n    size_t name_len;\n    const char *value;\n    size_t value_len;\n};\n\n/* returns number of bytes consumed if successful, -2 if request is partial,\n * -1 if failed */\nint phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len,\n                      int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len);\n\n/* ditto */\nint phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,\n                       struct phr_header *headers, size_t *num_headers, size_t last_len);\n\n/* ditto */\nint phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len);\n\n/* should be zero-filled before start */\nstruct phr_chunked_decoder {\n    size_t bytes_left_in_chunk; /* number of bytes left in current chunk */\n    char consume_trailer;       /* if trailing headers should be consumed */\n    char _hex_count;\n    char _state;\n};\n\n/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-\n * encoding headers.  When the function returns without an error, bufsz is\n * updated to the length of the decoded data available.  Applications should\n * repeatedly call the function while it returns -2 (incomplete) every time\n * supplying newly arrived data.  If the end of the chunked-encoded data is\n * found, the function returns a non-negative number indicating the number of\n * octets left undecoded, that starts from the offset returned by `*bufsz`.\n * Returns -1 on error.\n */\nssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);\n\n/* returns if the chunked decoder is in middle of chunked data */\nint phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "deps/uthash.h",
    "content": "/*\nCopyright (c) 2003-2025, Troy D. Hanson  https://troydhanson.github.io/uthash/\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER\nOR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/\n\n#ifndef UTHASH_H\n#define UTHASH_H\n\n#define UTHASH_VERSION 2.3.0\n\n#include <string.h>   /* memcmp, memset, strlen */\n#include <stddef.h>   /* ptrdiff_t */\n#include <stdlib.h>   /* exit */\n\n#if defined(HASH_NO_STDINT) && HASH_NO_STDINT\n/* The user doesn't have <stdint.h>, and must figure out their own way\n   to provide definitions for uint8_t and uint32_t. */\n#else\n#include <stdint.h>   /* uint8_t, uint32_t */\n#endif\n\n/* These macros use decltype or the earlier __typeof GNU extension.\n   As decltype is only available in newer compilers (VS2010 or gcc 4.3+\n   when compiling c++ source) this code uses whatever method is needed\n   or, for VS2008 where neither is available, uses casting workarounds. */\n#if !defined(DECLTYPE) && !defined(NO_DECLTYPE)\n#if defined(_MSC_VER)   /* MS compiler */\n#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */\n#define DECLTYPE(x) (decltype(x))\n#else                   /* VS2008 or older (or VS2010 in C mode) */\n#define NO_DECLTYPE\n#endif\n#elif defined(__MCST__)  /* Elbrus C Compiler */\n#define DECLTYPE(x) (__typeof(x))\n#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)\n#define NO_DECLTYPE\n#else                   /* GNU, Sun and other compilers */\n#define DECLTYPE(x) (__typeof(x))\n#endif\n#endif\n\n#ifdef NO_DECLTYPE\n#define DECLTYPE(x)\n#define DECLTYPE_ASSIGN(dst,src)                                                 \\\ndo {                                                                             \\\n  char **_da_dst = (char**)(&(dst));                                             \\\n  *_da_dst = (char*)(src);                                                       \\\n} while (0)\n#else\n#define DECLTYPE_ASSIGN(dst,src)                                                 \\\ndo {                                                                             \\\n  (dst) = DECLTYPE(dst)(src);                                                    \\\n} while (0)\n#endif\n\n#ifndef uthash_malloc\n#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */\n#endif\n#ifndef uthash_free\n#define uthash_free(ptr,sz) free(ptr)     /* free fcn                        */\n#endif\n#ifndef uthash_bzero\n#define uthash_bzero(a,n) memset(a,'\\0',n)\n#endif\n#ifndef uthash_strlen\n#define uthash_strlen(s) strlen(s)\n#endif\n\n#ifndef HASH_FUNCTION\n#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv)\n#endif\n\n#ifndef HASH_KEYCMP\n#define HASH_KEYCMP(a,b,n) memcmp(a,b,n)\n#endif\n\n#ifndef uthash_noexpand_fyi\n#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */\n#endif\n#ifndef uthash_expand_fyi\n#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */\n#endif\n\n#ifndef HASH_NONFATAL_OOM\n#define HASH_NONFATAL_OOM 0\n#endif\n\n#if HASH_NONFATAL_OOM\n/* malloc failures can be recovered from */\n\n#ifndef uthash_nonfatal_oom\n#define uthash_nonfatal_oom(obj) do {} while (0)    /* non-fatal OOM error */\n#endif\n\n#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0)\n#define IF_HASH_NONFATAL_OOM(x) x\n\n#else\n/* malloc failures result in lost memory, hash tables are unusable */\n\n#ifndef uthash_fatal\n#define uthash_fatal(msg) exit(-1)        /* fatal OOM error */\n#endif\n\n#define HASH_RECORD_OOM(oomed) uthash_fatal(\"out of memory\")\n#define IF_HASH_NONFATAL_OOM(x)\n\n#endif\n\n/* initial number of buckets */\n#define HASH_INITIAL_NUM_BUCKETS 32U     /* initial number of buckets        */\n#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */\n#define HASH_BKT_CAPACITY_THRESH 10U     /* expand when bucket count reaches */\n\n/* calculate the element whose hash handle address is hhp */\n#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))\n/* calculate the hash handle from element address elp */\n#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho)))\n\n#define HASH_ROLLBACK_BKT(hh, head, itemptrhh)                                   \\\ndo {                                                                             \\\n  struct UT_hash_handle *_hd_hh_item = (itemptrhh);                              \\\n  unsigned _hd_bkt;                                                              \\\n  HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);         \\\n  (head)->hh.tbl->buckets[_hd_bkt].count++;                                      \\\n  _hd_hh_item->hh_next = NULL;                                                   \\\n  _hd_hh_item->hh_prev = NULL;                                                   \\\n} while (0)\n\n#define HASH_VALUE(keyptr,keylen,hashv)                                          \\\ndo {                                                                             \\\n  HASH_FUNCTION(keyptr, keylen, hashv);                                          \\\n} while (0)\n\n#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out)                 \\\ndo {                                                                             \\\n  (out) = NULL;                                                                  \\\n  if (head) {                                                                    \\\n    unsigned _hf_bkt;                                                            \\\n    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt);                  \\\n    if (HASH_BLOOM_TEST((head)->hh.tbl, hashval)) {                              \\\n      HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \\\n    }                                                                            \\\n  }                                                                              \\\n} while (0)\n\n#define HASH_FIND(hh,head,keyptr,keylen,out)                                     \\\ndo {                                                                             \\\n  (out) = NULL;                                                                  \\\n  if (head) {                                                                    \\\n    unsigned _hf_hashv;                                                          \\\n    HASH_VALUE(keyptr, keylen, _hf_hashv);                                       \\\n    HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out);             \\\n  }                                                                              \\\n} while (0)\n\n#ifdef HASH_BLOOM\n#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)\n#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)\n#define HASH_BLOOM_MAKE(tbl,oomed)                                               \\\ndo {                                                                             \\\n  (tbl)->bloom_nbits = HASH_BLOOM;                                               \\\n  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \\\n  if (!(tbl)->bloom_bv) {                                                        \\\n    HASH_RECORD_OOM(oomed);                                                      \\\n  } else {                                                                       \\\n    uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                           \\\n    (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                     \\\n  }                                                                              \\\n} while (0)\n\n#define HASH_BLOOM_FREE(tbl)                                                     \\\ndo {                                                                             \\\n  uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                              \\\n} while (0)\n\n#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))\n#define HASH_BLOOM_BITTEST(bv,idx) ((bv[(idx)/8U] & (1U << ((idx)%8U))) != 0)\n\n#define HASH_BLOOM_ADD(tbl,hashv)                                                \\\n  HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))\n\n#define HASH_BLOOM_TEST(tbl,hashv)                                               \\\n  HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))\n\n#else\n#define HASH_BLOOM_MAKE(tbl,oomed)\n#define HASH_BLOOM_FREE(tbl)\n#define HASH_BLOOM_ADD(tbl,hashv)\n#define HASH_BLOOM_TEST(tbl,hashv) 1\n#define HASH_BLOOM_BYTELEN 0U\n#endif\n\n#define HASH_MAKE_TABLE(hh,head,oomed)                                           \\\ndo {                                                                             \\\n  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table));         \\\n  if (!(head)->hh.tbl) {                                                         \\\n    HASH_RECORD_OOM(oomed);                                                      \\\n  } else {                                                                       \\\n    uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table));                         \\\n    (head)->hh.tbl->tail = &((head)->hh);                                        \\\n    (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                      \\\n    (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;            \\\n    (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                  \\\n    (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                    \\\n        HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket));               \\\n    (head)->hh.tbl->signature = HASH_SIGNATURE;                                  \\\n    if (!(head)->hh.tbl->buckets) {                                              \\\n      HASH_RECORD_OOM(oomed);                                                    \\\n      uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                        \\\n    } else {                                                                     \\\n      uthash_bzero((head)->hh.tbl->buckets,                                      \\\n          HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket));             \\\n      HASH_BLOOM_MAKE((head)->hh.tbl, oomed);                                    \\\n      IF_HASH_NONFATAL_OOM(                                                      \\\n        if (oomed) {                                                             \\\n          uthash_free((head)->hh.tbl->buckets,                                   \\\n              HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));           \\\n          uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                    \\\n        }                                                                        \\\n      )                                                                          \\\n    }                                                                            \\\n  }                                                                              \\\n} while (0)\n\n#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \\\ndo {                                                                             \\\n  (replaced) = NULL;                                                             \\\n  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \\\n  if (replaced) {                                                                \\\n    HASH_DELETE(hh, head, replaced);                                             \\\n  }                                                                              \\\n  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \\\n} while (0)\n\n#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \\\ndo {                                                                             \\\n  (replaced) = NULL;                                                             \\\n  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \\\n  if (replaced) {                                                                \\\n    HASH_DELETE(hh, head, replaced);                                             \\\n  }                                                                              \\\n  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \\\n} while (0)\n\n#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \\\ndo {                                                                             \\\n  unsigned _hr_hashv;                                                            \\\n  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \\\n  HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \\\n} while (0)\n\n#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn)    \\\ndo {                                                                             \\\n  unsigned _hr_hashv;                                                            \\\n  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \\\n  HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \\\n} while (0)\n\n#define HASH_APPEND_LIST(hh, head, add)                                          \\\ndo {                                                                             \\\n  (add)->hh.next = NULL;                                                         \\\n  (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);           \\\n  (head)->hh.tbl->tail->next = (add);                                            \\\n  (head)->hh.tbl->tail = &((add)->hh);                                           \\\n} while (0)\n\n#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn)                                 \\\ndo {                                                                             \\\n  do {                                                                           \\\n    if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) {                             \\\n      break;                                                                     \\\n    }                                                                            \\\n  } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next));           \\\n} while (0)\n\n#ifdef NO_DECLTYPE\n#undef HASH_AKBI_INNER_LOOP\n#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn)                                 \\\ndo {                                                                             \\\n  char *_hs_saved_head = (char*)(head);                                          \\\n  do {                                                                           \\\n    DECLTYPE_ASSIGN(head, _hs_iter);                                             \\\n    if (cmpfcn(head, add) > 0) {                                                 \\\n      DECLTYPE_ASSIGN(head, _hs_saved_head);                                     \\\n      break;                                                                     \\\n    }                                                                            \\\n    DECLTYPE_ASSIGN(head, _hs_saved_head);                                       \\\n  } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next));           \\\n} while (0)\n#endif\n\n#if HASH_NONFATAL_OOM\n\n#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed)            \\\ndo {                                                                             \\\n  if (!(oomed)) {                                                                \\\n    unsigned _ha_bkt;                                                            \\\n    (head)->hh.tbl->num_items++;                                                 \\\n    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                  \\\n    HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed);    \\\n    if (oomed) {                                                                 \\\n      HASH_ROLLBACK_BKT(hh, head, &(add)->hh);                                   \\\n      HASH_DELETE_HH(hh, head, &(add)->hh);                                      \\\n      (add)->hh.tbl = NULL;                                                      \\\n      uthash_nonfatal_oom(add);                                                  \\\n    } else {                                                                     \\\n      HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                   \\\n      HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                \\\n    }                                                                            \\\n  } else {                                                                       \\\n    (add)->hh.tbl = NULL;                                                        \\\n    uthash_nonfatal_oom(add);                                                    \\\n  }                                                                              \\\n} while (0)\n\n#else\n\n#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed)            \\\ndo {                                                                             \\\n  unsigned _ha_bkt;                                                              \\\n  (head)->hh.tbl->num_items++;                                                   \\\n  HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                    \\\n  HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed);      \\\n  HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                       \\\n  HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \\\n} while (0)\n\n#endif\n\n\n#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \\\ndo {                                                                             \\\n  IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; )                                     \\\n  (add)->hh.hashv = (hashval);                                                   \\\n  (add)->hh.key = (char*) (keyptr);                                              \\\n  (add)->hh.keylen = (unsigned) (keylen_in);                                     \\\n  if (!(head)) {                                                                 \\\n    (add)->hh.next = NULL;                                                       \\\n    (add)->hh.prev = NULL;                                                       \\\n    HASH_MAKE_TABLE(hh, add, _ha_oomed);                                         \\\n    IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { )                                    \\\n      (head) = (add);                                                            \\\n    IF_HASH_NONFATAL_OOM( } )                                                    \\\n  } else {                                                                       \\\n    void *_hs_iter = (head);                                                     \\\n    (add)->hh.tbl = (head)->hh.tbl;                                              \\\n    HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn);                                 \\\n    if (_hs_iter) {                                                              \\\n      (add)->hh.next = _hs_iter;                                                 \\\n      if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) {     \\\n        HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add);              \\\n      } else {                                                                   \\\n        (head) = (add);                                                          \\\n      }                                                                          \\\n      HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add);                      \\\n    } else {                                                                     \\\n      HASH_APPEND_LIST(hh, head, add);                                           \\\n    }                                                                            \\\n  }                                                                              \\\n  HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed);       \\\n  HASH_FSCK(hh, head, \"HASH_ADD_KEYPTR_BYHASHVALUE_INORDER\");                    \\\n} while (0)\n\n#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn)             \\\ndo {                                                                             \\\n  unsigned _hs_hashv;                                                            \\\n  HASH_VALUE(keyptr, keylen_in, _hs_hashv);                                      \\\n  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \\\n} while (0)\n\n#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \\\n  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)\n\n#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn)                 \\\n  HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)\n\n#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add)        \\\ndo {                                                                             \\\n  IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; )                                     \\\n  (add)->hh.hashv = (hashval);                                                   \\\n  (add)->hh.key = (const void*) (keyptr);                                        \\\n  (add)->hh.keylen = (unsigned) (keylen_in);                                     \\\n  if (!(head)) {                                                                 \\\n    (add)->hh.next = NULL;                                                       \\\n    (add)->hh.prev = NULL;                                                       \\\n    HASH_MAKE_TABLE(hh, add, _ha_oomed);                                         \\\n    IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { )                                    \\\n      (head) = (add);                                                            \\\n    IF_HASH_NONFATAL_OOM( } )                                                    \\\n  } else {                                                                       \\\n    (add)->hh.tbl = (head)->hh.tbl;                                              \\\n    HASH_APPEND_LIST(hh, head, add);                                             \\\n  }                                                                              \\\n  HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed);       \\\n  HASH_FSCK(hh, head, \"HASH_ADD_KEYPTR_BYHASHVALUE\");                            \\\n} while (0)\n\n#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \\\ndo {                                                                             \\\n  unsigned _ha_hashv;                                                            \\\n  HASH_VALUE(keyptr, keylen_in, _ha_hashv);                                      \\\n  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add);      \\\n} while (0)\n\n#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add)            \\\n  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)\n\n#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \\\n  HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)\n\n#define HASH_TO_BKT(hashv,num_bkts,bkt)                                          \\\ndo {                                                                             \\\n  bkt = ((hashv) & ((num_bkts) - 1U));                                           \\\n} while (0)\n\n/* delete \"delptr\" from the hash table.\n * \"the usual\" patch-up process for the app-order doubly-linked-list.\n * The use of _hd_hh_del below deserves special explanation.\n * These used to be expressed using (delptr) but that led to a bug\n * if someone used the same symbol for the head and deletee, like\n *  HASH_DELETE(hh,users,users);\n * We want that to work, but by changing the head (users) below\n * we were forfeiting our ability to further refer to the deletee (users)\n * in the patch-up process. Solution: use scratch space to\n * copy the deletee pointer, then the latter references are via that\n * scratch pointer rather than through the repointed (users) symbol.\n */\n#define HASH_DELETE(hh,head,delptr)                                              \\\n    HASH_DELETE_HH(hh, head, &(delptr)->hh)\n\n#define HASH_DELETE_HH(hh,head,delptrhh)                                         \\\ndo {                                                                             \\\n  const struct UT_hash_handle *_hd_hh_del = (delptrhh);                          \\\n  if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) {                \\\n    HASH_BLOOM_FREE((head)->hh.tbl);                                             \\\n    uthash_free((head)->hh.tbl->buckets,                                         \\\n                (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket));    \\\n    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \\\n    (head) = NULL;                                                               \\\n  } else {                                                                       \\\n    unsigned _hd_bkt;                                                            \\\n    if (_hd_hh_del == (head)->hh.tbl->tail) {                                    \\\n      (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev);     \\\n    }                                                                            \\\n    if (_hd_hh_del->prev != NULL) {                                              \\\n      HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next;   \\\n    } else {                                                                     \\\n      DECLTYPE_ASSIGN(head, _hd_hh_del->next);                                   \\\n    }                                                                            \\\n    if (_hd_hh_del->next != NULL) {                                              \\\n      HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev;   \\\n    }                                                                            \\\n    HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);        \\\n    HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);               \\\n    (head)->hh.tbl->num_items--;                                                 \\\n  }                                                                              \\\n  HASH_FSCK(hh, head, \"HASH_DELETE_HH\");                                         \\\n} while (0)\n\n/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */\n#define HASH_FIND_STR(head,findstr,out)                                          \\\ndo {                                                                             \\\n    unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr);            \\\n    HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out);                     \\\n} while (0)\n#define HASH_ADD_STR(head,strfield,add)                                          \\\ndo {                                                                             \\\n    unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield);    \\\n    HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add);                  \\\n} while (0)\n#define HASH_REPLACE_STR(head,strfield,add,replaced)                             \\\ndo {                                                                             \\\n    unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield);    \\\n    HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced);    \\\n} while (0)\n#define HASH_FIND_INT(head,findint,out)                                          \\\n    HASH_FIND(hh,head,findint,sizeof(int),out)\n#define HASH_ADD_INT(head,intfield,add)                                          \\\n    HASH_ADD(hh,head,intfield,sizeof(int),add)\n#define HASH_REPLACE_INT(head,intfield,add,replaced)                             \\\n    HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)\n#define HASH_FIND_PTR(head,findptr,out)                                          \\\n    HASH_FIND(hh,head,findptr,sizeof(void *),out)\n#define HASH_ADD_PTR(head,ptrfield,add)                                          \\\n    HASH_ADD(hh,head,ptrfield,sizeof(void *),add)\n#define HASH_REPLACE_PTR(head,ptrfield,add,replaced)                             \\\n    HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)\n#define HASH_DEL(head,delptr)                                                    \\\n    HASH_DELETE(hh,head,delptr)\n\n/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.\n * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.\n */\n#ifdef HASH_DEBUG\n#include <stdio.h>   /* fprintf, stderr */\n#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0)\n#define HASH_FSCK(hh,head,where)                                                 \\\ndo {                                                                             \\\n  struct UT_hash_handle *_thh;                                                   \\\n  if (head) {                                                                    \\\n    unsigned _bkt_i;                                                             \\\n    unsigned _count = 0;                                                         \\\n    char *_prev;                                                                 \\\n    for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) {           \\\n      unsigned _bkt_count = 0;                                                   \\\n      _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                            \\\n      _prev = NULL;                                                              \\\n      while (_thh) {                                                             \\\n        if (_prev != (char*)(_thh->hh_prev)) {                                   \\\n          HASH_OOPS(\"%s: invalid hh_prev %p, actual %p\\n\",                       \\\n              (where), (void*)_thh->hh_prev, (void*)_prev);                      \\\n        }                                                                        \\\n        _bkt_count++;                                                            \\\n        _prev = (char*)(_thh);                                                   \\\n        _thh = _thh->hh_next;                                                    \\\n      }                                                                          \\\n      _count += _bkt_count;                                                      \\\n      if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {                \\\n        HASH_OOPS(\"%s: invalid bucket count %u, actual %u\\n\",                    \\\n            (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);         \\\n      }                                                                          \\\n    }                                                                            \\\n    if (_count != (head)->hh.tbl->num_items) {                                   \\\n      HASH_OOPS(\"%s: invalid hh item count %u, actual %u\\n\",                     \\\n          (where), (head)->hh.tbl->num_items, _count);                           \\\n    }                                                                            \\\n    _count = 0;                                                                  \\\n    _prev = NULL;                                                                \\\n    _thh =  &(head)->hh;                                                         \\\n    while (_thh) {                                                               \\\n      _count++;                                                                  \\\n      if (_prev != (char*)_thh->prev) {                                          \\\n        HASH_OOPS(\"%s: invalid prev %p, actual %p\\n\",                            \\\n            (where), (void*)_thh->prev, (void*)_prev);                           \\\n      }                                                                          \\\n      _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                         \\\n      _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL);     \\\n    }                                                                            \\\n    if (_count != (head)->hh.tbl->num_items) {                                   \\\n      HASH_OOPS(\"%s: invalid app item count %u, actual %u\\n\",                    \\\n          (where), (head)->hh.tbl->num_items, _count);                           \\\n    }                                                                            \\\n  }                                                                              \\\n} while (0)\n#else\n#define HASH_FSCK(hh,head,where)\n#endif\n\n/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to\n * the descriptor to which this macro is defined for tuning the hash function.\n * The app can #include <unistd.h> to get the prototype for write(2). */\n#ifdef HASH_EMIT_KEYS\n#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \\\ndo {                                                                             \\\n  unsigned _klen = fieldlen;                                                     \\\n  write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                  \\\n  write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen);                        \\\n} while (0)\n#else\n#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)\n#endif\n\n/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */\n#define HASH_BER(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned _hb_keylen = (unsigned)keylen;                                        \\\n  const unsigned char *_hb_key = (const unsigned char*)(key);                    \\\n  (hashv) = 0;                                                                   \\\n  while (_hb_keylen-- != 0U) {                                                   \\\n    (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++;                           \\\n  }                                                                              \\\n} while (0)\n\n\n/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at\n * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx\n * (archive link: https://archive.is/Ivcan )\n */\n#define HASH_SAX(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned _sx_i;                                                                \\\n  const unsigned char *_hs_key = (const unsigned char*)(key);                    \\\n  hashv = 0;                                                                     \\\n  for (_sx_i=0; _sx_i < keylen; _sx_i++) {                                       \\\n    hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i];                       \\\n  }                                                                              \\\n} while (0)\n/* FNV-1a variation */\n#define HASH_FNV(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned _fn_i;                                                                \\\n  const unsigned char *_hf_key = (const unsigned char*)(key);                    \\\n  (hashv) = 2166136261U;                                                         \\\n  for (_fn_i=0; _fn_i < keylen; _fn_i++) {                                       \\\n    hashv = hashv ^ _hf_key[_fn_i];                                              \\\n    hashv = hashv * 16777619U;                                                   \\\n  }                                                                              \\\n} while (0)\n\n#define HASH_OAT(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned _ho_i;                                                                \\\n  const unsigned char *_ho_key=(const unsigned char*)(key);                      \\\n  hashv = 0;                                                                     \\\n  for(_ho_i=0; _ho_i < keylen; _ho_i++) {                                        \\\n      hashv += _ho_key[_ho_i];                                                   \\\n      hashv += (hashv << 10);                                                    \\\n      hashv ^= (hashv >> 6);                                                     \\\n  }                                                                              \\\n  hashv += (hashv << 3);                                                         \\\n  hashv ^= (hashv >> 11);                                                        \\\n  hashv += (hashv << 15);                                                        \\\n} while (0)\n\n#define HASH_JEN_MIX(a,b,c)                                                      \\\ndo {                                                                             \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  a -= b; a -= c; a ^= ( c >> 13 );                                              \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  b -= c; b -= a; b ^= ( a << 8 );                                               \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  c -= a; c -= b; c ^= ( b >> 13 );                                              \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  a -= b; a -= c; a ^= ( c >> 12 );                                              \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  b -= c; b -= a; b ^= ( a << 16 );                                              \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  c -= a; c -= b; c ^= ( b >> 5 );                                               \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  a -= b; a -= c; a ^= ( c >> 3 );                                               \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  b -= c; b -= a; b ^= ( a << 10 );                                              \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  c -= a; c -= b; c ^= ( b >> 15 );                                              \\\n} while (0)\n\n#define HASH_JEN(key,keylen,hashv)                                               \\\n/* coverity[overflow_const] - intentional wrapping in Jenkins hash */            \\\ndo {                                                                             \\\n  unsigned _hj_i,_hj_j,_hj_k;                                                    \\\n  unsigned const char *_hj_key=(unsigned const char*)(key);                      \\\n  hashv = 0xfeedbeefu;                                                           \\\n  _hj_i = _hj_j = 0x9e3779b9u;                                                   \\\n  _hj_k = (unsigned)(keylen);                                                    \\\n  while (_hj_k >= 12U) {                                                         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    _hj_i +=    (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 )                      \\\n        + ( (unsigned)_hj_key[2] << 16 )                                         \\\n        + ( (unsigned)_hj_key[3] << 24 ) );                                      \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    _hj_j +=    (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 )                      \\\n        + ( (unsigned)_hj_key[6] << 16 )                                         \\\n        + ( (unsigned)_hj_key[7] << 24 ) );                                      \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 )                         \\\n        + ( (unsigned)_hj_key[10] << 16 )                                        \\\n        + ( (unsigned)_hj_key[11] << 24 ) );                                     \\\n                                                                                 \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n     HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                          \\\n                                                                                 \\\n     _hj_key += 12;                                                              \\\n     _hj_k -= 12U;                                                               \\\n  }                                                                              \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  hashv += (unsigned)(keylen);                                                   \\\n  switch ( _hj_k ) {                                                             \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 10: hashv += ( (unsigned)_hj_key[9] << 16 );  /* FALLTHROUGH */         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 9:  hashv += ( (unsigned)_hj_key[8] << 8 );   /* FALLTHROUGH */         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 8:  _hj_j += ( (unsigned)_hj_key[7] << 24 );  /* FALLTHROUGH */         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 7:  _hj_j += ( (unsigned)_hj_key[6] << 16 );  /* FALLTHROUGH */         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 6:  _hj_j += ( (unsigned)_hj_key[5] << 8 );   /* FALLTHROUGH */         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 5:  _hj_j += _hj_key[4];                      /* FALLTHROUGH */         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 4:  _hj_i += ( (unsigned)_hj_key[3] << 24 );  /* FALLTHROUGH */         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 3:  _hj_i += ( (unsigned)_hj_key[2] << 16 );  /* FALLTHROUGH */         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 2:  _hj_i += ( (unsigned)_hj_key[1] << 8 );   /* FALLTHROUGH */         \\\n    /* coverity[overflow_const] - intentional wrapping in Jenkins hash */        \\\n    case 1:  _hj_i += _hj_key[0];                      /* FALLTHROUGH */         \\\n    default: ;                                                                   \\\n  }                                                                              \\\n  /* coverity[overflow_const] - intentional wrapping in Jenkins hash */          \\\n  HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \\\n} while (0)\n\n/* The Paul Hsieh hash function */\n#undef get16bits\n#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)             \\\n  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)\n#define get16bits(d) (*((const uint16_t *) (d)))\n#endif\n\n#if !defined (get16bits)\n#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)             \\\n                       +(uint32_t)(((const uint8_t *)(d))[0]) )\n#endif\n#define HASH_SFH(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned const char *_sfh_key=(unsigned const char*)(key);                     \\\n  uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen;                                \\\n                                                                                 \\\n  unsigned _sfh_rem = _sfh_len & 3U;                                             \\\n  _sfh_len >>= 2;                                                                \\\n  hashv = 0xcafebabeu;                                                           \\\n                                                                                 \\\n  /* Main loop */                                                                \\\n  for (;_sfh_len > 0U; _sfh_len--) {                                             \\\n    hashv    += get16bits (_sfh_key);                                            \\\n    _sfh_tmp  = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv;              \\\n    hashv     = (hashv << 16) ^ _sfh_tmp;                                        \\\n    _sfh_key += 2U*sizeof (uint16_t);                                            \\\n    hashv    += hashv >> 11;                                                     \\\n  }                                                                              \\\n                                                                                 \\\n  /* Handle end cases */                                                         \\\n  switch (_sfh_rem) {                                                            \\\n    case 3: hashv += get16bits (_sfh_key);                                       \\\n            hashv ^= hashv << 16;                                                \\\n            hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18;              \\\n            hashv += hashv >> 11;                                                \\\n            break;                                                               \\\n    case 2: hashv += get16bits (_sfh_key);                                       \\\n            hashv ^= hashv << 11;                                                \\\n            hashv += hashv >> 17;                                                \\\n            break;                                                               \\\n    case 1: hashv += *_sfh_key;                                                  \\\n            hashv ^= hashv << 10;                                                \\\n            hashv += hashv >> 1;                                                 \\\n            break;                                                               \\\n    default: ;                                                                   \\\n  }                                                                              \\\n                                                                                 \\\n  /* Force \"avalanching\" of final 127 bits */                                    \\\n  hashv ^= hashv << 3;                                                           \\\n  hashv += hashv >> 5;                                                           \\\n  hashv ^= hashv << 4;                                                           \\\n  hashv += hashv >> 17;                                                          \\\n  hashv ^= hashv << 25;                                                          \\\n  hashv += hashv >> 6;                                                           \\\n} while (0)\n\n/* iterate over items in a known bucket to find desired item */\n#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out)               \\\ndo {                                                                             \\\n  if ((head).hh_head != NULL) {                                                  \\\n    DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head));                     \\\n  } else {                                                                       \\\n    (out) = NULL;                                                                \\\n  }                                                                              \\\n  while ((out) != NULL) {                                                        \\\n    if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) {       \\\n      if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) {                  \\\n        break;                                                                   \\\n      }                                                                          \\\n    }                                                                            \\\n    if ((out)->hh.hh_next != NULL) {                                             \\\n      DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next));                \\\n    } else {                                                                     \\\n      (out) = NULL;                                                              \\\n    }                                                                            \\\n  }                                                                              \\\n} while (0)\n\n/* add an item to a bucket  */\n#define HASH_ADD_TO_BKT(head,hh,addhh,oomed)                                     \\\ndo {                                                                             \\\n  UT_hash_bucket *_ha_head = &(head);                                            \\\n  _ha_head->count++;                                                             \\\n  (addhh)->hh_next = _ha_head->hh_head;                                          \\\n  (addhh)->hh_prev = NULL;                                                       \\\n  if (_ha_head->hh_head != NULL) {                                               \\\n    _ha_head->hh_head->hh_prev = (addhh);                                        \\\n  }                                                                              \\\n  _ha_head->hh_head = (addhh);                                                   \\\n  if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \\\n      && !(addhh)->tbl->noexpand) {                                              \\\n    HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed);                              \\\n    IF_HASH_NONFATAL_OOM(                                                        \\\n      if (oomed) {                                                               \\\n        HASH_DEL_IN_BKT(head,addhh);                                             \\\n      }                                                                          \\\n    )                                                                            \\\n  }                                                                              \\\n} while (0)\n\n/* remove an item from a given bucket */\n#define HASH_DEL_IN_BKT(head,delhh)                                              \\\ndo {                                                                             \\\n  UT_hash_bucket *_hd_head = &(head);                                            \\\n  _hd_head->count--;                                                             \\\n  if (_hd_head->hh_head == (delhh)) {                                            \\\n    _hd_head->hh_head = (delhh)->hh_next;                                        \\\n  }                                                                              \\\n  if ((delhh)->hh_prev) {                                                        \\\n    (delhh)->hh_prev->hh_next = (delhh)->hh_next;                                \\\n  }                                                                              \\\n  if ((delhh)->hh_next) {                                                        \\\n    (delhh)->hh_next->hh_prev = (delhh)->hh_prev;                                \\\n  }                                                                              \\\n} while (0)\n\n/* Bucket expansion has the effect of doubling the number of buckets\n * and redistributing the items into the new buckets. Ideally the\n * items will distribute more or less evenly into the new buckets\n * (the extent to which this is true is a measure of the quality of\n * the hash function as it applies to the key domain).\n *\n * With the items distributed into more buckets, the chain length\n * (item count) in each bucket is reduced. Thus by expanding buckets\n * the hash keeps a bound on the chain length. This bounded chain\n * length is the essence of how a hash provides constant time lookup.\n *\n * The calculation of tbl->ideal_chain_maxlen below deserves some\n * explanation. First, keep in mind that we're calculating the ideal\n * maximum chain length based on the *new* (doubled) bucket count.\n * In fractions this is just n/b (n=number of items,b=new num buckets).\n * Since the ideal chain length is an integer, we want to calculate\n * ceil(n/b). We don't depend on floating point arithmetic in this\n * hash, so to calculate ceil(n/b) with integers we could write\n *\n *      ceil(n/b) = (n/b) + ((n%b)?1:0)\n *\n * and in fact a previous version of this hash did just that.\n * But now we have improved things a bit by recognizing that b is\n * always a power of two. We keep its base 2 log handy (call it lb),\n * so now we can write this with a bit shift and logical AND:\n *\n *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)\n *\n */\n#define HASH_EXPAND_BUCKETS(hh,tbl,oomed)                                        \\\ndo {                                                                             \\\n  unsigned _he_bkt;                                                              \\\n  unsigned _he_bkt_i;                                                            \\\n  struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                   \\\n  UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                  \\\n  _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                              \\\n           sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U);             \\\n  if (!_he_new_buckets) {                                                        \\\n    HASH_RECORD_OOM(oomed);                                                      \\\n  } else {                                                                       \\\n    uthash_bzero(_he_new_buckets,                                                \\\n        sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U);                \\\n    (tbl)->ideal_chain_maxlen =                                                  \\\n       ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) +                      \\\n       ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U);    \\\n    (tbl)->nonideal_items = 0;                                                   \\\n    for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) {           \\\n      _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head;                             \\\n      while (_he_thh != NULL) {                                                  \\\n        _he_hh_nxt = _he_thh->hh_next;                                           \\\n        HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt);           \\\n        _he_newbkt = &(_he_new_buckets[_he_bkt]);                                \\\n        if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) {                 \\\n          (tbl)->nonideal_items++;                                               \\\n          if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \\\n            _he_newbkt->expand_mult++;                                           \\\n          }                                                                      \\\n        }                                                                        \\\n        _he_thh->hh_prev = NULL;                                                 \\\n        _he_thh->hh_next = _he_newbkt->hh_head;                                  \\\n        if (_he_newbkt->hh_head != NULL) {                                       \\\n          _he_newbkt->hh_head->hh_prev = _he_thh;                                \\\n        }                                                                        \\\n        _he_newbkt->hh_head = _he_thh;                                           \\\n        _he_thh = _he_hh_nxt;                                                    \\\n      }                                                                          \\\n    }                                                                            \\\n    uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \\\n    (tbl)->num_buckets *= 2U;                                                    \\\n    (tbl)->log2_num_buckets++;                                                   \\\n    (tbl)->buckets = _he_new_buckets;                                            \\\n    (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ?   \\\n        ((tbl)->ineff_expands+1U) : 0U;                                          \\\n    if ((tbl)->ineff_expands > 1U) {                                             \\\n      (tbl)->noexpand = 1;                                                       \\\n      uthash_noexpand_fyi(tbl);                                                  \\\n    }                                                                            \\\n    uthash_expand_fyi(tbl);                                                      \\\n  }                                                                              \\\n} while (0)\n\n\n/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */\n/* Note that HASH_SORT assumes the hash handle name to be hh.\n * HASH_SRT was added to allow the hash handle name to be passed in. */\n#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)\n#define HASH_SRT(hh,head,cmpfcn)                                                 \\\ndo {                                                                             \\\n  unsigned _hs_i;                                                                \\\n  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \\\n  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \\\n  if (head != NULL) {                                                            \\\n    _hs_insize = 1;                                                              \\\n    _hs_looping = 1;                                                             \\\n    _hs_list = &((head)->hh);                                                    \\\n    while (_hs_looping != 0U) {                                                  \\\n      _hs_p = _hs_list;                                                          \\\n      _hs_list = NULL;                                                           \\\n      _hs_tail = NULL;                                                           \\\n      _hs_nmerges = 0;                                                           \\\n      while (_hs_p != NULL) {                                                    \\\n        _hs_nmerges++;                                                           \\\n        _hs_q = _hs_p;                                                           \\\n        _hs_psize = 0;                                                           \\\n        for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) {                           \\\n          _hs_psize++;                                                           \\\n          _hs_q = ((_hs_q->next != NULL) ?                                       \\\n            HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                   \\\n          if (_hs_q == NULL) {                                                   \\\n            break;                                                               \\\n          }                                                                      \\\n        }                                                                        \\\n        _hs_qsize = _hs_insize;                                                  \\\n        while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) {    \\\n          if (_hs_psize == 0U) {                                                 \\\n            _hs_e = _hs_q;                                                       \\\n            _hs_q = ((_hs_q->next != NULL) ?                                     \\\n              HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                 \\\n            _hs_qsize--;                                                         \\\n          } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) {                     \\\n            _hs_e = _hs_p;                                                       \\\n            if (_hs_p != NULL) {                                                 \\\n              _hs_p = ((_hs_p->next != NULL) ?                                   \\\n                HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL);               \\\n            }                                                                    \\\n            _hs_psize--;                                                         \\\n          } else if ((cmpfcn(                                                    \\\n                DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)),             \\\n                DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q))              \\\n                )) <= 0) {                                                       \\\n            _hs_e = _hs_p;                                                       \\\n            if (_hs_p != NULL) {                                                 \\\n              _hs_p = ((_hs_p->next != NULL) ?                                   \\\n                HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL);               \\\n            }                                                                    \\\n            _hs_psize--;                                                         \\\n          } else {                                                               \\\n            _hs_e = _hs_q;                                                       \\\n            _hs_q = ((_hs_q->next != NULL) ?                                     \\\n              HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                 \\\n            _hs_qsize--;                                                         \\\n          }                                                                      \\\n          if ( _hs_tail != NULL ) {                                              \\\n            _hs_tail->next = ((_hs_e != NULL) ?                                  \\\n              ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL);                       \\\n          } else {                                                               \\\n            _hs_list = _hs_e;                                                    \\\n          }                                                                      \\\n          if (_hs_e != NULL) {                                                   \\\n            _hs_e->prev = ((_hs_tail != NULL) ?                                  \\\n              ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL);                    \\\n          }                                                                      \\\n          _hs_tail = _hs_e;                                                      \\\n        }                                                                        \\\n        _hs_p = _hs_q;                                                           \\\n      }                                                                          \\\n      if (_hs_tail != NULL) {                                                    \\\n        _hs_tail->next = NULL;                                                   \\\n      }                                                                          \\\n      if (_hs_nmerges <= 1U) {                                                   \\\n        _hs_looping = 0;                                                         \\\n        (head)->hh.tbl->tail = _hs_tail;                                         \\\n        DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list));           \\\n      }                                                                          \\\n      _hs_insize *= 2U;                                                          \\\n    }                                                                            \\\n    HASH_FSCK(hh, head, \"HASH_SRT\");                                             \\\n  }                                                                              \\\n} while (0)\n\n/* This function selects items from one hash into another hash.\n * The end result is that the selected items have dual presence\n * in both hashes. There is no copy of the items made; rather\n * they are added into the new hash through a secondary hash\n * hash handle that must be present in the structure. */\n#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \\\ndo {                                                                             \\\n  unsigned _src_bkt, _dst_bkt;                                                   \\\n  void *_last_elt = NULL, *_elt;                                                 \\\n  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \\\n  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \\\n  if ((src) != NULL) {                                                           \\\n    for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {    \\\n      for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;               \\\n        _src_hh != NULL;                                                         \\\n        _src_hh = _src_hh->hh_next) {                                            \\\n        _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                         \\\n        if (cond(_elt)) {                                                        \\\n          IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; )                             \\\n          _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho);          \\\n          _dst_hh->key = _src_hh->key;                                           \\\n          _dst_hh->keylen = _src_hh->keylen;                                     \\\n          _dst_hh->hashv = _src_hh->hashv;                                       \\\n          _dst_hh->prev = _last_elt;                                             \\\n          _dst_hh->next = NULL;                                                  \\\n          if (_last_elt_hh != NULL) {                                            \\\n            _last_elt_hh->next = _elt;                                           \\\n          }                                                                      \\\n          if ((dst) == NULL) {                                                   \\\n            DECLTYPE_ASSIGN(dst, _elt);                                          \\\n            HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed);                             \\\n            IF_HASH_NONFATAL_OOM(                                                \\\n              if (_hs_oomed) {                                                   \\\n                uthash_nonfatal_oom(_elt);                                       \\\n                (dst) = NULL;                                                    \\\n                continue;                                                        \\\n              }                                                                  \\\n            )                                                                    \\\n          } else {                                                               \\\n            _dst_hh->tbl = (dst)->hh_dst.tbl;                                    \\\n          }                                                                      \\\n          HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);      \\\n          HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \\\n          (dst)->hh_dst.tbl->num_items++;                                        \\\n          IF_HASH_NONFATAL_OOM(                                                  \\\n            if (_hs_oomed) {                                                     \\\n              HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh);                           \\\n              HASH_DELETE_HH(hh_dst, dst, _dst_hh);                              \\\n              _dst_hh->tbl = NULL;                                               \\\n              uthash_nonfatal_oom(_elt);                                         \\\n              continue;                                                          \\\n            }                                                                    \\\n          )                                                                      \\\n          HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv);                          \\\n          _last_elt = _elt;                                                      \\\n          _last_elt_hh = _dst_hh;                                                \\\n        }                                                                        \\\n      }                                                                          \\\n    }                                                                            \\\n  }                                                                              \\\n  HASH_FSCK(hh_dst, dst, \"HASH_SELECT\");                                         \\\n} while (0)\n\n#define HASH_CLEAR(hh,head)                                                      \\\ndo {                                                                             \\\n  if ((head) != NULL) {                                                          \\\n    HASH_BLOOM_FREE((head)->hh.tbl);                                             \\\n    uthash_free((head)->hh.tbl->buckets,                                         \\\n                (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket));      \\\n    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \\\n    (head) = NULL;                                                               \\\n  }                                                                              \\\n} while (0)\n\n#define HASH_OVERHEAD(hh,head)                                                   \\\n (((head) != NULL) ? (                                                           \\\n (size_t)(((head)->hh.tbl->num_items   * sizeof(UT_hash_handle))   +             \\\n          ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket))   +             \\\n           sizeof(UT_hash_table)                                   +             \\\n           (HASH_BLOOM_BYTELEN))) : 0U)\n\n#ifdef NO_DECLTYPE\n#define HASH_ITER(hh,head,el,tmp)                                                \\\nfor(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \\\n  (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))\n#else\n#define HASH_ITER(hh,head,el,tmp)                                                \\\nfor(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL));      \\\n  (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))\n#endif\n\n/* obtain a count of items in the hash */\n#define HASH_COUNT(head) HASH_CNT(hh,head)\n#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)\n\ntypedef struct UT_hash_bucket {\n   struct UT_hash_handle *hh_head;\n   unsigned count;\n\n   /* expand_mult is normally set to 0. In this situation, the max chain length\n    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If\n    * the bucket's chain exceeds this length, bucket expansion is triggered).\n    * However, setting expand_mult to a non-zero value delays bucket expansion\n    * (that would be triggered by additions to this particular bucket)\n    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.\n    * (The multiplier is simply expand_mult+1). The whole idea of this\n    * multiplier is to reduce bucket expansions, since they are expensive, in\n    * situations where we know that a particular bucket tends to be overused.\n    * It is better to let its chain length grow to a longer yet-still-bounded\n    * value, than to do an O(n) bucket expansion too often.\n    */\n   unsigned expand_mult;\n\n} UT_hash_bucket;\n\n/* random signature used only to find hash tables in external analysis */\n#define HASH_SIGNATURE 0xa0111fe1u\n#define HASH_BLOOM_SIGNATURE 0xb12220f2u\n\ntypedef struct UT_hash_table {\n   UT_hash_bucket *buckets;\n   unsigned num_buckets, log2_num_buckets;\n   unsigned num_items;\n   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */\n   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */\n\n   /* in an ideal situation (all buckets used equally), no bucket would have\n    * more than ceil(#items/#buckets) items. that's the ideal chain length. */\n   unsigned ideal_chain_maxlen;\n\n   /* nonideal_items is the number of items in the hash whose chain position\n    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven\n    * hash distribution; reaching them in a chain traversal takes >ideal steps */\n   unsigned nonideal_items;\n\n   /* ineffective expands occur when a bucket doubling was performed, but\n    * afterward, more than half the items in the hash had nonideal chain\n    * positions. If this happens on two consecutive expansions we inhibit any\n    * further expansion, as it's not helping; this happens when the hash\n    * function isn't a good fit for the key domain. When expansion is inhibited\n    * the hash will still work, albeit no longer in constant time. */\n   unsigned ineff_expands, noexpand;\n\n   uint32_t signature; /* used only to find hash tables in external analysis */\n#ifdef HASH_BLOOM\n   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */\n   uint8_t *bloom_bv;\n   uint8_t bloom_nbits;\n#endif\n\n} UT_hash_table;\n\ntypedef struct UT_hash_handle {\n   struct UT_hash_table *tbl;\n   void *prev;                       /* prev element in app order      */\n   void *next;                       /* next element in app order      */\n   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */\n   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */\n   const void *key;                  /* ptr to enclosing struct's key  */\n   unsigned keylen;                  /* enclosing struct's key len     */\n   unsigned hashv;                   /* result of hash-fcn(key)        */\n} UT_hash_handle;\n\n#endif /* UTHASH_H */\n"
  },
  {
    "path": "deps/utlist.h",
    "content": "/*\nCopyright (c) 2007-2025, Troy D. Hanson  https://troydhanson.github.io/uthash/\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER\nOR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/\n\n#ifndef UTLIST_H\n#define UTLIST_H\n\n#define UTLIST_VERSION 2.3.0\n\n#include <assert.h>\n\n/*\n * This file contains macros to manipulate singly and doubly-linked lists.\n *\n * 1. LL_ macros:  singly-linked lists.\n * 2. DL_ macros:  doubly-linked lists.\n * 3. CDL_ macros: circular doubly-linked lists.\n *\n * To use singly-linked lists, your structure must have a \"next\" pointer.\n * To use doubly-linked lists, your structure must \"prev\" and \"next\" pointers.\n * Either way, the pointer to the head of the list must be initialized to NULL.\n *\n * ----------------.EXAMPLE -------------------------\n * struct item {\n *      int id;\n *      struct item *prev, *next;\n * }\n *\n * struct item *list = NULL:\n *\n * int main() {\n *      struct item *item;\n *      ... allocate and populate item ...\n *      DL_APPEND(list, item);\n * }\n * --------------------------------------------------\n *\n * For doubly-linked lists, the append and delete macros are O(1)\n * For singly-linked lists, append and delete are O(n) but prepend is O(1)\n * The sort macro is O(n log(n)) for all types of single/double/circular lists.\n */\n\n/* These macros use decltype or the earlier __typeof GNU extension.\n   As decltype is only available in newer compilers (VS2010 or gcc 4.3+\n   when compiling c++ source) this code uses whatever method is needed\n   or, for VS2008 where neither is available, uses casting workarounds. */\n#if !defined(LDECLTYPE) && !defined(NO_DECLTYPE)\n#if defined(_MSC_VER)   /* MS compiler */\n#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */\n#define LDECLTYPE(x) decltype(x)\n#else                   /* VS2008 or older (or VS2010 in C mode) */\n#define NO_DECLTYPE\n#endif\n#elif defined(__MCST__)  /* Elbrus C Compiler */\n#define LDECLTYPE(x) __typeof(x)\n#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)\n#define NO_DECLTYPE\n#else                   /* GNU, Sun and other compilers */\n#define LDECLTYPE(x) __typeof(x)\n#endif\n#endif\n\n/* for VS2008 we use some workarounds to get around the lack of decltype,\n * namely, we always reassign our tmp variable to the list head if we need\n * to dereference its prev/next pointers, and save/restore the real head.*/\n#ifdef NO_DECLTYPE\n#define IF_NO_DECLTYPE(x) x\n#define LDECLTYPE(x) char*\n#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }\n#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next))\n#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }\n/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */\n#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }\n#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }\n#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }\n#else\n#define IF_NO_DECLTYPE(x)\n#define UTLIST_SV(elt,list)\n#define UTLIST_NEXT(elt,list,next) ((elt)->next)\n#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to)\n/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */\n#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to)\n#define UTLIST_RS(list)\n#define UTLIST_CASTASGN(a,b) (a)=(b)\n#endif\n\n/******************************************************************************\n * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort    *\n * Unwieldy variable names used here to avoid shadowing passed-in variables.  *\n *****************************************************************************/\n#define LL_SORT(list, cmp)                                                                     \\\n    LL_SORT2(list, cmp, next)\n\n#define LL_SORT2(list, cmp, next)                                                              \\\ndo {                                                                                           \\\n  LDECLTYPE(list) _ls_p;                                                                       \\\n  LDECLTYPE(list) _ls_q;                                                                       \\\n  LDECLTYPE(list) _ls_e;                                                                       \\\n  LDECLTYPE(list) _ls_tail;                                                                    \\\n  IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;)                                                        \\\n  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \\\n  if (list) {                                                                                  \\\n    _ls_insize = 1;                                                                            \\\n    _ls_looping = 1;                                                                           \\\n    while (_ls_looping) {                                                                      \\\n      UTLIST_CASTASGN(_ls_p,list);                                                             \\\n      (list) = NULL;                                                                           \\\n      _ls_tail = NULL;                                                                         \\\n      _ls_nmerges = 0;                                                                         \\\n      while (_ls_p) {                                                                          \\\n        _ls_nmerges++;                                                                         \\\n        _ls_q = _ls_p;                                                                         \\\n        _ls_psize = 0;                                                                         \\\n        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \\\n          _ls_psize++;                                                                         \\\n          UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list);        \\\n          if (!_ls_q) break;                                                                   \\\n        }                                                                                      \\\n        _ls_qsize = _ls_insize;                                                                \\\n        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \\\n          if (_ls_psize == 0) {                                                                \\\n            _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q =                                      \\\n              UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--;                      \\\n          } else if (_ls_qsize == 0 || !_ls_q) {                                               \\\n            _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p =                                      \\\n              UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--;                      \\\n          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \\\n            _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p =                                      \\\n              UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--;                      \\\n          } else {                                                                             \\\n            _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q =                                      \\\n              UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--;                      \\\n          }                                                                                    \\\n          if (_ls_tail) {                                                                      \\\n            UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \\\n          } else {                                                                             \\\n            UTLIST_CASTASGN(list,_ls_e);                                                       \\\n          }                                                                                    \\\n          _ls_tail = _ls_e;                                                                    \\\n        }                                                                                      \\\n        _ls_p = _ls_q;                                                                         \\\n      }                                                                                        \\\n      if (_ls_tail) {                                                                          \\\n        UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list);   \\\n      }                                                                                        \\\n      if (_ls_nmerges <= 1) {                                                                  \\\n        _ls_looping=0;                                                                         \\\n      }                                                                                        \\\n      _ls_insize *= 2;                                                                         \\\n    }                                                                                          \\\n  }                                                                                            \\\n} while (0)\n\n\n#define DL_SORT(list, cmp)                                                                     \\\n    DL_SORT2(list, cmp, prev, next)\n\n#define DL_SORT2(list, cmp, prev, next)                                                        \\\ndo {                                                                                           \\\n  LDECLTYPE(list) _ls_p;                                                                       \\\n  LDECLTYPE(list) _ls_q;                                                                       \\\n  LDECLTYPE(list) _ls_e;                                                                       \\\n  LDECLTYPE(list) _ls_tail;                                                                    \\\n  IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;)                                                        \\\n  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \\\n  if (list) {                                                                                  \\\n    _ls_insize = 1;                                                                            \\\n    _ls_looping = 1;                                                                           \\\n    while (_ls_looping) {                                                                      \\\n      UTLIST_CASTASGN(_ls_p,list);                                                             \\\n      (list) = NULL;                                                                           \\\n      _ls_tail = NULL;                                                                         \\\n      _ls_nmerges = 0;                                                                         \\\n      while (_ls_p) {                                                                          \\\n        _ls_nmerges++;                                                                         \\\n        _ls_q = _ls_p;                                                                         \\\n        _ls_psize = 0;                                                                         \\\n        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \\\n          _ls_psize++;                                                                         \\\n          UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list);        \\\n          if (!_ls_q) break;                                                                   \\\n        }                                                                                      \\\n        _ls_qsize = _ls_insize;                                                                \\\n        while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) {                                \\\n          if (_ls_psize == 0) {                                                                \\\n            _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q =                                      \\\n              UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--;                      \\\n          } else if ((_ls_qsize == 0) || (!_ls_q)) {                                           \\\n            _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p =                                      \\\n              UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--;                      \\\n          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \\\n            _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p =                                      \\\n              UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--;                      \\\n          } else {                                                                             \\\n            _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q =                                      \\\n              UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--;                      \\\n          }                                                                                    \\\n          if (_ls_tail) {                                                                      \\\n            UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \\\n          } else {                                                                             \\\n            UTLIST_CASTASGN(list,_ls_e);                                                       \\\n          }                                                                                    \\\n          UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list);   \\\n          _ls_tail = _ls_e;                                                                    \\\n        }                                                                                      \\\n        _ls_p = _ls_q;                                                                         \\\n      }                                                                                        \\\n      UTLIST_CASTASGN((list)->prev, _ls_tail);                                                 \\\n      UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list);     \\\n      if (_ls_nmerges <= 1) {                                                                  \\\n        _ls_looping=0;                                                                         \\\n      }                                                                                        \\\n      _ls_insize *= 2;                                                                         \\\n    }                                                                                          \\\n  }                                                                                            \\\n} while (0)\n\n#define CDL_SORT(list, cmp)                                                                    \\\n    CDL_SORT2(list, cmp, prev, next)\n\n#define CDL_SORT2(list, cmp, prev, next)                                                       \\\ndo {                                                                                           \\\n  LDECLTYPE(list) _ls_p;                                                                       \\\n  LDECLTYPE(list) _ls_q;                                                                       \\\n  LDECLTYPE(list) _ls_e;                                                                       \\\n  LDECLTYPE(list) _ls_tail;                                                                    \\\n  LDECLTYPE(list) _ls_oldhead;                                                                 \\\n  LDECLTYPE(list) _tmp;                                                                        \\\n  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \\\n  if (list) {                                                                                  \\\n    _ls_insize = 1;                                                                            \\\n    _ls_looping = 1;                                                                           \\\n    while (_ls_looping) {                                                                      \\\n      UTLIST_CASTASGN(_ls_p,list);                                                             \\\n      UTLIST_CASTASGN(_ls_oldhead,list);                                                       \\\n      (list) = NULL;                                                                           \\\n      _ls_tail = NULL;                                                                         \\\n      _ls_nmerges = 0;                                                                         \\\n      while (_ls_p) {                                                                          \\\n        _ls_nmerges++;                                                                         \\\n        _ls_q = _ls_p;                                                                         \\\n        _ls_psize = 0;                                                                         \\\n        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \\\n          _ls_psize++;                                                                         \\\n          UTLIST_SV(_ls_q,list);                                                               \\\n          if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) {                                   \\\n            _ls_q = NULL;                                                                      \\\n          } else {                                                                             \\\n            _ls_q = UTLIST_NEXT(_ls_q,list,next);                                              \\\n          }                                                                                    \\\n          UTLIST_RS(list);                                                                     \\\n          if (!_ls_q) break;                                                                   \\\n        }                                                                                      \\\n        _ls_qsize = _ls_insize;                                                                \\\n        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \\\n          if (_ls_psize == 0) {                                                                \\\n            _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q =                                      \\\n              UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--;                      \\\n            if (_ls_q == _ls_oldhead) { _ls_q = NULL; }                                        \\\n          } else if (_ls_qsize == 0 || !_ls_q) {                                               \\\n            _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p =                                      \\\n              UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--;                      \\\n            if (_ls_p == _ls_oldhead) { _ls_p = NULL; }                                        \\\n          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \\\n            _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p =                                      \\\n              UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--;                      \\\n            if (_ls_p == _ls_oldhead) { _ls_p = NULL; }                                        \\\n          } else {                                                                             \\\n            _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q =                                      \\\n              UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--;                      \\\n            if (_ls_q == _ls_oldhead) { _ls_q = NULL; }                                        \\\n          }                                                                                    \\\n          if (_ls_tail) {                                                                      \\\n            UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \\\n          } else {                                                                             \\\n            UTLIST_CASTASGN(list,_ls_e);                                                       \\\n          }                                                                                    \\\n          UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list);   \\\n          _ls_tail = _ls_e;                                                                    \\\n        }                                                                                      \\\n        _ls_p = _ls_q;                                                                         \\\n      }                                                                                        \\\n      UTLIST_CASTASGN((list)->prev,_ls_tail);                                                  \\\n      UTLIST_CASTASGN(_tmp,list);                                                              \\\n      UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list);     \\\n      if (_ls_nmerges <= 1) {                                                                  \\\n        _ls_looping=0;                                                                         \\\n      }                                                                                        \\\n      _ls_insize *= 2;                                                                         \\\n    }                                                                                          \\\n  }                                                                                            \\\n} while (0)\n\n/******************************************************************************\n * singly linked list macros (non-circular)                                   *\n *****************************************************************************/\n#define LL_PREPEND(head,add)                                                                   \\\n    LL_PREPEND2(head,add,next)\n\n#define LL_PREPEND2(head,add,next)                                                             \\\ndo {                                                                                           \\\n  (add)->next = (head);                                                                        \\\n  (head) = (add);                                                                              \\\n} while (0)\n\n#define LL_CONCAT(head1,head2)                                                                 \\\n    LL_CONCAT2(head1,head2,next)\n\n#define LL_CONCAT2(head1,head2,next)                                                           \\\ndo {                                                                                           \\\n  LDECLTYPE(head1) _tmp;                                                                       \\\n  if (head1) {                                                                                 \\\n    _tmp = (head1);                                                                            \\\n    while (_tmp->next) { _tmp = _tmp->next; }                                                  \\\n    _tmp->next=(head2);                                                                        \\\n  } else {                                                                                     \\\n    (head1)=(head2);                                                                           \\\n  }                                                                                            \\\n} while (0)\n\n#define LL_APPEND(head,add)                                                                    \\\n    LL_APPEND2(head,add,next)\n\n#define LL_APPEND2(head,add,next)                                                              \\\ndo {                                                                                           \\\n  LDECLTYPE(head) _tmp;                                                                        \\\n  (add)->next=NULL;                                                                            \\\n  if (head) {                                                                                  \\\n    _tmp = (head);                                                                             \\\n    while (_tmp->next) { _tmp = _tmp->next; }                                                  \\\n    _tmp->next=(add);                                                                          \\\n  } else {                                                                                     \\\n    (head)=(add);                                                                              \\\n  }                                                                                            \\\n} while (0)\n\n#define LL_INSERT_INORDER(head,add,cmp)                                                        \\\n    LL_INSERT_INORDER2(head,add,cmp,next)\n\n#define LL_INSERT_INORDER2(head,add,cmp,next)                                                  \\\ndo {                                                                                           \\\n  LDECLTYPE(head) _tmp;                                                                        \\\n  if (head) {                                                                                  \\\n    LL_LOWER_BOUND2(head, _tmp, add, cmp, next);                                               \\\n    LL_APPEND_ELEM2(head, _tmp, add, next);                                                    \\\n  } else {                                                                                     \\\n    (head) = (add);                                                                            \\\n    (head)->next = NULL;                                                                       \\\n  }                                                                                            \\\n} while (0)\n\n#define LL_LOWER_BOUND(head,elt,like,cmp)                                                      \\\n    LL_LOWER_BOUND2(head,elt,like,cmp,next)\n\n#define LL_LOWER_BOUND2(head,elt,like,cmp,next)                                                \\\n  do {                                                                                         \\\n    if ((head) == NULL || (cmp(head, like)) >= 0) {                                            \\\n      (elt) = NULL;                                                                            \\\n    } else {                                                                                   \\\n      for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) {                         \\\n        if (cmp((elt)->next, like) >= 0) {                                                     \\\n          break;                                                                               \\\n        }                                                                                      \\\n      }                                                                                        \\\n    }                                                                                          \\\n  } while (0)\n\n#define LL_DELETE(head,del)                                                                    \\\n    LL_DELETE2(head,del,next)\n\n#define LL_DELETE2(head,del,next)                                                              \\\ndo {                                                                                           \\\n  LDECLTYPE(head) _tmp;                                                                        \\\n  if ((head) == (del)) {                                                                       \\\n    (head)=(head)->next;                                                                       \\\n  } else {                                                                                     \\\n    _tmp = (head);                                                                             \\\n    while (_tmp->next && (_tmp->next != (del))) {                                              \\\n      _tmp = _tmp->next;                                                                       \\\n    }                                                                                          \\\n    if (_tmp->next) {                                                                          \\\n      _tmp->next = (del)->next;                                                                \\\n    }                                                                                          \\\n  }                                                                                            \\\n} while (0)\n\n#define LL_COUNT(head,el,counter)                                                              \\\n    LL_COUNT2(head,el,counter,next)                                                            \\\n\n#define LL_COUNT2(head,el,counter,next)                                                        \\\ndo {                                                                                           \\\n  (counter) = 0;                                                                               \\\n  LL_FOREACH2(head,el,next) { ++(counter); }                                                   \\\n} while (0)\n\n#define LL_FOREACH(head,el)                                                                    \\\n    LL_FOREACH2(head,el,next)\n\n#define LL_FOREACH2(head,el,next)                                                              \\\n    for ((el) = (head); el; (el) = (el)->next)\n\n#define LL_FOREACH_SAFE(head,el,tmp)                                                           \\\n    LL_FOREACH_SAFE2(head,el,tmp,next)\n\n#define LL_FOREACH_SAFE2(head,el,tmp,next)                                                     \\\n  for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp))\n\n#define LL_SEARCH_SCALAR(head,out,field,val)                                                   \\\n    LL_SEARCH_SCALAR2(head,out,field,val,next)\n\n#define LL_SEARCH_SCALAR2(head,out,field,val,next)                                             \\\ndo {                                                                                           \\\n    LL_FOREACH2(head,out,next) {                                                               \\\n      if ((out)->field == (val)) break;                                                        \\\n    }                                                                                          \\\n} while (0)\n\n#define LL_SEARCH(head,out,elt,cmp)                                                            \\\n    LL_SEARCH2(head,out,elt,cmp,next)\n\n#define LL_SEARCH2(head,out,elt,cmp,next)                                                      \\\ndo {                                                                                           \\\n    LL_FOREACH2(head,out,next) {                                                               \\\n      if ((cmp(out,elt))==0) break;                                                            \\\n    }                                                                                          \\\n} while (0)\n\n#define LL_REPLACE_ELEM2(head, el, add, next)                                                  \\\ndo {                                                                                           \\\n LDECLTYPE(head) _tmp;                                                                         \\\n assert((head) != NULL);                                                                       \\\n assert((el) != NULL);                                                                         \\\n assert((add) != NULL);                                                                        \\\n (add)->next = (el)->next;                                                                     \\\n if ((head) == (el)) {                                                                         \\\n  (head) = (add);                                                                              \\\n } else {                                                                                      \\\n  _tmp = (head);                                                                               \\\n  while (_tmp->next && (_tmp->next != (el))) {                                                 \\\n   _tmp = _tmp->next;                                                                          \\\n  }                                                                                            \\\n  if (_tmp->next) {                                                                            \\\n    _tmp->next = (add);                                                                        \\\n  }                                                                                            \\\n }                                                                                             \\\n} while (0)\n\n#define LL_REPLACE_ELEM(head, el, add)                                                         \\\n    LL_REPLACE_ELEM2(head, el, add, next)\n\n#define LL_PREPEND_ELEM2(head, el, add, next)                                                  \\\ndo {                                                                                           \\\n if (el) {                                                                                     \\\n  LDECLTYPE(head) _tmp;                                                                        \\\n  assert((head) != NULL);                                                                      \\\n  assert((add) != NULL);                                                                       \\\n  (add)->next = (el);                                                                          \\\n  if ((head) == (el)) {                                                                        \\\n   (head) = (add);                                                                             \\\n  } else {                                                                                     \\\n   _tmp = (head);                                                                              \\\n   while (_tmp->next && (_tmp->next != (el))) {                                                \\\n    _tmp = _tmp->next;                                                                         \\\n   }                                                                                           \\\n   if (_tmp->next) {                                                                           \\\n     _tmp->next = (add);                                                                       \\\n   }                                                                                           \\\n  }                                                                                            \\\n } else {                                                                                      \\\n  LL_APPEND2(head, add, next);                                                                 \\\n }                                                                                             \\\n} while (0)                                                                                    \\\n\n#define LL_PREPEND_ELEM(head, el, add)                                                         \\\n    LL_PREPEND_ELEM2(head, el, add, next)\n\n#define LL_APPEND_ELEM2(head, el, add, next)                                                   \\\ndo {                                                                                           \\\n if (el) {                                                                                     \\\n  assert((head) != NULL);                                                                      \\\n  assert((add) != NULL);                                                                       \\\n  (add)->next = (el)->next;                                                                    \\\n  (el)->next = (add);                                                                          \\\n } else {                                                                                      \\\n  LL_PREPEND2(head, add, next);                                                                \\\n }                                                                                             \\\n} while (0)                                                                                    \\\n\n#define LL_APPEND_ELEM(head, el, add)                                                          \\\n    LL_APPEND_ELEM2(head, el, add, next)\n\n#ifdef NO_DECLTYPE\n/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */\n\n#undef LL_CONCAT2\n#define LL_CONCAT2(head1,head2,next)                                                           \\\ndo {                                                                                           \\\n  char *_tmp;                                                                                  \\\n  if (head1) {                                                                                 \\\n    _tmp = (char*)(head1);                                                                     \\\n    while ((head1)->next) { (head1) = (head1)->next; }                                         \\\n    (head1)->next = (head2);                                                                   \\\n    UTLIST_RS(head1);                                                                          \\\n  } else {                                                                                     \\\n    (head1)=(head2);                                                                           \\\n  }                                                                                            \\\n} while (0)\n\n#undef LL_APPEND2\n#define LL_APPEND2(head,add,next)                                                              \\\ndo {                                                                                           \\\n  if (head) {                                                                                  \\\n    (add)->next = head;     /* use add->next as a temp variable */                             \\\n    while ((add)->next->next) { (add)->next = (add)->next->next; }                             \\\n    (add)->next->next=(add);                                                                   \\\n  } else {                                                                                     \\\n    (head)=(add);                                                                              \\\n  }                                                                                            \\\n  (add)->next=NULL;                                                                            \\\n} while (0)\n\n#undef LL_INSERT_INORDER2\n#define LL_INSERT_INORDER2(head,add,cmp,next)                                                  \\\ndo {                                                                                           \\\n  if ((head) == NULL || (cmp(head, add)) >= 0) {                                               \\\n    (add)->next = (head);                                                                      \\\n    (head) = (add);                                                                            \\\n  } else {                                                                                     \\\n    char *_tmp = (char*)(head);                                                                \\\n    while ((head)->next != NULL && (cmp((head)->next, add)) < 0) {                             \\\n      (head) = (head)->next;                                                                   \\\n    }                                                                                          \\\n    (add)->next = (head)->next;                                                                \\\n    (head)->next = (add);                                                                      \\\n    UTLIST_RS(head);                                                                           \\\n  }                                                                                            \\\n} while (0)\n\n#undef LL_DELETE2\n#define LL_DELETE2(head,del,next)                                                              \\\ndo {                                                                                           \\\n  if ((head) == (del)) {                                                                       \\\n    (head)=(head)->next;                                                                       \\\n  } else {                                                                                     \\\n    char *_tmp = (char*)(head);                                                                \\\n    while ((head)->next && ((head)->next != (del))) {                                          \\\n      (head) = (head)->next;                                                                   \\\n    }                                                                                          \\\n    if ((head)->next) {                                                                        \\\n      (head)->next = ((del)->next);                                                            \\\n    }                                                                                          \\\n    UTLIST_RS(head);                                                                           \\\n  }                                                                                            \\\n} while (0)\n\n#undef LL_REPLACE_ELEM2\n#define LL_REPLACE_ELEM2(head, el, add, next)                                                  \\\ndo {                                                                                           \\\n  assert((head) != NULL);                                                                      \\\n  assert((el) != NULL);                                                                        \\\n  assert((add) != NULL);                                                                       \\\n  if ((head) == (el)) {                                                                        \\\n    (head) = (add);                                                                            \\\n  } else {                                                                                     \\\n    (add)->next = head;                                                                        \\\n    while ((add)->next->next && ((add)->next->next != (el))) {                                 \\\n      (add)->next = (add)->next->next;                                                         \\\n    }                                                                                          \\\n    if ((add)->next->next) {                                                                   \\\n      (add)->next->next = (add);                                                               \\\n    }                                                                                          \\\n  }                                                                                            \\\n  (add)->next = (el)->next;                                                                    \\\n} while (0)\n\n#undef LL_PREPEND_ELEM2\n#define LL_PREPEND_ELEM2(head, el, add, next)                                                  \\\ndo {                                                                                           \\\n  if (el) {                                                                                    \\\n    assert((head) != NULL);                                                                    \\\n    assert((add) != NULL);                                                                     \\\n    if ((head) == (el)) {                                                                      \\\n      (head) = (add);                                                                          \\\n    } else {                                                                                   \\\n      (add)->next = (head);                                                                    \\\n      while ((add)->next->next && ((add)->next->next != (el))) {                               \\\n        (add)->next = (add)->next->next;                                                       \\\n      }                                                                                        \\\n      if ((add)->next->next) {                                                                 \\\n        (add)->next->next = (add);                                                             \\\n      }                                                                                        \\\n    }                                                                                          \\\n    (add)->next = (el);                                                                        \\\n  } else {                                                                                     \\\n    LL_APPEND2(head, add, next);                                                               \\\n  }                                                                                            \\\n} while (0)                                                                                    \\\n\n#endif /* NO_DECLTYPE */\n\n/******************************************************************************\n * doubly linked list macros (non-circular)                                   *\n *****************************************************************************/\n#define DL_PREPEND(head,add)                                                                   \\\n    DL_PREPEND2(head,add,prev,next)\n\n#define DL_PREPEND2(head,add,prev,next)                                                        \\\ndo {                                                                                           \\\n (add)->next = (head);                                                                         \\\n if (head) {                                                                                   \\\n   (add)->prev = (head)->prev;                                                                 \\\n   (head)->prev = (add);                                                                       \\\n } else {                                                                                      \\\n   (add)->prev = (add);                                                                        \\\n }                                                                                             \\\n (head) = (add);                                                                               \\\n} while (0)\n\n#define DL_APPEND(head,add)                                                                    \\\n    DL_APPEND2(head,add,prev,next)\n\n#define DL_APPEND2(head,add,prev,next)                                                         \\\ndo {                                                                                           \\\n  if (head) {                                                                                  \\\n      (add)->prev = (head)->prev;                                                              \\\n      (head)->prev->next = (add);                                                              \\\n      (head)->prev = (add);                                                                    \\\n      (add)->next = NULL;                                                                      \\\n  } else {                                                                                     \\\n      (head)=(add);                                                                            \\\n      (head)->prev = (head);                                                                   \\\n      (head)->next = NULL;                                                                     \\\n  }                                                                                            \\\n} while (0)\n\n#define DL_INSERT_INORDER(head,add,cmp)                                                        \\\n    DL_INSERT_INORDER2(head,add,cmp,prev,next)\n\n#define DL_INSERT_INORDER2(head,add,cmp,prev,next)                                             \\\ndo {                                                                                           \\\n  LDECLTYPE(head) _tmp;                                                                        \\\n  if (head) {                                                                                  \\\n    DL_LOWER_BOUND2(head, _tmp, add, cmp, next);                                               \\\n    DL_APPEND_ELEM2(head, _tmp, add, prev, next);                                              \\\n  } else {                                                                                     \\\n    (head) = (add);                                                                            \\\n    (head)->prev = (head);                                                                     \\\n    (head)->next = NULL;                                                                       \\\n  }                                                                                            \\\n} while (0)\n\n#define DL_LOWER_BOUND(head,elt,like,cmp)                                                      \\\n    DL_LOWER_BOUND2(head,elt,like,cmp,next)\n\n#define DL_LOWER_BOUND2(head,elt,like,cmp,next)                                                \\\ndo {                                                                                           \\\n  if ((head) == NULL || (cmp(head, like)) >= 0) {                                              \\\n    (elt) = NULL;                                                                              \\\n  } else {                                                                                     \\\n    for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) {                           \\\n      if ((cmp((elt)->next, like)) >= 0) {                                                     \\\n        break;                                                                                 \\\n      }                                                                                        \\\n    }                                                                                          \\\n  }                                                                                            \\\n} while (0)\n\n#define DL_CONCAT(head1,head2)                                                                 \\\n    DL_CONCAT2(head1,head2,prev,next)\n\n#define DL_CONCAT2(head1,head2,prev,next)                                                      \\\ndo {                                                                                           \\\n  LDECLTYPE(head1) _tmp;                                                                       \\\n  if (head2) {                                                                                 \\\n    if (head1) {                                                                               \\\n        UTLIST_CASTASGN(_tmp, (head2)->prev);                                                  \\\n        (head2)->prev = (head1)->prev;                                                         \\\n        (head1)->prev->next = (head2);                                                         \\\n        UTLIST_CASTASGN((head1)->prev, _tmp);                                                  \\\n    } else {                                                                                   \\\n        (head1)=(head2);                                                                       \\\n    }                                                                                          \\\n  }                                                                                            \\\n} while (0)\n\n#define DL_DELETE(head,del)                                                                    \\\n    DL_DELETE2(head,del,prev,next)\n\n#define DL_DELETE2(head,del,prev,next)                                                         \\\ndo {                                                                                           \\\n  assert((head) != NULL);                                                                      \\\n  assert((del)->prev != NULL);                                                                 \\\n  if ((del)->prev == (del)) {                                                                  \\\n      (head)=NULL;                                                                             \\\n  } else if ((del) == (head)) {                                                                \\\n      assert((del)->next != NULL);                                                             \\\n      (del)->next->prev = (del)->prev;                                                         \\\n      (head) = (del)->next;                                                                    \\\n  } else {                                                                                     \\\n      (del)->prev->next = (del)->next;                                                         \\\n      if ((del)->next) {                                                                       \\\n          (del)->next->prev = (del)->prev;                                                     \\\n      } else {                                                                                 \\\n          (head)->prev = (del)->prev;                                                          \\\n      }                                                                                        \\\n  }                                                                                            \\\n} while (0)\n\n#define DL_COUNT(head,el,counter)                                                              \\\n    DL_COUNT2(head,el,counter,next)                                                            \\\n\n#define DL_COUNT2(head,el,counter,next)                                                        \\\ndo {                                                                                           \\\n  (counter) = 0;                                                                               \\\n  DL_FOREACH2(head,el,next) { ++(counter); }                                                   \\\n} while (0)\n\n#define DL_FOREACH(head,el)                                                                    \\\n    DL_FOREACH2(head,el,next)\n\n#define DL_FOREACH2(head,el,next)                                                              \\\n    for ((el) = (head); el; (el) = (el)->next)\n\n/* this version is safe for deleting the elements during iteration */\n#define DL_FOREACH_SAFE(head,el,tmp)                                                           \\\n    DL_FOREACH_SAFE2(head,el,tmp,next)\n\n#define DL_FOREACH_SAFE2(head,el,tmp,next)                                                     \\\n  for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp))\n\n/* these are identical to their singly-linked list counterparts */\n#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR\n#define DL_SEARCH LL_SEARCH\n#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2\n#define DL_SEARCH2 LL_SEARCH2\n\n#define DL_REPLACE_ELEM2(head, el, add, prev, next)                                            \\\ndo {                                                                                           \\\n assert((head) != NULL);                                                                       \\\n assert((el) != NULL);                                                                         \\\n assert((add) != NULL);                                                                        \\\n if ((head) == (el)) {                                                                         \\\n  (head) = (add);                                                                              \\\n  (add)->next = (el)->next;                                                                    \\\n  if ((el)->next == NULL) {                                                                    \\\n   (add)->prev = (add);                                                                        \\\n  } else {                                                                                     \\\n   (add)->prev = (el)->prev;                                                                   \\\n   (add)->next->prev = (add);                                                                  \\\n  }                                                                                            \\\n } else {                                                                                      \\\n  (add)->next = (el)->next;                                                                    \\\n  (add)->prev = (el)->prev;                                                                    \\\n  (add)->prev->next = (add);                                                                   \\\n  if ((el)->next == NULL) {                                                                    \\\n   (head)->prev = (add);                                                                       \\\n  } else {                                                                                     \\\n   (add)->next->prev = (add);                                                                  \\\n  }                                                                                            \\\n }                                                                                             \\\n} while (0)\n\n#define DL_REPLACE_ELEM(head, el, add)                                                         \\\n    DL_REPLACE_ELEM2(head, el, add, prev, next)\n\n#define DL_PREPEND_ELEM2(head, el, add, prev, next)                                            \\\ndo {                                                                                           \\\n if (el) {                                                                                     \\\n  assert((head) != NULL);                                                                      \\\n  assert((add) != NULL);                                                                       \\\n  (add)->next = (el);                                                                          \\\n  (add)->prev = (el)->prev;                                                                    \\\n  (el)->prev = (add);                                                                          \\\n  if ((head) == (el)) {                                                                        \\\n   (head) = (add);                                                                             \\\n  } else {                                                                                     \\\n   (add)->prev->next = (add);                                                                  \\\n  }                                                                                            \\\n } else {                                                                                      \\\n  DL_APPEND2(head, add, prev, next);                                                           \\\n }                                                                                             \\\n} while (0)                                                                                    \\\n\n#define DL_PREPEND_ELEM(head, el, add)                                                         \\\n    DL_PREPEND_ELEM2(head, el, add, prev, next)\n\n#define DL_APPEND_ELEM2(head, el, add, prev, next)                                             \\\ndo {                                                                                           \\\n if (el) {                                                                                     \\\n  assert((head) != NULL);                                                                      \\\n  assert((add) != NULL);                                                                       \\\n  (add)->next = (el)->next;                                                                    \\\n  (add)->prev = (el);                                                                          \\\n  (el)->next = (add);                                                                          \\\n  if ((add)->next) {                                                                           \\\n   (add)->next->prev = (add);                                                                  \\\n  } else {                                                                                     \\\n   (head)->prev = (add);                                                                       \\\n  }                                                                                            \\\n } else {                                                                                      \\\n  DL_PREPEND2(head, add, prev, next);                                                          \\\n }                                                                                             \\\n} while (0)                                                                                    \\\n\n#define DL_APPEND_ELEM(head, el, add)                                                          \\\n   DL_APPEND_ELEM2(head, el, add, prev, next)\n\n#ifdef NO_DECLTYPE\n/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */\n\n#undef DL_INSERT_INORDER2\n#define DL_INSERT_INORDER2(head,add,cmp,prev,next)                                             \\\ndo {                                                                                           \\\n  if ((head) == NULL) {                                                                        \\\n    (add)->prev = (add);                                                                       \\\n    (add)->next = NULL;                                                                        \\\n    (head) = (add);                                                                            \\\n  } else if ((cmp(head, add)) >= 0) {                                                          \\\n    (add)->prev = (head)->prev;                                                                \\\n    (add)->next = (head);                                                                      \\\n    (head)->prev = (add);                                                                      \\\n    (head) = (add);                                                                            \\\n  } else {                                                                                     \\\n    char *_tmp = (char*)(head);                                                                \\\n    while ((head)->next && (cmp((head)->next, add)) < 0) {                                     \\\n      (head) = (head)->next;                                                                   \\\n    }                                                                                          \\\n    (add)->prev = (head);                                                                      \\\n    (add)->next = (head)->next;                                                                \\\n    (head)->next = (add);                                                                      \\\n    UTLIST_RS(head);                                                                           \\\n    if ((add)->next) {                                                                         \\\n      (add)->next->prev = (add);                                                               \\\n    } else {                                                                                   \\\n      (head)->prev = (add);                                                                    \\\n    }                                                                                          \\\n  }                                                                                            \\\n} while (0)\n#endif /* NO_DECLTYPE */\n\n/******************************************************************************\n * circular doubly linked list macros                                         *\n *****************************************************************************/\n#define CDL_APPEND(head,add)                                                                   \\\n    CDL_APPEND2(head,add,prev,next)\n\n#define CDL_APPEND2(head,add,prev,next)                                                        \\\ndo {                                                                                           \\\n if (head) {                                                                                   \\\n   (add)->prev = (head)->prev;                                                                 \\\n   (add)->next = (head);                                                                       \\\n   (head)->prev = (add);                                                                       \\\n   (add)->prev->next = (add);                                                                  \\\n } else {                                                                                      \\\n   (add)->prev = (add);                                                                        \\\n   (add)->next = (add);                                                                        \\\n   (head) = (add);                                                                             \\\n }                                                                                             \\\n} while (0)\n\n#define CDL_PREPEND(head,add)                                                                  \\\n    CDL_PREPEND2(head,add,prev,next)\n\n#define CDL_PREPEND2(head,add,prev,next)                                                       \\\ndo {                                                                                           \\\n if (head) {                                                                                   \\\n   (add)->prev = (head)->prev;                                                                 \\\n   (add)->next = (head);                                                                       \\\n   (head)->prev = (add);                                                                       \\\n   (add)->prev->next = (add);                                                                  \\\n } else {                                                                                      \\\n   (add)->prev = (add);                                                                        \\\n   (add)->next = (add);                                                                        \\\n }                                                                                             \\\n (head) = (add);                                                                               \\\n} while (0)\n\n#define CDL_INSERT_INORDER(head,add,cmp)                                                       \\\n    CDL_INSERT_INORDER2(head,add,cmp,prev,next)\n\n#define CDL_INSERT_INORDER2(head,add,cmp,prev,next)                                            \\\ndo {                                                                                           \\\n  LDECLTYPE(head) _tmp;                                                                        \\\n  if (head) {                                                                                  \\\n    CDL_LOWER_BOUND2(head, _tmp, add, cmp, next);                                              \\\n    CDL_APPEND_ELEM2(head, _tmp, add, prev, next);                                             \\\n  } else {                                                                                     \\\n    (head) = (add);                                                                            \\\n    (head)->next = (head);                                                                     \\\n    (head)->prev = (head);                                                                     \\\n  }                                                                                            \\\n} while (0)\n\n#define CDL_LOWER_BOUND(head,elt,like,cmp)                                                     \\\n    CDL_LOWER_BOUND2(head,elt,like,cmp,next)\n\n#define CDL_LOWER_BOUND2(head,elt,like,cmp,next)                                               \\\ndo {                                                                                           \\\n  if ((head) == NULL || (cmp(head, like)) >= 0) {                                              \\\n    (elt) = NULL;                                                                              \\\n  } else {                                                                                     \\\n    for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) {                         \\\n      if ((cmp((elt)->next, like)) >= 0) {                                                     \\\n        break;                                                                                 \\\n      }                                                                                        \\\n    }                                                                                          \\\n  }                                                                                            \\\n} while (0)\n\n#define CDL_DELETE(head,del)                                                                   \\\n    CDL_DELETE2(head,del,prev,next)\n\n#define CDL_DELETE2(head,del,prev,next)                                                        \\\ndo {                                                                                           \\\n  if (((head)==(del)) && ((head)->next == (head))) {                                           \\\n      (head) = NULL;                                                                           \\\n  } else {                                                                                     \\\n     (del)->next->prev = (del)->prev;                                                          \\\n     (del)->prev->next = (del)->next;                                                          \\\n     if ((del) == (head)) (head)=(del)->next;                                                  \\\n  }                                                                                            \\\n} while (0)\n\n#define CDL_COUNT(head,el,counter)                                                             \\\n    CDL_COUNT2(head,el,counter,next)                                                           \\\n\n#define CDL_COUNT2(head, el, counter,next)                                                     \\\ndo {                                                                                           \\\n  (counter) = 0;                                                                               \\\n  CDL_FOREACH2(head,el,next) { ++(counter); }                                                  \\\n} while (0)\n\n#define CDL_FOREACH(head,el)                                                                   \\\n    CDL_FOREACH2(head,el,next)\n\n#define CDL_FOREACH2(head,el,next)                                                             \\\n    for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next))\n\n#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2)                                                    \\\n    CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)\n\n#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)                                         \\\n  for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL;                                   \\\n       (el) && ((tmp2) = (el)->next, 1);                                                       \\\n       (el) = ((el) == (tmp1) ? NULL : (tmp2)))\n\n#define CDL_SEARCH_SCALAR(head,out,field,val)                                                  \\\n    CDL_SEARCH_SCALAR2(head,out,field,val,next)\n\n#define CDL_SEARCH_SCALAR2(head,out,field,val,next)                                            \\\ndo {                                                                                           \\\n    CDL_FOREACH2(head,out,next) {                                                              \\\n      if ((out)->field == (val)) break;                                                        \\\n    }                                                                                          \\\n} while (0)\n\n#define CDL_SEARCH(head,out,elt,cmp)                                                           \\\n    CDL_SEARCH2(head,out,elt,cmp,next)\n\n#define CDL_SEARCH2(head,out,elt,cmp,next)                                                     \\\ndo {                                                                                           \\\n    CDL_FOREACH2(head,out,next) {                                                              \\\n      if ((cmp(out,elt))==0) break;                                                            \\\n    }                                                                                          \\\n} while (0)\n\n#define CDL_REPLACE_ELEM2(head, el, add, prev, next)                                           \\\ndo {                                                                                           \\\n assert((head) != NULL);                                                                       \\\n assert((el) != NULL);                                                                         \\\n assert((add) != NULL);                                                                        \\\n if ((el)->next == (el)) {                                                                     \\\n  (add)->next = (add);                                                                         \\\n  (add)->prev = (add);                                                                         \\\n  (head) = (add);                                                                              \\\n } else {                                                                                      \\\n  (add)->next = (el)->next;                                                                    \\\n  (add)->prev = (el)->prev;                                                                    \\\n  (add)->next->prev = (add);                                                                   \\\n  (add)->prev->next = (add);                                                                   \\\n  if ((head) == (el)) {                                                                        \\\n   (head) = (add);                                                                             \\\n  }                                                                                            \\\n }                                                                                             \\\n} while (0)\n\n#define CDL_REPLACE_ELEM(head, el, add)                                                        \\\n    CDL_REPLACE_ELEM2(head, el, add, prev, next)\n\n#define CDL_PREPEND_ELEM2(head, el, add, prev, next)                                           \\\ndo {                                                                                           \\\n  if (el) {                                                                                    \\\n    assert((head) != NULL);                                                                    \\\n    assert((add) != NULL);                                                                     \\\n    (add)->next = (el);                                                                        \\\n    (add)->prev = (el)->prev;                                                                  \\\n    (el)->prev = (add);                                                                        \\\n    (add)->prev->next = (add);                                                                 \\\n    if ((head) == (el)) {                                                                      \\\n      (head) = (add);                                                                          \\\n    }                                                                                          \\\n  } else {                                                                                     \\\n    CDL_APPEND2(head, add, prev, next);                                                        \\\n  }                                                                                            \\\n} while (0)\n\n#define CDL_PREPEND_ELEM(head, el, add)                                                        \\\n    CDL_PREPEND_ELEM2(head, el, add, prev, next)\n\n#define CDL_APPEND_ELEM2(head, el, add, prev, next)                                            \\\ndo {                                                                                           \\\n if (el) {                                                                                     \\\n  assert((head) != NULL);                                                                      \\\n  assert((add) != NULL);                                                                       \\\n  (add)->next = (el)->next;                                                                    \\\n  (add)->prev = (el);                                                                          \\\n  (el)->next = (add);                                                                          \\\n  (add)->next->prev = (add);                                                                   \\\n } else {                                                                                      \\\n  CDL_PREPEND2(head, add, prev, next);                                                         \\\n }                                                                                             \\\n} while (0)\n\n#define CDL_APPEND_ELEM(head, el, add)                                                         \\\n    CDL_APPEND_ELEM2(head, el, add, prev, next)\n\n#ifdef NO_DECLTYPE\n/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */\n\n#undef CDL_INSERT_INORDER2\n#define CDL_INSERT_INORDER2(head,add,cmp,prev,next)                                            \\\ndo {                                                                                           \\\n  if ((head) == NULL) {                                                                        \\\n    (add)->prev = (add);                                                                       \\\n    (add)->next = (add);                                                                       \\\n    (head) = (add);                                                                            \\\n  } else if ((cmp(head, add)) >= 0) {                                                          \\\n    (add)->prev = (head)->prev;                                                                \\\n    (add)->next = (head);                                                                      \\\n    (add)->prev->next = (add);                                                                 \\\n    (head)->prev = (add);                                                                      \\\n    (head) = (add);                                                                            \\\n  } else {                                                                                     \\\n    char *_tmp = (char*)(head);                                                                \\\n    while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) {                      \\\n      (head) = (head)->next;                                                                   \\\n    }                                                                                          \\\n    (add)->prev = (head);                                                                      \\\n    (add)->next = (head)->next;                                                                \\\n    (add)->next->prev = (add);                                                                 \\\n    (head)->next = (add);                                                                      \\\n    UTLIST_RS(head);                                                                           \\\n  }                                                                                            \\\n} while (0)\n#endif /* NO_DECLTYPE */\n\n#endif /* UTLIST_H */\n"
  },
  {
    "path": "doc/historical/old-regex.txt",
    "content": "This is the description of the regex used previously for topic/subscription\nmatching. It is reproduced here for posterity.\n\nWhen a message is ready to be published at the broker, we need to check all\nof the subscriptions to see which ones the message should be sent to. This\nwould be easy without wildcards, but requires a bit more work with them.\n\nThe regex used to do the matching is of the form below for a topic of a/b/c:\n\n^(?:(?:(a|\\+)(?!$))(?:(?:/(?:(b|\\+)(?!$)))(?:(?:/(?:c|\\+))|/#)?|/#)?|#)$\n\nIn general, we're matching (a or +) followed by (the next levels of\nhierarchy or #).\nMore specifically, all the levels of hierarchy must match, unless the last\nlevel is #.\n\n^(?:\t\t\t\t\t\t\t# Must start at beginning of string\n\t\t(?:\t\t\t\t\t\t# (Level 1 hierarchy)\n\t\t\t(a|\\+)(?!$) \t\t# Match a or +, but only if not EOL.\n\t\t)\t\t\t\t\t\t# AND\n\t\t(?:\n\t\t\t(?:\t\t\t\t\t# (Level 2 hierarchy)\n\t\t\t\t/\t\t\t\t# Match /\n\t\t\t\t(?:\t\t\t\t# AND\n\t\t\t\t\t(b|\\+)(?!$)\t# Match b or +, but only if not EOL.\n\t\t\t\t)\n\t\t\t)\t\t\t\t\t# AND\n\t\t\t(?:\n\t\t\t\t(?:\t\t\t\t# (Level 3 hierarchy)\n\t\t\t\t\t/\t\t\t# Match /\n\t\t\t\t\t(?:\t\t\t# AND\n\t\t\t\t\t\tc|\\+\t# Match c or +.\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t|\t\t\t\t# OR (instead of level 3)\n\t\t\t\t/#\t\t\t\t# Match /# at level 3\n\t\t\t)?\t\t\t\t\t# Level 3 exist 1/0 times\n\t\t\t|\t\t\t\t\t# OR (instead of level 2)\n\t\t\t/#\t\t\t\t\t# Match /# at level 2\n\t\t)?\t\t\t\t\t\t# Level 2 exist 1/0 times\n\t\t|\t\t\t\t\t\t# OR (instead of level 1)\n\t\t#\t\t\t\t\t\t# Match # at level 1\n\t)$\t\t\t\t\t\t\t# Must end on EOL.\n"
  },
  {
    "path": "doc/historical/topic-match.kds",
    "content": "S'^(?:(?:(a|\\\\+)(?!$))(?:(?:/(?:(b|\\\\+)(?!$)))(?:(?:/(?:c|\\\\+))|/#)?|/#)?|#)$'\np1\n.S'a/#\\na/b/c\\na/b/+\\na/b\\na/+\\n+\\n+/b\\n+/+/+\\n+/b/c\\na/c'\np2\n.I8\n.S''\n."
  },
  {
    "path": "doc/joss-paper/codemeta.json",
    "content": "{\n  \"@context\": \"https://raw.githubusercontent.com/codemeta/codemeta/master/codemeta.jsonld\",\n  \"@type\": \"Code\",\n  \"author\": [\n    {\n      \"@id\": \"http://orcid.org/0000-0001-9218-7797\",\n      \"@type\": \"Person\",\n      \"email\": \"\",\n      \"name\": \"Roger A. Light\",\n      \"affiliation\": \"\"\n    }\n  ],\n  \"identifier\": \"\",\n  \"codeRepository\": \"https://github.com/eclipse/mosquitto\",\n  \"datePublished\": \"2017-05-17\",\n  \"dateModified\": \"2017-05-17\",\n  \"dateCreated\": \"2017-05-17\",\n  \"description\": \"Broker and client implementation of the MQTT protocol.\",\n  \"keywords\": \"IoT, MQTT, messaging, pubsub\",\n  \"license\": \"EPL 2.0 / EDL 2.0\",\n  \"title\": \"Mosquitto\",\n  \"version\": \"v1.4.11\"\n}"
  },
  {
    "path": "doc/joss-paper/paper.bib",
    "content": "@inproceedings{Schulz_2014,\n\ttitle = {Real-time animation of equipment in a remote laboratory},\n\tdoi = {10.1109/REV.2014.6784247},\n\tbooktitle = {2014 11th {International} {Conference} on {Remote} {Engineering} and {Virtual} {Instrumentation} ({REV})},\n\tauthor = {Schulz, M. and Chen, F. and Payne, L.},\n\tmonth = feb,\n\tyear = {2014},\n\tkeywords = {Animation, cameras, client-server systems, computer aided instruction, computer animation, Computer architecture, data path, data streaming, Electron tubes, Engines, equipment animation, Hardware, message server, MIT iLabs shared architecture, MQTT, real-time animation, real-time systems, remote laboratory, servers, software architecture, student experiments, webcam},\n\tpages = {172--176}\n}\n\n@inproceedings{Antonic_2015,\n\ttitle = {Comparison of the {CUPUS} middleware and {MQTT} protocol for smart city services},\n\tdoi = {10.1109/ConTEL.2015.7231225},\n\tbooktitle = {2015 13th {International} {Conference} on {Telecommunications} ({Con}TEL)},\n\tauthor = {Antonić, A. and Marjanović, M. and Skočir, P. and Žarko, I. P.},\n\tmonth = jul,\n\tyear = {2015},\n\tkeywords = {cloud-based publish-subscribe middleware, cloud computing, CUPUS middleware, Engines, FP7 project OpenIoT platform, Internet of Things, IoT services, message passing, message queue telemetry transport protocol, message queuing solutions, middleware, Mobile communication, mobile computing, mobile devices, Mobile handsets, MQTT protocol, open-source cloud platform, Protocols, public domain software, sensors, smart cities, smart city services, telemetry, transport protocols, wearable sensors, wireless sensor networks, WSNs},\n\tpages = {1--8}\n}\n\n@inproceedings{Thangavel_2014,\n\ttitle = {Performance evaluation of {MQTT} and {CoAP} via a common middleware},\n\tdoi = {10.1109/ISSNIP.2014.6827678},\n\tbooktitle = {2014 {IEEE} {Ninth} {International} {Conference} on {Intelligent} {Sensors}, {Sensor} {Networks} and {Information} {Processing} ({ISSNIP})},\n\tauthor = {Thangavel, D. and Ma, X. and Valera, A. and Tan, H. X. and Tan, C. K. Y.},\n\tmonth = apr,\n\tyear = {2014},\n\tkeywords = {bandwidth consumption, CoAP, constrained application protocol, data transmission, delays, end-to-end delay, gateways, internetworking, Logic gates, message queue telemetry transport, middleware, MQTT, Packet loss, Protocols, Quality of service, queueing theory, sensor nodes, servers, wireless sensor networks},\n\tpages = {1--6}\n}\n\n@inproceedings{Kang_2017,\n\ttitle = {Room {Temperature} {Control} and {Fire} {Alarm} \\#x002F;{Suppression} {IoT} {Service} {Using} {MQTT} on {AWS}},\n\tdoi = {10.1109/PlatCon.2017.7883724},\n\tabstract = {In this paper we build an MQTT(Message Queue Telemetry Transportation) broker on Amazon Web Service(AWS). The MQTT broker has been utilized as a platform to provide the Internet of Things(IoT) services which monitor and control room temperatures, and sense, alarm, and suppress fire. Arduino was used as the IoT end device connecting sensors and actuators to the platform via Wi-Fi channel. We created smart home scenario and designed IoT massages satisfying the scenario requirement. We also implemented the smart some system in hardware and software, and verified the system operation. We show that MQTT and AWS are good technical candidates for small IoT business applications.},\n\tbooktitle = {2017 {International} {Conference} on {Platform} {Technology} and {Service} ({PlatCon})},\n\tauthor = {Kang, D. H. and Park, M. S. and Kim, H. S. and Kim, D. y and Kim, S. H. and Son, H. J. and Lee, S. G.},\n\tmonth = feb,\n\tyear = {2017},\n\tnote = {00000},\n\tkeywords = {Actuators, Amazon web service, AWS, electronic messaging, fire alarm-suppression IoT service, fires, Internet of Things, Internet of Things services, message queue telemetry transportation, MQTT broker, queueing theory, room temperature control, room temperature monitoring, sensors, small IoT business applications, smart home scenario, smart some system, telemetry, temperature 293 K to 298 K, Web services, Wi-Fi channel, wireless LAN},\n\tpages = {1--5}\n}\n\n@inproceedings{Fremantle_2014,\n\ttitle = {Federated {Identity} and {Access} {Management} for the {Internet} of {Things}},\n\tdoi = {10.1109/SIoT.2014.8},\n\tabstract = {We examine the use of Federated Identity and Access Management (FIAM) approaches for the Internet of Things (IoT). We look at specific challenges that devices, sensors and actuators have, and look for approaches to address them. OAuth is a widely deployed protocol – built on top of HTTP – for applying FIAM to Web systems. We explore the use of OAuth for IoT systems that instead use the lightweight MQTT 3.1 protocol. In order to evaluate this area, we built a prototype that uses OAuth 2.0 to enable access control to information distributed via MQTT. We evaluate the results of this prototyping activity, and assess the strengths and weaknesses of this approach, and the benefits of using the FIAM approaches with IoT and Machine to Machine (M2M) scenarios. Finally we outline areas for further research.},\n\tbooktitle = {2014 {International} {Workshop} on {Secure} {Internet} of {Things}},\n\tauthor = {Fremantle, P. and Aziz, B. and Kopecký, J. and Scott, P.},\n\tmonth = sep,\n\tyear = {2014},\n\tnote = {00026},\n\tkeywords = {access control, Authentication, authorisation, Authorization, federated identity and access management, FIAM, Hip, Internet of Things, IoT, M2M scenarios, machine to machine scenarios, MQTT 3.1 protocol, OAuth 2.0, Protocols, servers},\n\tpages = {10--17}\n}\n\n@article{Bellavista_2017,\n\ttitle = {The {PeRvasive} {Environment} {Sensing} and {Sharing} {Solution}},\n\tvolume = {9},\n\tcopyright = {http://creativecommons.org/licenses/by/3.0/},\n\turl = {http://www.mdpi.com/2071-1050/9/4/585},\n\tdoi = {10.3390/su9040585},\n\tabstract = {to stimulate better user behavior and improve environmental and economic sustainability, it is of paramount importance to make citizens effectively aware of the quality of the environment in which they live every day. in particular, we claim that users could significantly benefit from cost-effective efficient internet-of-things (iot) solutions that provide them with up-to-date live information about air pollution in the areas where they live, suitably adapted to different situations and with different levels of dynamically selected granularities (e.g., at home/district/city levels). our pervasive environment sensing and sharing (press) project has the ambition of increasing users’ awareness of the natural environment they live in, as a first step towards improved sustainability; the primary target is the efficient provisioning of real-time user-centric information about environmental conditions in the surroundings, and in particular about air pollution. to this purpose, we have designed, implemented, and thoroughly evaluated the press framework, which is capable of achieving good flexibility and scalability while integrating heterogeneous monitoring data, ranging from sensed air pollution to user-provided quality perceptions. among the elements of technical originality, press exploits extended kura iot gateways with novel congestion detection and recovery mechanisms that allow us to optimize bandwidth allocation between in-the-field press components and the cloud. the reported performance results show the feasibility of the proposed solution, by pointing out not only the scalability and efficiency of the adopted message-based solution that uses message queue telemetry transport (mqtt) and websockets, but also the capability of press to quickly identify and manage traffic congestions, thus, ensuring good quality levels to final users.},\n\tlanguage = {en},\n\tnumber = {4},\n\turldate = {2017-05-17},\n\tjournal = {sustainability},\n\tauthor = {Bellavista, Paolo and Giannelli, Carlo and Zamagna, Riccardo},\n\tmonth = apr,\n\tyear = {2017},\n\tnote = {00000},\n\tkeywords = {dynamic extensibility, environmental monitoring, heterogeneous monitoring data, mqtt, scalability, traffic congestion management, websockets},\n\tpages = {585}\n}\n"
  },
  {
    "path": "doc/joss-paper/paper.md",
    "content": "---\ntitle: 'Mosquitto: server and client implementation of the MQTT protocol'\ntags:\n  - Internet of Things\n  - MQTT\n  - Pubsub\n  - Messaging\nauthors:\n  - name: Roger A Light\n    orcid: 0000-0001-9218-7797\ndate: 17 May 2017\nbibliography: paper.bib\n---\n\n# Summary\n\nMosquitto provides standards compliant server and client implementations of the\n[MQTT](http://mqtt.org/) messaging protocol. MQTT uses a publish/subscribe\nmodel, has low network overhead and can be implemented on low power devices\nsuch microcontrollers that might be used in remote Internet of Things sensors.\nAs such, Mosquitto is intended for use in all situations where there is a need\nfor lightweight messaging, particularly on constrained devices with limited\nresources.\n\nThe Mosquitto project is a member of the [Eclipse Foundation](http://eclipse.org/)\n\nThere are three parts to the project.\n\n* The main `mosquitto` server\n* The `mosquitto_pub` and `mosquitto_sub` client utilities that are one method of communicating with an MQTT server\n* An MQTT client library written in C, with a C++ wrapper\n\nMosquitto allows research directly related to the MQTT protocol itself, such as\ncomparing the performance of MQTT and the Constrained Application Protocol\n(CoAP) [@Thangavel_2014] or investigating the use of OAuth in MQTT\n[@Fremantle_2014].  Mosquitto supports other research activities as a useful\nblock for building larger systems and has been used to evaluate MQTT for use in\nSmart City Services [@Antonic_2015], and in the development of an environmental\nmonitoring system [@Bellavista_2017]. Mosquitto has also been used to support\nresearch less directly as part of a scheme for remote control of an experiment\n[@Schulz_2014].\n\nOutside of academia, Mosquitto is used in other open source projects such as\nthe [openHAB](http://www.openhab.org/) home automation project and\n[OwnTracks](http://owntracks.org/), the personal location tracking project, and\nhas been integrated into commercial products.\n\n# References\n\n"
  },
  {
    "path": "docker/1.6-openssl/Dockerfile",
    "content": "FROM alpine:3.23\n\nENV VERSION=1.6.15 \\\n    DOWNLOAD_SHA256=5ff2271512f745bf1a451072cd3768a5daed71e90c5179fae12b049d6c02aa0f \\\n    GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7\n\nLABEL \\\n    org.opencontainers.image.authors=\"Roger Light <roger@atchoo.org>\" \\\n    org.opencontainers.image.title=\"eclipse-mosquitto\" \\\n    org.opencontainers.image.description=\"Eclipse Mosquitto MQTT Broker\" \\\n    org.opencontainers.image.url=\"https://mosquitto.org/\" \\\n    org.opencontainers.image.documentation=\"https://mosquitto.org/documentation/\" \\\n    org.opencontainers.image.source=\"https://github.com/eclipse-mosquitto/mosquitto\" \\\n    org.opencontainers.image.licenses=\"EPL-2.0 OR BSD-3-Clause\" \\\n    org.opencontainers.image.version=${VERSION}\n\nRUN set -x && \\\n    apk --no-cache add --virtual build-deps \\\n        build-base \\\n        cmake \\\n        gnupg \\\n        libwebsockets-dev \\\n        linux-headers \\\n        openssl-dev \\\n        util-linux-dev && \\\n    wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \\\n    echo \"$DOWNLOAD_SHA256  /tmp/mosq.tar.gz\" | sha256sum -c - && \\\n    wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \\\n    export GNUPGHOME=\"$(mktemp -d)\" && \\\n    found=''; \\\n    for server in \\\n        hkps://keys.openpgp.org \\\n        hkp://keyserver.ubuntu.com:80 \\\n        pgp.mit.edu \\\n    ; do \\\n        echo \"Fetching GPG key $GPG_KEYS from $server\"; \\\n        gpg --keyserver \"$server\" --keyserver-options timeout=10 --recv-keys \"$GPG_KEYS\" && found=yes && break; \\\n    done; \\\n    test -z \"$found\" && echo >&2 \"error: failed to fetch GPG key $GPG_KEYS\" && exit 1; \\\n    gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \\\n    gpgconf --kill all && \\\n    rm -rf \"$GNUPGHOME\" /tmp/mosq.tar.gz.asc && \\\n    mkdir -p /build/mosq && \\\n    tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \\\n    rm /tmp/mosq.tar.gz && \\\n    make -C /build/mosq -j \"$(nproc)\" \\\n        CFLAGS=\"-Wall -O2\" \\\n        WITH_ADNS=no \\\n        WITH_DOCS=no \\\n        WITH_SHARED_LIBRARIES=yes \\\n        WITH_SRV=no \\\n        WITH_STRIP=yes \\\n        WITH_WEBSOCKETS=yes \\\n        prefix=/usr \\\n        binary && \\\n    addgroup -S -g 1883 mosquitto 2>/dev/null && \\\n    adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \\\n    mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \\\n    install -d /usr/sbin/ && \\\n    install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \\\n    install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \\\n    install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \\\n    install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \\\n    install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \\\n    install -s -m755 /build/mosq/src/mosquitto_passwd /usr/bin/mosquitto_passwd && \\\n    install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \\\n    install -Dm644 /build/mosq/epl-v10 /usr/share/licenses/mosquitto/epl-v10 && \\\n    install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \\\n    chown -R mosquitto:mosquitto /mosquitto && \\\n    apk --no-cache add \\\n        ca-certificates \\\n        libwebsockets \\\n        tzdata && \\\n    apk del build-deps && \\\n    rm -rf /build\n\nVOLUME [\"/mosquitto/data\", \"/mosquitto/log\"]\n\n# Set up the entry point script and default command\nCOPY docker-entrypoint.sh /\nEXPOSE 1883\nENTRYPOINT [\"/docker-entrypoint.sh\"]\nCMD [\"/usr/sbin/mosquitto\", \"-c\", \"/mosquitto/config/mosquitto.conf\"]\n"
  },
  {
    "path": "docker/1.6-openssl/README.md",
    "content": "# Eclipse Mosquitto Docker Image\nContainers built with this Dockerfile build as source from published tarballs.\n\n## Mount Points\nA docker mount point has been created in the image to be used for configuration.\n```\n/mosquitto/config\n```\n\nTwo docker volumes have been created in the image to be used for persistent storage and logs.\n```\n/mosquitto/data\n/mosquitto/log\n```\n\n## User/Group\n\nThe image runs mosquitto under the mosquitto user and group, which are created\nwith a uid and gid of 1883.\n\n## Configuration\nWhen creating a container from the image, the default configuration values are used.\nTo use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf`\n```\ndocker run -it -p 1883:1883 -v <absolute-path-to-configuration-file>:/mosquitto/config/mosquitto.conf eclipse-mosquitto:<version>\n```\n\n:boom: if the mosquitto configuration (mosquitto.conf) was modified\nto use non-default ports, the docker run command will need to be updated\nto expose the ports that have been configured, for example:\n\n```\ndocker run -it -p 1883:1883 -p 8080:8080 -v <absolute-path-to-configuration-file>:/mosquitto/config/mosquitto.conf eclipse-mosquitto:<version>\n```\n\nConfiguration can be changed to:\n\n* persist data to `/mosquitto/data`\n* log to `/mosquitto/log/mosquitto.log`\n\ni.e. add the following to `mosquitto.conf`:\n```\npersistence true\npersistence_location /mosquitto/data/\n\nlog_dest file /mosquitto/log/mosquitto.log\n```\n\n**Note**: For any volume used, the data will be persistent between containers.\n"
  },
  {
    "path": "docker/1.6-openssl/docker-entrypoint.sh",
    "content": "#!/bin/ash\nset -e\n\n# Set permissions\nuser=\"$(id -u)\"\nif [ \"$user\" = '0' ]; then\n\t[ -d \"/mosquitto\" ] && chown -R mosquitto:mosquitto /mosquitto || true\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "docker/2.0-openssl/Dockerfile",
    "content": "FROM alpine:3.23\n\nENV VERSION=2.0.22 \\\n    DOWNLOAD_SHA256=2f752589ef7db40260b633fbdb536e9a04b446a315138d64a7ff3c14e2de6b68 \\\n    GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7\n\nLABEL \\\n    org.opencontainers.image.authors=\"Roger Light <roger@atchoo.org>\" \\\n    org.opencontainers.image.title=\"eclipse-mosquitto\" \\\n    org.opencontainers.image.description=\"Eclipse Mosquitto MQTT Broker\" \\\n    org.opencontainers.image.url=\"https://mosquitto.org/\" \\\n    org.opencontainers.image.documentation=\"https://mosquitto.org/documentation/\" \\\n    org.opencontainers.image.source=\"https://github.com/eclipse-mosquitto/mosquitto\" \\\n    org.opencontainers.image.licenses=\"EPL-2.0 OR BSD-3-Clause\" \\\n    org.opencontainers.image.version=${VERSION}\n\nRUN set -x && \\\n    apk --no-cache add --virtual build-deps \\\n        build-base \\\n        cmake \\\n        cjson-dev \\\n        gnupg \\\n        libwebsockets-dev \\\n        linux-headers \\\n        openssl-dev \\\n        util-linux-dev && \\\n    wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \\\n    echo \"$DOWNLOAD_SHA256  /tmp/mosq.tar.gz\" | sha256sum -c - && \\\n    wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \\\n    export GNUPGHOME=\"$(mktemp -d)\" && \\\n    found=''; \\\n    for server in \\\n        hkps://keys.openpgp.org \\\n        hkp://keyserver.ubuntu.com:80 \\\n        pgp.mit.edu \\\n    ; do \\\n        echo \"Fetching GPG key $GPG_KEYS from $server\"; \\\n        gpg --keyserver \"$server\" --keyserver-options timeout=10 --recv-keys \"$GPG_KEYS\" && found=yes && break; \\\n    done; \\\n    test -z \"$found\" && echo >&2 \"error: failed to fetch GPG key $GPG_KEYS\" && exit 1; \\\n    gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \\\n    gpgconf --kill all && \\\n    rm -rf \"$GNUPGHOME\" /tmp/mosq.tar.gz.asc && \\\n    mkdir -p /build/mosq && \\\n    tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \\\n    rm /tmp/mosq.tar.gz && \\\n    make -C /build/mosq -j \"$(nproc)\" \\\n        CFLAGS=\"-Wall -O2 -I/build\" \\\n        WITH_ADNS=no \\\n        WITH_DOCS=no \\\n        WITH_SHARED_LIBRARIES=yes \\\n        WITH_SRV=no \\\n        WITH_STRIP=yes \\\n        WITH_WEBSOCKETS=yes \\\n        prefix=/usr \\\n        binary && \\\n    addgroup -S -g 1883 mosquitto 2>/dev/null && \\\n    adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \\\n    mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \\\n    install -d /usr/sbin/ && \\\n    install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \\\n    install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \\\n    install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \\\n    install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \\\n    install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \\\n    install -s -m755 /build/mosq/apps/mosquitto_ctrl/mosquitto_ctrl /usr/bin/mosquitto_ctrl && \\\n    install -s -m755 /build/mosq/apps/mosquitto_passwd/mosquitto_passwd /usr/bin/mosquitto_passwd && \\\n    install -s -m755 /build/mosq/plugins/dynamic-security/mosquitto_dynamic_security.so /usr/lib/mosquitto_dynamic_security.so && \\\n    install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \\\n    install -Dm644 /build/mosq/epl-v20 /usr/share/licenses/mosquitto/epl-v20 && \\\n    install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \\\n    chown -R mosquitto:mosquitto /mosquitto && \\\n    apk --no-cache add \\\n        ca-certificates \\\n        cjson \\\n        libwebsockets \\\n        tzdata && \\\n    apk del build-deps && \\\n    rm -rf /build\n\nVOLUME [\"/mosquitto/data\", \"/mosquitto/log\"]\n\n# Set up the entry point script and default command\nCOPY docker-entrypoint.sh mosquitto-no-auth.conf /\nEXPOSE 1883\nENTRYPOINT [\"/docker-entrypoint.sh\"]\nCMD [\"/usr/sbin/mosquitto\", \"-c\", \"/mosquitto/config/mosquitto.conf\"]\n"
  },
  {
    "path": "docker/2.0-openssl/README.md",
    "content": "# Eclipse Mosquitto Docker Image\nContainers built with this Dockerfile build as source from published tarballs.\n\n## Mount Points\nA docker mount point has been created in the image to be used for configuration.\n```\n/mosquitto/config\n```\n\nTwo docker volumes have been created in the image to be used for persistent storage and logs.\n```\n/mosquitto/data\n/mosquitto/log\n```\n\n## User/Group\n\nThe image runs mosquitto under the mosquitto user and group, which are created\nwith a uid and gid of 1883.\n\n## Running without a configuration file\nMosquitto 2.0 requires you to configure listeners and authentication before it\nwill allow connections from anything other than the loopback interface. In the\ncontext of a container, this means you would normally need to provide a\nconfiguration file with your settings.\n\nIf you wish to run mosquitto without any authentication, and without setting\nany other configuration options, you can do so by using a configuration\nprovided in the container for this purpose:\n```\ndocker run -it -p 1883:1883 eclipse-mosquitto:<version> mosquitto -c /mosquitto-no-auth.conf\n```\n\n## Configuration\nTo use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf`\n\n```\ndocker run -it -p 1883:1883 -v <absolute-path-to-configuration-file>:/mosquitto/config/mosquitto.conf eclipse-mosquitto:<version>\n```\n\nYour configuration file must include a `listener`, and you must configure some\nform of authentication or allow unauthenticated access. If you do not do this,\nclients will be unable to connect.\n\n\nFile based authentication and authorisation:\n```\nlistener 1883\npassword_file /mosquitto/data/mosquitto.password_file\nacl_file /mosquitto/data/mosquitto.aclfile\n```\n\nPlugin based authentication and authorisation:\n```\nlistener 1883\nplugin /usr/lib/mosquitto_dynamic_security.so\nplugin_opt_config_file /mosquitto/data/mosquitto-dynsec.json\n```\n\nUnauthenticated access:\n```\nlistener 1883\nallow_anonymous true\n```\n\n:boom: if the mosquitto configuration (mosquitto.conf) was modified\nto use non-default ports, the docker run command will need to be updated\nto expose the ports that have been configured, for example:\n\n```\ndocker run -it -p 1883:1883 -p 8080:8080 -v <absolute-path-to-configuration-file>:/mosquitto/config/mosquitto.conf eclipse-mosquitto:<version>\n```\n\n**Important**: The default configuration only listens on the loopback\ninterface. This means that there is no way to access Mosquitto in the docker\ncontainer without using a custom configuration containing at least a listener.\nYou also need to make a decision to allow anonymous connections or to set up a\ndifferent method of client authentication.\n\ni.e. to configure a Mosquitto docker container as if it was running locally,\nadd the following to `mosquitto.conf`:\n```\nlistener 1883\nallow_anonymous true\n```\n\nConfiguration can be changed to:\n\n* persist data to `/mosquitto/data`\n* log to `/mosquitto/log/mosquitto.log`\n\ni.e. add the following to `mosquitto.conf`:\n```\npersistence true\npersistence_location /mosquitto/data/\n\nlog_dest file /mosquitto/log/mosquitto.log\n```\n\n**Note**: For any volume used, the data will be persistent between containers.\n"
  },
  {
    "path": "docker/2.0-openssl/docker-entrypoint.sh",
    "content": "#!/bin/ash\nset -e\n\n# Set permissions\nuser=\"$(id -u)\"\nif [ \"$user\" = '0' ]; then\n\t[ -d \"/mosquitto\" ] && chown -R mosquitto:mosquitto /mosquitto || true\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "docker/2.0-openssl/mosquitto-no-auth.conf",
    "content": "# This is a Mosquitto configuration file that creates a listener on port 1883\n# that allows unauthenticated access.\n\nlistener 1883\nallow_anonymous true\n"
  },
  {
    "path": "docker/2.1-alpine/Dockerfile",
    "content": "FROM alpine:3.23\n\nENV VERSION=2.1.2 \\\n    DOWNLOAD_SHA256=fd905380691ac65ea5a93779e8214941829e3d6e038d5edff9eac5fd74cbed02 \\\n    GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7\n\nLABEL \\\n    org.opencontainers.image.authors=\"Roger Light <roger@atchoo.org>\" \\\n    org.opencontainers.image.title=\"eclipse-mosquitto\" \\\n    org.opencontainers.image.description=\"Eclipse Mosquitto MQTT Broker\" \\\n    org.opencontainers.image.url=\"https://mosquitto.org/\" \\\n    org.opencontainers.image.documentation=\"https://mosquitto.org/documentation/\" \\\n    org.opencontainers.image.source=\"https://github.com/eclipse-mosquitto/mosquitto\" \\\n    org.opencontainers.image.licenses=\"EPL-2.0 OR BSD-3-Clause\" \\\n    org.opencontainers.image.version=${VERSION}\n\nRUN set -x && \\\n    apk --no-cache add --virtual build-deps \\\n        argon2-dev \\\n        build-base \\\n        cjson-dev \\\n        cmake \\\n        gnupg \\\n        libedit-dev \\\n        libmicrohttpd-dev \\\n        linux-headers \\\n        openssl-dev \\\n        sqlite-dev \\\n        util-linux-dev && \\\n    wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \\\n    echo \"$DOWNLOAD_SHA256  /tmp/mosq.tar.gz\" | sha256sum -c - && \\\n    wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \\\n    export GNUPGHOME=\"$(mktemp -d)\" && \\\n    found=''; \\\n    for server in \\\n        hkps://keys.openpgp.org \\\n        hkp://keyserver.ubuntu.com:80 \\\n        pgp.mit.edu \\\n    ; do \\\n        echo \"Fetching GPG key $GPG_KEYS from $server\"; \\\n        gpg --keyserver \"$server\" --keyserver-options timeout=10 --recv-keys \"$GPG_KEYS\" && found=yes && break; \\\n    done; \\\n    test -z \"$found\" && echo >&2 \"error: failed to fetch GPG key $GPG_KEYS\" && exit 1; \\\n    gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \\\n    gpgconf --kill all && \\\n    rm -rf \"$GNUPGHOME\" /tmp/mosq.tar.gz.asc && \\\n    mkdir -p /build/mosq && \\\n    tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \\\n    rm /tmp/mosq.tar.gz && \\\n    make -C /build/mosq -j \"$(nproc)\" \\\n        CFLAGS=\"-Wall -O2 -I/build -DHTTP_API_DIR=\\\\\\\"/usr/share/mosquitto/dashboard\\\\\\\"\" \\\n        WITH_ADNS=no \\\n        WITH_DOCS=no \\\n        WITH_SHARED_LIBRARIES=yes \\\n        WITH_SRV=no \\\n        WITH_STRIP=yes \\\n        WITH_WEBSOCKETS=yes \\\n        prefix=/usr \\\n        binary && \\\n    addgroup -S -g 1883 mosquitto 2>/dev/null && \\\n    adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \\\n    mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \\\n    install -d /usr/sbin/ && \\\n    install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \\\n    install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \\\n    install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \\\n    install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \\\n    install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \\\n    install -s -m755 /build/mosq/apps/mosquitto_ctrl/mosquitto_ctrl /usr/bin/mosquitto_ctrl && \\\n    install -s -m755 /build/mosq/apps/mosquitto_passwd/mosquitto_passwd /usr/bin/mosquitto_passwd && \\\n    install -s -m755 /build/mosq/apps/mosquitto_signal/mosquitto_signal /usr/bin/mosquitto_signal && \\\n    install -s -m755 /build/mosq/plugins/acl-file/mosquitto_acl_file.so /usr/lib/mosquitto_acl_file.so && \\\n    install -s -m755 /build/mosq/plugins/dynamic-security/mosquitto_dynamic_security.so /usr/lib/mosquitto_dynamic_security.so && \\\n    install -s -m755 /build/mosq/plugins/password-file/mosquitto_password_file.so /usr/lib/mosquitto_password_file.so && \\\n    install -s -m755 /build/mosq/plugins/persist-sqlite/mosquitto_persist_sqlite.so /usr/lib/mosquitto_persist_sqlite.so && \\\n    install -s -m755 /build/mosq/plugins/sparkplug-aware/mosquitto_sparkplug_aware.so /usr/lib/mosquitto_sparkplug_aware.so && \\\n    install -m644 /build/mosq/docker/2.1-alpine/mosquitto.conf /mosquitto/config/mosquitto.conf && \\\n    install -m644 /build/mosq/docker/2.1-ubuntu/mosquitto.conf /mosquitto-no-auth.conf && \\\n    install -d /usr/share/mosquitto && \\\n    cp -r /build/mosq/dashboard/src /usr/share/mosquitto/dashboard && \\\n    install -Dm644 /build/mosq/epl-v20 /usr/share/licenses/mosquitto/epl-v20 && \\\n    install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \\\n    chown -R mosquitto:mosquitto /mosquitto && \\\n    apk --no-cache add \\\n        argon2-libs \\\n        ca-certificates \\\n        cjson \\\n        libedit \\\n        libmicrohttpd \\\n        sqlite-libs \\\n        tzdata && \\\n    apk del build-deps && \\\n    rm -rf /build\n\nVOLUME [\"/mosquitto/data\", \"/mosquitto/log\"]\n\n# Set up the entry point script and default command\nCOPY docker-entrypoint.sh /\nEXPOSE 1883\nENTRYPOINT [\"/docker-entrypoint.sh\"]\nCMD [\"/usr/sbin/mosquitto\", \"-c\", \"/mosquitto/config/mosquitto.conf\"]\n"
  },
  {
    "path": "docker/2.1-alpine/README.md",
    "content": "# Eclipse Mosquitto Docker Image\nContainers built with this Dockerfile build as source from published tarballs.\n\n## Mount Points\nA docker mount point has been created in the image to be used for configuration.\n```\n/mosquitto/config\n```\n\nTwo docker volumes have been created in the image to be used for persistent storage and logs.\n```\n/mosquitto/data\n/mosquitto/log\n```\n\n## User/Group\n\nThe image runs mosquitto under the mosquitto user and group, which are created\nwith a uid and gid of 1883.\n\n## Running without a configuration file\nMosquitto 2.0 and up requires you to configure listeners and authentication\nbefore it will allow connections from anything other than the loopback\ninterface. In the context of a container, this means you would normally need to\nprovide a configuration file with your settings.\n\nHowever, this container provides a default configuration which listens on port\n1883 for unauthenticated access, and port 9883 for the local http dashboard.\nIf you wish to run mosquitto without any authentication, and without setting\nany other configuration options, you can run without a configuration by binding\nthe appropriate network ports:\n```\ndocker run -it -p 1883:1883 -p localhost:9883:9883 eclipse-mosquitto:<version>\n```\n\n## Configuration\nTo use a custom configuration file, create a **local** config directory with a\nmosquitto.conf inside, then mount this directory to `/mosquitto/config`\n\n```\ndocker run -it -p 1883:1883 -v <absolute-path-to-config-directory>:/mosquitto/config eclipse-mosquitto:<version>\n```\n\nYour configuration file must include a `listener`, and you must configure some\nform of authentication or allow unauthenticated access. If you do not do this,\nclients will be unable to connect.\n\n\nFile based authentication and authorisation:\n```\nlistener 1883\nplugin /usr/lib/mosquitto_password_file.so\nplugin_opt_password_file /mosquitto/data/mosquitto.password_file\n\nplugin /usr/lib/mosquitto_acl_file.so\nplugin_opt_acl_file /mosquitto/data/mosquitto.aclfile\n```\n\nPlugin based authentication and authorisation:\n```\nlistener 1883\nplugin /usr/lib/mosquitto_dynamic_security.so\nplugin_opt_config_file /mosquitto/data/mosquitto-dynsec.json\n```\n\nUnauthenticated access:\n```\nlistener 1883\nallow_anonymous true\n```\n\n:boom: if the mosquitto configuration (mosquitto.conf) was modified\nto use non-default ports, the docker run command will need to be updated\nto expose the ports that have been configured, for example:\n\n```\ndocker run -it -p 1883:1883 -p 8080:8080 -v <absolute-path-to-config-directory>:/mosquitto/config eclipse-mosquitto:<version>\n```\n\nConfiguration can be changed to:\n\n* persist data to `/mosquitto/data`\n* log to `/mosquitto/log/mosquitto.log`\n\ni.e. add the following to `mosquitto.conf`:\n```\npersistence_location /mosquitto/data/\nplugin /usr/lib/mosquitto_persist_sqlite.so\n\nlog_dest file /mosquitto/log/mosquitto.log\n```\n\n**Note**: For any volume used, the data will be persistent between containers.\n"
  },
  {
    "path": "docker/2.1-alpine/docker-entrypoint.sh",
    "content": "#!/bin/ash\nset -e\n\n# Set permissions\nuser=\"$(id -u)\"\nif [ \"$PUID\" = \"\" ]; then\n\tPUID=\"1883\"\nfi\nif [ \"$PGID\" = \"\" ]; then\n\tPGID=\"1883\"\nfi\nif [ \"$user\" = '0' ]; then\n\t[ -d \"/mosquitto/data\" ] && chown -R ${PUID}:${PGID} /mosquitto/data || true\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "docker/2.1-alpine/mosquitto.conf",
    "content": "# This is a Mosquitto configuration file that creates a listener on port 1883\n# that allows unauthenticated access.\n\nlistener 1883\nallow_anonymous true\n\nlistener 9883\nprotocol http_api\nhttp_dir /usr/share/mosquitto/dashboard\n"
  },
  {
    "path": "docker/2.1-ubuntu/Dockerfile",
    "content": "FROM ubuntu:24.04\n\nENV VERSION=2.1.2 \\\n    DOWNLOAD_SHA256=fd905380691ac65ea5a93779e8214941829e3d6e038d5edff9eac5fd74cbed02 \\\n    GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7\n\nLABEL \\\n    org.opencontainers.image.authors=\"Roger Light <roger@atchoo.org>\" \\\n    org.opencontainers.image.title=\"eclipse-mosquitto\" \\\n    org.opencontainers.image.description=\"Eclipse Mosquitto MQTT Broker\" \\\n    org.opencontainers.image.url=\"https://mosquitto.org/\" \\\n    org.opencontainers.image.documentation=\"https://mosquitto.org/documentation/\" \\\n    org.opencontainers.image.source=\"https://github.com/eclipse-mosquitto/mosquitto\" \\\n    org.opencontainers.image.licenses=\"EPL-2.0 OR BSD-3-Clause\" \\\n    org.opencontainers.image.version=${VERSION}\n\nRUN set -x && \\\n    apt-get update && \\\n    apt-get install -y \\\n        build-essential \\\n        cmake \\\n        gnupg \\\n        libargon2-dev \\\n        libcjson-dev \\\n        libedit-dev \\\n        libmicrohttpd-dev \\\n        libssl-dev \\\n        libsqlite3-dev \\\n        wget && \\\n    wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \\\n    echo \"$DOWNLOAD_SHA256  /tmp/mosq.tar.gz\" | sha256sum -c - && \\\n    wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \\\n    export GNUPGHOME=\"$(mktemp -d)\" && \\\n    found=''; \\\n    for server in \\\n        hkps://keys.openpgp.org \\\n        hkp://keyserver.ubuntu.com:80 \\\n        pgp.mit.edu \\\n    ; do \\\n        echo \"Fetching GPG key $GPG_KEYS from $server\"; \\\n        gpg --keyserver \"$server\" --keyserver-options timeout=10 --recv-keys \"$GPG_KEYS\" && found=yes && break; \\\n    done; \\\n    test -z \"$found\" && echo >&2 \"error: failed to fetch GPG key $GPG_KEYS\" && exit 1; \\\n    gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \\\n    gpgconf --kill all && \\\n    rm -rf \"$GNUPGHOME\" /tmp/mosq.tar.gz.asc && \\\n    mkdir -p /build/mosq && \\\n    tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \\\n    rm /tmp/mosq.tar.gz && \\\n    make -C /build/mosq -j \"$(nproc)\" \\\n        CFLAGS=\"-Wall -O2 -I/build -DHTTP_API_DIR=\\\\\\\"/usr/share/mosquitto/dashboard\\\\\\\"\" \\\n        WITH_ADNS=no \\\n        WITH_DOCS=no \\\n        WITH_SHARED_LIBRARIES=yes \\\n        WITH_SRV=no \\\n        WITH_STRIP=yes \\\n        WITH_WEBSOCKETS=yes \\\n        prefix=/usr \\\n        binary && \\\n    addgroup --system --quiet --gid 1883 mosquitto 2>/dev/null && \\\n    adduser --system --quiet --no-create-home --ingroup mosquitto --uid 1883 --home /var/empty --shell /usr/sbin/nologin mosquitto 2>/dev/null && \\\n    mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \\\n    install -d /usr/sbin/ && \\\n    install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \\\n    install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \\\n    install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \\\n    install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \\\n    install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \\\n    install -s -m755 /build/mosq/apps/mosquitto_ctrl/mosquitto_ctrl /usr/bin/mosquitto_ctrl && \\\n    install -s -m755 /build/mosq/apps/mosquitto_passwd/mosquitto_passwd /usr/bin/mosquitto_passwd && \\\n    install -s -m755 /build/mosq/apps/mosquitto_signal/mosquitto_signal /usr/bin/mosquitto_signal && \\\n    install -s -m755 /build/mosq/plugins/acl-file/mosquitto_acl_file.so /usr/lib/mosquitto_acl_file.so && \\\n    install -s -m755 /build/mosq/plugins/dynamic-security/mosquitto_dynamic_security.so /usr/lib/mosquitto_dynamic_security.so && \\\n    install -s -m755 /build/mosq/plugins/password-file/mosquitto_password_file.so /usr/lib/mosquitto_password_file.so && \\\n    install -s -m755 /build/mosq/plugins/persist-sqlite/mosquitto_persist_sqlite.so /usr/lib/mosquitto_persist_sqlite.so && \\\n    install -s -m755 /build/mosq/plugins/sparkplug-aware/mosquitto_sparkplug_aware.so /usr/lib/mosquitto_sparkplug_aware.so && \\\n    install -m644 /build/mosq/docker/2.1-ubuntu/mosquitto.conf /mosquitto/config/mosquitto.conf && \\\n    install -m644 /build/mosq/docker/2.1-ubuntu/mosquitto.conf /mosquitto-no-auth.conf && \\\n    install -d /usr/share/mosquitto && \\\n    cp -r /build/mosq/dashboard/src /usr/share/mosquitto/dashboard && \\\n    install -Dm644 /build/mosq/epl-v20 /usr/share/licenses/mosquitto/epl-v20 && \\\n    install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \\\n    chown -R mosquitto:mosquitto /mosquitto && \\\n    apt-get install \\\n        ca-certificates \\\n        libargon2-1 \\\n        libcjson1 \\\n        libedit2 \\\n        libmicrohttpd12 \\\n        libsqlite3-0 && \\\n    apt-get clean && \\\n    apt-get remove --purge --auto-remove -y build-essential cmake gnupg && \\\n    rm -rf /var/lib/apt/lists/* && \\\n    rm -rf /build\n\nVOLUME [\"/mosquitto/data\", \"/mosquitto/log\"]\n\n# Set up the entry point script and default command\nCOPY docker-entrypoint.sh /\nEXPOSE 1883\nENTRYPOINT [\"/docker-entrypoint.sh\"]\nCMD [\"/usr/sbin/mosquitto\", \"-c\", \"/mosquitto/config/mosquitto.conf\"]\n"
  },
  {
    "path": "docker/2.1-ubuntu/README.md",
    "content": "# Eclipse Mosquitto Docker Image\nContainers built with this Dockerfile build as source from published tarballs.\n\n## Mount Points\nA docker mount point has been created in the image to be used for configuration.\n```\n/mosquitto/config\n```\n\nTwo docker volumes have been created in the image to be used for persistent storage and logs.\n```\n/mosquitto/data\n/mosquitto/log\n```\n\n## User/Group\n\nThe image runs mosquitto under the mosquitto user and group, which are created\nwith a uid and gid of 1883.\n\n## Running without a configuration file\nMosquitto 2.0 and up requires you to configure listeners and authentication\nbefore it will allow connections from anything other than the loopback\ninterface. In the context of a container, this means you would normally need to\nprovide a configuration file with your settings.\n\nHowever, this container provides a default configuration which listens on port\n1883 for unauthenticated access, and port 9883 for the local http dashboard.\nIf you wish to run mosquitto without any authentication, and without setting\nany other configuration options, you can run without a configuration by binding\nthe appropriate network ports:\n```\ndocker run -it -p 1883:1883 -p localhost:9883:9883 eclipse-mosquitto:<version>\n```\n\n## Configuration\nTo use a custom configuration file, create a **local** config directory with a\nmosquitto.conf inside, then mount this directory to `/mosquitto/config`\n\n```\ndocker run -it -p 1883:1883 -v <absolute-path-to-config-directory>:/mosquitto/config eclipse-mosquitto:<version>\n```\n\nYour configuration file must include a `listener`, and you must configure some\nform of authentication or allow unauthenticated access. If you do not do this,\nclients will be unable to connect.\n\n\nFile based authentication and authorisation:\n```\nlistener 1883\nplugin /usr/lib/mosquitto_password_file.so\nplugin_opt_password_file /mosquitto/data/mosquitto.password_file\n\nplugin /usr/lib/mosquitto_acl_file.so\nplugin_opt_acl_file /mosquitto/data/mosquitto.aclfile\n```\n\nPlugin based authentication and authorisation:\n```\nlistener 1883\nplugin /usr/lib/mosquitto_dynamic_security.so\nplugin_opt_config_file /mosquitto/data/mosquitto-dynsec.json\n```\n\nUnauthenticated access:\n```\nlistener 1883\nallow_anonymous true\n```\n\n:boom: if the mosquitto configuration (mosquitto.conf) was modified\nto use non-default ports, the docker run command will need to be updated\nto expose the ports that have been configured, for example:\n\n```\ndocker run -it -p 1883:1883 -p 8080:8080 -v <absolute-path-to-config-directory>:/mosquitto/config eclipse-mosquitto:<version>\n```\n\nConfiguration can be changed to:\n\n* persist data to `/mosquitto/data`\n* log to `/mosquitto/log/mosquitto.log`\n\ni.e. add the following to `mosquitto.conf`:\n```\npersistence_location /mosquitto/data/\nplugin /usr/lib/mosquitto_persist_sqlite.so\n\nlog_dest file /mosquitto/log/mosquitto.log\n```\n\n**Note**: For any volume used, the data will be persistent between containers.\n"
  },
  {
    "path": "docker/2.1-ubuntu/docker-entrypoint.sh",
    "content": "#!/bin/sh\nset -e\n\n# Set permissions\nuser=\"$(id -u)\"\nif [ \"$PUID\" = \"\" ]; then\n\tPUID=\"1883\"\nfi\nif [ \"$PGID\" = \"\" ]; then\n\tPGID=\"1883\"\nfi\nif [ \"$user\" = '0' ]; then\n\t[ -d \"/mosquitto/data\" ] && chown -R ${PUID}:${PGID} /mosquitto/data || true\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "docker/2.1-ubuntu/mosquitto.conf",
    "content": "# This is a Mosquitto configuration file that creates a listener on port 1883\n# that allows unauthenticated access.\n\nlistener 1883\nallow_anonymous true\n\nlistener 9883\nprotocol http_api\nhttp_dir /usr/share/mosquitto/dashboard\n"
  },
  {
    "path": "docker/README.md",
    "content": "# Docker Images\n\nThis directory contains Docker files for Mosquitto.\n\nThe `2.0` directory contains the latest version of Mosquitto for that\nseries, it uses libressl. The `2.0-openssl` directory is identical except that\nit uses openssl instead of libressl, and enables TLS-PSK and TLS v1.3 cipher\nsupport.\n\nThe `1.6` directory contains the version of Mosquitto based on the 1.6 branch.\nIt uses libressl. The `1.6-openssl` directory is identical except that it uses\nopenssl instead of libressl, and enables TLS-PSK support.\n\nThe `1.5` directory contains the version of Mosquitto based on the 1.5 branch.\nIt uses libressl. The `1.5-openssl` directory is identical except that it uses\nopenssl instead of libressl, and enables TLS-PSK support.\n\nThe `generic` directory contains a generic Dockerfile that can be used to build\narbitrary versions of Mosquitto based on the released tarballs as follows:\n\n```\ncd generic\ndocker build -t eclipse-mosquitto:1.5.1 --build-arg VERSION=\"1.5.1\" .\ndocker run --rm -it eclipse-mosquitto:1.5.1\n```\n\nThe `local` directory can be used to build an image based on the files in the\nworking directory by using `make localdocker` from the root of the repository.\n"
  },
  {
    "path": "docker/generic/Dockerfile",
    "content": "FROM alpine:3.23\n\nLABEL maintainer=\"Roger Light <roger@atchoo.org>\" \\\n    description=\"Eclipse Mosquitto MQTT Broker\"\n\nARG VERSION\nRUN test -n \"${VERSION}\"\n\nENV \\\n    GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \\\n    LWS_VERSION=4.2.1 \\\n    LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 \\\n    CJSON_VERSION=1.7.14 \\\n    CJSON_SHA256=fb50a663eefdc76bafa80c82bc045af13b1363e8f45cec8b442007aef6a41343\n\nLABEL \\\n    org.opencontainers.image.authors=\"Roger Light <roger@atchoo.org>\" \\\n    org.opencontainers.image.title=\"eclipse-mosquitto\" \\\n    org.opencontainers.image.description=\"Eclipse Mosquitto MQTT Broker\" \\\n    org.opencontainers.image.url=\"https://mosquitto.org/\" \\\n    org.opencontainers.image.documentation=\"https://mosquitto.org/documentation/\" \\\n    org.opencontainers.image.source=\"https://github.com/eclipse-mosquitto/mosquitto\" \\\n    org.opencontainers.image.licenses=\"EPL-2.0 OR BSD-3-Clause\" \\\n    org.opencontainers.image.version=${VERSION}\n\nRUN set -x && \\\n    apk --no-cache add --virtual build-deps \\\n        build-base \\\n        cmake \\\n        gnupg \\\n        linux-headers \\\n        openssl-dev \\\n        util-linux-dev && \\\n    wget https://github.com/warmcat/libwebsockets/archive/v${LWS_VERSION}.tar.gz -O /tmp/lws.tar.gz && \\\n    echo \"$LWS_SHA256  /tmp/lws.tar.gz\" | sha256sum -c - && \\\n    mkdir -p /build/lws && \\\n    tar --strip=1 -xf /tmp/lws.tar.gz -C /build/lws && \\\n    rm /tmp/lws.tar.gz && \\\n    cd /build/lws && \\\n    cmake . \\\n        -DCMAKE_BUILD_TYPE=MinSizeRel \\\n        -DCMAKE_INSTALL_PREFIX=/usr \\\n        -DDISABLE_WERROR=ON \\\n        -DLWS_IPV6=ON \\\n        -DLWS_WITHOUT_BUILTIN_GETIFADDRS=ON \\\n        -DLWS_WITHOUT_CLIENT=ON \\\n        -DLWS_WITHOUT_EXTENSIONS=ON \\\n        -DLWS_WITHOUT_TESTAPPS=ON \\\n        -DLWS_WITH_EXTERNAL_POLL=ON \\\n        -DLWS_WITH_HTTP2=OFF \\\n        -DLWS_WITH_SHARED=OFF \\\n        -DLWS_WITH_ZIP_FOPS=OFF \\\n        -DLWS_WITH_ZLIB=OFF && \\\n    make -j \"$(nproc)\" && \\\n    rm -rf /root/.cmake && \\\n    wget https://github.com/DaveGamble/cJSON/archive/v${CJSON_VERSION}.tar.gz -O /tmp/cjson.tar.gz && \\\n    echo \"$CJSON_SHA256  /tmp/cjson.tar.gz\" | sha256sum -c - && \\\n    mkdir -p /build/cjson && \\\n    tar --strip=1 -xf /tmp/cjson.tar.gz -C /build/cjson && \\\n    rm /tmp/cjson.tar.gz && \\\n    cd /build/cjson && \\\n    cmake . \\\n        -DCMAKE_BUILD_TYPE=MinSizeRel \\\n        -DBUILD_SHARED_AND_STATIC_LIBS=OFF \\\n        -DBUILD_SHARED_LIBS=OFF \\\n        -DCJSON_BUILD_SHARED_LIBS=OFF \\\n        -DCJSON_OVERRIDE_BUILD_SHARED_LIBS=OFF \\\n        -DCMAKE_INSTALL_PREFIX=/usr && \\\n    make -j \"$(nproc)\" && \\\n    rm -rf /root/.cmake && \\\n    wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \\\n    wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \\\n    export GNUPGHOME=\"$(mktemp -d)\" && \\\n    found=''; \\\n    for server in \\\n        hkps://keys.openpgp.org \\\n        hkp://keyserver.ubuntu.com:80 \\\n        pgp.mit.edu \\\n    ; do \\\n        echo \"Fetching GPG key $GPG_KEYS from $server\"; \\\n        gpg --keyserver \"$server\" --keyserver-options timeout=10 --recv-keys \"$GPG_KEYS\" && found=yes && break; \\\n    done; \\\n    test -z \"$found\" && echo >&2 \"error: failed to fetch GPG key $GPG_KEYS\" && exit 1; \\\n    gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \\\n    gpgconf --kill all && \\\n    rm -rf \"$GNUPGHOME\" /tmp/mosq.tar.gz.asc && \\\n    mkdir -p /build/mosq && \\\n    tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \\\n    rm /tmp/mosq.tar.gz && \\\n    make -C /build/mosq -j \"$(nproc)\" \\\n        CFLAGS=\"-Wall -O2 -I/build/lws/include -I/build\" \\\n        LDFLAGS=\"-L/build/lws/lib -L/build/cjson\" \\\n        WITH_ADNS=no \\\n        WITH_DOCS=no \\\n        WITH_SHARED_LIBRARIES=yes \\\n        WITH_SRV=no \\\n        WITH_STRIP=yes \\\n        WITH_TLS_PSK=no \\\n        WITH_WEBSOCKETS=yes \\\n        prefix=/usr \\\n        binary && \\\n    addgroup -S -g 1883 mosquitto 2>/dev/null && \\\n    adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \\\n    mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \\\n    install -d /usr/sbin/ && \\\n    install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \\\n    install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \\\n    install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \\\n    install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \\\n    install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \\\n    install -s -m755 /build/mosq/apps/mosquitto_ctrl/mosquitto_ctrl /usr/bin/mosquitto_ctrl && \\\n    install -s -m755 /build/mosq/apps/mosquitto_passwd/mosquitto_passwd /usr/bin/mosquitto_passwd && \\\n    install -s -m755 /build/mosq/plugins/dynamic-security/mosquitto_dynamic_security.so /usr/lib/mosquitto_dynamic_security.so && \\\n    install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \\\n    install -Dm644 /build/cjson/LICENSE /usr/share/licenses/cJSON/LICENSE && \\\n    install -Dm644 /build/lws/LICENSE /usr/share/licenses/libwebsockets/LICENSE && \\\n    install -Dm644 /build/mosq/epl-v20 /usr/share/licenses/mosquitto/epl-v20 && \\\n    install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \\\n    chown -R mosquitto:mosquitto /mosquitto && \\\n    apk --no-cache add \\\n        ca-certificates \\\n        tzdata && \\\n    apk del build-deps && \\\n    rm -rf /build\n\nVOLUME [\"/mosquitto/data\", \"/mosquitto/log\"]\n\n# Set up the entry point script and default command\nCOPY docker-entrypoint.sh mosquitto-no-auth.conf /\nEXPOSE 1883\nENTRYPOINT [\"/docker-entrypoint.sh\"]\nCMD [\"/usr/sbin/mosquitto\", \"-c\", \"/mosquitto/config/mosquitto.conf\"]\n"
  },
  {
    "path": "docker/generic/README.md",
    "content": "# Eclipse Mosquitto Docker Image\nContainers built with this Dockerfile build as source from published tarballs.\n\n## Mount Points\nA docker mount point has been created in the image to be used for configuration.\n```\n/mosquitto/config\n```\n\nTwo docker volumes have been created in the image to be used for persistent storage and logs.\n```\n/mosquitto/data\n/mosquitto/log\n```\n\n## User/Group\n\nThe image runs mosquitto under the mosquitto user and group, which are created\nwith a uid and gid of 1883.\n\n## Running without a configuration file\nMosquitto 2.0 requires you to configure listeners and authentication before it\nwill allow connections from anything other than the loopback interface. In the\ncontext of a container, this means you would normally need to provide a\nconfiguration file with your settings.\n\nIf you wish to run mosquitto without any authentication, and without setting\nany other configuration options, you can do so by using a configuration\nprovided in the container for this purpose:\n```\ndocker run -it -p 1883:1883 eclipse-mosquitto:<version> mosquitto -c /mosquitto-no-auth.conf\n```\n\n## Configuration\nWhen creating a container from the image, the default configuration values are used.\nTo use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf`\n```\ndocker run -it -p 1883:1883 -v <absolute-path-to-configuration-file>:/mosquitto/config/mosquitto.conf eclipse-mosquitto:<version>\n```\n\nConfiguration can be changed to:\n\n* persist data to `/mosquitto/data`\n* log to `/mosquitto/log/mosquitto.log`\n\ni.e. add the following to `mosquitto.conf`:\n```\npersistence true\npersistence_location /mosquitto/data/\n\nlog_dest file /mosquitto/log/mosquitto.log\n```\n\n**Note**: For any volume used, the data will be persistent between containers.\n\n## Build\nBuild and tag the docker image for a specific version:\n```\ndocker build -t eclipse-mosquitto:<version> --build-arg VERSION=\"<version>\" .\n```\n\n## Run\nRun a container using the new image:\n```\ndocker run -it -p 1883:1883 -v <path-to-configuration-file>:/mosquitto/config/mosquitto.conf -v /mosquitto/data -v /mosquitto/log eclipse-mosquitto:<version>\n```\n:boom: if the mosquitto configuration (mosquitto.conf) was modified\nto use non-default ports, the docker run command will need to be updated\nto expose the ports that have been configured.\n\n**Important**: The default configuration only listens on the\nloopback interface. This means that there is no way to access Mosquitto in the\ndocker container without using a custom configuration containing at least\na listener. You also need to make a decision to allow anonymous connections or\nto set up a different method of client authentication.\n\ni.e. to configure a Mosquitto docker container as if it was running locally,\nadd the following to `mosquitto.conf`:\n```\nlistener 1883\nallow_anonymous true\n```\n"
  },
  {
    "path": "docker/generic/docker-entrypoint.sh",
    "content": "#!/bin/ash\nset -e\n\n# Set permissions\nuser=\"$(id -u)\"\nif [ \"$user\" = '0' ]; then\n\t[ -d \"/mosquitto\" ] && chown -R mosquitto:mosquitto /mosquitto || true\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "docker/generic/mosquitto-no-auth.conf",
    "content": "# This is a Mosquitto configuration file that creates a listener on port 1883\n# that allows unauthenticated access.\n\nlistener 1883\nallow_anonymous true\n"
  },
  {
    "path": "docker/local/Dockerfile",
    "content": "FROM alpine:3.23\n\nARG VERSION\n\nLABEL \\\n    org.opencontainers.image.authors=\"Roger Light <roger@atchoo.org>\" \\\n    org.opencontainers.image.title=\"eclipse-mosquitto\" \\\n    org.opencontainers.image.description=\"Eclipse Mosquitto MQTT Broker\" \\\n    org.opencontainers.image.url=\"https://mosquitto.org/\" \\\n    org.opencontainers.image.documentation=\"https://mosquitto.org/documentation/\" \\\n    org.opencontainers.image.source=\"https://github.com/eclipse-mosquitto/mosquitto\" \\\n    org.opencontainers.image.licenses=\"EPL-2.0 OR BSD-3-Clause\" \\\n    org.opencontainers.image.version=${VERSION}\n\nCOPY mosq.tar.gz /tmp\n\nRUN set -x && \\\n    apk --no-cache add --virtual build-deps \\\n        argon2-dev \\\n        build-base \\\n        cjson-dev \\\n        cmake \\\n        gnupg \\\n        libedit-dev \\\n        libmicrohttpd-dev \\\n        linux-headers \\\n        openssl-dev \\\n        sqlite-dev \\\n        util-linux-dev && \\\n    mkdir -p /build/mosq && \\\n    tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \\\n    rm /tmp/mosq.tar.gz && \\\n    make -C /build/mosq -j \"$(nproc)\" \\\n        CFLAGS=\"-Wall -O2 -I/build -DHTTP_API_DIR=\\\\\\\"/usr/share/mosquitto/dashboard\\\\\\\"\" \\\n        WITH_ADNS=no \\\n        WITH_DOCS=no \\\n        WITH_SHARED_LIBRARIES=yes \\\n        WITH_SRV=no \\\n        WITH_STRIP=yes \\\n        WITH_WEBSOCKETS=yes \\\n        prefix=/usr \\\n        binary && \\\n    addgroup -S -g 1883 mosquitto 2>/dev/null && \\\n    adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \\\n    mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \\\n    install -d /usr/sbin/ && \\\n    install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \\\n    install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \\\n    install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \\\n    install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \\\n    install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \\\n    install -s -m755 /build/mosq/apps/mosquitto_ctrl/mosquitto_ctrl /usr/bin/mosquitto_ctrl && \\\n    install -s -m755 /build/mosq/apps/mosquitto_passwd/mosquitto_passwd /usr/bin/mosquitto_passwd && \\\n    install -s -m755 /build/mosq/apps/mosquitto_signal/mosquitto_signal /usr/bin/mosquitto_signal && \\\n    install -s -m755 /build/mosq/plugins/acl-file/mosquitto_acl_file.so /usr/lib/mosquitto_acl_file.so && \\\n    install -s -m755 /build/mosq/plugins/dynamic-security/mosquitto_dynamic_security.so /usr/lib/mosquitto_dynamic_security.so && \\\n    install -s -m755 /build/mosq/plugins/password-file/mosquitto_password_file.so /usr/lib/mosquitto_password_file.so && \\\n    install -s -m755 /build/mosq/plugins/persist-sqlite/mosquitto_persist_sqlite.so /usr/lib/mosquitto_persist_sqlite.so && \\\n    install -s -m755 /build/mosq/plugins/sparkplug-aware/mosquitto_sparkplug_aware.so /usr/lib/mosquitto_sparkplug_aware.so && \\\n    install -m644 /build/mosq/docker/local/mosquitto.conf /mosquitto/config/mosquitto.conf && \\\n    install -d /usr/share/mosquitto && \\\n    cp -r /build/mosq/dashboard/src /usr/share/mosquitto/dashboard && \\\n    install -Dm644 /build/mosq/epl-v20 /usr/share/licenses/mosquitto/epl-v20 && \\\n    install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \\\n    chown -R mosquitto:mosquitto /mosquitto && \\\n    apk --no-cache add \\\n        argon2-libs \\\n        ca-certificates \\\n        cjson \\\n        libedit \\\n        libmicrohttpd \\\n        sqlite-libs \\\n        tzdata && \\\n    apk del build-deps && \\\n    rm -rf /build\n\nVOLUME [\"/mosquitto/data\", \"/mosquitto/log\"]\n\n# Set up the entry point script and default command\nCOPY docker-entrypoint.sh /\nEXPOSE 1883\nENTRYPOINT [\"/docker-entrypoint.sh\"]\nCMD [\"/usr/sbin/mosquitto\", \"-c\", \"/mosquitto/config/mosquitto.conf\"]\n"
  },
  {
    "path": "docker/local/README.md",
    "content": "# Eclipse Mosquitto Docker Image\nContainers built with this Dockerfile build from a source tarball \"mosq.tar.gz\"\nplaced in the local directory. Running `make localdocker` from the root of the\nrepository will generate the source tar and build the docker image.\n\n## Mount Points\nA docker mount point has been created in the image to be used for configuration.\n```\n/mosquitto/config\n```\n\nTwo docker volumes have been created in the image to be used for persistent storage and logs.\n```\n/mosquitto/data\n/mosquitto/log\n```\n\n## User/Group\n\nThe image runs mosquitto under the mosquitto user and group, which are created\nwith a uid and gid of 1883.\n\n## Configuration\nWhen creating a container from the image, the default configuration values are used.\nTo use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf`\n```\ndocker run -it -p 1883:1883 -v <absolute-path-to-configuration-file>:/mosquitto/config/mosquitto.conf eclipse-mosquitto:<version>\n```\n\n:boom: if the mosquitto configuration (mosquitto.conf) was modified\nto use non-default ports, the docker run command will need to be updated\nto expose the ports that have been configured, for example if you use port 8080\nfor websockets as well as port 1883:\n\n```\ndocker run -it -p 1883:1883 -p 8080:8080 -v <absolute-path-to-configuration-file>:/mosquitto/config/mosquitto.conf eclipse-mosquitto:<version>\n```\n\nConfiguration can be changed to:\n\n* persist data to `/mosquitto/data`\n* log to `/mosquitto/log/mosquitto.log`\n\ni.e. add the following to `mosquitto.conf`:\n```\npersistence true\npersistence_location /mosquitto/data/\n\nlog_dest file /mosquitto/log/mosquitto.log\n```\n\n**Note**: For any volume used, the data will be persistent between containers.\n"
  },
  {
    "path": "docker/local/docker-entrypoint.sh",
    "content": "#!/bin/ash\nset -e\n\n# Set permissions\nuser=\"$(id -u)\"\nif [ \"$PUID\" = \"\" ]; then\n\tPUID=\"mosquitto\"\nfi\nif [ \"$PGID\" = \"\" ]; then\n\tPGID=\"mosquitto\"\nfi\nif [ \"$user\" = '0' ]; then\n\t[ -d \"/mosquitto/data\" ] && chown -R ${PUID}:${PGID} /mosquitto/data || true\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "docker/local/mosquitto.conf",
    "content": "# This is a Mosquitto configuration file that creates a listener on port 1883\n# that allows unauthenticated access.\n\nlistener 1883\nallow_anonymous true\n\nlistener 9883\nprotocol http_api\nhttp_dir /usr/share/mosquitto/dashboard\n"
  },
  {
    "path": "edl-v10",
    "content": "Eclipse Distribution License - v 1.0\n\nCopyright (c) 2007, Eclipse Foundation, Inc. and its licensors.\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n  Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n  Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n  Neither the name of the Eclipse Foundation, Inc. nor the names of its\n  contributors may be used to endorse or promote products derived from this\n  software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n"
  },
  {
    "path": "epl-v20",
    "content": "Eclipse Public License - v 2.0\n\n    THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE\n    PUBLIC LICENSE (\"AGREEMENT\"). ANY USE, REPRODUCTION OR DISTRIBUTION\n    OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.\n\n1. DEFINITIONS\n\n\"Contribution\" means:\n\n  a) in the case of the initial Contributor, the initial content\n     Distributed under this Agreement, and\n\n  b) in the case of each subsequent Contributor:\n     i) changes to the Program, and\n     ii) additions to the Program;\n  where such changes and/or additions to the Program originate from\n  and are Distributed by that particular Contributor. A Contribution\n  \"originates\" from a Contributor if it was added to the Program by\n  such Contributor itself or anyone acting on such Contributor's behalf.\n  Contributions do not include changes or additions to the Program that\n  are not Modified Works.\n\n\"Contributor\" means any person or entity that Distributes the Program.\n\n\"Licensed Patents\" mean patent claims licensable by a Contributor which\nare necessarily infringed by the use or sale of its Contribution alone\nor when combined with the Program.\n\n\"Program\" means the Contributions Distributed in accordance with this\nAgreement.\n\n\"Recipient\" means anyone who receives the Program under this Agreement\nor any Secondary License (as applicable), including Contributors.\n\n\"Derivative Works\" shall mean any work, whether in Source Code or other\nform, that is based on (or derived from) the Program and for which the\neditorial revisions, annotations, elaborations, or other modifications\nrepresent, as a whole, an original work of authorship.\n\n\"Modified Works\" shall mean any work in Source Code or other form that\nresults from an addition to, deletion from, or modification of the\ncontents of the Program, including, for purposes of clarity any new file\nin Source Code form that contains any contents of the Program. Modified\nWorks shall not include works that contain only declarations,\ninterfaces, types, classes, structures, or files of the Program solely\nin each case in order to link to, bind by name, or subclass the Program\nor Modified Works thereof.\n\n\"Distribute\" means the acts of a) distributing or b) making available\nin any manner that enables the transfer of a copy.\n\n\"Source Code\" means the form of a Program preferred for making\nmodifications, including but not limited to software source code,\ndocumentation source, and configuration files.\n\n\"Secondary License\" means either the GNU General Public License,\nVersion 2.0, or any later versions of that license, including any\nexceptions or additional permissions as identified by the initial\nContributor.\n\n2. GRANT OF RIGHTS\n\n  a) Subject to the terms of this Agreement, each Contributor hereby\n  grants Recipient a non-exclusive, worldwide, royalty-free copyright\n  license to reproduce, prepare Derivative Works of, publicly display,\n  publicly perform, Distribute and sublicense the Contribution of such\n  Contributor, if any, and such Derivative Works.\n\n  b) Subject to the terms of this Agreement, each Contributor hereby\n  grants Recipient a non-exclusive, worldwide, royalty-free patent\n  license under Licensed Patents to make, use, sell, offer to sell,\n  import and otherwise transfer the Contribution of such Contributor,\n  if any, in Source Code or other form. This patent license shall\n  apply to the combination of the Contribution and the Program if, at\n  the time the Contribution is added by the Contributor, such addition\n  of the Contribution causes such combination to be covered by the\n  Licensed Patents. The patent license shall not apply to any other\n  combinations which include the Contribution. No hardware per se is\n  licensed hereunder.\n\n  c) Recipient understands that although each Contributor grants the\n  licenses to its Contributions set forth herein, no assurances are\n  provided by any Contributor that the Program does not infringe the\n  patent or other intellectual property rights of any other entity.\n  Each Contributor disclaims any liability to Recipient for claims\n  brought by any other entity based on infringement of intellectual\n  property rights or otherwise. As a condition to exercising the\n  rights and licenses granted hereunder, each Recipient hereby\n  assumes sole responsibility to secure any other intellectual\n  property rights needed, if any. For example, if a third party\n  patent license is required to allow Recipient to Distribute the\n  Program, it is Recipient's responsibility to acquire that license\n  before distributing the Program.\n\n  d) Each Contributor represents that to its knowledge it has\n  sufficient copyright rights in its Contribution, if any, to grant\n  the copyright license set forth in this Agreement.\n\n  e) Notwithstanding the terms of any Secondary License, no\n  Contributor makes additional grants to any Recipient (other than\n  those set forth in this Agreement) as a result of such Recipient's\n  receipt of the Program under the terms of a Secondary License\n  (if permitted under the terms of Section 3).\n\n3. REQUIREMENTS\n\n3.1 If a Contributor Distributes the Program in any form, then:\n\n  a) the Program must also be made available as Source Code, in\n  accordance with section 3.2, and the Contributor must accompany\n  the Program with a statement that the Source Code for the Program\n  is available under this Agreement, and informs Recipients how to\n  obtain it in a reasonable manner on or through a medium customarily\n  used for software exchange; and\n\n  b) the Contributor may Distribute the Program under a license\n  different than this Agreement, provided that such license:\n     i) effectively disclaims on behalf of all other Contributors all\n     warranties and conditions, express and implied, including\n     warranties or conditions of title and non-infringement, and\n     implied warranties or conditions of merchantability and fitness\n     for a particular purpose;\n\n     ii) effectively excludes on behalf of all other Contributors all\n     liability for damages, including direct, indirect, special,\n     incidental and consequential damages, such as lost profits;\n\n     iii) does not attempt to limit or alter the recipients' rights\n     in the Source Code under section 3.2; and\n\n     iv) requires any subsequent distribution of the Program by any\n     party to be under a license that satisfies the requirements\n     of this section 3.\n\n3.2 When the Program is Distributed as Source Code:\n\n  a) it must be made available under this Agreement, or if the\n  Program (i) is combined with other material in a separate file or\n  files made available under a Secondary License, and (ii) the initial\n  Contributor attached to the Source Code the notice described in\n  Exhibit A of this Agreement, then the Program may be made available\n  under the terms of such Secondary Licenses, and\n\n  b) a copy of this Agreement must be included with each copy of\n  the Program.\n\n3.3 Contributors may not remove or alter any copyright, patent,\ntrademark, attribution notices, disclaimers of warranty, or limitations\nof liability (\"notices\") contained within the Program from any copy of\nthe Program which they Distribute, provided that Contributors may add\ntheir own appropriate notices.\n\n4. COMMERCIAL DISTRIBUTION\n\nCommercial distributors of software may accept certain responsibilities\nwith respect to end users, business partners and the like. While this\nlicense is intended to facilitate the commercial use of the Program,\nthe Contributor who includes the Program in a commercial product\noffering should do so in a manner which does not create potential\nliability for other Contributors. Therefore, if a Contributor includes\nthe Program in a commercial product offering, such Contributor\n(\"Commercial Contributor\") hereby agrees to defend and indemnify every\nother Contributor (\"Indemnified Contributor\") against any losses,\ndamages and costs (collectively \"Losses\") arising from claims, lawsuits\nand other legal actions brought by a third party against the Indemnified\nContributor to the extent caused by the acts or omissions of such\nCommercial Contributor in connection with its distribution of the Program\nin a commercial product offering. The obligations in this section do not\napply to any claims or Losses relating to any actual or alleged\nintellectual property infringement. In order to qualify, an Indemnified\nContributor must: a) promptly notify the Commercial Contributor in\nwriting of such claim, and b) allow the Commercial Contributor to control,\nand cooperate with the Commercial Contributor in, the defense and any\nrelated settlement negotiations. The Indemnified Contributor may\nparticipate in any such claim at its own expense.\n\nFor example, a Contributor might include the Program in a commercial\nproduct offering, Product X. That Contributor is then a Commercial\nContributor. If that Commercial Contributor then makes performance\nclaims, or offers warranties related to Product X, those performance\nclaims and warranties are such Commercial Contributor's responsibility\nalone. Under this section, the Commercial Contributor would have to\ndefend claims against the other Contributors related to those performance\nclaims and warranties, and if a court requires any other Contributor to\npay any damages as a result, the Commercial Contributor must pay\nthose damages.\n\n5. NO WARRANTY\n\nEXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT\nPERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN \"AS IS\"\nBASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR\nIMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF\nTITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR\nPURPOSE. Each Recipient is solely responsible for determining the\nappropriateness of using and distributing the Program and assumes all\nrisks associated with its exercise of rights under this Agreement,\nincluding but not limited to the risks and costs of program errors,\ncompliance with applicable laws, damage to or loss of data, programs\nor equipment, and unavailability or interruption of operations.\n\n6. DISCLAIMER OF LIABILITY\n\nEXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT\nPERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS\nSHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST\nPROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE\nEXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n7. GENERAL\n\nIf any provision of this Agreement is invalid or unenforceable under\napplicable law, it shall not affect the validity or enforceability of\nthe remainder of the terms of this Agreement, and without further\naction by the parties hereto, such provision shall be reformed to the\nminimum extent necessary to make such provision valid and enforceable.\n\nIf Recipient institutes patent litigation against any entity\n(including a cross-claim or counterclaim in a lawsuit) alleging that the\nProgram itself (excluding combinations of the Program with other software\nor hardware) infringes such Recipient's patent(s), then such Recipient's\nrights granted under Section 2(b) shall terminate as of the date such\nlitigation is filed.\n\nAll Recipient's rights under this Agreement shall terminate if it\nfails to comply with any of the material terms or conditions of this\nAgreement and does not cure such failure in a reasonable period of\ntime after becoming aware of such noncompliance. If all Recipient's\nrights under this Agreement terminate, Recipient agrees to cease use\nand distribution of the Program as soon as reasonably practicable.\nHowever, Recipient's obligations under this Agreement and any licenses\ngranted by Recipient relating to the Program shall continue and survive.\n\nEveryone is permitted to copy and distribute copies of this Agreement,\nbut in order to avoid inconsistency the Agreement is copyrighted and\nmay only be modified in the following manner. The Agreement Steward\nreserves the right to publish new versions (including revisions) of\nthis Agreement from time to time. No one other than the Agreement\nSteward has the right to modify this Agreement. The Eclipse Foundation\nis the initial Agreement Steward. The Eclipse Foundation may assign the\nresponsibility to serve as the Agreement Steward to a suitable separate\nentity. Each new version of the Agreement will be given a distinguishing\nversion number. The Program (including Contributions) may always be\nDistributed subject to the version of the Agreement under which it was\nreceived. In addition, after a new version of the Agreement is published,\nContributor may elect to Distribute the Program (including its\nContributions) under the new version.\n\nExcept as expressly stated in Sections 2(a) and 2(b) above, Recipient\nreceives no rights or licenses to the intellectual property of any\nContributor under this Agreement, whether expressly, by implication,\nestoppel or otherwise. All rights in the Program not expressly granted\nunder this Agreement are reserved. Nothing in this Agreement is intended\nto be enforceable by any entity that is not a Contributor or Recipient.\nNo third-party beneficiary rights are created under this Agreement.\n\nExhibit A - Form of Secondary Licenses Notice\n\n\"This Source Code may also be made available under the following\nSecondary Licenses when the conditions for such availability set forth\nin the Eclipse Public License, v. 2.0 are satisfied: {name license(s),\nversion(s), and exceptions or additional permissions here}.\"\n\n  Simply including a copy of this Agreement, including this Exhibit A\n  is not sufficient to license the Source Code under Secondary Licenses.\n\n  If it is not possible or desirable to put the notice in a particular\n  file, then You may include the notice in a location (such as a LICENSE\n  file in a relevant directory) where a recipient would be likely to\n  look for such a notice.\n\n  You may add additional accurate notices of copyright ownership.\n"
  },
  {
    "path": "examples/mysql_log/Makefile",
    "content": "R=../..\nLOCAL_CFLAGS=-Wall -ggdb\nLOCAL_LDFLAGS=${LIBMOSQ} -lmysqlclient\n\n.PHONY: all clean\n\nall : mosquitto_mysql_log\n\nmosquitto_mysql_log : mysql_log.o\n\t${CC} $^ -o $@ ${LOCAL_LDFLAGS}\n\nmysql_log.o : mysql_log.c\n\t${CC} -c $^ -o $@ ${LOCAL_CFLAGS} -I${R}/lib\n\nclean :\n\t-rm -f *.o mosquitto_mysql_log\n"
  },
  {
    "path": "examples/mysql_log/mysql_log.c",
    "content": "#include <signal.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifndef WIN32\n#  include <unistd.h>\n#else\n#  include <process.h>\n#  define snprintf sprintf_s\n#endif\n\n#include <mosquitto.h>\n#include <mysql/mysql.h>\n\n#define db_host \"localhost\"\n#define db_username \"mqtt_log\"\n#define db_password \"password\"\n#define db_database \"mqtt_log\"\n#define db_port 3306\n\n#define db_query \"INSERT INTO mqtt_log (topic, payload) VALUES (?,?)\"\n\n#define mqtt_host \"localhost\"\n#define mqtt_port 1883\n\nstatic int run = 1;\nstatic MYSQL_STMT *stmt = NULL;\n\n\nvoid handle_signal(int s)\n{\n\trun = 0;\n}\n\n\nvoid connect_callback(struct mosquitto *mosq, void *obj, int result)\n{\n}\n\n\nvoid message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)\n{\n\tMYSQL_BIND bind[2];\n\n\tmemset(bind, 0, sizeof(bind));\n\n\tbind[0].buffer_type = MYSQL_TYPE_STRING;\n\tbind[0].buffer = message->topic;\n\tbind[0].buffer_length = strlen(message->topic);\n\t// Note: payload is normally a binary blob and could contains\n\t// NULL byte. This sample does not handle it and assume payload is a\n\t// string.\n\tbind[1].buffer_type = MYSQL_TYPE_STRING;\n\tbind[1].buffer = message->payload;\n\tbind[1].buffer_length = message->payloadlen;\n\n\tmysql_stmt_bind_param(stmt, bind);\n\tmysql_stmt_execute(stmt);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tMYSQL *connection;\n\tmy_bool reconnect = true;\n\tchar clientid[24];\n\tstruct mosquitto *mosq;\n\tint rc = 0;\n\n\tsignal(SIGINT, handle_signal);\n\tsignal(SIGTERM, handle_signal);\n\n\tmysql_library_init(0, NULL, NULL);\n\tmosquitto_lib_init();\n\n\tconnection = mysql_init(NULL);\n\n\tif(connection){\n\t\tmysql_options(connection, MYSQL_OPT_RECONNECT, &reconnect);\n\n\t\tconnection = mysql_real_connect(connection, db_host, db_username, db_password, db_database, db_port, NULL, 0);\n\n\t\tif(connection){\n\t\t\tstmt = mysql_stmt_init(connection);\n\n\t\t\tmysql_stmt_prepare(stmt, db_query, strlen(db_query));\n\n\t\t\tmemset(clientid, 0, 24);\n\t\t\tsnprintf(clientid, 23, \"mysql_log_%d\", getpid());\n\t\t\tmosq = mosquitto_new(clientid, true, connection);\n\t\t\tif(mosq){\n\t\t\t\tmosquitto_connect_callback_set(mosq, connect_callback);\n\t\t\t\tmosquitto_message_callback_set(mosq, message_callback);\n\n\n\t\t\t\trc = mosquitto_connect(mosq, mqtt_host, mqtt_port, 60);\n\n\t\t\t\tmosquitto_subscribe(mosq, NULL, \"#\", 0);\n\n\t\t\t\twhile(run){\n\t\t\t\t\trc = mosquitto_loop(mosq, -1, 1);\n\t\t\t\t\tif(run && rc){\n\t\t\t\t\t\tsleep(20);\n\t\t\t\t\t\tmosquitto_reconnect(mosq);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmosquitto_destroy(mosq);\n\t\t\t}\n\t\t\tmysql_stmt_close(stmt);\n\n\t\t\tmysql_close(connection);\n\t\t}else{\n\t\t\tfprintf(stderr, \"Error: Unable to connect to database.\\n\");\n\t\t\tprintf(\"%s\\n\", mysql_error(connection));\n\t\t\trc = 1;\n\t\t}\n\t}else{\n\t\tfprintf(stderr, \"Error: Unable to start mysql.\\n\");\n\t\trc = 1;\n\t}\n\n\tmysql_library_end();\n\tmosquitto_lib_cleanup();\n\n\treturn rc;\n}\n\n"
  },
  {
    "path": "examples/publish/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\n.PHONY: all\n\nall : basic-1 basic-websockets-1\n\nbasic-1 : basic-1.o\n\t${CROSS_COMPILE}${CC} $^ -o $@ ${LIBMOSQ}\n\nbasic-websockets-1 : basic-websockets-1.o\n\t${CROSS_COMPILE}${CC} $^ -o $@ ${LIBMOSQ}\n\nbasic-1.o : basic-1.c ${LIBMOSQ}\n\t${CROSS_COMPILE}${CC} -c $< -o $@ -I${R}/include ${LOCAL_CPPFLAGS} ${LOCAL_CFLAGS}\n\nbasic-websockets-1.o : basic-websockets-1.c ${LIBMOSQ}\n\t${CROSS_COMPILE}${CC} -c $< -o $@ -I${R}/include ${LOCAL_CPPFLAGS} ${LOCAL_CFLAGS}\n\n${LIBMOSQ} :\n\t$(MAKE) -C ${R}/lib\n\nclean :\n\t-rm -f *.o sub_single sub_multiple\n"
  },
  {
    "path": "examples/publish/basic-1.c",
    "content": "/*\n * This example shows how to publish messages from outside of the Mosquitto network loop.\n */\n\n#include <mosquitto.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#ifndef UNUSED\n#  define UNUSED(A) (void)(A)\n#endif\n\n\n/* Callback called when the client receives a CONNACK message from the broker. */\nvoid on_connect(struct mosquitto *mosq, void *obj, int reason_code)\n{\n\tUNUSED(obj);\n\n\t/* Print out the connection result. mosquitto_connack_string() produces an\n\t * appropriate string for MQTT v3.x clients, the equivalent for MQTT v5.0\n\t * clients is mosquitto_reason_string().\n\t */\n\tprintf(\"on_connect: %s\\n\", mosquitto_connack_string(reason_code));\n\tif(reason_code != 0){\n\t\t/* If the connection fails for any reason, we don't want to keep on\n\t\t * retrying in this example, so disconnect. Without this, the client\n\t\t * will attempt to reconnect. */\n\t\tmosquitto_disconnect(mosq);\n\t}\n\n\t/* You may wish to set a flag here to indicate to your application that the\n\t * client is now connected. */\n}\n\n\n/* Callback called when the client knows to the best of its abilities that a\n * PUBLISH has been successfully sent. For QoS 0 this means the message has\n * been completely written to the operating system. For QoS 1 this means we\n * have received a PUBACK from the broker. For QoS 2 this means we have\n * received a PUBCOMP from the broker. */\nvoid on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\n\tprintf(\"Message with mid %d has been published.\\n\", mid);\n}\n\n\nint get_temperature(void)\n{\n\tsleep(1); /* Prevent a storm of messages - this pretend sensor works at 1Hz */\n\treturn (int)random()%100;\n}\n\n\n/* This function pretends to read some data from a sensor and publish it.*/\nvoid publish_sensor_data(struct mosquitto *mosq)\n{\n\tchar payload[20];\n\tint temp;\n\tint rc;\n\n\t/* Get our pretend data */\n\ttemp = get_temperature();\n\t/* Print it to a string for easy human reading - payload format is highly\n\t * application dependent. */\n\tsnprintf(payload, sizeof(payload), \"%d\", temp);\n\n\t/* Publish the message\n\t * mosq - our client instance\n\t * *mid = NULL - we don't want to know what the message id for this message is\n\t * topic = \"example/temperature\" - the topic on which this message will be published\n\t * payloadlen = strlen(payload) - the length of our payload in bytes\n\t * payload - the actual payload\n\t * qos = 2 - publish with QoS 2 for this example\n\t * retain = false - do not use the retained message feature for this message\n\t */\n\trc = mosquitto_publish(mosq, NULL, \"example/temperature\", (int)strlen(payload), payload, 2, false);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tfprintf(stderr, \"Error publishing: %s\\n\", mosquitto_strerror(rc));\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tstruct mosquitto *mosq;\n\tint rc;\n\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\t/* Required before calling other mosquitto functions */\n\tmosquitto_lib_init();\n\n\t/* Create a new client instance.\n\t * id = NULL -> ask the broker to generate a client id for us\n\t * clean session = true -> the broker should remove old sessions when we connect\n\t * obj = NULL -> we aren't passing any of our private data for callbacks\n\t */\n\tmosq = mosquitto_new(NULL, true, NULL);\n\tif(mosq == NULL){\n\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\n\t/* Configure callbacks. This should be done before connecting ideally. */\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\t/* Connect to test.mosquitto.org on port 1883, with a keepalive of 60 seconds.\n\t * This call makes the socket connection only, it does not complete the MQTT\n\t * CONNECT/CONNACK flow, you should use mosquitto_loop_start() or\n\t * mosquitto_loop_forever() for processing net traffic. */\n\trc = mosquitto_connect(mosq, \"test.mosquitto.org\", 1883, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_destroy(mosq);\n\t\tfprintf(stderr, \"Error: %s\\n\", mosquitto_strerror(rc));\n\t\treturn 1;\n\t}\n\n\t/* Run the network loop in a background thread, this call returns quickly. */\n\trc = mosquitto_loop_start(mosq);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_destroy(mosq);\n\t\tfprintf(stderr, \"Error: %s\\n\", mosquitto_strerror(rc));\n\t\treturn 1;\n\t}\n\n\t/* At this point the client is connected to the network socket, but may not\n\t * have completed CONNECT/CONNACK.\n\t * It is fairly safe to start queuing messages at this point, but if you\n\t * want to be really sure you should wait until after a successful call to\n\t * the connect callback.\n\t * In this case we know it is 1 second before we start publishing.\n\t */\n\twhile(1){\n\t\tpublish_sensor_data(mosq);\n\t}\n\n\tmosquitto_lib_cleanup();\n\treturn 0;\n}\n\n"
  },
  {
    "path": "examples/publish/basic-websockets-1.c",
    "content": "/*\n * This example shows how to publish messages from outside of the Mosquitto network loop.\n *\n * This is identical to basic-1.c apart from that it uses WebSockets.\n */\n\n#include <mosquitto.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#ifndef UNUSED\n#  define UNUSED(A) (void)(A)\n#endif\n\n\n/* Callback called when the client receives a CONNACK message from the broker. */\nvoid on_connect(struct mosquitto *mosq, void *obj, int reason_code)\n{\n\tUNUSED(obj);\n\n\t/* Print out the connection result. mosquitto_connack_string() produces an\n\t * appropriate string for MQTT v3.x clients, the equivalent for MQTT v5.0\n\t * clients is mosquitto_reason_string().\n\t */\n\tprintf(\"on_connect: %s\\n\", mosquitto_connack_string(reason_code));\n\tif(reason_code != 0){\n\t\t/* If the connection fails for any reason, we don't want to keep on\n\t\t * retrying in this example, so disconnect. Without this, the client\n\t\t * will attempt to reconnect. */\n\t\tmosquitto_disconnect(mosq);\n\t}\n\n\t/* You may wish to set a flag here to indicate to your application that the\n\t * client is now connected. */\n}\n\n\n/* Callback called when the client knows to the best of its abilities that a\n * PUBLISH has been successfully sent. For QoS 0 this means the message has\n * been completely written to the operating system. For QoS 1 this means we\n * have received a PUBACK from the broker. For QoS 2 this means we have\n * received a PUBCOMP from the broker. */\nvoid on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\n\tprintf(\"Message with mid %d has been published.\\n\", mid);\n}\n\n\nint get_temperature(void)\n{\n\tsleep(1); /* Prevent a storm of messages - this pretend sensor works at 1Hz */\n\treturn (int)random()%100;\n}\n\n\n/* This function pretends to read some data from a sensor and publish it.*/\nvoid publish_sensor_data(struct mosquitto *mosq)\n{\n\tchar payload[20];\n\tint temp;\n\tint rc;\n\n\t/* Get our pretend data */\n\ttemp = get_temperature();\n\t/* Print it to a string for easy human reading - payload format is highly\n\t * application dependent. */\n\tsnprintf(payload, sizeof(payload), \"%d\", temp);\n\n\t/* Publish the message\n\t * mosq - our client instance\n\t * *mid = NULL - we don't want to know what the message id for this message is\n\t * topic = \"example/temperature\" - the topic on which this message will be published\n\t * payloadlen = strlen(payload) - the length of our payload in bytes\n\t * payload - the actual payload\n\t * qos = 2 - publish with QoS 2 for this example\n\t * retain = false - do not use the retained message feature for this message\n\t */\n\trc = mosquitto_publish(mosq, NULL, \"example/temperature\", (int)strlen(payload), payload, 2, false);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tfprintf(stderr, \"Error publishing: %s\\n\", mosquitto_strerror(rc));\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tstruct mosquitto *mosq;\n\tint rc;\n\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\t/* Required before calling other mosquitto functions */\n\tmosquitto_lib_init();\n\n\t/* Create a new client instance.\n\t * id = NULL -> ask the broker to generate a client id for us\n\t * clean session = true -> the broker should remove old sessions when we connect\n\t * obj = NULL -> we aren't passing any of our private data for callbacks\n\t */\n\tmosq = mosquitto_new(NULL, true, NULL);\n\tif(mosq == NULL){\n\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_TRANSPORT, MOSQ_T_WEBSOCKETS);\n\n\t/* Configure callbacks. This should be done before connecting ideally. */\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\t/* Connect to test.mosquitto.org on port 1883, with a keepalive of 60 seconds.\n\t * This call makes the socket connection only, it does not complete the MQTT\n\t * CONNECT/CONNACK flow, you should use mosquitto_loop_start() or\n\t * mosquitto_loop_forever() for processing net traffic. */\n\trc = mosquitto_connect(mosq, \"test.mosquitto.org\", 8080, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_destroy(mosq);\n\t\tfprintf(stderr, \"Error: %s\\n\", mosquitto_strerror(rc));\n\t\treturn 1;\n\t}\n\n\t/* Run the network loop in a background thread, this call returns quickly. */\n\trc = mosquitto_loop_start(mosq);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_destroy(mosq);\n\t\tfprintf(stderr, \"Error: %s\\n\", mosquitto_strerror(rc));\n\t\treturn 1;\n\t}\n\n\t/* At this point the client is connected to the network socket, but may not\n\t * have completed CONNECT/CONNACK.\n\t * It is fairly safe to start queuing messages at this point, but if you\n\t * want to be really sure you should wait until after a successful call to\n\t * the connect callback.\n\t * In this case we know it is 1 second before we start publishing.\n\t */\n\twhile(1){\n\t\tpublish_sensor_data(mosq);\n\t}\n\n\tmosquitto_lib_cleanup();\n\treturn 0;\n}\n\n"
  },
  {
    "path": "examples/subscribe/basic-1.c",
    "content": "/*\n * This example shows how to write a client that subscribes to a topic and does\n * not do anything other than handle the messages that are received.\n */\n\n#include <mosquitto.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n\n/* Callback called when the client receives a CONNACK message from the broker. */\nvoid on_connect(struct mosquitto *mosq, void *obj, int reason_code)\n{\n\tint rc;\n\t/* Print out the connection result. mosquitto_connack_string() produces an\n\t * appropriate string for MQTT v3.x clients, the equivalent for MQTT v5.0\n\t * clients is mosquitto_reason_string().\n\t */\n\tprintf(\"on_connect: %s\\n\", mosquitto_connack_string(reason_code));\n\tif(reason_code != 0){\n\t\t/* If the connection fails for any reason, we don't want to keep on\n\t\t * retrying in this example, so disconnect. Without this, the client\n\t\t * will attempt to reconnect. */\n\t\tmosquitto_disconnect(mosq);\n\t}\n\n\t/* Making subscriptions in the on_connect() callback means that if the\n\t * connection drops and is automatically resumed by the client, then the\n\t * subscriptions will be recreated when the client reconnects. */\n\trc = mosquitto_subscribe(mosq, NULL, \"example/temperature\", 1);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tfprintf(stderr, \"Error subscribing: %s\\n\", mosquitto_strerror(rc));\n\t\t/* We might as well disconnect if we were unable to subscribe */\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\n/* Callback called when the broker sends a SUBACK in response to a SUBSCRIBE. */\nvoid on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\tint i;\n\tbool have_subscription = false;\n\n\t/* In this example we only subscribe to a single topic at once, but a\n\t * SUBSCRIBE can contain many topics at once, so this is one way to check\n\t * them all. */\n\tfor(i=0; i<qos_count; i++){\n\t\tprintf(\"on_subscribe: %d:granted qos = %d\\n\", i, granted_qos[i]);\n\t\tif(granted_qos[i] <= 2){\n\t\t\thave_subscription = true;\n\t\t}\n\t}\n\tif(have_subscription == false){\n\t\t/* The broker rejected all of our subscriptions, we know we only sent\n\t\t * the one SUBSCRIBE, so there is no point remaining connected. */\n\t\tfprintf(stderr, \"Error: All subscriptions rejected.\\n\");\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\n/* Callback called when the client receives a message. */\nvoid on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)\n{\n\t/* This blindly prints the payload, but the payload can be anything so take care. */\n\tprintf(\"%s %d %s\\n\", msg->topic, msg->qos, (char *)msg->payload);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tstruct mosquitto *mosq;\n\tint rc;\n\n\t/* Required before calling other mosquitto functions */\n\tmosquitto_lib_init();\n\n\t/* Create a new client instance.\n\t * id = NULL -> ask the broker to generate a client id for us\n\t * clean session = true -> the broker should remove old sessions when we connect\n\t * obj = NULL -> we aren't passing any of our private data for callbacks\n\t */\n\tmosq = mosquitto_new(NULL, true, NULL);\n\tif(mosq == NULL){\n\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\n\t/* Configure callbacks. This should be done before connecting ideally. */\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe);\n\tmosquitto_message_callback_set(mosq, on_message);\n\n\t/* Connect to test.mosquitto.org on port 1883, with a keepalive of 60 seconds.\n\t * This call makes the socket connection only, it does not complete the MQTT\n\t * CONNECT/CONNACK flow, you should use mosquitto_loop_start() or\n\t * mosquitto_loop_forever() for processing net traffic. */\n\trc = mosquitto_connect(mosq, \"test.mosquitto.org\", 1883, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_destroy(mosq);\n\t\tfprintf(stderr, \"Error: %s\\n\", mosquitto_strerror(rc));\n\t\treturn 1;\n\t}\n\n\t/* Run the network loop in a blocking call. The only thing we do in this\n\t * example is to print incoming messages, so a blocking call here is fine.\n\t *\n\t * This call will continue forever, carrying automatic reconnections if\n\t * necessary, until the user calls mosquitto_disconnect().\n\t */\n\tmosquitto_loop_forever(mosq, -1, 1);\n\n\tmosquitto_lib_cleanup();\n\treturn 0;\n}\n\n"
  },
  {
    "path": "examples/subscribe_simple/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\n.PHONY: all\n\nall : sub_callback sub_single sub_multiple\n\nsub_callback : callback.o\n\t${CROSS_COMPILE}${CC} $^ -o $@ ${LIBMOSQ}\n\nsub_single : single.o\n\t${CROSS_COMPILE}${CC} $^ -o $@ ${LIBMOSQ}\n\nsub_multiple : multiple.o\n\t${CROSS_COMPILE}${CC} $^ -o $@ ${LIBMOSQ}\n\ncallback.o : callback.c ${LIBMOSQ}\n\t${CROSS_COMPILE}${CC} -c $< -o $@ -I${R}/lib ${LOCAL_CFLAGS}\n\nsingle.o : single.c ${LIBMOSQ}\n\t${CROSS_COMPILE}${CC} -c $< -o $@ -I${R}/lib ${LOCAL_CFLAGS}\n\nmultiple.o : multiple.c ${LIBMOSQ}\n\t${CROSS_COMPILE}${CC} -c $< -o $@ -I${R}/lib ${LOCAL_CFLAGS}\n\n${LIBMOSQ}:\n\t$(MAKE) -C ${R}/lib\n\nclean :\n\t-rm -f *.o sub_single sub_multiple\n"
  },
  {
    "path": "examples/subscribe_simple/callback.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include \"mosquitto.h\"\n\n\nint on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg)\n{\n\tprintf(\"%s %s (%d)\\n\", msg->topic, (const char *)msg->payload, msg->payloadlen);\n\treturn 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\n\tmosquitto_lib_init();\n\n\trc = mosquitto_subscribe_callback(\n\t\t\ton_message, NULL,\n\t\t\t\"irc/#\", 0,\n\t\t\t\"test.mosquitto.org\", 1883,\n\t\t\tNULL, 60, true,\n\t\t\tNULL, NULL,\n\t\t\tNULL, NULL);\n\n\tif(rc){\n\t\tprintf(\"Error: %s\\n\", mosquitto_strerror(rc));\n\t}\n\n\tmosquitto_lib_cleanup();\n\n\treturn rc;\n}\n\n"
  },
  {
    "path": "examples/subscribe_simple/multiple.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include \"mosquitto.h\"\n\n#define COUNT 3\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tint i;\n\tstruct mosquitto_message *msg;\n\n\tmosquitto_lib_init();\n\n\trc = mosquitto_subscribe_simple(\n\t\t\t&msg, COUNT, true,\n\t\t\t\"irc/#\", 0,\n\t\t\t\"test.mosquitto.org\", 1883,\n\t\t\tNULL, 60, true,\n\t\t\tNULL, NULL,\n\t\t\tNULL, NULL);\n\n\tif(rc){\n\t\tprintf(\"Error: %s\\n\", mosquitto_strerror(rc));\n\t\tmosquitto_lib_cleanup();\n\t\treturn rc;\n\t}\n\n\tfor(i=0; i<COUNT; i++){\n\t\tprintf(\"%s %s\\n\", msg[i].topic, (char *)msg[i].payload);\n\t\tmosquitto_message_free_contents(&msg[i]);\n\t}\n\tfree(msg);\n\n\tmosquitto_lib_cleanup();\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "examples/subscribe_simple/single.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include \"mosquitto.h\"\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto_message *msg;\n\n\tmosquitto_lib_init();\n\n\trc = mosquitto_subscribe_simple(\n\t\t\t&msg, 1, true,\n\t\t\t\"irc/#\", 0,\n\t\t\t\"test.mosquitto.org\", 1883,\n\t\t\tNULL, 60, true,\n\t\t\tNULL, NULL,\n\t\t\tNULL, NULL);\n\n\tif(rc){\n\t\tprintf(\"Error: %s\\n\", mosquitto_strerror(rc));\n\t\tmosquitto_lib_cleanup();\n\t\treturn rc;\n\t}\n\n\tprintf(\"%s %s\\n\", msg->topic, (char *)msg->payload);\n\tmosquitto_message_free(&msg);\n\n\tmosquitto_lib_cleanup();\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "examples/temperature_conversion/Makefile",
    "content": "R=../..\nLOCAL_CFLAGS=-Wall -ggdb -I${R}/lib -I${R}/lib/cpp\nLOCAL_LDFLAGS=-L${R}/lib ${R}/lib/cpp/libmosquittopp.so.1 ${LIBMOSQ}\n\n.PHONY: all clean\n\nall : mqtt_temperature_conversion\n\nmqtt_temperature_conversion : main.o temperature_conversion.o\n\t${CXX} $^ -o $@ ${LOCAL_LDFLAGS}\n\nmain.o : main.cpp\n\t${CXX} -c $^ -o $@ ${LOCAL_CFLAGS}\n\ntemperature_conversion.o : temperature_conversion.cpp\n\t${CXX} -c $^ -o $@ ${LOCAL_CFLAGS}\n\nclean :\n\t-rm -f *.o mqtt_temperature_conversion\n"
  },
  {
    "path": "examples/temperature_conversion/main.cpp",
    "content": "#include \"temperature_conversion.h\"\n\n\nint main(int argc, char *argv[])\n{\n\tclass mqtt_tempconv *tempconv;\n\tint rc;\n\n\tmosqpp::lib_init();\n\n\ttempconv = new mqtt_tempconv(\"tempconv\", \"localhost\", 1883);\n\ttempconv->loop_forever();\n\n\tmosqpp::lib_cleanup();\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "examples/temperature_conversion/readme.txt",
    "content": "This is a simple example of the C++ library mosquittopp.\n\nIt is a client that subscribes to the topic temperature/celsius which should\nhave temperature data in text form being published to it. It reads this data as\na Celsius temperature, converts to Fahrenheit and republishes on\ntemperature/fahrenheit.\n"
  },
  {
    "path": "examples/temperature_conversion/temperature_conversion.cpp",
    "content": "#include <cstdio>\n#include <cstring>\n\n#include \"temperature_conversion.h\"\n#include <mosquittopp.h>\n\nmqtt_tempconv::mqtt_tempconv(const char *id, const char *host, int port) : mosquittopp(id)\n{\n\tint keepalive = 60;\n\n\t/* Connect immediately. This could also be done by calling\n\t * mqtt_tempconv->connect(). */\n\tconnect(host, port, keepalive);\n};\n\nmqtt_tempconv::~mqtt_tempconv()\n{\n}\n\n\nvoid mqtt_tempconv::on_connect(int rc)\n{\n\tprintf(\"Connected with code %d.\\n\", rc);\n\tif(rc == 0){\n\t\t/* Only attempt to subscribe on a successful connect. */\n\t\tsubscribe(NULL, \"temperature/celsius\");\n\t}\n}\n\n\nvoid mqtt_tempconv::on_message(const struct mosquitto_message *message)\n{\n\tdouble temp_celsius, temp_fahrenheit;\n\tchar buf[51];\n\n\tif(!strcmp(message->topic, \"temperature/celsius\")){\n\t\tmemset(buf, 0, 51*sizeof(char));\n\t\t/* Copy N-1 bytes to ensure always 0 terminated. */\n\t\tmemcpy(buf, message->payload, 50*sizeof(char));\n\t\ttemp_celsius = atof(buf);\n\t\ttemp_fahrenheit = temp_celsius*9.0/5.0 + 32.0;\n\t\tsnprintf(buf, 50, \"%f\", temp_fahrenheit);\n\t\tpublish(NULL, \"temperature/fahrenheit\", strlen(buf), buf);\n\t}\n}\n\n\nvoid mqtt_tempconv::on_subscribe(int mid, int qos_count, const int *granted_qos)\n{\n\tprintf(\"Subscription succeeded.\\n\");\n}\n\n"
  },
  {
    "path": "examples/temperature_conversion/temperature_conversion.h",
    "content": "#ifndef TEMPERATURE_CONVERSION_H\n#define TEMPERATURE_CONVERSION_H\n\n#include <mosquittopp.h>\n\nclass mqtt_tempconv : public mosqpp::mosquittopp\n{\npublic:\n\tmqtt_tempconv(const char *id, const char *host, int port);\n\t~mqtt_tempconv();\n\n\tvoid on_connect(int rc);\n\tvoid on_message(const struct mosquitto_message *message);\n\tvoid on_subscribe(int mid, int qos_count, const int *granted_qos);\n};\n\n#endif\n"
  },
  {
    "path": "format.sh",
    "content": "#!/bin/bash\n\nfind -name '*.c' -exec uncrustify -c .uncrustify.cfg --no-backup {} \\;\nfind -name '*.cpp' -exec uncrustify -c .uncrustify.cfg --no-backup {} \\;\nfind -name '*.h' -exec uncrustify -c .uncrustify.cfg --no-backup {} \\;\n\ngit checkout deps/picohttpparser/picohttpparser.h\ngit checkout deps/picohttpparser/picohttpparser.c\ngit checkout deps/utlist.h\ngit checkout deps/uthash.h\nrm -f deps/uthash.h.uncrustify\n"
  },
  {
    "path": "fuzzing/Makefile",
    "content": ".PHONY: all clean\n\nall:\n\t./generate_packet_corpora.py\n\tzip -r corpora/db_dump_seed_corpus.zip ../test/apps/db_dump/data/\n\t$(MAKE) -C apps $@\n\t$(MAKE) -C broker $@\n\t$(MAKE) -C libcommon $@\n\t$(MAKE) -C plugins $@\n\nclean:\n\t-rm -rf corpora/broker corpora/client\n\t-rm -f corpora/broker_packet_seed_corpus.zip corpora/client_packet_seed_corpus.zip\n\t-rm -f corpora/db_dump_seed_corpus.zip\n\t$(MAKE) -C apps $@\n\t$(MAKE) -C broker $@\n\t$(MAKE) -C libcommon $@\n\t$(MAKE) -C plugins $@\n"
  },
  {
    "path": "fuzzing/apps/Makefile",
    "content": ".PHONY: all clean\n\nall:\n\t$(MAKE) -C db_dump $@\n\t$(MAKE) -C mosquitto_passwd $@\n\nclean:\n\t$(MAKE) -C db_dump $@\n\t$(MAKE) -C mosquitto_passwd $@\n"
  },
  {
    "path": "fuzzing/apps/db_dump/Makefile",
    "content": "R=../../..\ninclude ${R}/fuzzing/config.mk\n\n.PHONY: all clean\n\nFUZZERS:= \\\n\tdb_dump_fuzz_load \\\n\tdb_dump_fuzz_load_client_stats \\\n\tdb_dump_fuzz_load_stats\n\nLOCAL_CPPFLAGS+=\nLOCAL_CXXFLAGS+=-g -Wall -Werror -pthread\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=$(LIB_FUZZING_ENGINE) ${R}/apps/db_dump/mosquitto_db_dump.a ${R}/libcommon/libmosquitto_common.a -Wl,-Bstatic -largon2 -Wl,-Bdynamic -lcjson -lcrypto\n\nall: $(FUZZERS)\n\ndb_dump_fuzz_load : db_dump_fuzz_load.cpp\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/db_dump_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\ndb_dump_fuzz_load_client_stats : db_dump_fuzz_load_client_stats.cpp\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/db_dump_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\ndb_dump_fuzz_load_stats : db_dump_fuzz_load_stats.cpp\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/db_dump_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\nclean:\n\trm -f *.o $(FUZZERS)\n"
  },
  {
    "path": "fuzzing/apps/db_dump/db_dump_fuzz_load.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\n/*\n * Test loading a file\n */\n\n\n/* The fuzz-only main function. */\nextern \"C\" int db_dump_fuzz_main(int argc, char *argv[]);\n\n\nvoid run_db_dump(char *filename)\n{\n\tchar *argv[2];\n\tint argc = 2;\n\n\targv[0] = strdup(\"mosquitto_db_dump\");\n\targv[1] = filename;\n\n\tdb_dump_fuzz_main(argc, argv);\n\n\tfree(argv[0]);\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar filename[100];\n\tFILE *fptr;\n\n\tumask(0077);\n\n\tsnprintf(filename, sizeof(filename), \"db_dump_%d.db\", getpid());\n\tfptr = fopen(filename, \"wb\");\n\tif(!fptr){\n\t\treturn 1;\n\t}\n\tfwrite(data, 1, size, fptr);\n\tfclose(fptr);\n\n\trun_db_dump(filename);\n\n\tunlink(filename);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/apps/db_dump/db_dump_fuzz_load_client_stats.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\n/*\n * Test loading a file, with client stats\n */\n\n\n/* The fuzz-only main function. */\nextern \"C\" int db_dump_fuzz_main(int argc, char *argv[]);\n\n\nvoid run_db_dump(char *filename)\n{\n\tchar *argv[3];\n\tint argc = 3;\n\n\targv[0] = strdup(\"mosquitto_db_dump\");\n\targv[1] = strdup(\"--client-stats\");\n\targv[2] = filename;\n\n\tdb_dump_fuzz_main(argc, argv);\n\n\tfree(argv[0]);\n\tfree(argv[1]);\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar filename[100];\n\tFILE *fptr;\n\n\tumask(0077);\n\n\tsnprintf(filename, sizeof(filename), \"db_dump_client_stats_%d.db\", getpid());\n\tfptr = fopen(filename, \"wb\");\n\tif(!fptr){\n\t\treturn 1;\n\t}\n\tfwrite(data, 1, size, fptr);\n\tfclose(fptr);\n\n\trun_db_dump(filename);\n\n\tunlink(filename);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/apps/db_dump/db_dump_fuzz_load_stats.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\n/*\n * Test loading a file\n */\n\n\n/* The fuzz-only main function. */\nextern \"C\" int db_dump_fuzz_main(int argc, char *argv[]);\n\n\nvoid run_db_dump(char *filename)\n{\n\tchar *argv[3];\n\tint argc = 3;\n\n\targv[0] = strdup(\"mosquitto_db_dump\");\n\targv[1] = strdup(\"--stats\");\n\targv[2] = filename;\n\n\tdb_dump_fuzz_main(argc, argv);\n\n\tfree(argv[0]);\n\tfree(argv[1]);\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar filename[100];\n\tFILE *fptr;\n\n\tumask(0077);\n\n\tsnprintf(filename, sizeof(filename), \"db_dump_stats_%d.db\", getpid());\n\tfptr = fopen(filename, \"wb\");\n\tif(!fptr){\n\t\treturn 1;\n\t}\n\tfwrite(data, 1, size, fptr);\n\tfclose(fptr);\n\n\trun_db_dump(filename);\n\n\tunlink(filename);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/apps/mosquitto_passwd/Makefile",
    "content": "R=../../..\ninclude ${R}/fuzzing/config.mk\n\n.PHONY: all clean\n\nFUZZERS:= \\\n\tmosquitto_passwd_fuzz_load\n\nLOCAL_CPPFLAGS+=\nLOCAL_CXXFLAGS+=-g -Wall -Werror -pthread\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=$(LIB_FUZZING_ENGINE) ${R}/apps/mosquitto_passwd/mosquitto_passwd.a -lssl -lcrypto ${R}/libcommon/libmosquitto_common.a -Wl,-Bstatic -largon2 -Wl,-Bdynamic\n\nall: $(FUZZERS)\n\nmosquitto_passwd_fuzz_load : mosquitto_passwd_fuzz_load.cpp\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/db_dump_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\nclean:\n\trm -f *.o $(FUZZERS)\n"
  },
  {
    "path": "fuzzing/apps/mosquitto_passwd/mosquitto_passwd_fuzz_load.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\n/*\n * Test loading a file\n */\n\n\n/* The fuzz-only main function. */\nextern \"C\" int mosquitto_passwd_fuzz_main(int argc, char *argv[]);\n\n\nvoid run_mosquitto_passwd(char *filename)\n{\n\tchar *argv[5];\n\tint argc = 5;\n\n\targv[0] = strdup(\"mosquitto_passwd\");\n\targv[1] = strdup(\"-b\");\n\targv[2] = filename;\n\targv[3] = strdup(\"username\");\n\targv[4] = strdup(\"password\");\n\n\tmosquitto_passwd_fuzz_main(argc, argv);\n\n\tfree(argv[0]);\n\tfree(argv[1]);\n\tfree(argv[3]);\n\tfree(argv[4]);\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar filename[100];\n\tFILE *fptr;\n\n\tumask(0077);\n\n\tsnprintf(filename, sizeof(filename), \"mosquitto_passwd_%d\", getpid());\n\tfptr = fopen(filename, \"wb\");\n\tif(!fptr){\n\t\treturn 1;\n\t}\n\tfwrite(data, 1, size, fptr);\n\tfclose(fptr);\n\n\trun_mosquitto_passwd(filename);\n\n\tunlink(filename);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/broker/Makefile",
    "content": "R=../..\ninclude ${R}/fuzzing/config.mk\n\n.PHONY: all clean\n\nFUZZERS:= \\\n\tbroker_fuzz_acl_file \\\n\tbroker_fuzz_password_file \\\n\tbroker_fuzz_proxy_v1 \\\n\tbroker_fuzz_proxy_v2 \\\n\tbroker_fuzz_psk_file \\\n\tbroker_fuzz_queue_msg \\\n\tbroker_fuzz_read_handle \\\n\tbroker_fuzz_test_config\n\nLOCAL_CPPFLAGS+=-I${R}/include/ -I${R}/src -I${R}/lib -I${R} -I${R}/common -I${R}/deps \\\n\t-DWITH_BRIDGE -DWITH_BROKER -DWITH_CONTROL -DWITH_EPOLL \\\n\t-DWITH_MEMORY_TRACKING -DWITH_PERSISTENCE -DWITH_SOCKS -DWITH_SYSTEMD \\\n\t-DWITH_SYS_TREE -DWITH_TLS -DWITH_TLS_PSK -DWITH_UNIX_SOCKETS -DWITH_WEBSOCKETS=WS_IS_BUILTIN\nLOCAL_CXXFLAGS+=-g -Wall -Werror -pthread\nLOCAL_CFLAGS+=-g -Wall -Werror -pthread\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=$(LIB_FUZZING_ENGINE) -lssl -lcrypto -lcjson -lm ${R}/libcommon/libmosquitto_common.a -Wl,-Bdynamic -Wl,-Bstatic -largon2 -Wl,-Bdynamic\nBROKER_A=${R}/src/mosquitto_broker.a\n\nPACKET_FUZZERS:= \\\n\tbroker_fuzz_handle_auth \\\n\tbroker_fuzz_handle_connect \\\n\tbroker_fuzz_handle_publish \\\n\tbroker_fuzz_handle_subscribe \\\n\tbroker_fuzz_handle_unsubscribe\n\nall: $(FUZZERS) $(PACKET_FUZZERS)\n\n${PACKET_FUZZERS} : %: %.cpp fuzz_packet_read_base.o ${R}/src/mosquitto_broker.a\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< fuzz_packet_read_base.o  $(BROKER_A) $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\nfuzz_packet_read_base.o : fuzz_packet_read_base.c\n\t$(CC) $(LOCAL_CFLAGS) $(LOCAL_CPPFLAGS) -c -o $@ $<\n\nbroker_fuzz_acl_file : broker_fuzz_acl_file.cpp ${R}/src/mosquitto_broker.a\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< $(BROKER_A) $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/broker_acl_file_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\tcp ${R}/fuzzing/corpora/broker_acl_file.dict ${OUT}/$@.dict\n\nbroker_fuzz_password_file : broker_fuzz_password_file.cpp ${R}/src/mosquitto_broker.a\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< $(BROKER_A) $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/broker_password_file_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\nbroker_fuzz_proxy_v1 : broker_fuzz_proxy_v1.cpp ${R}/src/proxy_v1.o\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< ${R}/src/proxy_v1.o $(LOCAL_LIBADD)\n\tcp ${R}/fuzzing/corpora/broker_fuzz_proxy_v1_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\tinstall $@ ${OUT}/$@\n\nbroker_fuzz_proxy_v2 : broker_fuzz_proxy_v2.cpp ${R}/src/proxy_v2.o\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< ${R}/src/proxy_v2.o $(LOCAL_LIBADD)\n\tcp ${R}/fuzzing/corpora/broker_fuzz_proxy_v2_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\tinstall $@ ${OUT}/$@\n\nbroker_fuzz_psk_file : broker_fuzz_psk_file.cpp ${R}/src/mosquitto_broker.a\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< $(BROKER_A) $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/broker_psk_file_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\nbroker_fuzz_queue_msg : broker_fuzz_queue_msg.cpp ${R}/src/mosquitto_broker.a\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< $(BROKER_A) $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/broker_queue_msg_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\nbroker_fuzz_read_handle : broker_fuzz_read_handle.cpp fuzz_packet_read_base.o ${R}/src/mosquitto_broker.a\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(BROKER_A) $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/broker_packet_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\nbroker_fuzz_test_config : broker_fuzz_test_config.cpp ${R}/src/mosquitto_broker.a\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $< $(BROKER_A) $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/broker_fuzz_test_config_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\tcp ${R}/fuzzing/corpora/broker_conf.dict ${OUT}/$@.dict\n\nclean:\n\trm -f *.o $(FUZZERS) $(PACKET_FUZZERS)\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <arpa/inet.h>\n#include <errno.h>\n#include <netinet/in.h>\n#include <pthread.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"broker_fuzz.h\"\n\n#define PORT 1883\n\n/* The broker fuzz-only main function. */\nextern \"C\" int mosquitto_fuzz_main(int argc, char *argv[]);\n\n\nvoid *run_broker(void *args)\n{\n\tchar *argv[4];\n\tint argc = 4;\n\n\targv[0] = strdup(\"mosquitto\");\n\targv[1] = strdup(\"-v\");\n\targv[2] = strdup(\"-c\");\n\targv[3] = strdup(\"/tmp/mosquitto.conf\");\n\n\tmosquitto_fuzz_main(argc, argv);\n\n\tfor(int i=0; i<argc; i++){\n\t\tfree(argv[i]);\n\t}\n\n\tpthread_exit(NULL);\n\treturn NULL;\n}\n\n\nvoid recv_timeout(int sock, void *buf, size_t len, int timeout_us)\n{\n\tstruct timeval tv = {0, timeout_us};\n\n\tsetsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));\n\t(void)recv(sock, buf, len, 0);\n}\n\n\nint connect_retrying(int port)\n{\n\tstruct sockaddr_in addr;\n\tint sock;\n\tint rc;\n\n\tmemset(&addr, 0, sizeof(addr));\n\taddr.sin_family = AF_INET;\n\taddr.sin_port = htons(port);\n\taddr.sin_addr.s_addr = inet_addr(\"127.0.0.1\");\n\n\tsock = socket(AF_INET, SOCK_STREAM, 0);\n\twhile(1){\n\t\terrno = 0;\n\t\trc = connect(sock, (struct sockaddr *)&addr, sizeof(addr));\n\t\tif(rc < 0){\n\t\t\tstruct timespec ts;\n\t\t\tts.tv_sec = 0;\n\t\t\tts.tv_nsec = 10000000; /* 10ms */\n\t\t\tnanosleep(&ts, NULL);\n\t\t}else{\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn sock;\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tstruct fuzz_data fuzz;\n\tpthread_t thread;\n\tFILE *fptr;\n\n\tif(size < kMinInputLength || size > kMaxInputLength){\n\t\treturn 0;\n\t}\n\n\tsignal(SIGPIPE, SIG_IGN);\n\tumask(0077);\n\n\tmemset(&fuzz, 0, sizeof(fuzz));\n\tfuzz.port = PORT;\n\tfuzz.size = size;\n\tfuzz.data = (uint8_t *)data;\n\n\tfptr = fopen(\"/tmp/mosquitto.conf\", \"wb\");\n\tif(!fptr){\n\t\tprintf(\"FILE %s\\n\", strerror(errno));\n\t\tabort();\n\t}\n\tfprintf(fptr, \"user root\\n\");\n\tfprintf(fptr, \"listener %d\\n\", PORT);\n\tfprintf(fptr, \"allow_anonymous true\\n\");\n\tfclose(fptr);\n\n\tpthread_create(&thread, NULL, run_broker, &fuzz);\n\trun_client(&fuzz);\n\tpthread_join(thread, NULL);\n\n\tunlink(\"/tmp/mosquitto.conf\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz.h",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef BROKER_FUZZ_H\n#define BROKER_FUZZ_H\n\n#define kMinInputLength 5\n#define kMaxInputLength 10000\n\nstruct fuzz_data {\n\tuint8_t *data;\n\tsize_t size;\n\tuint16_t port;\n};\n\nvoid *run_broker(void *args);\nvoid recv_timeout(int sock, void *buf, size_t len, int timeout_us);\nint connect_retrying(int port);\nvoid run_client(struct fuzz_data *fuzz);\n\n#endif\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_acl_file.cpp",
    "content": "/*\nCopyright (c) 2024 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\n/*\n * Broker check of acl file\n */\nextern \"C\" {\n#include \"mosquitto_broker_internal.h\"\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar filename[100];\n\tFILE *fptr;\n\tstruct mosquitto__config config = {0};\n\n\tdb.config = &config;\n\tconfig.log_type = 0;\n\tconfig.log_dest = 0;\n\n\tumask(0077);\n\n\tsnprintf(filename, sizeof(filename), \"/tmp/acl_file_%d\", getpid());\n\tfptr = fopen(filename, \"wb\");\n\tif(!fptr){\n\t\treturn 1;\n\t}\n\tfwrite(data, 1, size, fptr);\n\tfclose(fptr);\n\n\tconfig.security_options.acl_data.acl_file = strdup(filename);\n\n\tlog__init(&config);\n\tmosquitto_security_init_default();\n\tmosquitto_security_cleanup_default();\n\tconfig__cleanup(&config);\n\n\tunlink(filename);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_handle_auth.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#define kMaxInputLength 100000\n#include \"fuzz_packet_read_base.h\"\n\n\nextern \"C\" int fuzz_packet_read_init(struct mosquitto *context)\n{\n\tcontext->protocol = mosq_p_mqtt5;\n\tcontext->auth_method = strdup(\"FUZZ\");\n\treturn !context->auth_method;\n}\n\n\nextern \"C\" void fuzz_packet_read_cleanup(struct mosquitto *context)\n{\n\tfree(context->auth_method);\n\tcontext->auth_method = NULL;\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tint rc = fuzz_packet_read_base(data, size, handle__auth);\n\treturn rc;\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_handle_connect.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#define kMaxInputLength 100000\n#include \"fuzz_packet_read_base.h\"\n\n\nextern \"C\" int fuzz_basic_auth(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_basic_auth *ed = (struct mosquitto_evt_basic_auth *)event_data;\n\n\t/* This is a check that is ultimately determined by the fuzz input data, so\n\t * the fuzzer can discover how to access both the fail/success cases.\n\t */\n\tif(ed->client->id && (ed->client->id[0]%2 == 0)){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nextern \"C\" int fuzz_packet_read_init(struct mosquitto *context)\n{\n\tcontext->listener->security_options->pid = (mosquitto_plugin_id_t *)calloc(1, sizeof(mosquitto_plugin_id_t));\n\tif(!context->listener->security_options->pid){\n\t\treturn 1;\n\t}\n\tmosquitto_callback_register(context->listener->security_options->pid,\n\t\t\tMOSQ_EVT_BASIC_AUTH, fuzz_basic_auth, NULL, NULL);\n\n\treturn 0;\n}\n\n\nextern \"C\" void fuzz_packet_read_cleanup(struct mosquitto *context)\n{\n\tmosquitto_callback_unregister(context->listener->security_options->pid,\n\t\t\tMOSQ_EVT_BASIC_AUTH, fuzz_basic_auth, NULL);\n\n\tfree(context->listener->security_options->pid);\n\tcontext->listener->security_options->pid = NULL;\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\treturn fuzz_packet_read_base(data, size, handle__connect);\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_handle_publish.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"fuzz_packet_read_base.h\"\n\n\nextern \"C\" int fuzz_acl_check(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_acl_check *ed = (struct mosquitto_evt_acl_check *)event_data;\n\n\t/* This is a check that is ultimately determined by the fuzz input data, so\n\t * the fuzzer can discover how to access both the fail/success cases.\n\t */\n\tif(ed->topic && (ed->topic[0]%2 == 0)){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nextern \"C\" int fuzz_packet_read_init(struct mosquitto *context)\n{\n\tcontext->listener->security_options->pid = (mosquitto_plugin_id_t *)calloc(1, sizeof(mosquitto_plugin_id_t));\n\tif(!context->listener->security_options->pid){\n\t\treturn 1;\n\t}\n\tmosquitto_callback_register(context->listener->security_options->pid,\n\t\t\tMOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL, NULL);\n\n\treturn 0;\n}\n\n\nextern \"C\" void fuzz_packet_read_cleanup(struct mosquitto *context)\n{\n\tmosquitto_callback_unregister(context->listener->security_options->pid,\n\t\t\tMOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL);\n\n\tfree(context->listener->security_options->pid);\n\tcontext->listener->security_options->pid = NULL;\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\treturn fuzz_packet_read_base(data, size, handle__publish);\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_handle_subscribe.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#define kMaxInputLength 100000\n#include \"fuzz_packet_read_base.h\"\n\n\nextern \"C\" int fuzz_acl_check(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_acl_check *ed = (struct mosquitto_evt_acl_check *)event_data;\n\n\t/* This is a check that is ultimately determined by the fuzz input data, so\n\t * the fuzzer can discover how to access both the fail/success cases.\n\t */\n\tif(ed->topic && (ed->topic[0]%2 == 0)){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nextern \"C\" int fuzz_packet_read_init(struct mosquitto *context)\n{\n\tcontext->listener->security_options->pid = (mosquitto_plugin_id_t *)calloc(1, sizeof(mosquitto_plugin_id_t));\n\tif(!context->listener->security_options->pid){\n\t\treturn 1;\n\t}\n\tmosquitto_callback_register(context->listener->security_options->pid,\n\t\t\tMOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL, NULL);\n\n\treturn 0;\n}\n\n\nextern \"C\" void fuzz_packet_read_cleanup(struct mosquitto *context)\n{\n\tmosquitto_callback_unregister(context->listener->security_options->pid,\n\t\t\tMOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL);\n\n\tfree(context->listener->security_options->pid);\n\tcontext->listener->security_options->pid = NULL;\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\treturn fuzz_packet_read_base(data, size, handle__subscribe);\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_handle_unsubscribe.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#define kMaxInputLength 100000\n#include \"fuzz_packet_read_base.h\"\n\n\nextern \"C\" int fuzz_acl_check(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_acl_check *ed = (struct mosquitto_evt_acl_check *)event_data;\n\n\t/* This is a check that is ultimately determined by the fuzz input data, so\n\t * the fuzzer can discover how to access both the fail/success cases.\n\t */\n\tif(ed->topic && (ed->topic[0]%2 == 0)){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nextern \"C\" int fuzz_packet_read_init(struct mosquitto *context)\n{\n\tcontext->listener->security_options->pid = (mosquitto_plugin_id_t *)calloc(1, sizeof(mosquitto_plugin_id_t));\n\tif(!context->listener->security_options->pid){\n\t\treturn 1;\n\t}\n\tmosquitto_callback_register(context->listener->security_options->pid,\n\t\t\tMOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL, NULL);\n\n\treturn 0;\n}\n\n\nextern \"C\" void fuzz_packet_read_cleanup(struct mosquitto *context)\n{\n\tmosquitto_callback_unregister(context->listener->security_options->pid,\n\t\t\tMOSQ_EVT_ACL_CHECK, fuzz_acl_check, NULL);\n\n\tfree(context->listener->security_options->pid);\n\tcontext->listener->security_options->pid = NULL;\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\treturn fuzz_packet_read_base(data, size, handle__unsubscribe);\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_password_file.cpp",
    "content": "/*\nCopyright (c) 2024 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\n/*\n * Broker check of password file\n */\nextern \"C\" {\n#include \"mosquitto_broker_internal.h\"\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar filename[100];\n\tFILE *fptr;\n\tstruct mosquitto__config config = {0};\n\n\tdb.config = &config;\n\tconfig.log_type = 0;\n\tconfig.log_dest = 0;\n\n\tumask(0077);\n\n\tsnprintf(filename, sizeof(filename), \"/tmp/password_file_%d\", getpid());\n\tfptr = fopen(filename, \"wb\");\n\tif(!fptr){\n\t\treturn 1;\n\t}\n\tfwrite(data, 1, size, fptr);\n\tfclose(fptr);\n\n\tconfig.security_options.password_data.password_file = strdup(filename);\n\n\tlog__init(&config);\n\tmosquitto_security_init_default();\n\tmosquitto_security_cleanup_default();\n\tconfig__cleanup(&config);\n\n\tunlink(filename);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_proxy_v1.cpp",
    "content": "/*\nCopyright (c) 2025 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\nstatic const uint8_t *packet_data = NULL;\nstatic int packet_data_pos = 0;\nstatic int packet_data_remaining = 0;\n\nextern \"C\" {\n#include \"mosquitto_broker_internal.h\"\n\n\nssize_t net__read(struct mosquitto *mosq, void *buf, size_t count)\n{\n\tint res = count < packet_data_remaining?count:packet_data_remaining;\n\tmemcpy(buf, &packet_data[packet_data_pos], res);\n\tpacket_data_remaining -= res;\n\treturn res;\n}\n\n\nint net__socket_get_address(mosq_sock_t sock, char *buf, size_t len, uint16_t *remote_port)\n{\n\tsnprintf(buf, len, \"localhost\");\n\t*remote_port = 1883;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint http__context_init(struct mosquitto *context)\n{\n\tcontext->transport = mosq_t_http;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tstruct mosquitto context {};\n\tstruct mosquitto__listener listener {};\n\n\tpacket_data = data;\n\tpacket_data_pos = 0;\n\tpacket_data_remaining = size;\n\n\tcontext.listener = &listener;\n\tcontext.proxy.cmd = -1;\n\tcontext.transport = mosq_t_proxy_v1;\n\n\twhile(packet_data_remaining > 0 && context.transport != mosq_t_tcp){\n\t\tint rc = proxy_v1__read(&context);\n\t\tif(rc){\n\t\t\tbreak;\n\t\t}\n\t}\n\tfree(context.address);\n\tfree(context.proxy.buf);\n\tfree(context.proxy.tls_version);\n\tfree(context.proxy.cipher);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_proxy_v2.cpp",
    "content": "/*\nCopyright (c) 2025 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\nstatic const uint8_t *packet_data = NULL;\nstatic int packet_data_pos = 0;\nstatic int packet_data_remaining = 0;\n\nextern \"C\" {\n#include \"mosquitto_broker_internal.h\"\n\n\nssize_t net__read(struct mosquitto *mosq, void *buf, size_t count)\n{\n\tint res = count < packet_data_remaining?count:packet_data_remaining;\n\tmemcpy(buf, &packet_data[packet_data_pos], res);\n\tpacket_data_remaining -= res;\n\treturn res;\n}\n\n\nint http__context_init(struct mosquitto *context)\n{\n\tcontext->transport = mosq_t_http;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tstruct mosquitto context {};\n\tstruct mosquitto__listener listener {};\n\n\tpacket_data = data;\n\tpacket_data_pos = 0;\n\tpacket_data_remaining = size;\n\n\tcontext.listener = &listener;\n\tcontext.proxy.cmd = -1;\n\tcontext.transport = mosq_t_proxy_v2;\n\n\twhile(packet_data_remaining > 0 && context.transport != mosq_t_tcp){\n\t\tint rc = proxy_v2__read(&context);\n\t\tif(rc){\n\t\t\tbreak;\n\t\t}\n\t}\n\tfree(context.address);\n\tfree(context.proxy.buf);\n\tfree(context.proxy.tls_version);\n\tfree(context.proxy.cipher);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_psk_file.cpp",
    "content": "/*\nCopyright (c) 2024 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\n/*\n * Broker check of psk file\n */\nextern \"C\" {\n#include \"mosquitto_broker_internal.h\"\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar filename[100];\n\tFILE *fptr;\n\tstruct mosquitto__config config = {0};\n\n\tdb.config = &config;\n\tconfig.log_type = 0;\n\tconfig.log_dest = 0;\n\n\tumask(0077);\n\n\tsnprintf(filename, sizeof(filename), \"/tmp/psk_file_%d\", getpid());\n\tfptr = fopen(filename, \"wb\");\n\tif(!fptr){\n\t\treturn 1;\n\t}\n\tfwrite(data, 1, size, fptr);\n\tfclose(fptr);\n\n\tconfig.security_options.psk_file = strdup(filename);\n\n\tlog__init(&config);\n\tmosquitto_security_init_default();\n\tmosquitto_security_cleanup_default();\n\tconfig__cleanup(&config);\n\n\tunlink(filename);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_queue_msg.cpp",
    "content": "/*\nCopyright (c) 2024 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <unistd.h>\n#include <sys/stat.h>\n\n/*\n * Broker check of acl file\n */\nextern \"C\" {\n#include \"mosquitto_broker_internal.h\"\n}\n\n\n//int sub__messages_queue(const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto__base_msg **stored)\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tstruct mosquitto__config config = {0};\n\tstruct mosquitto__base_msg basemsg, *pbasemsg;\n\n\tif(mosquitto_pub_topic_check2((const char *)data, size)){\n\t\t/* sub__messages_queue only receives topics that have already been\n\t\t * checked with mosquitto_pub_topic_check2(), so we give it that benefit\n\t\t * here. */\n\t\treturn 0;\n\t}\n\n\tmemset(&basemsg, 0, sizeof(basemsg));\n\tbasemsg.ref_count = 1;\n\tpbasemsg = &basemsg;\n\n\tdb.config = &config;\n\tconfig.log_type = 0;\n\tconfig.log_dest = 0;\n\tlog__init(&config);\n\tdb__open(&config);\n\n\tchar *data0 = (char *)calloc(1, size+1);\n\tmemcpy(data0, data, size);\n\tsub__messages_queue(\"fuzzer\", data0, 0, 1, &pbasemsg);\n\tfree(data0);\n\n\tdb__close();\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_read_handle.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"fuzz_packet_read_base.h\"\n\n\nextern \"C\" int fuzz_packet_read_init(struct mosquitto *context)\n{\n\treturn 0;\n}\n\n\nextern \"C\" void fuzz_packet_read_cleanup(struct mosquitto *context)\n{\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\treturn fuzz_packet_read_base(data, size, handle__packet);\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_test_config.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"broker_fuzz.h\"\n\n/*\n * Broker check of config only, the config isn't used\n */\n\n/* The broker fuzz-only main function. */\nextern \"C\" int mosquitto_fuzz_main(int argc, char *argv[]);\n\n\nvoid run_broker(char *filename)\n{\n\tchar *argv[5];\n\tint argc = 5;\n\n\targv[0] = strdup(\"mosquitto\");\n\targv[1] = strdup(\"--test-config\");\n\targv[2] = strdup(\"-q\");\n\targv[3] = strdup(\"-c\");\n\targv[4] = strdup(filename);\n\n\tmosquitto_fuzz_main(argc, argv);\n\n\tfor(int i=0; i<argc; i++){\n\t\tfree(argv[i]);\n\t}\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar filename[100];\n\tFILE *fptr;\n\n\tif(size < kMinInputLength){\n\t\treturn 0;\n\t}\n\n\tumask(0077);\n\n\tsnprintf(filename, sizeof(filename), \"/tmp/mosquitto_%d.conf\", getpid());\n\tfptr = fopen(filename, \"wb\");\n\tif(!fptr){\n\t\treturn 1;\n\t}\n\tfwrite(data, 1, size, fptr);\n\tfclose(fptr);\n\n\trun_broker(filename);\n\n\tunlink(filename);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/broker/broker_fuzz_with_init.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <arpa/inet.h>\n#include <errno.h>\n#include <netinet/in.h>\n#include <pthread.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"broker_fuzz.h\"\n\n#define PORT 1883\n\n/* The broker fuzz-only main function. */\nextern \"C\" int mosquitto_fuzz_main(int argc, char *argv[]);\n\n\nvoid *run_broker(void *args)\n{\n\tchar *argv[4];\n\tint argc = 4;\n\n\targv[0] = strdup(\"mosquitto\");\n\targv[1] = strdup(\"-q\");\n\targv[2] = strdup(\"-c\");\n\targv[3] = strdup(\"/tmp/mosquitto.conf\");\n\n\tmosquitto_fuzz_main(argc, argv);\n\n\tfor(int i=0; i<argc; i++){\n\t\tfree(argv[i]);\n\t}\n\n\tpthread_exit(NULL);\n\treturn NULL;\n}\n\n\nvoid recv_timeout(int sock, void *buf, size_t len, int timeout_us)\n{\n\tstruct timeval tv = {0, timeout_us};\n\n\tsetsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));\n\t(void)recv(sock, buf, len, 0);\n}\n\n\nint connect_retrying(int port)\n{\n\tstruct sockaddr_in addr;\n\tint sock;\n\tint rc;\n\n\tmemset(&addr, 0, sizeof(addr));\n\taddr.sin_family = AF_INET;\n\taddr.sin_port = htons(port);\n\taddr.sin_addr.s_addr = inet_addr(\"127.0.0.1\");\n\n\tsock = socket(AF_INET, SOCK_STREAM, 0);\n\twhile(1){\n\t\terrno = 0;\n\t\trc = connect(sock, (struct sockaddr *)&addr, sizeof(addr));\n\t\tif(rc < 0){\n\t\t\tstruct timespec ts;\n\t\t\tts.tv_sec = 0;\n\t\t\tts.tv_nsec = 10000000; /* 10ms */\n\t\t\tnanosleep(&ts, NULL);\n\t\t}else{\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn sock;\n}\n\n\nstatic bool initialise(pthread_t *thread)\n{\n\tFILE *fptr;\n\n\tsignal(SIGPIPE, SIG_IGN);\n\tumask(0077);\n\n\tfptr = fopen(\"/tmp/mosquitto.conf\", \"wb\");\n\tif(!fptr){\n\t\tprintf(\"FILE %s\\n\", strerror(errno));\n\t\tabort();\n\t}\n\tfprintf(fptr, \"user root\\n\");\n\tfprintf(fptr, \"listener %d\\n\", PORT);\n\tfprintf(fptr, \"allow_anonymous true\\n\");\n\tfclose(fptr);\n\n\tpthread_create(thread, NULL, run_broker, NULL);\n\n\treturn true;\n}\n\n\nvoid deinitialise(pthread_t *thread)\n{\n\tpthread_join(*thread, NULL);\n\tunlink(\"/tmp/mosquitto.conf\");\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tstruct fuzz_data fuzz;\n\tpthread_t thread;\n\tstatic bool initialised = false;\n\n\tif(!initialised){\n\t\tinitialised = initialise(&thread);\n\t}\n\n\tif(size < kMinInputLength || size > kMaxInputLength){\n\t\treturn 0;\n\t}\n\n\tmemset(&fuzz, 0, sizeof(fuzz));\n\tfuzz.port = PORT;\n\tfuzz.size = size;\n\tfuzz.data = (uint8_t *)data;\n\n\trun_client(&fuzz);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/broker/fuzz_packet_read_base.c",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"fuzz_packet_read_base.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto_internal.h\"\n\n#define kMinInputLength 3\n#define kMaxInputLength 268435455U\n\n\nint fuzz_packet_read_base(const uint8_t *data, size_t size, int (*packet_func)(struct mosquitto *))\n{\n\tstruct mosquitto *context = NULL;\n\tuint8_t *data_heap;\n\tstruct mosquitto__listener listener;\n\tstruct mosquitto__security_options secopts;\n\n\tif(size < kMinInputLength || size > kMaxInputLength){\n\t\treturn 0;\n\t}\n\n\tdb.config = (struct mosquitto__config *)calloc(1, sizeof(struct mosquitto__config));\n\tlog__init(db.config);\n\n\tmemset(&listener, 0, sizeof(listener));\n\tmemset(&secopts, 0, sizeof(secopts));\n\n\tcontext = context__init();\n\tif(!context){\n\t\treturn 1;\n\t}\n\tlistener.security_options = &secopts;\n\tcontext->listener = &listener;\n\tcontext->bridge = (struct mosquitto__bridge *)calloc(1, sizeof(struct mosquitto__bridge));;\n\n\tcontext->state = (enum mosquitto_client_state )data[0];\n\tcontext->protocol = (enum mosquitto__protocol )data[1];\n\tsize -= 2;\n\n\tdata_heap = (uint8_t *)malloc(size);\n\tif(!data_heap){\n\t\tfree(context->bridge);\n\t\tcontext->bridge = NULL;\n\t\tfree(db.config);\n\t\tdb.config = NULL;\n\t\treturn 1;\n\t}\n\n\tmemcpy(data_heap, &data[2], size);\n\n\tcontext->in_packet.command = data_heap[0];\n\tcontext->in_packet.payload = (uint8_t *)data_heap;\n\tcontext->in_packet.packet_length = (uint32_t )size; /* Safe cast, because we've already limited the size */\n\tcontext->in_packet.remaining_length = (uint32_t )(size-1);\n\tcontext->in_packet.pos = 1;\n\n\tif(fuzz_packet_read_init(context)){\n\t\tfree(context->bridge);\n\t\tcontext->bridge = NULL;\n\t\tfree(db.config);\n\t\treturn 1;\n\t}\n\tpacket_func(context);\n\tfuzz_packet_read_cleanup(context);\n\n\tfree(context->bridge);\n\tcontext->bridge = NULL;\n\n\tcontext__cleanup(context, true);\n\n\tfree(db.config);\n\n\treturn 0;\n}\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "fuzzing/broker/fuzz_packet_read_base.h",
    "content": "#ifndef FUZZ_PACKET_READ_BASE_H\n/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto_internal.h\"\n#include \"read_handle.h\"\n\n#define kMinInputLength 3\n#ifndef kMaxInputLength\n#  define kMaxInputLength 268435455U\n#endif\n\nint fuzz_packet_read_base(const uint8_t *data, size_t size, int (*packet_func)(struct mosquitto *));\nint fuzz_packet_read_init(struct mosquitto *context);\nvoid fuzz_packet_read_cleanup(struct mosquitto *context);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "fuzzing/config.mk",
    "content": "LOCAL_CPPFLAGS:=$(CPPFLAGS)\nLOCAL_CFLAGS:=$(CFLAGS)\nLOCAL_CXXFLAGS:=$(CXXFLAGS)\nLOCAL_LDFLAGS:=$(LDFLAGS)\nLOCAL_LIBADD:=$(LIBADD)\n\nLOCAL_CPPFLAGS+=-I${R} -I. -I${R}/include -I${R}/common\nLOCAL_CFLAGS+=\nLOCAL_LDFLAGS+=$(LOCAL_CFLAGS)\n"
  },
  {
    "path": "fuzzing/corpora/broker_acl_file.dict",
    "content": "\"deny\"\n\"pattern\"\n\"read\"\n\"readwrite\"\n\"topic\"\n\"user\"\n\"write\"\n"
  },
  {
    "path": "fuzzing/corpora/broker_conf.dict",
    "content": "\"0\"\n\"1\"\n\"3\"\n\"4\"\n\"5\"\n\"accept_protocol_versions\"\n\"acl_file\"\n\"address\"\n\"addresses\"\n\"all\"\n\"allow_anonymous\"\n\"allow_duplicate_messages\"\n\"allow_zero_length_clientid\"\n\"android\"\n\"auth_plugin\"\n\"auth_plugin_deny_special_chars\"\n\"auto_id_prefix\"\n\"automatic\"\n\"autosave_interval\"\n\"autosave_on_changes\"\n\"bind_address\"\n\"bind_interface\"\n\"bridge_alpn\"\n\"bridge_attempt_unsubscribe\"\n\"bridge_bind_address\"\n\"bridge_cafile\"\n\"bridge_capath\"\n\"bridge_certfile\"\n\"bridge_ciphers\"\n\"bridge_ciphers_tls1.3\"\n\"bridge_identity\"\n\"bridge_insecure\"\n\"bridge_keyfile\"\n\"bridge_max_packet_size\"\n\"bridge_max_topic_alias\"\n\"bridge_outgoing_retain\"\n\"bridge_protocol_version\"\n\"bridge_psk\"\n\"bridge_receive_maximum\"\n\"bridge_reload_type\"\n\"bridge_require_ocsp\"\n\"bridge_session_expiry_interval\"\n\"bridge_tcp_keepalive\"\n\"bridge_tcp_user_timeout\"\n\"bridge_tls_use_os_certs\"\n\"bridge_tls_version\"\n\"cafile\"\n\"capath\"\n\"certfile\"\n\"check_retain_source\"\n\"ciphers\"\n\"ciphers_tls1.3\"\n\"cleansession\"\n\"clientid\"\n\"clientid_prefixes\"\n\"connection\"\n\"connection_messages\"\n\"crlfile\"\n\"debug\"\n\"dhparamfile\"\n\"disable_client_cert_date_checks\"\n\"dlt\"\n\"enable_control_api\"\n\"engine\"\n\"error\"\n\"false\"\n\"file\"\n\"global_max_clients\"\n\"global_max_connections\"\n\"global_plugin\"\n\"http_dir\"\n\"idle_timeout\"\n\"immediate\"\n\"include_dir\"\n\"information\"\n\"internal\"\n\"ipv4\"\n\"ipv6\"\n\"keepalive_interval\"\n\"keyfile\"\n\"lazy\"\n\"listener\"\n\"local_cleansession\"\n\"local_clientid\"\n\"local_password\"\n\"local_username\"\n\"log_dest\"\n\"log_facility\"\n\"log_timestamp\"\n\"log_timestamp_format\"\n\"log_type\"\n\"manual\"\n\"max_connections\"\n\"max_inflight_bytes\"\n\"max_inflight_messages\"\n\"max_keepalive\"\n\"max_packet_size\"\n\"max_qos\"\n\"max_queued_bytes\"\n\"max_queued_messages\"\n\"max_topic_alias\"\n\"max_topic_alias_broker\"\n\"maximum_qos\"\n\"memory_limit\"\n\"message_size_limit\"\n\"mount_point\"\n\"mqtt\"\n\"mqttsn\"\n\"mqttv31\"\n\"mqttv311\"\n\"mqttv50\"\n\"none\"\n\"notice\"\n\"notification_topic\"\n\"notifications\"\n\"notifications_local_only\"\n\"once\"\n\"password\"\n\"password_file\"\n\"per_listener_settings\"\n\"persistence\"\n\"persistence_file\"\n\"persistence_location\"\n\"persistent_client_expiration\"\n\"pid_file\"\n\"plugin\"\n\"plugin_load\"\n\"plugin_use\"\n\"port\"\n\"protocol\"\n\"psk_file\"\n\"psk_hint\"\n\"queue_qos0_messages\"\n\"remote_clientid\"\n\"remote_password\"\n\"remote_username\"\n\"require_certificate\"\n\"restart_timeout\"\n\"retain_available\"\n\"retained_persistence\"\n\"retry_interval\"\n\"round_robin\"\n\"set_tcp_nodelay\"\n\"socket_domain\"\n\"start_type\"\n\"stderr\"\n\"stdout\"\n\"subscribe\"\n\"sys_interval\"\n\"syslog\"\n\"threshold\"\n\"tls_engine\"\n\"tls_engine_kpass_sha1\"\n\"tls_keyform\"\n\"tls_version\"\n\"topic\"\n\"true\"\n\"try_private\"\n\"unsubscribe\"\n\"upgrade_outgoing_qos\"\n\"use_identity_as_username\"\n\"use_subject_as_username\"\n\"use_username_as_clientid\"\n\"user\"\n\"username\"\n\"warning\"\n\"websockets\"\n\"websockets_headers_size\"\n\"websockets_log_level\"\n\"websockets_origin\"\n"
  },
  {
    "path": "fuzzing/generate_packet_corpora.py",
    "content": "#!/usr/bin/env python3\n\nimport inspect, os, sys\n\n# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder\ncmd_subfolder = os.path.realpath(\n    os.path.abspath(\n        os.path.join(os.path.split(inspect.getfile(inspect.currentframe()))[0], \"../test/broker\")\n    )\n)\nif cmd_subfolder not in sys.path:\n    sys.path.insert(0, cmd_subfolder)\n\nfrom collections import deque\nfrom os import mkdir,walk\nfrom pathlib import Path\nimport json\nimport re\nimport shutil\nimport msg_sequence_test\n\ndef gen_packet_corpus(packet_type, input_path):\n    try:\n        mkdir(\"corpora\")\n    except FileExistsError:\n        pass\n    try:\n        mkdir(f\"corpora/{packet_type}\")\n    except FileExistsError:\n        pass\n\n    data_path=Path(input_path)\n    rc = 0\n    sequences = []\n    for (_, _, filenames) in walk(data_path):\n        sequences.extend(filenames)\n        break\n\n    written_packets = {}\n    for seq in sorted(sequences):\n        if seq[-5:] != \".json\":\n            continue\n\n        with open(data_path/seq, \"r\") as f:\n            test_file = json.load(f)\n\n        for g in test_file:\n            group_name = g[\"group\"]\n            tests = g[\"tests\"]\n\n            for t in tests:\n                tname = group_name + \" \" + t[\"name\"]\n\n                fuzzi = 1\n                for m in t[\"msgs\"]:\n                    if m[\"type\"] == \"send\" or m[\"type\"] == \"recv\":\n                        fname = re.sub(r'[\\/ \\[\\]\\(\\)]+', '-', tname) + str(fuzzi) + \".raw\"\n                        payload = m[\"payload\"]\n\n                        # No duplicates please\n                        if payload not in written_packets:\n                            written_packets[payload] = 1\n                            with open(f\"corpora/{packet_type}/{fname}\", \"wb\") as f:\n                                f.write(msg_sequence_test.parse_message(payload))\n                            fuzzi += 1\n    shutil.make_archive(f\"corpora/{packet_type}_packet_seed_corpus\", 'zip', f\"corpora/{packet_type}\")\n\ngen_packet_corpus(\"broker\", \"../test/broker/data\")\ngen_packet_corpus(\"client\", \"../test/lib/data\")\n"
  },
  {
    "path": "fuzzing/libcommon/Makefile",
    "content": "R=../..\ninclude ${R}/fuzzing/config.mk\n\n.PHONY: all clean\n\nFUZZERS:= \\\n\tlibcommon_fuzz_property \\\n\tlibcommon_fuzz_pub_topic_check2 \\\n\tlibcommon_fuzz_sub_topic_check2 \\\n\tlibcommon_fuzz_topic_matching \\\n\tlibcommon_fuzz_topic_tokenise \\\n\tlibcommon_fuzz_utf8\n\nLOCAL_CPPFLAGS+=-I${R}/include/ -I${SRC}/libprotobuf-mutator -I${SRC}/LPM/external.protobuf/include\nLOCAL_CXXFLAGS+=-g -Wall -Werror -pthread -Wno-deprecated-builtins\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=$(LIB_FUZZING_ENGINE) -lssl -lcrypto ${R}/libcommon/libmosquitto_common.a -Wl,-Bstatic -largon2 -Wl,-Bdynamic\nPROTOBUF_LIBS=${SRC}/LPM/src/libfuzzer/libprotobuf-mutator-libfuzzer.a \\\n\t\t\t  ${SRC}/LPM/src/libprotobuf-mutator.a \\\n\t\t\t  -Wl,--start-group \\\n\t\t\t  ${SRC}/LPM/external.protobuf/lib/lib*.a \\\n\t\t\t  -Wl,--end-group\nPROTOC?=${SRC}/LPM/external.protobuf/bin/protoc\n\nall: $(FUZZERS)\n\nlibcommon_fuzz_property.pb.cc : libcommon_fuzz_property.proto\n\t$(PROTOC) --cpp_out=. $^\n\nlibcommon_fuzz_property : libcommon_fuzz_property.cpp libcommon_fuzz_property.pb.cc\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD) $(PROTOBUF_LIBS)\n\tinstall $@ ${OUT}/$@\n\nlibcommon_fuzz_pub_topic_check2 : libcommon_fuzz_pub_topic_check2.cpp\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\nlibcommon_fuzz_sub_topic_check2 : libcommon_fuzz_sub_topic_check2.cpp\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\nlibcommon_fuzz_topic_matching : libcommon_fuzz_topic_matching.cpp libcommon_fuzz_topic_matching.pb.cc\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD) $(PROTOBUF_LIBS)\n\tinstall $@ ${OUT}/$@\n\nlibcommon_fuzz_topic_matching.pb.cc : libcommon_fuzz_topic_matching.proto\n\t$(PROTOC) --cpp_out=. $^\n\nlibcommon_fuzz_topic_tokenise : libcommon_fuzz_topic_tokenise.cpp\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\nlibcommon_fuzz_utf8 : libcommon_fuzz_utf8.cpp\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\nclean:\n\trm -f *.o $(FUZZERS)\n"
  },
  {
    "path": "fuzzing/libcommon/libcommon_fuzz_property.cpp",
    "content": "#include \"src/libfuzzer/libfuzzer_macro.h\"\n\n#include \"libcommon_fuzz_property.pb.h\"\n#include \"mosquitto.h\"\n\nDEFINE_PROTO_FUZZER(const fuzz_property::FuzzerInput& fuzzer_input)\n{\n\tmosquitto_property *prop_list = nullptr;\n\tmosquitto_property *prop_copy = nullptr;\n\n\tfor(const fuzz_property::Property& property : fuzzer_input.properties()){\n\t\tint identifier = property.identifier();\n\t\tswitch(property.data_case()){\n\t\t\tcase fuzz_property::Property::DataCase::kUint8Value:\n\t\t\t\tmosquitto_property_add_byte(&prop_list, identifier, property.uint8_value() % 256);\n\t\t\t\tbreak;\n\t\t\tcase fuzz_property::Property::DataCase::kUint16Value:\n\t\t\t\tmosquitto_property_add_int16(&prop_list, identifier, property.uint16_value() % 65536);\n\t\t\t\tbreak;\n\t\t\tcase fuzz_property::Property::DataCase::kUint32Value:\n\t\t\t\tmosquitto_property_add_int32(&prop_list, identifier, property.uint32_value());\n\t\t\t\tbreak;\n\t\t\tcase fuzz_property::Property::DataCase::kVarintValue:\n\t\t\t\tmosquitto_property_add_varint(&prop_list, identifier, property.varint_value());\n\t\t\t\tbreak;\n\t\t\tcase fuzz_property::Property::DataCase::kBinaryValue:\n\t\t\t\tmosquitto_property_add_binary(&prop_list, identifier,\n\t\t\t\t\t\tproperty.binary_value().c_str(),\n\t\t\t\t\t\tproperty.binary_value().size());\n\t\t\t\tbreak;\n\t\t\tcase fuzz_property::Property::DataCase::kStringValue:\n\t\t\t\tmosquitto_property_add_string(&prop_list, identifier, property.string_value().c_str());\n\t\t\t\tbreak;\n\t\t\tcase fuzz_property::Property::DataCase::kStringpairValue:\n\t\t\t\tmosquitto_property_add_string_pair(&prop_list, identifier,\n\t\t\t\t\t\tproperty.stringpair_value().name().c_str(),\n\t\t\t\t\t\tproperty.stringpair_value().value().c_str());\n\t\t\t\tbreak;\n\t\t\tcase fuzz_property::Property::DataCase::DATA_NOT_SET:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tmosquitto_property_copy_all(&prop_copy, prop_list);\n\tmosquitto_property_free_all(&prop_list);\n\tmosquitto_property_free_all(&prop_copy);\n}\n"
  },
  {
    "path": "fuzzing/libcommon/libcommon_fuzz_property.proto",
    "content": "syntax = \"proto2\";\n\npackage fuzz_property;\n\nmessage Property {\n\tmessage StringPair {\n\t\trequired string name = 1;\n\t\trequired string value = 2;\n\t}\n\n\trequired uint32 identifier = 1;\n\toneof data {\n\t\tuint32 uint8_value = 2;\n\t\tuint32 uint16_value = 3;\n\t\tuint32 uint32_value = 4;\n\t\tuint32 varint_value = 5;\n\t\tbytes binary_value = 6;\n\t\tstring string_value = 7;\n\t\tStringPair stringpair_value = 8;\n\t}\n}\n\nmessage FuzzerInput {\n\trepeated Property properties = 1;\n}\n"
  },
  {
    "path": "fuzzing/libcommon/libcommon_fuzz_pub_topic_check2.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <mosquitto.h>\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tmosquitto_pub_topic_check2((const char *)data, size);\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/libcommon/libcommon_fuzz_sub_topic_check2.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <mosquitto.h>\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tmosquitto_sub_topic_check2((const char *)data, size);\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/libcommon/libcommon_fuzz_topic_matching.cpp",
    "content": "#include \"src/libfuzzer/libfuzzer_macro.h\"\n\n#include \"libcommon_fuzz_topic_matching.pb.h\"\n#include \"mosquitto.h\"\n\nDEFINE_PROTO_FUZZER(const fuzz_topic_matches_sub::FuzzerInput& fuzzer_input)\n{\n\tbool result;\n\tconst char *string1 = fuzzer_input.string1().c_str();\n\tconst char *string2 = fuzzer_input.string2().c_str();\n\tconst char *username = nullptr;\n\tconst char *clientid = nullptr;\n\n\tif(fuzzer_input.has_username()){\n\t\tusername = fuzzer_input.username().c_str();\n\t}\n\tif(fuzzer_input.has_clientid()){\n\t\tclientid = fuzzer_input.clientid().c_str();\n\t}\n\n\t//targeted_function_1(fuzzer_input.arg1(), fuzzer_input.arg2(), fuzzer_input.arg3());\n\tmosquitto_topic_matches_sub(string1, string2, &result);\n\tmosquitto_topic_matches_sub2(string1, strlen(string1), string2, strlen(string2), &result);\n\tmosquitto_topic_matches_sub_with_pattern(string1, string2, clientid, username, &result);\n\n\tmosquitto_sub_matches_acl(string1, string2, &result);\n\tmosquitto_sub_matches_acl_with_pattern(string1, string2, clientid, username, &result);\n}\n"
  },
  {
    "path": "fuzzing/libcommon/libcommon_fuzz_topic_matching.proto",
    "content": "syntax = \"proto2\";\n\npackage fuzz_topic_matches_sub;\n\nmessage FuzzerInput {\n\trequired string string1 = 1;\n\trequired string string2 = 2;\n\toptional string username = 3;\n\toptional string clientid = 4;\n}\n"
  },
  {
    "path": "fuzzing/libcommon/libcommon_fuzz_topic_tokenise.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <mosquitto.h>\n#include <cstdlib>\n#include <cstring>\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar *buf;\n\tbuf = (char *)calloc(1, size+1); /* ensure ull terminated */\n\tif(buf){\n\t\tchar **topics = NULL;;\n\t\tint count;\n\n\t\tmemcpy(buf, data, size);\n\t\tmosquitto_sub_topic_tokenise(buf, &topics, &count);\n\t\tmosquitto_sub_topic_tokens_free(&topics, count);\n\t\tfree(buf);\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/libcommon/libcommon_fuzz_utf8.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include <mosquitto.h>\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tmosquitto_validate_utf8((const char *)data, (int)size);\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/plugins/Makefile",
    "content": ".PHONY: all clean\n\nall:\n\t$(MAKE) -C dynamic-security $@\n\nclean:\n\t$(MAKE) -C dynamic-security $@\n"
  },
  {
    "path": "fuzzing/plugins/dynamic-security/Makefile",
    "content": "R=../../..\ninclude ${R}/fuzzing/config.mk\n\n.PHONY: all clean\n\nFUZZERS:= \\\n\tdynsec_fuzz_load\n\nLOCAL_CPPFLAGS+= \\\n\t-I${R} -I${R}/common -I${R}/include -I${R}/lib -I${R}/src -I${R}/deps -I${R}/plugins/dynamic-security \\\n\t-DWITH_BRIDGE -DWITH_BROKER -DWITH_CONTROL -DWITH_EPOLL \\\n\t-DWITH_MEMORY_TRACKING -DWITH_PERSISTENCE -DWITH_SOCKS -DWITH_SYSTEMD \\\n\t-DWITH_SYS_TREE -DWITH_TLS -DWITH_TLS_PSK -DWITH_UNIX_SOCKETS -DWITH_WEBSOCKETS=WS_IS_BUILTIN\nLOCAL_CXXFLAGS+=-g -Wall -Werror -pthread\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=$(LIB_FUZZING_ENGINE) \\\n\t\t\t  ${R}/plugins/dynamic-security/mosquitto_dynamic_security.a \\\n\t\t\t  ${R}/src/mosquitto_broker.a \\\n\t\t\t  ${R}/libcommon/libmosquitto_common.a \\\n\t\t\t  -lssl -lcrypto -lcjson -lm \\\n\t\t\t  -Wl,-Bdynamic -Wl,-Bstatic -largon2 -Wl,-Bdynamic\n\nall: $(FUZZERS)\n\ndynsec_fuzz_load : dynsec_fuzz_load.cpp\n\t$(CXX) $(LOCAL_CXXFLAGS) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\tinstall $@ ${OUT}/$@\n\tcp ${R}/fuzzing/corpora/dynsec_config_seed_corpus.zip ${OUT}/$@_seed_corpus.zip\n\nclean:\n\trm -f *.o $(FUZZERS) *.gcno *.gcda\n"
  },
  {
    "path": "fuzzing/plugins/dynamic-security/dynsec_fuzz_load.cpp",
    "content": "/*\nCopyright (c) 2023 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdio>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <mosquitto.h>\n#include <mosquitto_broker_internal.h>\n\n#ifdef __cplusplus\n}\n#endif\n\n/*\n * Test loading a file\n */\n\nextern struct mosquitto_db db;\n\n\nvoid run_dynsec(char *filename)\n{\n\tstruct mosquitto_plugin_id_t identifier;\n\tstruct mosquitto_opt options[1];\n\n\tdb.config = (struct mosquitto__config *)calloc(1, sizeof(struct mosquitto__config));\n\tlog__init(db.config);\n\n\tmemset(&identifier, 0, sizeof(identifier));\n\n\toptions[0].key = strdup(\"config_file\");\n\toptions[0].value = filename;\n\n\tmosquitto_plugin_init(&identifier, NULL, options, 1);\n\tmosquitto_plugin_cleanup(NULL, options, 1);\n\n\tfree(options[0].key);\n\tfree(db.config);\n\tfree(identifier.plugin_name);\n\tfree(identifier.plugin_version);\n\tdb.config = NULL;\n}\n\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)\n{\n\tchar filename[100];\n\tFILE *fptr;\n\n\tumask(0077);\n\n\tsnprintf(filename, sizeof(filename), \"/tmp/dynsec%d.conf\", getpid());\n\tfptr = fopen(filename, \"wb\");\n\tif(!fptr){\n\t\treturn 1;\n\t}\n\tfwrite(data, 1, size, fptr);\n\tfclose(fptr);\n\n\trun_dynsec(filename);\n\n\tunlink(filename);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fuzzing/scripts/oss-fuzz-build.sh",
    "content": "#!/bin/bash -eu\n#\n# Copyright (c) 2023 Cedalo GmbH\n#\n# All rights reserved. This program and the accompanying materials\n# are made available under the terms of the Eclipse Public License 2.0\n# and Eclipse Distribution License v1.0 which accompany this distribution.\n#\n# The Eclipse Public License is available at\n#   https://www.eclipse.org/legal/epl-2.0/\n# and the Eclipse Distribution License is available at\n#   http://www.eclipse.org/org/documents/edl-v10.php.\n#\n# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n#\n# Contributors:\n#    Roger Light - initial implementation and documentation.\n\nexport CC=\"${CC:-clang}\"\nexport CXX=\"${CXX:-clang++}\"\nexport CFLAGS\nexport CXXFLAGS\nexport LDFLAGS\n\n# Build direct broker dependency - cJSON\n# Note that other dependencies, i.e. sqlite are not yet built because they are\n# only used by plugins and not currently otherwise used.\ncd ${SRC}/cJSON\ncmake \\\n\t-DBUILD_SHARED_LIBS=OFF \\\n\t-DCMAKE_C_FLAGS=-fPIC \\\n\t-DENABLE_CJSON_TEST=OFF \\\n\t.\nmake -j $(nproc)\nmake install\n\n# Build broker and library static libraries\ncd ${SRC}/mosquitto\nmake \\\n\tWITH_STATIC_LIBRARIES=yes \\\n\tWITH_DOCS=no \\\n\tWITH_FUZZING=yes \\\n\tWITH_EDITLINE=no \\\n\tWITH_HTTP_API=no \\\n\t-j $(nproc)\n"
  },
  {
    "path": "fuzzing/scripts/oss-fuzz-dependencies.sh",
    "content": "#!/bin/bash -eu\n#\n# Copyright (c) 2023 Cedalo GmbH\n#\n# All rights reserved. This program and the accompanying materials\n# are made available under the terms of the Eclipse Public License 2.0\n# and Eclipse Distribution License v1.0 which accompany this distribution.\n#\n# The Eclipse Public License is available at\n#   https://www.eclipse.org/legal/epl-2.0/\n# and the Eclipse Distribution License is available at\n#   http://www.eclipse.org/org/documents/edl-v10.php.\n#\n# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n#\n# Contributors:\n#    Roger Light - initial implementation and documentation.\n\nset -e\n\n# Note that sqlite3 is required as a build dep of a plugin which is not\n# currently part of fuzz testing. Once it is part of fuzz testing, sqlite will\n# need to be built statically.\napt-get update && apt-get install -y \\\n\tcmake \\\n\tlibargon2-dev \\\n\tlibedit-dev \\\n\tliblzma-dev \\\n\tlibmicrohttpd-dev \\\n\tlibsqlite3-dev \\\n\tlibtool-bin \\\n\tlibz-dev \\\n\tmake \\\n\tninja-build \\\n\tpkg-config\ngit clone https://github.com/ralight/cJSON ${SRC}/cJSON\n\n# If building outside of oss-fuzz, we need LPM\nif [ ! -d ${SRC}/LPM ]; then\n\tgit clone https://github.com/google/libprotobuf-mutator ${SRC}/libprotobuf-mutator\n\n\tmkdir ${SRC}/LPM \\\n\t\t&& cd ${SRC}/LPM \\\n\t\t&& cmake ../libprotobuf-mutator -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON -DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release \\\n\t\t&& ninja\nfi\n"
  },
  {
    "path": "include/mosquitto/broker.h",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * File: mosquitto_broker.h\n *\n * This header contains functions for use by plugins.\n */\n#ifndef MOSQUITTO_BROKER_H\n#define MOSQUITTO_BROKER_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#if defined(WIN32) && defined(mosquitto_EXPORTS)\n#   define mosq_EXPORT  __declspec(dllexport)\n#else\n#   define mosq_EXPORT\n#endif\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <time.h>\n\n#include <mosquitto/mqtt_protocol.h>\n#include <mosquitto/defs.h>\n\nenum mosquitto_protocol {\n\tmp_mqtt,\n\tmp_mqttsn,\n\tmp_websockets,\n\tmp_http_api,\n};\n\nenum mosquitto_broker_msg_direction {\n\tmosq_bmd_in = 0,\n\tmosq_bmd_out = 1,\n\tmosq_bmd_all = 2,\n};\n\n\n/* =========================================================================\n *\n * Section: General structs\n *\n * ========================================================================= */\n\nstruct mosquitto_client {\n\tchar *clientid;\n\tchar *username;\n\tchar *auth_method;\n\ttime_t will_delay_time;\n\ttime_t session_expiry_time;\n\tuint32_t will_delay_interval;\n\tuint32_t session_expiry_interval;\n\tuint32_t max_packet_size;\n\tuint16_t listener_port;\n\tuint8_t max_qos;\n\tbool retain_available;\n\tvoid *future2[8];\n};\n\nstruct mosquitto_subscription {\n\tchar *clientid;\n\tchar *topic_filter;\n\tmosquitto_property *properties;\n\tuint32_t identifier;\n\tuint8_t options;\n\tuint8_t padding[3];\n\tvoid *future2[8];\n};\n\nstruct mosquitto_base_msg {\n\tuint64_t store_id;\n\tint64_t expiry_time;\n\tchar *topic;\n\tvoid *payload;\n\tchar *source_id;\n\tchar *source_username;\n\tmosquitto_property *properties;\n\tuint32_t payloadlen;\n\tuint16_t source_mid;\n\tuint16_t source_port;\n\tuint8_t qos;\n\tbool retain;\n\tuint8_t padding[6];\n\tvoid *future2[8];\n};\n\nstruct mosquitto_client_msg {\n\tconst char *clientid;\n\tuint64_t cmsg_id;\n\tuint64_t store_id;\n\tuint32_t subscription_identifier;\n\tuint16_t mid;\n\tuint8_t qos;\n\tbool retain;\n\tuint8_t dup;\n\tuint8_t direction;\n\tuint8_t state;\n\tuint8_t padding[5];\n\tvoid *future2[8];\n};\n\nstruct mosquitto_will_msg {\n\tconst char *clientid;\n\tvoid *payload;\n\tchar *topic;\n\tmosquitto_property *properties;\n\tuint32_t payloadlen;\n\tuint8_t qos;\n\tbool retain;\n};\n\n/* =========================================================================\n *\n * Section: Register callbacks.\n *\n * ========================================================================= */\n\n/* Callback events */\nenum mosquitto_plugin_event {\n\tMOSQ_EVT_RELOAD = 1,\n\tMOSQ_EVT_ACL_CHECK = 2,\n\tMOSQ_EVT_BASIC_AUTH = 3,\n\tMOSQ_EVT_EXT_AUTH_START = 4,\n\tMOSQ_EVT_EXT_AUTH_CONTINUE = 5,\n\tMOSQ_EVT_CONTROL = 6,\n\tMOSQ_EVT_MESSAGE = 7, // deprecated name\n\tMOSQ_EVT_MESSAGE_IN = 7,\n\tMOSQ_EVT_PSK_KEY = 8,\n\tMOSQ_EVT_TICK = 9,\n\tMOSQ_EVT_DISCONNECT = 10,\n\tMOSQ_EVT_CONNECT = 11,\n\tMOSQ_EVT_SUBSCRIBE = 12,\n\tMOSQ_EVT_UNSUBSCRIBE = 13,\n\tMOSQ_EVT_PERSIST_RESTORE = 14,\n\tMOSQ_EVT_PERSIST_BASE_MSG_ADD = 15,\n\tMOSQ_EVT_PERSIST_BASE_MSG_DELETE = 16,\n\tMOSQ_EVT_PERSIST_RETAIN_MSG_SET = 17,\n\tMOSQ_EVT_PERSIST_RETAIN_MSG_DELETE = 18,\n\tMOSQ_EVT_PERSIST_CLIENT_ADD = 19,\n\tMOSQ_EVT_PERSIST_CLIENT_DELETE = 20,\n\tMOSQ_EVT_PERSIST_CLIENT_UPDATE = 21,\n\tMOSQ_EVT_PERSIST_SUBSCRIPTION_ADD = 22,\n\tMOSQ_EVT_PERSIST_SUBSCRIPTION_DELETE = 23,\n\tMOSQ_EVT_PERSIST_CLIENT_MSG_ADD = 24,\n\tMOSQ_EVT_PERSIST_CLIENT_MSG_DELETE = 25,\n\tMOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE = 26,\n\tMOSQ_EVT_MESSAGE_OUT = 27,\n\tMOSQ_EVT_CLIENT_OFFLINE = 28,\n\tMOSQ_EVT_PERSIST_WILL_ADD = 29,\n\tMOSQ_EVT_PERSIST_WILL_DELETE = 30,\n};\n\n/* Data for the MOSQ_EVT_RELOAD event */\nstruct mosquitto_evt_reload {\n\tvoid *future;\n\tstruct mosquitto_opt *options;\n\tint option_count;\n\tvoid *future2[4];\n};\n\n/* Data for the MOSQ_EVT_ACL_CHECK event */\nstruct mosquitto_evt_acl_check {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tconst char *topic;\n\tconst void *payload;\n\tmosquitto_property *properties;\n\tint access;\n\tuint32_t payloadlen;\n\tuint8_t qos;\n\tbool retain;\n\tvoid *future2[4];\n};\n\n/* Data for the MOSQ_EVT_BASIC_AUTH event */\nstruct mosquitto_evt_basic_auth {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tchar *username;\n\tchar *password;\n\tunion {\n\t\tvoid *future2[4];\n\t\tstruct {\n\t\t\tuint16_t password_len;\n\t\t};\n\t};\n};\n\n/* Data for the MOSQ_EVT_PSK_KEY event */\nstruct mosquitto_evt_psk_key {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tconst char *hint;\n\tconst char *identity;\n\tchar *key;\n\tint max_key_len;\n\tvoid *future2[4];\n};\n\n/* Data for the MOSQ_EVT_EXTENDED_AUTH event */\nstruct mosquitto_evt_extended_auth {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tconst void *data_in;\n\tvoid *data_out;\n\tuint16_t data_in_len;\n\tuint16_t data_out_len;\n\tconst char *auth_method;\n\tvoid *future2[3];\n};\n\n/* Data for the MOSQ_EVT_CONTROL event */\nstruct mosquitto_evt_control {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tconst char *topic;\n\tconst void *payload;\n\tconst mosquitto_property *properties;\n\tchar *reason_string;\n\tuint32_t payloadlen;\n\tuint8_t qos;\n\tuint8_t reason_code;\n\tbool retain;\n\tuint8_t padding;\n\tvoid *future2[4];\n};\n\n/* Data for the MOSQ_EVT_MESSAGE_IN and MOSQ_EVT_MESSAGE_OUT events */\nstruct mosquitto_evt_message {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tchar *topic;\n\tvoid *payload;\n\tmosquitto_property *properties;\n\tchar *reason_string;\n\tuint32_t payloadlen;\n\tuint8_t qos;\n\tuint8_t reason_code;\n\tbool retain;\n\tuint8_t padding;\n\tvoid *future2[4];\n};\n\n\n/* Data for the MOSQ_EVT_TICK event */\nstruct mosquitto_evt_tick {\n\tvoid *future;\n\tlong now_ns;\n\tlong next_ms;\n\ttime_t now_s;\n\ttime_t next_s;\n\tvoid *future2[4];\n};\n\n/* Data for the MOSQ_EVT_CONNECT event */\nstruct mosquitto_evt_connect {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tvoid *future2[4];\n};\n\n/* Data for the MOSQ_EVT_DISCONNECT event */\nstruct mosquitto_evt_disconnect {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tint reason;\n\tvoid *future2[4];\n};\n\n/* Data for the MOSQ_EVT_CLIENT_OFFLINE event */\nstruct mosquitto_evt_client_offline {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tint reason;\n\tvoid *future2[4];\n};\n\n/* Data for the MOSQ_EVT_SUBSCRIBE event */\nstruct mosquitto_evt_subscribe {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tstruct mosquitto_subscription data;\n\tvoid *future2[8];\n};\n\n\n/* Data for the MOSQ_EVT_UNSUBSCRIBE event */\nstruct mosquitto_evt_unsubscribe {\n\tvoid *future;\n\tstruct mosquitto *client;\n\tstruct mosquitto_subscription data;\n\tvoid *future2[8];\n};\n\n\n/* Data for the MOSQ_EVT_PERSIST_RESTORE event */\n/* NOTE: The persistence interface is currently marked as unstable, which means\n * it may change in a future minor release. */\nstruct mosquitto_evt_persist_restore {\n\tvoid *future[8];\n};\n\n\n/* Data for the MOSQ_EVT_PERSIST_CLIENT_ADD/_DELETE/_UPDATE event */\n/* NOTE: The persistence interface is currently marked as unstable, which means\n * it may change in a future minor release. */\nstruct mosquitto_evt_persist_client {\n\tvoid *future;\n\tstruct mosquitto_client data;\n\tvoid *future2[8];\n};\n\n\n/* Data for the MOSQ_EVT_PERSIST_SUBSCRIPTION_ADD/_DELETE event */\n/* NOTE: The persistence interface is currently marked as unstable, which means\n * it may change in a future minor release. */\nstruct mosquitto_evt_persist_subscription {\n\tvoid *future;\n\tstruct mosquitto_subscription data;\n\tvoid *future2[8];\n};\n\n\n/* Data for the MOSQ_EVT_PERSIST_CLIENT_MSG_ADD/_DELETE/_UPDATE event */\n/* NOTE: The persistence interface is currently marked as unstable, which means\n * it may change in a future minor release. */\nstruct mosquitto_evt_persist_client_msg {\n\tvoid *future;\n\tstruct mosquitto_client_msg data;\n\tvoid *future2[8];\n};\n\n\n/* Data for the MOSQ_EVT_PERSIST_BASE_MSG_ADD/_DELETE/_LOAD event */\n/* NOTE: The persistence interface is currently marked as unstable, which means\n * it may change in a future minor release. */\nstruct mosquitto_evt_persist_base_msg {\n\tvoid *future;\n\tstruct mosquitto_base_msg data;\n\tvoid *future2[8];\n};\n\n\n/* Data for the MOSQ_EVT_PERSIST_RETAIN_MSG_SET/_DELETE event */\n/* NOTE: The persistence interface is currently marked as unstable, which means\n * it may change in a future minor release. */\nstruct mosquitto_evt_persist_retain_msg {\n\tvoid *future;\n\tconst char *topic;\n\tuint64_t store_id;\n\tvoid *future2[8];\n};\n\n/* Data for the MOSQ_EVT_PERSIST_WILL_ADD/_DELETE */\n/* NOTE: The persistence interface is currently marked as unstable, which means\n * it may change in a future minor release. */\nstruct mosquitto_evt_persist_will_msg {\n\tvoid *future;\n\tstruct mosquitto_will_msg data;\n\tvoid *future2[8];\n};\n\n\n/* Callback definition */\ntypedef int (*MOSQ_FUNC_generic_callback)(int, void *, void *);\n\ntypedef struct mosquitto_plugin_id_t mosquitto_plugin_id_t;\n\n/*\n * Function: mosquitto_plugin_set_info\n *\n * Set plugin name and version information for the broker to report. It is\n * recommended this is used in the mosquitto_plugin_init() call.\n */\nmosq_EXPORT int mosquitto_plugin_set_info(\n\t\tmosquitto_plugin_id_t *identifier,\n\t\tconst char *plugin_name,\n\t\tconst char *plugin_version);\n\n\n/*\n * Function: mosquitto_callback_register\n *\n * Register a callback for an event.\n *\n * Parameters:\n *  identifier - the plugin identifier, as provided by <mosquitto_plugin_init>.\n *  event - the event to register a callback for. Can be one of:\n *          * MOSQ_EVT_RELOAD\n *              Called when the broker is sent a signal indicating it should\n *              reload its configuration.\n *          * MOSQ_EVT_ACL_CHECK\n *              Called when a publish/subscribe/unsubscribe command is received\n *              and the broker wants to check when the client is allowed to carry\n *              out this command.\n *          * MOSQ_EVT_BASIC_AUTH\n *              Called when a client connects to the broker, to allow the\n *              username/password/clientid to be authenticated.\n *          * MOSQ_EVT_EXT_AUTH_START\n *              Called when an MQTT v5 client connects, if it is using extended\n *              authentication.\n *          * MOSQ_EVT_EXT_AUTH_CONTINUE\n *              Called when an MQTT v5 client connects, if it is using extended\n *              authentication.\n *          * MOSQ_EVT_CONTROL\n *              Called on receipt of a $CONTROL message that the plugin has\n *              registered for.\n *          * MOSQ_EVT_MESSAGE_IN\n *              Called for each incoming PUBLISH message after it has been received\n *              and authorised. The contents of the message can be modified.\n *          * MOSQ_EVT_MESSAGE_OUT\n *              Called for each outgoing PUBLISH message after it has been authorised,\n *              but before it is sent to each subscribing client. The contents of the\n *              message can be modified.\n *          * MOSQ_EVT_PSK_KEY\n *              Called when a client connects with TLS-PSK and the broker needs\n *              the PSK information.\n *          * MOSQ_EVT_TICK\n *              Called periodically by the broker. The next_s and next_ms\n *              values of the event data can be used to set a minimum interval\n *              that the broker will wait before calling the tick event again\n *              for this callback.\n *          * MOSQ_EVT_DISCONNECT\n *              Called when a client disconnects from the broker.\n *          * MOSQ_EVT_CONNECT\n *              Called when a client has successfully connected to the broker,\n *              i.e. has been authenticated.\n *          * MOSQ_EVT_SUBSCRIBE\n *              Called when a client has made a successful subscription\n *              request, but before the subscription is applied. The\n *              subscription request can be modified, although this is not\n *              recommended.\n *          * MOSQ_EVT_UNSUBSCRIBE\n *              Called when a client has made a successful unsubscription\n *              request, but before the unsubscription is applied. The\n *              unsubscription request can be modified, although this is not\n *              recommended.\n *          * MOSQ_EVT_PERSIST_RESTORE\n *              Called on startup when a persistence plugin should restore\n *              persistence data.\n *          * MOSQ_EVT_PERSIST_BASE_MSG_ADD\n *              Called when a persistence plugin must add a base message to its\n *              store.\n *          * MOSQ_EVT_PERSIST_BASE_MSG_DELETE\n *              Called when a persistence plugin must delete a base message\n *              from its store.\n *          * MOSQ_EVT_PERSIST_RETAIN_MSG_SET\n *              Called when a persistence plugin must set or update a retained\n *              message in its store.\n *          * MOSQ_EVT_PERSIST_RETAIN_MSG_DELETE\n *              Called when a persistence plugin must delete a retained message\n *              from its store.\n *          * MOSQ_EVT_PERSIST_CLIENT_ADD\n *              Called when a persistence plugin must add a client session to\n *              its store.\n *          * MOSQ_EVT_PERSIST_CLIENT_DELETE\n *              Called when a persistence plugin must delete a client session\n *              from its store.\n *          * MOSQ_EVT_PERSIST_CLIENT_UPDATE\n *              Called when a persistence plugin must update a client session\n *              in its store.\n *          * MOSQ_EVT_PERSIST_SUBSCRIPTION_ADD\n *              Called when a persistence plugin must add a client subscription\n *              to its store.\n *          * MOSQ_EVT_PERSIST_SUBSCRIPTION_DELETE\n *              Called when a persistence plugin must delete a client\n *              subscription from its store.\n *          * MOSQ_EVT_PERSIST_CLIENT_MSG_ADD\n *              Called when a persistence plugin must add a client message to\n *              its store.\n *          * MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE\n *              Called when a persistence plugin must delete a client message\n *              from its store.\n *          * MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE\n *              Called when a persistence plugin must update a client message\n *              in its store.\n *\n *  cb_func - the callback function\n *  event_data - event specific data\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if cb_func is NULL\n *\tMOSQ_ERR_NOMEM - on out of memory\n *\tMOSQ_ERR_ALREADY_EXISTS - if cb_func has already been registered for this event\n *\tMOSQ_ERR_NOT_SUPPORTED - if the event is not supported\n */\nmosq_EXPORT int mosquitto_callback_register(\n\t\tmosquitto_plugin_id_t *identifier,\n\t\tint event,\n\t\tMOSQ_FUNC_generic_callback cb_func,\n\t\tconst void *event_data,\n\t\tvoid *userdata);\n\n/*\n * Function: mosquitto_callback_unregister\n *\n * Unregister a previously registered callback function.\n *\n * Parameters:\n *  identifier - the plugin identifier, as provided by <mosquitto_plugin_init>.\n *  event - the event to register a callback for. Can be one of:\n *          * MOSQ_EVT_RELOAD\n *          * MOSQ_EVT_ACL_CHECK\n *          * MOSQ_EVT_BASIC_AUTH\n *          * MOSQ_EVT_EXT_AUTH_START\n *          * MOSQ_EVT_EXT_AUTH_CONTINUE\n *          * MOSQ_EVT_CONTROL\n *          * MOSQ_EVT_MESSAGE_IN\n *          * MOSQ_EVT_MESSAGE_OUT\n *          * MOSQ_EVT_PSK_KEY\n *          * MOSQ_EVT_TICK\n *          * MOSQ_EVT_DISCONNECT\n *          * MOSQ_EVT_CONNECT\n *          * MOSQ_EVT_SUBSCRIBE\n *          * MOSQ_EVT_UNSUBSCRIBE\n *          * MOSQ_EVT_PERSIST_RESTORE\n *          * MOSQ_EVT_PERSIST_BASE_MSG_ADD\n *          * MOSQ_EVT_PERSIST_BASE_MSG_DELETE\n *          * MOSQ_EVT_PERSIST_RETAIN_MSG_SET\n *          * MOSQ_EVT_PERSIST_RETAIN_MSG_DELETE\n *          * MOSQ_EVT_PERSIST_CLIENT_ADD\n *          * MOSQ_EVT_PERSIST_CLIENT_DELETE\n *          * MOSQ_EVT_PERSIST_CLIENT_UPDATE\n *          * MOSQ_EVT_PERSIST_SUBSCRIPTION_ADD\n *          * MOSQ_EVT_PERSIST_SUBSCRIPTION_DELETE\n *          * MOSQ_EVT_PERSIST_CLIENT_MSG_ADD\n *          * MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE\n *          * MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE\n *  cb_func - the callback function\n *  event_data - event specific data\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if cb_func is NULL\n *\tMOSQ_ERR_NOT_FOUND - if cb_func was not registered for this event\n *\tMOSQ_ERR_NOT_SUPPORTED - if the event is not supported\n */\nmosq_EXPORT int mosquitto_callback_unregister(\n\t\tmosquitto_plugin_id_t *identifier,\n\t\tint event,\n\t\tMOSQ_FUNC_generic_callback cb_func,\n\t\tconst void *event_data);\n\n\n/* =========================================================================\n *\n * Section: Utility Functions\n *\n * Use these functions from within your plugin.\n *\n * ========================================================================= */\n\n\n/*\n * Function: mosquitto_log_printf\n *\n * Write a log message using the broker configured logging.\n *\n * Parameters:\n * \tlevel -    Log message priority. Can currently be one of:\n *\n *             * MOSQ_LOG_INFO\n *             * MOSQ_LOG_NOTICE\n *             * MOSQ_LOG_WARNING\n *             * MOSQ_LOG_ERR\n *             * MOSQ_LOG_DEBUG\n *             * MOSQ_LOG_SUBSCRIBE (not recommended for use by plugins)\n *             * MOSQ_LOG_UNSUBSCRIBE (not recommended for use by plugins)\n *\n *             These values are defined in mosquitto.h.\n *\n *\tfmt, ... - printf style format and arguments.\n */\nmosq_EXPORT void mosquitto_log_printf(int level, const char *fmt, ...);\n\n\n/* =========================================================================\n *\n * Client Functions\n *\n * Use these functions to access client information.\n *\n * ========================================================================= */\n\n/*\n * Function: mosquitto_client\n *\n * Retrieve the struct mosquitto for a client id, or NULL if the client is not connected.\n */\nmosq_EXPORT struct mosquitto *mosquitto_client(const char *clientid);\n\n\n/*\n * Function: mosquitto_client_address\n *\n * Retrieve the IP address of the client as a string.\n */\nmosq_EXPORT const char *mosquitto_client_address(const struct mosquitto *client);\n\n\n/*\n * Function: mosquitto_client_port\n *\n * Retrieve the network port number the client connected to, or 0 on error.\n */\nmosq_EXPORT int mosquitto_client_port(const struct mosquitto *client);\n\n\n/*\n * Function: mosquitto_client_clean_session\n *\n * Retrieve the clean session flag value for a client.\n */\nmosq_EXPORT bool mosquitto_client_clean_session(const struct mosquitto *client);\n\n\n/*\n * Function: mosquitto_client_id\n *\n * Retrieve the client id associated with a client.\n */\nmosq_EXPORT const char *mosquitto_client_id(const struct mosquitto *client);\n\n\n/*\n * Function: mosquitto_client_id_hashv\n *\n * Retrieve the hash value associated with a client id.\n */\nmosq_EXPORT unsigned mosquitto_client_id_hashv(const struct mosquitto *client);\n\n\n/*\n * Function: mosquitto_client_keepalive\n *\n * Retrieve the keepalive value for a client.\n */\nmosq_EXPORT int mosquitto_client_keepalive(const struct mosquitto *client);\n\n\n/*\n * Function: mosquitto_client_certificate\n *\n * If TLS support is enabled, return the certificate provided by a client as an\n * X509 pointer from openssl. If the client did not provide a certificate, then\n * NULL will be returned. This function will only ever return a non-NULL value\n * if the `require_certificate` option is set to true.\n *\n * When you have finished with the x509 pointer, it must be freed using\n * X509_free().\n *\n * If TLS is not supported, this function will always return NULL.\n */\nmosq_EXPORT void *mosquitto_client_certificate(const struct mosquitto *client);\n\n\n/*\n * Function: mosquitto_client_protocol\n *\n * Retrieve the protocol with which the client has connected. Can be one of:\n *\n * mp_mqtt (MQTT over TCP)\n * mp_mqttsn (MQTT-SN)\n * mp_websockets (MQTT over Websockets)\n */\nmosq_EXPORT int mosquitto_client_protocol(const struct mosquitto *client);\n\n\n/*\n * Function: mosquitto_client_protocol_version\n *\n * Retrieve the MQTT protocol version with which the client has connected. Can be one of:\n *\n * Returns:\n *   3 - for MQTT v3 / v3.1\n *   4 - for MQTT v3.1.1\n *   5 - for MQTT v5\n */\nmosq_EXPORT int mosquitto_client_protocol_version(const struct mosquitto *client);\n\n\n/*\n * Function: mosquitto_client_sub_count\n *\n * Retrieve the number of subscriptions that have been made by a client.\n */\nmosq_EXPORT int mosquitto_client_sub_count(const struct mosquitto *client);\n\n\n/*\n * Function: mosquitto_client_username\n *\n * Retrieve the username associated with a client.\n */\nmosq_EXPORT const char *mosquitto_client_username(const struct mosquitto *client);\n\n\n/* Function: mosquitto_set_username\n *\n * Set the username for a client.\n *\n * This removes and replaces the current username for a client and hence\n * updates its access.\n *\n * username can be NULL, in which case the client will become anonymous, but\n * must not be zero length.\n *\n * In the case of error, the client will be left with its original username.\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if client is NULL, or if username is zero length\n *   MOSQ_ERR_NOMEM - on out of memory\n */\nmosq_EXPORT int mosquitto_set_username(struct mosquitto *client, const char *username);\n\n/* Function: mosquitto_set_clientid\n *\n * Set the client id for a client.\n *\n * This effectively forces the client onto another message queue.\n * Can be used to scope client ids by prefixing the client id with some user-specific data.\n * eg. \"client1\" could become \"user1-client1\".\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if client is NULL, or if clientid is not a valid utf-8 string\n *   MOSQ_ERR_NOMEM - on out of memory\n */\nmosq_EXPORT int mosquitto_set_clientid(struct mosquitto *client, const char *clientid);\n\n\n/* =========================================================================\n *\n * Section: Client control\n *\n * ========================================================================= */\n\n/* Function: mosquitto_kick_client_by_clientid\n *\n * Forcefully disconnect a client from the broker.\n *\n * If clientid != NULL, then the client with the matching client id is\n *   disconnected from the broker.\n * If clientid == NULL, then all clients are disconnected from the broker.\n *\n * If with_will == true, then if the client has a Last Will and Testament\n *   defined then this will be sent. If false, the LWT will not be sent.\n */\nmosq_EXPORT int mosquitto_kick_client_by_clientid(const char *clientid, bool with_will);\n\n/* Function: mosquitto_kick_client_by_username\n *\n * Forcefully disconnect a client from the broker.\n *\n * If username != NULL, then all clients with a matching username are kicked\n *   from the broker.\n * If username == NULL, then all clients that do not have a username are\n *   kicked.\n *\n * If with_will == true, then if the client has a Last Will and Testament\n *   defined then this will be sent. If false, the LWT will not be sent.\n */\nmosq_EXPORT int mosquitto_kick_client_by_username(const char *username, bool with_will);\n\n/* Function: mosquitto_apply_on_all_clients\n *\n * Apply a given functor to all clients\n *\n * The functor will be applied to all existing client structures. If the functor\n * returns an error code the iteration over the clients will be stopped. The\n * functor_context pointer maybe used to pass additional data structures into\n * the functor as second argument.\n *\n * The result value will be the result of the last functor invoked.\n */\nmosq_EXPORT int mosquitto_apply_on_all_clients(int (*FUNC_client_functor)(const struct mosquitto *, void *), void *functor_context);\n\n/* =========================================================================\n *\n * Section: Publishing functions\n *\n * ========================================================================= */\n\n/* Function: mosquitto_broker_publish\n *\n * Publish a message from within a plugin.\n *\n * This function allows a plugin to publish a message. Messages published in\n * this way are treated as coming from the broker and so will not be passed to\n * `mosquitto_auth_acl_check(, MOSQ_ACL_WRITE, , )` for checking. Read access\n * will be enforced as normal for individual clients when they are due to\n * receive the message.\n *\n * It can be used to send messages to all clients that have a matching\n * subscription, or to a single client whether or not it has a matching\n * subscription.\n *\n * Parameters:\n *  clientid -   optional string. If set to NULL, the message is delivered to all\n *               clients. If non-NULL, the message is delivered only to the\n *               client with the corresponding client id. If the client id\n *               specified is not connected, the message will be dropped.\n *  topic -      message topic\n *  payloadlen - payload length in bytes. Can be 0 for an empty payload.\n *  payload -    payload bytes. If payloadlen > 0 this must not be NULL. Must\n *               be allocated on the heap. Will be freed by mosquitto after use if the\n *               function returns success.\n *  qos -        message QoS to use.\n *  retain -     should retain be set on the message. This does not apply if\n *               clientid is non-NULL.\n *  properties - MQTT v5 properties to attach to the message. If the function\n *               returns success, then properties is owned by the broker and\n *               will be freed at a later point.\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if topic is NULL, if payloadlen < 0, if payloadlen > 0\n *                    and payload is NULL, if qos is not 0, 1, or 2.\n *   MOSQ_ERR_NOMEM - on out of memory\n */\nmosq_EXPORT int mosquitto_broker_publish(\n\t\tconst char *clientid,\n\t\tconst char *topic,\n\t\tint payloadlen,\n\t\tvoid *payload,\n\t\tint qos,\n\t\tbool retain,\n\t\tmosquitto_property *properties);\n\n\n/* Function: mosquitto_broker_publish_copy\n *\n * Publish a message from within a plugin.\n *\n * This function is identical to mosquitto_broker_publish, except that a copy\n * of `payload` is taken.\n *\n * Parameters:\n *  clientid -   optional string. If set to NULL, the message is delivered to all\n *               clients. If non-NULL, the message is delivered only to the\n *               client with the corresponding client id. If the client id\n *               specified is not connected, the message will be dropped.\n *  topic -      message topic\n *  payloadlen - payload length in bytes. Can be 0 for an empty payload.\n *  payload -    payload bytes. If payloadlen > 0 this must not be NULL.\n *\t             Memory remains the property of the calling function.\n *  qos -        message QoS to use.\n *  retain -     should retain be set on the message. This does not apply if\n *               clientid is non-NULL.\n *  properties - MQTT v5 properties to attach to the message. If the function\n *               returns success, then properties is owned by the broker and\n *               will be freed at a later point.\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if topic is NULL, if payloadlen < 0, if payloadlen > 0\n *                    and payload is NULL, if qos is not 0, 1, or 2.\n *   MOSQ_ERR_NOMEM - on out of memory\n */\nmosq_EXPORT int mosquitto_broker_publish_copy(\n\t\tconst char *clientid,\n\t\tconst char *topic,\n\t\tint payloadlen,\n\t\tconst void *payload,\n\t\tint qos,\n\t\tbool retain,\n\t\tmosquitto_property *properties);\n\n/* Function: mosquitto_complete_basic_auth\n *\n * Complete a delayed authentication request.\n *\n * Useful for plugins that subscribe to the MOSQ_EVT_BASIC_AUTH event. If your\n * plugin makes authentication requests that are not \"instant\", in particular\n * if they communicate with an external service, then instead of blocking for a\n * reply and returning MOSQ_ERR_SUCCESS or MOSQ_ERR_AUTH, the plugin can return\n * MOSQ_ERR_AUTH_DELAYED. This means that the plugin is promising to tell the\n * broker the authentication result in the future. Once the plugin has an\n * answer, it should call `mosquitto_complete_basic_auth()` passing the client\n * id and the result.\n *\n * Result:\n *  MOSQ_ERR_SUCCESS - the client successfully authenticated\n *  MOSQ_ERR_AUTH - the client authentication failed\n *\n * Other error codes can be used if more appropriate, and the client connection\n * will still be rejected, e.g. MOSQ_ERR_NOMEM.\n *\n * The plugin may use extra threads to handle the authentication requests, but\n * the call to `mosquitto_complete_basic_auth()` must happen in the main\n * mosquitto thread. Using the MOSQ_EVT_TICK event for this is suggested.\n */\nmosq_EXPORT void mosquitto_complete_basic_auth(const char *clientid, int result);\n\n\n/* Function: mosquitto_broker_node_id_set\n *\n * Set a node ID for this broker between 0-1023 inclusive. This is used to help\n * generate unique client message IDs and hence can be useful for persistence\n * plugins where brokers are sharing a database. It is down to the plugin to ensure\n * this ID is unique.\n *\n * Result:\n *  MOSQ_ERR_SUCCESS - on success\n *  MOSQ_ERR_INVAL - the value was > 1023.\n */\nmosq_EXPORT int mosquitto_broker_node_id_set(uint16_t id);\n\n\n/* =================================================================\n *\n * Persistence interface\n *\n * ================================================================= */\n\n/* NOTE: The persistence interface is currently marked as unstable, which means\n * it may change in a future minor release. */\n\n\n/* Function: mosquitto_persist_client_add\n *\n * Use to add a new client session, in particular when restoring on starting\n * the broker.\n *\n * Parameters:\n *   client->clientid - the client id of the client to add.\n *          This must be allocated on the heap and becomes the property of the\n *          broker immediately this call is made. Must not be NULL.\n *   client->username - the username for the client session, or NULL. Must\n *          be allocated on the heap and becomes the property of the broker\n *          immediately this call is made.\n *   client->auth_method - the MQTT v5 extended authentication method,\n *          or NULL. Must be allocated on the heap and becomes the property of\n *          the broker immediately this call is made.\n *   client->will_delay_time - the actual will delay time for this client\n *   client->session_expiry_time - the actual session expiry time for this\n *          client\n *   client->will_delay_interval - the MQTT v5 will delay interval for this\n *          client\n *   client->maximum_packet_size - the MQTT v5 maximum packet size parameter\n *          for this client\n *   client->listener_port - the listener port that this client last connected to\n *   client->max_qos - the MQTT v5 maximum QoS parameter for this client\n *   client->retain_available - the MQTT v5 retain available parameter for this\n *          client\n *\n *   All other members of struct mosquitto_client are unused.\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if client or client->plugin_clientid is NULL, or if a\n *          client with the same ID already exists.\n *   MOSQ_ERR_NOMEM - on out of memory\n */\nmosq_EXPORT int mosquitto_persist_client_add(struct mosquitto_client *client);\n\n\n/* Function: mosquitto_persist_client_update\n *\n * Use to update client session parameters\n *\n * Parameters:\n *   client->clientid - the client id of the client to update\n *          The broker will *not* modify this string and it remains the\n *          property of the plugin.\n *   client->username - the new username for the client session, or NULL. Must\n *          be allocated on the heap and becomes the property of the broker\n *          immediately this call is made.\n *   client->will_delay_time - the actual will delay time for this client\n *   client->session_expiry_time - the actual session expiry time for this\n *          client\n *   client->will_delay_interval - the MQTT v5 will delay interval for this\n *          client\n *   client->maximum_packet_size - the MQTT v5 maximum packet size parameter\n *          for this client\n *   client->listener_port - the listener port that this client last connected to\n *   client->max_qos - the MQTT v5 maximum QoS parameter for this client\n *   client->retain_available - the MQTT v5 retain available parameter for this\n *          client\n *\n *   All other members of struct mosquitto_client are unused.\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if client or client->clientid is NULL\n *   MOSQ_ERR_NOT_FOUND - the client is not found\n */\nmosq_EXPORT int mosquitto_persist_client_update(struct mosquitto_client *client);\n\n\n/* Function: mosquitto_persist_client_delete\n *\n * Use to delete client session for a client from the broker\n *\n * Parameters:\n *   clientid - the client id of the client to delete\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if clientid is NULL\n *   MOSQ_ERR_NOT_FOUND - the referenced client is not found\n */\nmosq_EXPORT int mosquitto_persist_client_delete(const char *clientid);\n\n\n/* Function: mosquitto_persist_client_msg_add\n *\n * Use to add a client message for a particular client.\n *\n * Parameters:\n *   client_msg->clientid - the client id of the client that the\n *          message belongs to.\n *   client_msg->cmsg_id - the ID of this client message.\n *   client_msg->store_id - the ID of the base message that this client\n *          message references.\n *   client_msg->subscription_identifier - the MQTT v5 subscription identifier,\n *          for outgoing messages only.\n *   client_msg->mid - the MQTT message ID of the new message\n *   client_msg->qos - the MQTT QoS of the new message\n *   client_msg->retain - the retain flag of the message\n *   client_msg->dup - the dup flag of the message\n *   client_msg->direction - the direction of the new message from the perspective\n *          of the broker (mosq_bmd_in / mosq_bmd_out)\n *   client_msg->state - the current message state\n *\n *   All other members of struct mosquitto_client_msg are unused.\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if client_msg or client_msg->plugin_clientid is NULL\n *   MOSQ_ERR_NOT_FOUND - the client or base message is not found\n */\nmosq_EXPORT int mosquitto_persist_client_msg_add(struct mosquitto_client_msg *client_msg);\n\n\n/* Function: mosquitto_persist_client_msg_delete\n *\n * Use to delete a client message for a particular client.\n *\n * Parameters:\n *   client_msg->clientid - the client id of the client that the\n *          message belongs to.\n *   client_msg->cmsg_id - the client message id of the affected message\n *   client_msg->mid - the MQTT message id of the affected message\n *   client_msg->qos - the MQTT QoS of the affected message\n *   client_msg->direction - the direction of the message from the perspective\n *          of the broker (mosq_bmd_in / mosq_bmd_out)\n *\n *   All other members of struct mosquitto_client_msg are unused.\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if client_msg or client_msg->clientid is NULL\n *   MOSQ_ERR_NOT_FOUND - the client is not found\n */\nmosq_EXPORT int mosquitto_persist_client_msg_delete(struct mosquitto_client_msg *client_msg);\n\n\n/* Function: mosquitto_persist_client_msg_update\n *\n * Use to update the state of a client message for a particular client.\n *\n * Parameters:\n *   client_msg->clientid - the client id of the client that the\n *          message belongs to.\n *   client_msg->cmsg_id - the client message id of the affected message\n *   client_msg->mid - the MQTT message id of the affected message\n *   client_msg->qos - the MQTT QoS of the affected message\n *   client_msg->direction - the direction of the message from the perspective\n *          of the broker (mosq_bmd_in / mosq_bmd_out)\n *   client_msg->state - the new state of the message\n *\n *   All other members of struct mosquitto_client_msg are unused.\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if client_msg or client_msg->clientid is NULL\n *   MOSQ_ERR_NOT_FOUND - the client is not found\n */\nmosq_EXPORT int mosquitto_persist_client_msg_update(struct mosquitto_client_msg *client_msg);\n\n\n/* Function: mosquitto_persist_client_msg_clear\n *\n * Clear all messages for the listed client and direction.\n *\n * Parameters:\n *   client_msg->clientid - the client id of the client that the\n *          message belongs to.\n *   client_msg->direction - the direction of the messages to be cleared, from\n *          the perspective of the broker (mosq_bmd_in / mosq_bmd_out / mosq_bmd_all)\n *\n *   All other members of struct mosquitto_client_msg are unused.\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if client_msg or client_msg->clientid is NULL\n *   MOSQ_ERR_NOT_FOUND - the client is not found\n */\nmosq_EXPORT int mosquitto_persist_client_msg_clear(struct mosquitto_client_msg *client_msg);\n\n/* Function: mosquitto_persist_base_msg_add\n *\n * Use to add a new base message. Any client messages or retained messages\n * refering to this base message must be added afterwards.\n *\n * Parameters:\n *   msg->store_id - the base message ID\n *   msg->expiry_time - the time at which the message expires, or 0.\n *   msg->topic - the message topic.\n *          Must be allocated on the heap and becomes the property of the\n *          broker immediately this call is made.\n *   msg->payload - the message payload.\n *          Must be allocated on the heap and becomes the property of the\n *          broker immediately this call is made.\n *   msg->source_id - the client id of the client that the\n *          message originated with, or NULL.\n *          The broker will *not* modify this string and it remains the\n *          property of the plugin.\n *   msg->source_username - the username of the client that the\n *          message originated with, or NULL.\n *          The broker will *not* modify this string and it remains the\n *          property of the plugin.\n *   msg->properties - list of MQTT v5 message properties for this message.\n *          Must be allocated on the heap and becomes the property of the\n *          broker immediately this call is made.\n *   msg->payloadlen - the length of the payload, in bytes\n *   msg->source_mid - the mid of the source message\n *   msg->source_port - the network port number that the originating client was\n *          connected to, or 0.\n *   msg->qos - the message QoS as delivered to the broker\n *   msg->retain - the message retain flag as delivered to the broker\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_NOMEM - on out of memory\n */\nmosq_EXPORT int mosquitto_persist_base_msg_add(struct mosquitto_base_msg *msg);\n\n\n/* Function: mosquitto_persist_base_msg_delete\n *\n * Use to delete a base message.\n *\n * Parameters:\n *   store_id - the base message ID\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n */\nmosq_EXPORT int mosquitto_persist_base_msg_delete(uint64_t store_id);\n\n\n/* Function: mosquitto_persist_subscription_add\n *\n * Use to add a new subscription for a client\n *\n * Parameters:\n *   sub->clientid - the client id of the client the new subscription is for\n *   sub->topic_filter - the topic filter for the subscription\n *   sub->options - the QoS and other flags for this subscription\n *   sub->identifier - the MQTT v5 subscription id, or 0\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if sub, clientid, or topic_filter are NULL, or are zero length\n *   MOSQ_ERR_NOT_FOUND - the referenced client was not found\n *   MOSQ_ERR_NOMEM - on out of memory\n */\nmosq_EXPORT int mosquitto_subscription_add(const struct mosquitto_subscription *sub);\n\n\n/* Function: mosquitto_persist_subscription_delete\n *\n * Use to delete a subscription for a client\n *\n * Parameters:\n *   clientid - the client id of the client the new subscription is for\n *   topic_filter - the topic filter for the subscription\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if clientid or topic are NULL, or are zero length\n *   MOSQ_ERR_NOT_FOUND - the referenced client was not found\n *   MOSQ_ERR_NOMEM - on out of memory\n */\nmosq_EXPORT int mosquitto_subscription_delete(const char *clientid, const char *topic_filter);\n\n\n/* Function: mosquitto_persist_retain_msg_set\n *\n * Use to set a retained message. It is not required to delete a retained\n * message for an existing topic first.\n *\n * Parameters:\n *   topic - the topic that the message references\n *   store_id - the ID of the base message that is to be retained\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if topic is NULL\n *   MOSQ_ERR_NOT_FOUND - the referenced base message was not found\n *   MOSQ_ERR_NOMEM - on out of memory\n */\nmosq_EXPORT int mosquitto_persist_retain_msg_set(const char *topic, uint64_t store_id);\n\n\n/* Function: mosquitto_persist_retain_msg_delete\n *\n * Use to delete a retained message.\n *\n * Parameters:\n *   topic - the topic that the message references\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   MOSQ_ERR_INVAL - if topic is NULL\n *   MOSQ_ERR_NOMEM - on out of memory\n */\nmosq_EXPORT int mosquitto_persist_retain_msg_delete(const char *topic);\n\n/* Function: mosquitto_persistence_location\n *\n * Returns the `persistence_location` config option, or the contents of the\n * MOSQUITTO_PERSISTENCE_LOCATION environment variable, if set.\n *\n * This location should be used by plugins needing to store persistent data.\n * Use of sub directories is recommended.\n *\n * Returns:\n *   A valid pointer to the persistence location string\n *   A NULL pointer if neither the option nor the variable are set\n */\nmosq_EXPORT const char *mosquitto_persistence_location(void);\n\n/* Function: mosquitto_client_will_set\n *\n * Set a will message for the client.\n *\n * Parameters:\n *     clientid -   clientid of the sesion to set the will message for.\n *     topic -      the topic on which to publish the will.\n *     payloadlen - the size of the payload (bytes). Valid values are between 0 and\n *                  268,435,455.\n *     payload -    pointer to the data to send. If payloadlen > 0 this must be a\n *                  valid memory location. The payload will be copied.\n *     qos -        integer value 0, 1 or 2 indicating the Quality of Service to be\n *                  used for the will.\n *     retain -     set to true to make the will a retained message.\n *     properties - list of MQTT 5 properties. Can be NULL. The property list\n *                  becomes the property of the broker and will be freed by the\n *                  broker, if the function call was successfull.\n *\n * Returns:\n *     MOSQ_ERR_SUCCESS -        on success.\n *     MOSQ_ERR_NOT_FOUND -      if the client is not found.\n *     MOSQ_ERR_INVAL -          if the input parameters were invalid.\n *     MOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n *     MOSQ_ERR_PAYLOAD_SIZE -   if payloadlen is too large.\n *     MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8.\n */\nmosq_EXPORT int mosquitto_client_will_set(const char *clientid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties);\n\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "include/mosquitto/broker_control.h",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * File: mosquitto/broker_control.h\n *\n * This header contains functions for use by plugins using the CONTROL event.\n */\n#ifndef MOSQUITTO_BROKER_CONTROL_H\n#define MOSQUITTO_BROKER_CONTROL_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <cjson/cJSON.h>\n\n#include <mosquitto/broker.h>\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n\n\n/* =========================================================================\n *\n * Section: $CONTROL event helpers\n *\n * ========================================================================= */\n\nstruct mosquitto_control_cmd {\n\tstruct mosquitto *client;\n\tcJSON *j_responses;\n\tcJSON *j_command;\n\tchar *correlation_data;\n\tconst char *command_name;\n};\n\nmosq_EXPORT void mosquitto_control_command_reply(struct mosquitto_control_cmd *cmd, const char *error);\nmosq_EXPORT void mosquitto_control_send_response(cJSON *tree, const char *topic);\nmosq_EXPORT int mosquitto_control_generic_callback(struct mosquitto_evt_control *event_data, const char *response_topic, void *userdata,\n\t\tint (*cmd_cb)(struct mosquitto_control_cmd *cmd, void *userdata));\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "include/mosquitto/broker_plugin.h",
    "content": "/*\nCopyright (c) 2012-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_BROKER_PLUGIN_H\n#define MOSQUITTO_BROKER_PLUGIN_H\n\n/*\n * File: mosquitto_plugin.h\n *\n * This header contains function declarations for use when writing a Mosquitto plugin.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* The generic plugin interface starts at version 5 */\n#define MOSQ_PLUGIN_VERSION 5\n\n/* The old auth only interface stopped at version 4 */\n#define MOSQ_AUTH_PLUGIN_VERSION 4\n\n#define MOSQ_ACL_NONE 0x00\n#define MOSQ_ACL_READ 0x01\n#define MOSQ_ACL_WRITE 0x02\n#define MOSQ_ACL_SUBSCRIBE 0x04\n#define MOSQ_ACL_UNSUBSCRIBE 0x08\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n\nstruct mosquitto;\n\nstruct mosquitto_opt {\n\tchar *key;\n\tchar *value;\n};\n\nstruct mosquitto_auth_opt {\n\tchar *key;\n\tchar *value;\n};\n\nstruct mosquitto_acl_msg {\n\tconst char *topic;\n\tconst void *payload;\n\tlong payloadlen;\n\tint qos;\n\tbool retain;\n};\n\n#ifdef WIN32\n#  define mosq_plugin_EXPORT __declspec(dllexport)\n#else\n#  define mosq_plugin_EXPORT\n#endif\n\n/*\n * To create an authentication plugin you must include this file then implement\n * the functions listed in the \"Plugin Functions\" section below. The resulting\n * code should then be compiled as a shared library. Using gcc this can be\n * achieved as follows:\n *\n * gcc -I<path to mosquitto_plugin.h> -fPIC -shared plugin.c -o plugin.so\n *\n * On Mac OS X:\n *\n * gcc -I<path to mosquitto_plugin.h> -fPIC -shared plugin.c -undefined dynamic_lookup -o plugin.so\n *\n */\n\n/* =========================================================================\n *\n * Helper Functions\n *\n * ========================================================================= */\n\n/* There are functions that are available for plugin developers to use in\n * mosquitto_broker.h, including logging and accessor functions.\n */\n\n\n/* =========================================================================\n *\n * Section: Plugin Functions v5\n *\n * This is the plugin version 5 interface, which covers authentication, access\n * control, the $CONTROL topic space handling, and message inspection and\n * modification.\n *\n * This interface is available from v2.0 onwards.\n *\n * There are just three functions to implement in your plugin. You should\n * register callbacks to handle different events in your\n * mosquitto_plugin_init() function. See mosquitto_broker.h for the events and\n * callback registering functions.\n *\n * ========================================================================= */\n\n/*\n * Function: mosquitto_plugin_version\n *\n * The broker will attempt to call this function immediately after loading the\n * plugin to check it is a supported plugin version. Your code must simply\n * return the plugin interface version you support, i.e. 5.\n *\n * The supported_versions array tells you which plugin versions the broker supports.\n *\n * If the broker does not support the version that you require, return -1 to\n * indicate failure.\n *\n * HELPER: If you only wish to declare support for a single version, you can\n * use the helper macro:\n *\n * MOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n */\nmosq_plugin_EXPORT int mosquitto_plugin_version(int supported_version_count, const int *supported_versions);\n\n#define MOSQUITTO_PLUGIN_DECLARE_VERSION(A) \\\n\t\tint mosquitto_plugin_version(int supported_version_count, const int *supported_versions) \\\n\t\t{ \\\n\t\t\tint i; \\\n\t\t\tfor(i=0; i<supported_version_count; i++){ \\\n\t\t\t\tif(supported_versions[i] == (A)){ \\\n\t\t\t\t\treturn (A); \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t\treturn -1; \\\n\t\t}\n\n/*\n * Function: mosquitto_plugin_init\n *\n * Called after the plugin has been loaded and <mosquitto_plugin_version>\n * has been called. This will only ever be called once and can be used to\n * initialise the plugin.\n *\n * Parameters:\n *\n *  identifier -     This is a pointer to an opaque structure which you must\n *                   save and use when registering/unregistering callbacks.\n *\tuser_data -      The pointer set here will be passed to the other plugin\n *\t                 functions. Use to hold connection information for example.\n *\topts -           Pointer to an array of struct mosquitto_opt, which\n *\t                 provides the plugin options defined in the configuration file.\n *\topt_count -      The number of elements in the opts array.\n *\n * Return value:\n *\tReturn 0 on success\n *\tReturn >0 on failure.\n */\nmosq_plugin_EXPORT int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **userdata, struct mosquitto_opt *options, int option_count);\n\n\n/*\n * Function: mosquitto_plugin_cleanup\n *\n * Called when the broker is shutting down. This will only ever be called once\n * per plugin.\n *\n * If you do not need to do any of your own cleanup, this function is not\n * required. The broker will automatically unregister your callbacks.\n *\n * Parameters:\n *\n *\tuser_data -      The pointer provided in <mosquitto_plugin_init>.\n *\topts -           Pointer to an array of struct mosquitto_opt, which\n *\t                 provides the plugin options defined in the configuration file.\n *\topt_count -      The number of elements in the opts array.\n *\n * Return value:\n *\tReturn 0 on success\n *\tReturn >0 on failure.\n */\nmosq_plugin_EXPORT int mosquitto_plugin_cleanup(void *userdata, struct mosquitto_opt *options, int option_count);\n\n\n\n/* =========================================================================\n *\n * Section: Plugin Functions v4\n *\n * This is the plugin version 4 interface, which is exclusively for\n * authentication and access control, and which is still supported for existing\n * plugins. If you are developing a new plugin, please use the v5 interface.\n *\n * You must implement these functions in your plugin.\n *\n * Authentication plugins can implement one or both of authentication and\n * access control. If your plugin does not wish to handle either of\n * authentication or access control it should return MOSQ_ERR_PLUGIN_DEFER. In\n * this case, the next plugin will handle it. If all plugins return\n * MOSQ_ERR_PLUGIN_DEFER, the request will be denied.\n *\n * For each check, the following flow happens:\n *\n * * The default password file and/or acl file checks are made. If either one\n *   of these is not defined, then they are considered to be deferred. If either\n *   one accepts the check, no further checks are made. If an error occurs, the\n *   check is denied\n * * The first plugin does the check, if it returns anything other than\n *   MOSQ_ERR_PLUGIN_DEFER, then the check returns immediately. If the plugin\n *   returns MOSQ_ERR_PLUGIN_DEFER then the next plugin runs its check.\n * * If the final plugin returns MOSQ_ERR_PLUGIN_DEFER, then access will be\n *   denied.\n * ========================================================================= */\n\n/*\n * Function: mosquitto_auth_plugin_version\n *\n * The broker will call this function immediately after loading the plugin to\n * check it is a supported plugin version. Your code must simply return\n * the version of the plugin interface you support, i.e. 4.\n */\nmosq_plugin_EXPORT int mosquitto_auth_plugin_version(void);\n\n\n/*\n * Function: mosquitto_auth_plugin_init\n *\n * Called after the plugin has been loaded and <mosquitto_auth_plugin_version>\n * has been called. This will only ever be called once and can be used to\n * initialise the plugin.\n *\n * Parameters:\n *\n *\tuser_data -      The pointer set here will be passed to the other plugin\n *\t                 functions. Use to hold connection information for example.\n *\topts -           Pointer to an array of struct mosquitto_opt, which\n *\t                 provides the plugin options defined in the configuration file.\n *\topt_count -      The number of elements in the opts array.\n *\n * Return value:\n *\tReturn 0 on success\n *\tReturn >0 on failure.\n */\nmosq_plugin_EXPORT int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *opts, int opt_count);\n\n\n/*\n * Function: mosquitto_auth_plugin_cleanup\n *\n * Called when the broker is shutting down. This will only ever be called once\n * per plugin.\n * Note that <mosquitto_auth_security_cleanup> will be called directly before\n * this function.\n *\n * Parameters:\n *\n *\tuser_data -      The pointer provided in <mosquitto_auth_plugin_init>.\n *\topts -           Pointer to an array of struct mosquitto_opt, which\n *\t                 provides the plugin options defined in the configuration file.\n *\topt_count -      The number of elements in the opts array.\n *\n * Return value:\n *\tReturn 0 on success\n *\tReturn >0 on failure.\n */\nmosq_plugin_EXPORT int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count);\n\n\n/*\n * Function: mosquitto_auth_security_init\n *\n * This function is called in two scenarios:\n *\n * 1. When the broker starts up.\n * 2. If the broker is requested to reload its configuration whilst running. In\n *    this case, <mosquitto_auth_security_cleanup> will be called first, then\n *    this function will be called.  In this situation, the reload parameter\n *    will be true.\n *\n * Parameters:\n *\n *\tuser_data -      The pointer provided in <mosquitto_auth_plugin_init>.\n *\topts -           Pointer to an array of struct mosquitto_opt, which\n *\t                 provides the plugin options defined in the configuration file.\n *\topt_count -      The number of elements in the opts array.\n *\treload -         If set to false, this is the first time the function has\n *\t                 been called. If true, the broker has received a signal\n *\t                 asking to reload its configuration.\n *\n * Return value:\n *\tReturn 0 on success\n *\tReturn >0 on failure.\n */\nmosq_plugin_EXPORT int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *opts, int opt_count, bool reload);\n\n\n/*\n * Function: mosquitto_auth_security_cleanup\n *\n * This function is called in two scenarios:\n *\n * 1. When the broker is shutting down.\n * 2. If the broker is requested to reload its configuration whilst running. In\n *    this case, this function will be called, followed by\n *    <mosquitto_auth_security_init>. In this situation, the reload parameter\n *    will be true.\n *\n * Parameters:\n *\n *\tuser_data -      The pointer provided in <mosquitto_auth_plugin_init>.\n *\topts -           Pointer to an array of struct mosquitto_opt, which\n *\t                 provides the plugin options defined in the configuration file.\n *\topt_count -      The number of elements in the opts array.\n *\treload -         If set to false, this is the first time the function has\n *\t                 been called. If true, the broker has received a signal\n *\t                 asking to reload its configuration.\n *\n * Return value:\n *\tReturn 0 on success\n *\tReturn >0 on failure.\n */\nmosq_plugin_EXPORT int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count, bool reload);\n\n\n/*\n * Function: mosquitto_auth_acl_check\n *\n * Called by the broker when topic access must be checked. access will be one\n * of:\n *  MOSQ_ACL_SUBSCRIBE when a client is asking to subscribe to a topic string.\n *                     This differs from MOSQ_ACL_READ in that it allows you to\n *                     deny access to topic strings rather than by pattern. For\n *                     example, you may use MOSQ_ACL_SUBSCRIBE to deny\n *                     subscriptions to '#', but allow all topics in\n *                     MOSQ_ACL_READ. This allows clients to subscribe to any\n *                     topic they want, but not discover what topics are in use\n *                     on the server.\n *  MOSQ_ACL_READ      when a message is about to be sent to a client (i.e. whether\n *                     it can read that topic or not).\n *  MOSQ_ACL_WRITE     when a message has been received from a client (i.e. whether\n *                     it can write to that topic or not).\n *\n * Return:\n *\tMOSQ_ERR_SUCCESS if access was granted.\n *\tMOSQ_ERR_ACL_DENIED if access was not granted.\n *\tMOSQ_ERR_UNKNOWN for an application specific error.\n *\tMOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check.\n */\nmosq_plugin_EXPORT int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg);\n\n\n/*\n * Function: mosquitto_auth_unpwd_check\n *\n * This function is OPTIONAL. Only include this function in your plugin if you\n * are making basic username/password checks.\n *\n * Called by the broker when a username/password must be checked.\n *\n * Return:\n *\tMOSQ_ERR_SUCCESS if the user is authenticated.\n *\tMOSQ_ERR_AUTH if authentication failed.\n *\tMOSQ_ERR_UNKNOWN for an application specific error.\n *\tMOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check.\n */\nmosq_plugin_EXPORT int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password);\n\n\n/*\n * Function: mosquitto_psk_key_get\n *\n * This function is OPTIONAL. Only include this function in your plugin if you\n * are making TLS-PSK checks.\n *\n * Called by the broker when a client connects to a listener using TLS/PSK.\n * This is used to retrieve the pre-shared-key associated with a client\n * identity.\n *\n * Examine hint and identity to determine the required PSK (which must be a\n * hexadecimal string with no leading \"0x\") and copy this string into key.\n *\n * Parameters:\n *\tuser_data -   the pointer provided in <mosquitto_auth_plugin_init>.\n *\thint -        the psk_hint for the listener the client is connecting to.\n *\tidentity -    the identity string provided by the client\n *\tkey -         a string where the hex PSK should be copied\n *\tmax_key_len - the size of key\n *\n * Return value:\n *\tReturn 0 on success.\n *\tReturn >0 on failure.\n *\tReturn MOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check.\n */\nmosq_plugin_EXPORT int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len);\n\n/*\n * Function: mosquitto_auth_start\n *\n * This function is OPTIONAL. Only include this function in your plugin if you\n * are making extended authentication checks.\n *\n * Parameters:\n *\tuser_data -   the pointer provided in <mosquitto_auth_plugin_init>.\n *\tmethod - the authentication method\n *\treauth - this is set to false if this is the first authentication attempt\n *\t         on a connection, set to true if the client is attempting to\n *\t         reauthenticate.\n *\tdata_in - pointer to authentication data, or NULL\n *\tdata_in_len - length of data_in, in bytes\n *\tdata_out - if your plugin wishes to send authentication data back to the\n *\t           client, allocate some memory using malloc or friends and set\n *\t           data_out. The broker will free the memory after use.\n *\tdata_out_len - Set the length of data_out in bytes.\n *\n * Return value:\n *\tReturn MOSQ_ERR_SUCCESS if authentication was successful.\n *\tReturn MOSQ_ERR_AUTH_CONTINUE if the authentication is a multi step process and can continue.\n *\tReturn MOSQ_ERR_AUTH if authentication was valid but did not succeed.\n *\tReturn any other relevant positive integer MOSQ_ERR_* to produce an error.\n */\nmosq_plugin_EXPORT int mosquitto_auth_start(void *user_data, struct mosquitto *client, const char *method, bool reauth, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len);\n\nmosq_plugin_EXPORT int mosquitto_auth_continue(void *user_data, struct mosquitto *client, const char *method, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len);\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/defs.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_DEFS_H\n#define MOSQUITTO_DEFS_H\n\n/*\n * File: mosquitto/defs.h\n *\n * This header contains defines and enums used by the mosquitto broker and\n * libmosquitto, the Mosquitto client library.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <mosquitto/mqtt_protocol.h>\n\n/* Log types */\n#define MOSQ_LOG_NONE           0\n#define MOSQ_LOG_INFO           (1<<0)\n#define MOSQ_LOG_NOTICE         (1<<1)\n#define MOSQ_LOG_WARNING        (1<<2)\n#define MOSQ_LOG_ERR            (1<<3)\n#define MOSQ_LOG_DEBUG          (1<<4)\n#define MOSQ_LOG_SUBSCRIBE      (1<<5)\n#define MOSQ_LOG_UNSUBSCRIBE    (1<<6)\n#define MOSQ_LOG_WEBSOCKETS     (1<<7)\n#define MOSQ_LOG_INTERNAL       0x80000000U\n#define MOSQ_LOG_ALL            0xFFFFFFFFU\n\n/* Enum: mosq_err_t\n * Integer values returned from many libmosquitto functions. */\nenum mosq_err_t {\n\tMOSQ_ERR_QUOTA_EXCEEDED = -6,\n\tMOSQ_ERR_AUTH_DELAYED = -5,\n\tMOSQ_ERR_AUTH_CONTINUE = -4,\n\tMOSQ_ERR_NO_SUBSCRIBERS = -3,\n\tMOSQ_ERR_SUB_EXISTS = -2,\n\tMOSQ_ERR_CONN_PENDING = -1,\n\tMOSQ_ERR_SUCCESS = 0,\n\tMOSQ_ERR_NOMEM = 1,\n\tMOSQ_ERR_PROTOCOL = 2,\n\tMOSQ_ERR_INVAL = 3,\n\tMOSQ_ERR_NO_CONN = 4,\n\tMOSQ_ERR_CONN_REFUSED = 5,\n\tMOSQ_ERR_NOT_FOUND = 6,\n\tMOSQ_ERR_CONN_LOST = 7,\n\tMOSQ_ERR_TLS = 8,\n\tMOSQ_ERR_PAYLOAD_SIZE = 9,\n\tMOSQ_ERR_NOT_SUPPORTED = 10,\n\tMOSQ_ERR_AUTH = 11,\n\tMOSQ_ERR_ACL_DENIED = 12,\n\tMOSQ_ERR_UNKNOWN = 13,\n\tMOSQ_ERR_ERRNO = 14,\n\tMOSQ_ERR_EAI = 15,\n\tMOSQ_ERR_PROXY = 16,\n\tMOSQ_ERR_PLUGIN_DEFER = 17,\n\tMOSQ_ERR_MALFORMED_UTF8 = 18,\n\tMOSQ_ERR_KEEPALIVE = 19,\n\tMOSQ_ERR_LOOKUP = 20,\n\tMOSQ_ERR_MALFORMED_PACKET = 21,\n\tMOSQ_ERR_DUPLICATE_PROPERTY = 22,\n\tMOSQ_ERR_TLS_HANDSHAKE = 23,\n\tMOSQ_ERR_QOS_NOT_SUPPORTED = 24,\n\tMOSQ_ERR_OVERSIZE_PACKET = 25,\n\tMOSQ_ERR_OCSP = 26,\n\tMOSQ_ERR_TIMEOUT = 27,\n\t/* 28, 29, 30 - was internal only, moved to MQTT v5 section. */\n\tMOSQ_ERR_ALREADY_EXISTS = 31,\n\tMOSQ_ERR_PLUGIN_IGNORE = 32,\n\tMOSQ_ERR_HTTP_BAD_ORIGIN = 33,\n\n\t/* MQTT v5 direct equivalents 128-255 */\n\tMOSQ_ERR_UNSPECIFIED = 128,\n\t/* MOSQ_ERR_MALFORMED_PACKET = 129, // 21 above */\n\tMOSQ_ERR_IMPLEMENTATION_SPECIFIC = 131,\n\tMOSQ_ERR_UNSUPPORTED_PROTOCOL_VERSION = 132,\n\tMOSQ_ERR_CLIENT_IDENTIFIER_NOT_VALID = 133,\n\tMOSQ_ERR_BAD_USERNAME_OR_PASSWORD = 134,\n\t/* MOSQ_ERR_NOT_AUTHORIZED = 135, //  11 above */\n\tMOSQ_ERR_SERVER_UNAVAILABLE = 136,\n\tMOSQ_ERR_SERVER_BUSY = 137,\n\tMOSQ_ERR_BANNED = 138,\n\tMOSQ_ERR_SERVER_SHUTTING_DOWN = 139,\n\tMOSQ_ERR_BAD_AUTHENTICATION_METHOD = 140,\n\t/* MOSQ_ERR_KEEP_ALIVE_TIMEOUT = 141, // 19 above */\n\tMOSQ_ERR_SESSION_TAKEN_OVER = 142,\n\tMOSQ_ERR_TOPIC_FILTER_INVALID = 143,\n\tMOSQ_ERR_TOPIC_NAME_INVALID = 144,\n\tMOSQ_ERR_PACKET_ID_IN_USE = 145,\n\tMOSQ_ERR_PACKET_ID_NOT_FOUND = 146,\n\tMOSQ_ERR_RECEIVE_MAXIMUM_EXCEEDED = 147,\n\tMOSQ_ERR_TOPIC_ALIAS_INVALID = 148,\n\t/* MOSQ_ERR_PACKET_TOO_LARGE = 149, // 25 above */\n\tMOSQ_ERR_MESSAGE_RATE_TOO_HIGH = 150,\n\t/* MOSQ_ERR_QUOTA_EXCEEDED = 151, */\n\tMOSQ_ERR_ADMINISTRATIVE_ACTION = 152,\n\tMOSQ_ERR_PAYLOAD_FORMAT_INVALID = 153,\n\tMOSQ_ERR_RETAIN_NOT_SUPPORTED = 154,\n\t/* MOSQ_ERR_QOS_NOT_SUPPORTED = 155, // 24 above */\n\tMOSQ_ERR_USE_ANOTHER_SERVER = 156,\n\tMOSQ_ERR_SERVER_MOVED = 157,\n\tMOSQ_ERR_SHARED_SUBS_NOT_SUPPORTED = 158,\n\tMOSQ_ERR_CONNECTION_RATE_EXCEEDED = 159,\n\tMOSQ_ERR_MAXIMUM_CONNECT_TIME = 160,\n\tMOSQ_ERR_SUBSCRIPTION_IDS_NOT_SUPPORTED = 161,\n\tMOSQ_ERR_WILDCARD_SUBS_NOT_SUPPORTED = 162,\n};\n\nenum mosq_transport_t {\n\tMOSQ_T_TCP = 1,\n\tMOSQ_T_WEBSOCKETS = 2,\n};\n\n/* MQTT specification restricts client ids to a maximum of 23 characters */\n#define MOSQ_MQTT_ID_MAX_LENGTH 23\n\n#define MQTT_PROTOCOL_V31 3\n#define MQTT_PROTOCOL_V311 4\n#define MQTT_PROTOCOL_V5 5\n\nstruct mosquitto;\ntypedef struct mqtt5__property mosquitto_property;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libcommon.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_H\n#define MOSQUITTO_LIBCOMMON_H\n\n/*\n * File: mosquitto/libcommon.h\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef WIN32\n#  ifdef libmosquitto_common_EXPORTS\n#    define libmosqcommon_EXPORT __declspec(dllexport)\n#  else\n#    define libmosqcommon_EXPORT  __declspec(dllimport)\n#  endif\n#else\n#  define libmosqcommon_EXPORT\n#endif\n\n#include <mosquitto/libcommon_base64.h>\n#include <mosquitto/libcommon_cjson.h>\n#include <mosquitto/libcommon_file.h>\n#include <mosquitto/libcommon_memory.h>\n#include <mosquitto/libcommon_password.h>\n#include <mosquitto/libcommon_properties.h>\n#include <mosquitto/libcommon_random.h>\n#include <mosquitto/libcommon_string.h>\n#include <mosquitto/libcommon_time.h>\n#include <mosquitto/libcommon_topic.h>\n#include <mosquitto/libcommon_utf8.h>\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n"
  },
  {
    "path": "include/mosquitto/libcommon_base64.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_BASE64_H\n#define MOSQUITTO_LIBCOMMON_BASE64_H\n\n/*\n * File: mosquitto/libcommon_base64.h\n *\n * This header contains functions for handling base64\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nlibmosqcommon_EXPORT int mosquitto_base64_encode(const unsigned char *in, size_t in_len, char **encoded);\nlibmosqcommon_EXPORT int mosquitto_base64_decode(const char *in, unsigned char **decoded, unsigned int *decoded_len);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "include/mosquitto/libcommon_cjson.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_CJSON_H\n#define MOSQUITTO_LIBCOMMON_CJSON_H\n\n/*\n * File: mosquitto/libcommon_cjson.h\n *\n * This header contains functions for handling cJSON objects\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <cjson/cJSON.h>\n\nlibmosqcommon_EXPORT cJSON *mosquitto_properties_to_json(const mosquitto_property *properties);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "include/mosquitto/libcommon_file.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_FILE_H\n#define MOSQUITTO_LIBCOMMON_FILE_H\n\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdio.h>\n\n/*\n * File: mosquitto/libcommon_file.h\n *\n * This header contains functions and definitions for reading/writing files.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Function: mosquitto_fopen\n */\nlibmosqcommon_EXPORT FILE *mosquitto_fopen(const char *path, const char *mode, bool restrict_read);\n\n\n/*\n * Function: mosquitto_fgets\n */\nlibmosqcommon_EXPORT char *mosquitto_fgets(char **buf, int *buflen, FILE *stream);\n\n/*\n * Function: mosquitto_write_file\n */\nlibmosqcommon_EXPORT int mosquitto_write_file(const char *target_path, bool restrict_read, int (*write_fn)(FILE *fptr, void *user_data), void *user_data, void (*log_fn)(const char *msg));\n\n\n/*\n * Function: mosquitto_read_file\n */\nlibmosqcommon_EXPORT int mosquitto_read_file(const char *file, bool restrict_read, char **buf, size_t *buflen);\n\n\n/*\n * Function: mosquitto_trimblanks\n *\n * Removes blanks from the end of a string.\n */\nlibmosqcommon_EXPORT char *mosquitto_trimblanks(char *str);\n\nlibmosqcommon_EXPORT extern void (*libcommon_vprintf)(const char *fmt, va_list va);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libcommon_memory.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_MEMORY_H\n#define MOSQUITTO_LIBCOMMON_MEMORY_H\n\n/*\n * File: mosquitto/libcommon_memory.h\n *\n * This header contains functions and definitions for allocating and freeing\n * memory in broker plugins\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* =========================================================================\n *\n * Section: Memory allocation.\n *\n * Use these functions when allocating or freeing memory to have your memory\n * included in the memory tracking on the broker.\n *\n * ========================================================================= */\n\n/*\n * Function: mosquitto_calloc\n */\nlibmosqcommon_EXPORT void *mosquitto_calloc(size_t nmemb, size_t size);\n\n/*\n * Function: mosquitto_free\n */\nlibmosqcommon_EXPORT void mosquitto_free(void *mem);\n\n/*\n * Function: mosquitto_malloc\n */\nlibmosqcommon_EXPORT void *mosquitto_malloc(size_t size);\n\n/*\n * Function: mosquitto_realloc\n */\nlibmosqcommon_EXPORT void *mosquitto_realloc(void *ptr, size_t size);\n\n/*\n * Function: mosquitto_strdup\n */\nlibmosqcommon_EXPORT char *mosquitto_strdup(const char *s);\n\n/*\n * Function: mosquitto_strndup\n */\nlibmosqcommon_EXPORT char *mosquitto_strndup(const char *s, size_t n);\n\nlibmosqcommon_EXPORT void mosquitto_memory_set_limit(size_t lim);\nlibmosqcommon_EXPORT unsigned long mosquitto_memory_used(void);\nlibmosqcommon_EXPORT unsigned long mosquitto_max_memory_used(void);\n\n#define mosquitto_FREE(A) do{ mosquitto_free(A); (A) = NULL;}while(0)\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "include/mosquitto/libcommon_password.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_PASSWORD_H\n#define MOSQUITTO_LIBCOMMON_PASSWORD_H\n\n/*\n * File: mosquitto/libcommon_password.h\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum mosquitto_pwhash_type {\n\tMOSQ_PW_DEFAULT,\n\tMOSQ_PW_SHA512 = 6,\n\tMOSQ_PW_SHA512_PBKDF2 = 7,\n\tMOSQ_PW_ARGON2ID = 8,\n};\n\nenum mosquitto_pw_params {\n\tMOSQ_PW_PARAM_ITERATIONS = 1,\n};\n\nstruct mosquitto_pw;\n\nlibmosqcommon_EXPORT void mosquitto_pw_set_valid(struct mosquitto_pw *pw, bool valid);\nlibmosqcommon_EXPORT bool mosquitto_pw_is_valid(struct mosquitto_pw *pw);\n\nlibmosqcommon_EXPORT int mosquitto_pw_new(struct mosquitto_pw **pw, enum mosquitto_pwhash_type hashtype);\nlibmosqcommon_EXPORT void mosquitto_pw_cleanup(struct mosquitto_pw *pw);\nlibmosqcommon_EXPORT int mosquitto_pw_hash_encoded(struct mosquitto_pw *pw, const char *password);\nlibmosqcommon_EXPORT const char *mosquitto_pw_get_encoded(struct mosquitto_pw *pw);\nlibmosqcommon_EXPORT int mosquitto_pw_verify(struct mosquitto_pw *pw, const char *password);\nlibmosqcommon_EXPORT int mosquitto_pw_set_param(struct mosquitto_pw *pw, int param, int value);\nlibmosqcommon_EXPORT int mosquitto_pw_decode(struct mosquitto_pw *pw, const char *encoded_password);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n"
  },
  {
    "path": "include/mosquitto/libcommon_properties.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_PROPERTIES_H\n#define MOSQUITTO_LIBCOMMON_PROPERTIES_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* =============================================================================\n *\n * Section: Properties\n *\n * =============================================================================\n */\n\n\n/*\n * Function: mosquitto_property_add_byte\n *\n * Add a new byte property to a property list.\n *\n * If *proplist == NULL, a new list will be created, otherwise the new property\n * will be appended to the list.\n *\n * Parameters:\n *\tproplist - pointer to mosquitto_property pointer, the list of properties\n *\tidentifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR)\n *\tvalue - integer value for the new property\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL\n *\tMOSQ_ERR_NOMEM - on out of memory\n *\n * Example:\n * > mosquitto_property *proplist = NULL;\n * > mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_IDENTIFIER, 1);\n */\nlibmosqcommon_EXPORT int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value);\n\n/*\n * Function: mosquitto_property_add_int16\n *\n * Add a new int16 property to a property list.\n *\n * If *proplist == NULL, a new list will be created, otherwise the new property\n * will be appended to the list.\n *\n * Parameters:\n *\tproplist - pointer to mosquitto_property pointer, the list of properties\n *\tidentifier - property identifier (e.g. MQTT_PROP_RECEIVE_MAXIMUM)\n *\tvalue - integer value for the new property\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL\n *\tMOSQ_ERR_NOMEM - on out of memory\n *\n * Example:\n * > mosquitto_property *proplist = NULL;\n * > mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1000);\n */\nlibmosqcommon_EXPORT int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value);\n\n/*\n * Function: mosquitto_property_add_int32\n *\n * Add a new int32 property to a property list.\n *\n * If *proplist == NULL, a new list will be created, otherwise the new property\n * will be appended to the list.\n *\n * Parameters:\n *\tproplist - pointer to mosquitto_property pointer, the list of properties\n *\tidentifier - property identifier (e.g. MQTT_PROP_MESSAGE_EXPIRY_INTERVAL)\n *\tvalue - integer value for the new property\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL\n *\tMOSQ_ERR_NOMEM - on out of memory\n *\n * Example:\n * > mosquitto_property *proplist = NULL;\n * > mosquitto_property_add_int32(&proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 86400);\n */\nlibmosqcommon_EXPORT int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value);\n\n/*\n * Function: mosquitto_property_add_varint\n *\n * Add a new varint property to a property list.\n *\n * If *proplist == NULL, a new list will be created, otherwise the new property\n * will be appended to the list.\n *\n * Parameters:\n *\tproplist - pointer to mosquitto_property pointer, the list of properties\n *\tidentifier - property identifier (e.g. MQTT_PROP_SUBSCRIPTION_IDENTIFIER)\n *\tvalue - integer value for the new property\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL\n *\tMOSQ_ERR_NOMEM - on out of memory\n *\n * Example:\n * > mosquitto_property *proplist = NULL;\n * > mosquitto_property_add_varint(&proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 1);\n */\nlibmosqcommon_EXPORT int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value);\n\n/*\n * Function: mosquitto_property_add_binary\n *\n * Add a new binary property to a property list.\n *\n * If *proplist == NULL, a new list will be created, otherwise the new property\n * will be appended to the list.\n *\n * Parameters:\n *\tproplist - pointer to mosquitto_property pointer, the list of properties\n *\tidentifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR)\n *\tvalue - pointer to the property data\n *\tlen - length of property data in bytes\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL\n *\tMOSQ_ERR_NOMEM - on out of memory\n *\n * Example:\n * > mosquitto_property *proplist = NULL;\n * > mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len);\n */\nlibmosqcommon_EXPORT int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len);\n\n/*\n * Function: mosquitto_property_add_string\n *\n * Add a new string property to a property list.\n *\n * If *proplist == NULL, a new list will be created, otherwise the new property\n * will be appended to the list.\n *\n * Parameters:\n *\tproplist - pointer to mosquitto_property pointer, the list of properties\n *\tidentifier - property identifier (e.g. MQTT_PROP_CONTENT_TYPE)\n *\tvalue - string value for the new property, must be UTF-8 and zero terminated\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if identifier is invalid, if value is NULL, or if proplist is NULL\n *\tMOSQ_ERR_NOMEM - on out of memory\n *\tMOSQ_ERR_MALFORMED_UTF8 - value is not valid UTF-8.\n *\n * Example:\n * > mosquitto_property *proplist = NULL;\n * > mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, \"application/json\");\n */\nlibmosqcommon_EXPORT int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value);\n\n/*\n * Function: mosquitto_property_add_string_pair\n *\n * Add a new string pair property to a property list.\n *\n * If *proplist == NULL, a new list will be created, otherwise the new property\n * will be appended to the list.\n *\n * Parameters:\n *\tproplist - pointer to mosquitto_property pointer, the list of properties\n *\tidentifier - MQTT property identifier (e.g. MQTT_PROP_USER_PROPERTY from <mosquitto/mqtt_protocol.h>)\n *\tname - string name for the new property, must be UTF-8 and zero terminated\n *\tvalue - string value for the new property, must be UTF-8 and zero terminated\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if identifier is invalid, if name or value is NULL, or if proplist is NULL\n *\tMOSQ_ERR_NOMEM - on out of memory\n *\tMOSQ_ERR_MALFORMED_UTF8 - if name or value are not valid UTF-8.\n *\n * Example:\n * > mosquitto_property *proplist = NULL;\n * > mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, \"client\", \"mosquitto_pub\");\n */\nlibmosqcommon_EXPORT int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value);\n\n\n/*\n * Function: mosquitto_property_remove\n *\n * Remove a property from a property list. The property will not be freed.\n *\n * Parameters:\n *\tproplist - pointer to mosquitto_property pointer, the list of properties\n *\tproperty - pointer to the property to remove\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if proplist is NULL or property is NULL\n *\tMOSQ_ERR_NOT_FOUND - if the property was not found\n */\nlibmosqcommon_EXPORT int mosquitto_property_remove(mosquitto_property **proplist, const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_identifier\n *\n * Return the property identifier for a single property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  A valid property identifier on success\n *  0 - on error\n */\nlibmosqcommon_EXPORT int mosquitto_property_identifier(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_next\n *\n * Return the next property in a property list. Use to iterate over a property\n * list, e.g.:\n *\n * (start code)\n * for(prop = proplist; prop != NULL; prop = mosquitto_property_next(prop)){\n * \tif(mosquitto_property_identifier(prop) == MQTT_PROP_CONTENT_TYPE){\n * \t\t...\n * \t}\n * }\n * (end)\n *\n * Parameters:\n *\tproplist - pointer to mosquitto_property pointer, the list of properties\n *\n * Returns:\n *\tPointer to the next item in the list\n *\tNULL, if proplist is NULL, or if there are no more items in the list.\n */\nlibmosqcommon_EXPORT mosquitto_property *mosquitto_property_next(const mosquitto_property *proplist);\n\n\n/*\n * Function: mosquitto_property_read_byte\n *\n * Attempt to read a byte property matching an identifier, from a property list\n * or single property. This function can search for multiple entries of the\n * same identifier by using the returned value and skip_first. Note however\n * that it is forbidden for most properties to be duplicated.\n *\n * If the property is not found, *value will not be modified, so it is safe to\n * pass a variable with a default value to be potentially overwritten:\n *\n * (start code)\n * uint16_t keepalive = 60; // default value\n * // Get value from property list, or keep default if not found.\n * mosquitto_property_read_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, &keepalive, false);\n * (end)\n *\n * Parameters:\n *\tproplist - mosquitto_property pointer, the list of properties or single property\n *\tidentifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR)\n *\tvalue - pointer to store the value, or NULL if the value is not required.\n *\tskip_first - boolean that indicates whether the first item in the list\n *\t             should be ignored or not. Should usually be set to false.\n *\n * Returns:\n *\tA valid property pointer if the property is found\n *\tNULL, if the property is not found, or proplist is NULL.\n *\n * Example:\n * (start code)\n *\t// proplist is obtained from a callback\n *\tmosquitto_property *prop;\n *\tprop = mosquitto_property_read_byte(proplist, identifier, &value, false);\n *\twhile(prop){\n *\t\tprintf(\"value: %s\\n\", value);\n *\t\tprop = mosquitto_property_read_byte(prop, identifier, &value);\n *\t}\n * (end)\n */\nlibmosqcommon_EXPORT const mosquitto_property *mosquitto_property_read_byte(\n\t\tconst mosquitto_property *proplist,\n\t\tint identifier,\n\t\tuint8_t *value,\n\t\tbool skip_first);\n\n/*\n * Function: mosquitto_property_read_int16\n *\n * Read an int16 property value from a property.\n *\n * Parameters:\n *\tproperty - property to read\n *\tidentifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR)\n *\tvalue - pointer to store the value, or NULL if the value is not required.\n *\tskip_first - boolean that indicates whether the first item in the list\n *\t             should be ignored or not. Should usually be set to false.\n *\n * Returns:\n *\tA valid property pointer if the property is found\n *\tNULL, if the property is not found, or proplist is NULL.\n *\n * Example:\n *\tSee <mosquitto_property_read_byte>\n */\nlibmosqcommon_EXPORT const mosquitto_property *mosquitto_property_read_int16(\n\t\tconst mosquitto_property *proplist,\n\t\tint identifier,\n\t\tuint16_t *value,\n\t\tbool skip_first);\n\n/*\n * Function: mosquitto_property_read_int32\n *\n * Read an int32 property value from a property.\n *\n * Parameters:\n *\tproperty - pointer to mosquitto_property pointer, the list of properties\n *\tidentifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR)\n *\tvalue - pointer to store the value, or NULL if the value is not required.\n *\tskip_first - boolean that indicates whether the first item in the list\n *\t             should be ignored or not. Should usually be set to false.\n *\n * Returns:\n *\tA valid property pointer if the property is found\n *\tNULL, if the property is not found, or proplist is NULL.\n *\n * Example:\n *\tSee <mosquitto_property_read_byte>\n */\nlibmosqcommon_EXPORT const mosquitto_property *mosquitto_property_read_int32(\n\t\tconst mosquitto_property *proplist,\n\t\tint identifier,\n\t\tuint32_t *value,\n\t\tbool skip_first);\n\n/*\n * Function: mosquitto_property_read_varint\n *\n * Read a varint property value from a property.\n *\n * Parameters:\n *\tproperty - property to read\n *\tidentifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR)\n *\tvalue - pointer to store the value, or NULL if the value is not required.\n *\tskip_first - boolean that indicates whether the first item in the list\n *\t             should be ignored or not. Should usually be set to false.\n *\n * Returns:\n *\tA valid property pointer if the property is found\n *\tNULL, if the property is not found, or proplist is NULL.\n *\n * Example:\n *\tSee <mosquitto_property_read_byte>\n */\nlibmosqcommon_EXPORT const mosquitto_property *mosquitto_property_read_varint(\n\t\tconst mosquitto_property *proplist,\n\t\tint identifier,\n\t\tuint32_t *value,\n\t\tbool skip_first);\n\n/*\n * Function: mosquitto_property_read_binary\n *\n * Read a binary property value from a property.\n *\n * On success, value must be free()'d by the application.\n *\n * Parameters:\n *\tproperty - property to read\n *\tidentifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR)\n *\tvalue - pointer to store the value, or NULL if the value is not required.\n *\t        Will be set to NULL if the value has zero length.\n *\tskip_first - boolean that indicates whether the first item in the list\n *\t             should be ignored or not. Should usually be set to false.\n *\n * Returns:\n *\tA valid property pointer if the property is found\n *\tNULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred.\n *\n * Example:\n *\tSee <mosquitto_property_read_byte>\n */\nlibmosqcommon_EXPORT const mosquitto_property *mosquitto_property_read_binary(\n\t\tconst mosquitto_property *proplist,\n\t\tint identifier,\n\t\tvoid **value,\n\t\tuint16_t *len,\n\t\tbool skip_first);\n\n/*\n * Function: mosquitto_property_read_string\n *\n * Read a string property value from a property.\n *\n * On success, value must be free()'d by the application.\n *\n * Parameters:\n *\tproperty - property to read\n *\tidentifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR)\n *\tvalue - pointer to char*, for the property data to be stored in, or NULL if\n *\t        the value is not required.\n *\t        Will be set to NULL if the value has zero length.\n *\tskip_first - boolean that indicates whether the first item in the list\n *\t             should be ignored or not. Should usually be set to false.\n *\n * Returns:\n *\tA valid property pointer if the property is found\n *\tNULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred.\n *\n * Example:\n *\tSee <mosquitto_property_read_byte>\n */\nlibmosqcommon_EXPORT const mosquitto_property *mosquitto_property_read_string(\n\t\tconst mosquitto_property *proplist,\n\t\tint identifier,\n\t\tchar **value,\n\t\tbool skip_first);\n\n/*\n * Function: mosquitto_property_read_string_pair\n *\n * Read a string pair property value pair from a property.\n *\n * On success, name and value must be free()'d by the application.\n *\n * Parameters:\n *\tproperty - property to read\n *\tidentifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR)\n *\tname - pointer to char* for the name property data to be stored in, or NULL\n *\t       if the name is not required.\n *\t       Will be set to NULL if the name has zero length.\n *\tvalue - pointer to char*, for the property data to be stored in, or NULL if\n *\t        the value is not required.\n *\t        Will be set to NULL if the value has zero length.\n *\tskip_first - boolean that indicates whether the first item in the list\n *\t             should be ignored or not. Should usually be set to false.\n *\n * Returns:\n *\tA valid property pointer if the property is found\n *\tNULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred.\n *\n * Example:\n *\tSee <mosquitto_property_read_byte>\n */\nlibmosqcommon_EXPORT const mosquitto_property *mosquitto_property_read_string_pair(\n\t\tconst mosquitto_property *proplist,\n\t\tint identifier,\n\t\tchar **name,\n\t\tchar **value,\n\t\tbool skip_first);\n\n/*\n * Function: mosquitto_property_type\n *\n * Return the property type for a single property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  A valid property type on success\n *  0 - on error\n */\nlibmosqcommon_EXPORT int mosquitto_property_type(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_byte_value\n *\n * Return the property value for a byte type property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  Byte value on success\n *  0 - on error (property is NULL, or not a byte)\n */\nlibmosqcommon_EXPORT uint8_t mosquitto_property_byte_value(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_int16_value\n *\n * Return the property value for an int16 type property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  Int16 value on success\n *  0 - on error (property is NULL, or not a int16)\n */\nlibmosqcommon_EXPORT uint16_t mosquitto_property_int16_value(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_int32_value\n *\n * Return the property value for an int32 type property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  Int32 value on success\n *  0 - on error (property is NULL, or not a int32)\n */\nlibmosqcommon_EXPORT uint32_t mosquitto_property_int32_value(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_varint_value\n *\n * Return the property value for a varint type property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  Varint value on success\n *  0 - on error (property is NULL, or not a varint)\n */\nlibmosqcommon_EXPORT uint32_t mosquitto_property_varint_value(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_binary_value\n *\n * Return the property value for a binary type property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  Binary value on success\n *  NULL - on error (property is NULL, or not a binary)\n */\nlibmosqcommon_EXPORT const void *mosquitto_property_binary_value(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_byte_value_length\n *\n * Return the property value for a byte type property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  Binary value length on success\n *  0 - on error (property is NULL, or not a binary)\n */\nlibmosqcommon_EXPORT uint16_t mosquitto_property_binary_value_length(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_string_value\n *\n * Return the property value for a string or string pair type property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  String value on success\n *  NULL - on error (property is NULL, or not a string or string pair)\n */\nlibmosqcommon_EXPORT const char *mosquitto_property_string_value(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_string_value_length\n *\n * Return the length of the property value for a string or string pair type property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  Value length on success\n *  0 - on error (property is NULL, or not a string or string pair)\n */\nlibmosqcommon_EXPORT uint16_t mosquitto_property_string_value_length(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_string_value\n *\n * Return the property name for a string pair type property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  String name on success\n *  NULL - on error (property is NULL, or not a string pair)\n */\nlibmosqcommon_EXPORT const char *mosquitto_property_string_name(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_string_name_length\n *\n * Return the property name length for a string pair type property.\n *\n * Parameters:\n *\tproperty - pointer to a valid mosquitto_property pointer.\n *\n * Returns:\n *  Name length on success\n *  0 - on error (property is NULL, or not a string pair)\n */\nlibmosqcommon_EXPORT uint16_t mosquitto_property_string_name_length(const mosquitto_property *property);\n\n\n/*\n * Function: mosquitto_property_free_all\n *\n * Free all properties from a list of properties. Frees the list and sets *properties to NULL.\n *\n * Parameters:\n *   properties - list of properties to free\n *\n * Example:\n * > mosquitto_properties *properties = NULL;\n * > // Add properties\n * > mosquitto_property_free_all(&properties);\n */\nlibmosqcommon_EXPORT void mosquitto_property_free_all(mosquitto_property **properties);\n\n/*\n * Function: mosquitto_property_copy_all\n *\n * Parameters:\n *    dest - pointer for new property list\n *    src - property list\n *\n * Returns:\n *    MOSQ_ERR_SUCCESS - on successful copy\n *    MOSQ_ERR_INVAL - if dest is NULL\n *    MOSQ_ERR_NOMEM - on out of memory (dest will be set to NULL)\n */\nlibmosqcommon_EXPORT int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src);\n\n/*\n * Function: mosquitto_property_check_command\n *\n * Check whether a property identifier is valid for the given command.\n *\n * Parameters:\n *   command - MQTT command (e.g. CMD_CONNECT)\n *\tidentifier - MQTT property identifier (e.g. MQTT_PROP_USER_PROPERTY from <mosquitto/mqtt_protocol.h>)\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - if the identifier is valid for command\n *   MOSQ_ERR_PROTOCOL - if the identifier is not valid for use with command.\n */\nlibmosqcommon_EXPORT int mosquitto_property_check_command(int command, int identifier);\n\n\n/*\n * Function: mosquitto_property_check_all\n *\n * Check whether a list of properties are valid for a particular command,\n * whether there are duplicates, and whether the values are valid where\n * possible.\n *\n * Note that this function is used internally in the library whenever\n * properties are passed to it, so in basic use this is not needed, but should\n * be helpful to check property lists *before* the point of using them.\n *\n * Parameters:\n *\tcommand - MQTT command (e.g. CMD_CONNECT)\n *\tproperties - list of MQTT properties to check.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - if all properties are valid\n *\tMOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden.\n *\tMOSQ_ERR_PROTOCOL - if any property is invalid\n */\nlibmosqcommon_EXPORT int mosquitto_property_check_all(int command, const mosquitto_property *properties);\n\n/*\n * Function: mosquitto_property_identifier_to_string\n *\n * Return the property name as a string for a property identifier.\n * The property name is as defined in the MQTT specification, with - as a\n * separator, for example: payload-format-indicator.\n *\n * Parameters:\n *\tidentifier - MQTT property identifier (e.g. MQTT_PROP_USER_PROPERTY from <mosquitto/mqtt_protocol.h>)\n *\n * Returns:\n *  A const string to the property name on success\n *  NULL on failure\n */\nlibmosqcommon_EXPORT const char *mosquitto_property_identifier_to_string(int identifier);\n\n\n/* Function: mosquitto_string_to_property_info\n *\n * Parse a property name string and convert to a property identifier and data type.\n * The property name is as defined in the MQTT specification, with - as a\n * separator, for example: payload-format-indicator.\n *\n * Parameters:\n *\tpropname - the string to parse\n *\tidentifier - pointer to an int to receive the property identifier\n *\ttype - pointer to an int to receive the property type\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - if the string does not match a property\n *\n * Example:\n * (start code)\n *\tmosquitto_string_to_property_info(\"response-topic\", &id, &type);\n *\t// id == MQTT_PROP_RESPONSE_TOPIC\n *\t// type == MQTT_PROP_TYPE_STRING\n * (end)\n */\nlibmosqcommon_EXPORT int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type);\n\n\nlibmosqcommon_EXPORT void mosquitto_property_free(mosquitto_property **property);\nlibmosqcommon_EXPORT unsigned int mosquitto_property_get_length(const mosquitto_property *property);\nlibmosqcommon_EXPORT unsigned int mosquitto_property_get_length_all(const mosquitto_property *property);\nlibmosqcommon_EXPORT unsigned int mosquitto_property_get_remaining_length(const mosquitto_property *props);\nlibmosqcommon_EXPORT unsigned int mosquitto_varint_bytes(uint32_t word);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libcommon_random.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_RANDOM_H\n#define MOSQUITTO_LIBCOMMON_RANDOM_H\n\n/*\n * File: mosquitto/libcommon_random.h\n *\n * This header contains functions for obtaining random numbers.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Function: mosquitto_getrandom\n *\n * Get random bytes.\n *\n * Parameters:\n *\tbytes -  a buffer to store the random bytes, at least count bytes long.\n *\tcount -  the number or bytes to retrieve\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -        on success\n * \tMOSQ_ERR_UNKNOWN -        if an error occurred\n */\nlibmosqcommon_EXPORT int mosquitto_getrandom(void *bytes, int count);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libcommon_string.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_STRING_H\n#define MOSQUITTO_LIBCOMMON_STRING_H\n\n/*\n * File: mosquitto.h\n *\n * This header contains functions and definitions for use with libmosquitto, the Mosquitto client library.\n *\n * The definitions are also used in Mosquitto broker plugins, and some functions are available to plugins.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Function: mosquitto_strerror\n *\n * Call to obtain a const string description of a mosquitto error number.\n *\n * Parameters:\n *\tmosq_errno - a mosquitto error number.\n *\n * Returns:\n *\tA constant string describing the error.\n */\nlibmosqcommon_EXPORT const char *mosquitto_strerror(int mosq_errno);\n\n/*\n * Function: mosquitto_connack_string\n *\n * Call to obtain a const string description of an MQTT connection result.\n *\n * Parameters:\n *\tconnack_code - an MQTT connection result.\n *\n * Returns:\n *\tA constant string describing the result.\n */\nlibmosqcommon_EXPORT const char *mosquitto_connack_string(int connack_code);\n\n/*\n * Function: mosquitto_reason_string\n *\n * Call to obtain a const string description of an MQTT reason code.\n *\n * Parameters:\n *\treason_code - an MQTT reason code.\n *\n * Returns:\n *\tA constant string describing the reason.\n */\nlibmosqcommon_EXPORT const char *mosquitto_reason_string(int reason_code);\n\n/* Function: mosquitto_string_to_command\n *\n * Take a string input representing an MQTT command and convert it to the\n * libmosquitto integer representation.\n *\n * Parameters:\n *   str - the string to parse.\n *   cmd - pointer to an int, for the result.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL - on an invalid input.\n *\n * Example:\n * (start code)\n *  mosquitto_string_to_command(\"CONNECT\", &cmd);\n *  // cmd == CMD_CONNECT\n * (end)\n */\nlibmosqcommon_EXPORT int mosquitto_string_to_command(const char *str, int *cmd);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libcommon_time.h",
    "content": "#ifndef LIBMOSQUITTO_COMMON_TIME_H\n#define LIBMOSQUITTO_COMMON_TIME_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <time.h>\n\n/* Function: mosquitto_time_init\n *\n * Initialises the time source to use the best source available at run time.\n */\nlibmosqcommon_EXPORT void mosquitto_time_init(void);\n\n/* Function: mosquitto_time\n *\n * Returns an indication of the current time in seconds. The exact type of\n * value varies depending on the platform in use, but in most cases will be a\n * monotonically increasing value that does not relate to the real clock time.\n *\n * Returns:\n *    Indication of the current time, in seconds\n */\nlibmosqcommon_EXPORT time_t mosquitto_time(void);\n\n/* Function: mosquitto_time_ns\n *\n * Returns the current clock time in seconds and nanoseconds. The resolution of\n * the nanosecond value varies depending on the platform in use.\n *\n * The value returned may be decrease as well as increase in response to system\n * clock changes.\n *\n * Parameters:\n *    s - the output pointer for the number of seconds\n *    ns - the output pointer for the number of nanoseconds\n */\nlibmosqcommon_EXPORT void mosquitto_time_ns(time_t *s, long *ns);\n\n/* Function: mosquitto_time_cmp\n *\n * Returns < 0 if the time t1 is smaller (earlier) than t2\n * Returns > 0 if the time t1 is greater (later) than t2\n * Returns == 0 if the time t1 is exactly equal to t2\n */\nlibmosqcommon_EXPORT long mosquitto_time_cmp(time_t t1_s, long t1_ns, time_t t2_s, long t2_ns);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libcommon_topic.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_TOPIC_H\n#define MOSQUITTO_LIBCOMMON_TOPIC_H\n\n/*\n * File: mosquitto/libcommon_topic.h\n *\n * This header contains functions and definitions for checking and manipulating topic strings.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Function: mosquitto_sub_topic_tokenise\n *\n * Tokenise a topic or subscription string into an array of strings\n * representing the topic hierarchy.\n *\n * For example:\n *\n *    subtopic: \"a/deep/topic/hierarchy\"\n *\n *    Would result in:\n *\n *       topics[0] = \"a\"\n *       topics[1] = \"deep\"\n *       topics[2] = \"topic\"\n *       topics[3] = \"hierarchy\"\n *\n *    and:\n *\n *    subtopic: \"/a/deep/topic/hierarchy/\"\n *\n *    Would result in:\n *\n *       topics[0] = NULL\n *       topics[1] = \"a\"\n *       topics[2] = \"deep\"\n *       topics[3] = \"topic\"\n *       topics[4] = \"hierarchy\"\n *\n * Parameters:\n *\tsubtopic - the subscription/topic to tokenise\n *\ttopics -   a pointer to store the array of strings\n *\tcount -    an int pointer to store the number of items in the topics array.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -        on success\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8\n *\n * Example:\n *\n * > char **topics;\n * > int topic_count;\n * > int i;\n * >\n * > mosquitto_sub_topic_tokenise(\"$SYS/broker/uptime\", &topics, &topic_count);\n * >\n * > for(i=0; i<token_count; i++){\n * >     printf(\"%d: %s\\n\", i, topics[i]);\n * > }\n *\n * See Also:\n *\t<mosquitto_sub_topic_tokens_free>\n */\nlibmosqcommon_EXPORT int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count);\n\n/*\n * Function: mosquitto_sub_topic_tokens_free\n *\n * Free memory that was allocated in <mosquitto_sub_topic_tokenise>.\n *\n * Parameters:\n *\ttopics - pointer to string array.\n *\tcount - count of items in string array.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n *\n * See Also:\n *\t<mosquitto_sub_topic_tokenise>\n */\nlibmosqcommon_EXPORT int mosquitto_sub_topic_tokens_free(char ***topics, int count);\n\n/*\n * Function: mosquitto_topic_matches_sub\n *\n * Check whether a topic matches a subscription.\n *\n * For example:\n *\n * foo/bar would match the subscription foo/# or +/bar\n * non/matching would not match the subscription non/+/+\n *\n * Parameters:\n *\tsub - subscription string to check topic against.\n *\ttopic - topic to check.\n *\tresult - bool pointer to hold result. Will be set to true if the topic\n *\t         matches the subscription.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n */\nlibmosqcommon_EXPORT int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result);\n\n/*\n * Function: mosquitto_topic_matches_sub2\n *\n * Identical to <mosquitto_topic_matches_sub>. The sublen and topiclen\n * parameters are *IGNORED*.\n */\nlibmosqcommon_EXPORT int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result);\n\n\n/*\n * Function: mosquitto_topic_matches_sub_with_pattern\n *\n * Check whether a topic matches a subscription, with client id/username\n * pattern substitution.\n *\n * Any instances of a subscriptions hierarchy that are exactly %c or %u will be\n * replaced with the client id or username respectively.\n *\n * For example:\n *\n * mosquitto_topic_matches_sub_with_pattern(\"sensors/%c/temperature\", \"sensors/kitchen/temperature\", \"kitchen\", NULL, &result)\n * -> this will match\n *\n * mosquitto_topic_matches_sub_with_pattern(\"sensors/%c/temperature\", \"sensors/bathroom/temperature\", \"kitchen\", NULL, &result)\n * -> this will not match\n *\n * mosquitto_topic_matches_sub_with_pattern(\"sensors/%count/temperature\", \"sensors/kitchen/temperature\", \"kitchen\", NULL, &result)\n * -> this will not match - the `%count` is not treated as a pattern\n *\n * mosquitto_topic_matches_sub_with_pattern(\"%c/%c/%u/%u\", \"kitchen/kitchen/bathroom/bathroom\", \"kitchen\", \"bathroom\", &result)\n * -> this will match\n *\n * Parameters:\n *\tsub - subscription string to check topic against.\n *\ttopic - topic to check.\n *\tclientid - client id to substitute in patterns. If NULL, then any %c patterns will not match.\n *\tusername - username to substitute in patterns. If NULL, then any %u patterns will not match.\n *\tresult - bool pointer to hold result. Will be set to true if the topic\n *\t         matches the subscription.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n */\nlibmosqcommon_EXPORT int mosquitto_topic_matches_sub_with_pattern(const char *sub, const char *topic, const char *clientid, const char *username, bool *result);\n\n\n/*\n * Function: mosquitto_sub_matches_acl\n *\n * Check whether a subscription matches an ACL topic filter\n *\n * For example:\n *\n * The subscription $SYS/broker/# would match against the ACL $SYS/#\n * The subscription $SYS/broker/# would not match against the ACL $SYS/broker/uptime\n *\n * Parameters:\n *\tacl - topic filter string to check sub against.\n *\tsub - subscription topic to check.\n *\tresult - bool pointer to hold result. Will be set to true if the subscription\n *\t         matches the acl.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n */\nlibmosqcommon_EXPORT int mosquitto_sub_matches_acl(const char *acl, const char *sub, bool *result);\n\n\n/*\n * Function: mosquitto_sub_matches_acl_with_pattern\n *\n * Check whether a subscription (a topic filter with wildcards) matches an ACL\n * (a topic filter with wildcards) , with client id/username pattern\n * substitution.\n *\n * Any instances of an ACL hierarchy that are exactly %c or %u will be\n * replaced with the client id or username respectively.\n *\n * For example:\n *\n * mosquitto_sub_matches_acl_with_pattern(\"sensors/%c/+\", \"sensors/kitchen/temperature\", \"kitchen\", NULL, &result)\n * -> this will match\n *\n * mosquitto_sub_matches_acl_with_pattern(\"sensors/%c/+\", \"sensors/bathroom/temperature\", \"kitchen\", NULL, &result)\n * -> this will not match\n *\n * mosquitto_sub_matches_acl_with_pattern(\"sensors/%count/+\", \"sensors/kitchen/temperature\", \"kitchen\", NULL, &result)\n * -> this will not match - the `%count` is not treated as a pattern\n *\n * mosquitto_sub_matches_acl_with_pattern(\"%c/%c/%u/+\", \"kitchen/kitchen/bathroom/bathroom\", \"kitchen\", \"bathroom\", &result)\n * -> this will match\n *\n * Parameters:\n *\tacl - ACL topic filter string to check sub against.\n *\tsub - subscription to check.\n *\tclientid - client id to substitute in patterns. If NULL, then any %c patterns will not match.\n *\tusername - username to substitute in patterns. If NULL, then any %u patterns will not match.\n *\tresult - bool pointer to hold result. Will be set to true if the subscription\n *\t         matches the ACL.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success\n *\tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n */\nlibmosqcommon_EXPORT int mosquitto_sub_matches_acl_with_pattern(const char *acl, const char *sub, const char *clientid, const char *username, bool *result);\n\n\n/*\n * Function: mosquitto_pub_topic_check\n *\n * Check whether a topic to be used for publishing is valid.\n *\n * This searches for + or # in a topic and checks its length.\n *\n * This check is already carried out in <mosquitto_publish> and\n * <mosquitto_will_set>, there is no need to call it directly before them. It\n * may be useful if you wish to check the validity of a topic in advance of\n * making a connection for example.\n *\n * Parameters:\n *   topic - the topic to check\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS -        for a valid topic\n *   MOSQ_ERR_INVAL -          if the topic contains a + or a #, or if it is too long.\n *   MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8\n *\n * See Also:\n *   <mosquitto_sub_topic_check>\n */\nlibmosqcommon_EXPORT int mosquitto_pub_topic_check(const char *topic);\n\n/*\n * Function: mosquitto_pub_topic_check2\n *\n * Check whether a topic to be used for publishing is valid.\n *\n * This searches for + or # in a topic and checks its length.\n *\n * This check is already carried out in <mosquitto_publish> and\n * <mosquitto_will_set>, there is no need to call it directly before them. It\n * may be useful if you wish to check the validity of a topic in advance of\n * making a connection for example.\n *\n * Parameters:\n *   topic - the topic to check\n *   topiclen - length of the topic in bytes\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS -        for a valid topic\n *   MOSQ_ERR_INVAL -          if the topic contains a + or a #, or if it is too long.\n *   MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8\n *\n * See Also:\n *   <mosquitto_sub_topic_check>\n */\nlibmosqcommon_EXPORT int mosquitto_pub_topic_check2(const char *topic, size_t topiclen);\n\n/*\n * Function: mosquitto_sub_topic_check\n *\n * Check whether a topic to be used for subscribing is valid.\n *\n * This searches for + or # in a topic and checks that they aren't in invalid\n * positions, such as with foo/#/bar, foo/+bar or foo/bar#, and checks its\n * length.\n *\n * This check is already carried out in <mosquitto_subscribe> and\n * <mosquitto_unsubscribe>, there is no need to call it directly before them.\n * It may be useful if you wish to check the validity of a topic in advance of\n * making a connection for example.\n *\n * Parameters:\n *   topic - the topic to check\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS -        for a valid topic\n *   MOSQ_ERR_INVAL -          if the topic contains a + or a # that is in an\n *                             invalid position, or if it is too long.\n *   MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8\n *\n * See Also:\n *   <mosquitto_sub_topic_check>\n */\nlibmosqcommon_EXPORT int mosquitto_sub_topic_check(const char *topic);\n\n/*\n * Function: mosquitto_sub_topic_check2\n *\n * Check whether a topic to be used for subscribing is valid.\n *\n * This searches for + or # in a topic and checks that they aren't in invalid\n * positions, such as with foo/#/bar, foo/+bar or foo/bar#, and checks its\n * length.\n *\n * This check is already carried out in <mosquitto_subscribe> and\n * <mosquitto_unsubscribe>, there is no need to call it directly before them.\n * It may be useful if you wish to check the validity of a topic in advance of\n * making a connection for example.\n *\n * Parameters:\n *   topic - the topic to check\n *   topiclen - the length in bytes of the topic\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS -        for a valid topic\n *   MOSQ_ERR_INVAL -          if the topic contains a + or a # that is in an\n *                             invalid position, or if it is too long.\n *   MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8\n *\n * See Also:\n *   <mosquitto_sub_topic_check>\n */\nlibmosqcommon_EXPORT int mosquitto_sub_topic_check2(const char *topic, size_t topiclen);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libcommon_utf8.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBCOMMON_UTF8_H\n#define MOSQUITTO_LIBCOMMON_UTF8_H\n\n/*\n * File: mosquitto/libcommon_utf8.h\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Function: mosquitto_validate_utf8\n *\n * Helper function to validate whether a UTF-8 string is valid, according to\n * the UTF-8 spec and the MQTT additions.\n *\n * Parameters:\n *   str - a string to check\n *   len - the length of the string in bytes\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS -        on success\n *   MOSQ_ERR_INVAL -          if str is NULL or len<0 or len>65536\n *   MOSQ_ERR_MALFORMED_UTF8 - if str is not valid UTF-8\n */\nlibmosqcommon_EXPORT int mosquitto_validate_utf8(const char *str, int len);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_H\n#define MOSQUITTO_LIBMOSQUITTO_H\n\n/*\n * File: mosquitto/libmosquitto.h\n *\n * This header contains functions and definitions for use with libmosquitto, the Mosquitto client library.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n\n#ifdef WIN32\n#  ifndef LIBMOSQUITTO_STATIC\n#    ifdef libmosquitto_EXPORTS\n#      define libmosq_EXPORT  __declspec(dllexport)\n#    else\n#      define libmosq_EXPORT  __declspec(dllimport)\n#    endif\n#  else\n#    define libmosq_EXPORT\n#  endif\n#else\n#  define libmosq_EXPORT\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER < 1900 && !defined(bool)\n#   ifndef __cplusplus\n#       define bool char\n#       define true 1\n#       define false 0\n#   endif\n#else\n#   ifndef __cplusplus\n#       include <stdbool.h>\n#   endif\n#endif\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <mosquitto/defs.h>\n#include <mosquitto/mqtt_protocol.h>\n\n#define LIBMOSQUITTO_MAJOR 2\n#define LIBMOSQUITTO_MINOR 1\n#define LIBMOSQUITTO_REVISION 0\n/* LIBMOSQUITTO_VERSION_NUMBER looks like 1002001 for e.g. version 1.2.1. */\n#define LIBMOSQUITTO_VERSION_NUMBER (LIBMOSQUITTO_MAJOR*1000000+LIBMOSQUITTO_MINOR*1000+LIBMOSQUITTO_REVISION)\n\n/* Enum: mosq_opt_t\n *\n * Client options.\n *\n * See <mosquitto_int_option>, <mosquitto_string_option>, and <mosquitto_void_option>.\n */\nenum mosq_opt_t {\n\tMOSQ_OPT_PROTOCOL_VERSION = 1,\n\tMOSQ_OPT_SSL_CTX = 2,\n\tMOSQ_OPT_SSL_CTX_WITH_DEFAULTS = 3,\n\tMOSQ_OPT_RECEIVE_MAXIMUM = 4,\n\tMOSQ_OPT_SEND_MAXIMUM = 5,\n\tMOSQ_OPT_TLS_KEYFORM = 6,\n\tMOSQ_OPT_TLS_ENGINE = 7,\n\tMOSQ_OPT_TLS_ENGINE_KPASS_SHA1 = 8,\n\tMOSQ_OPT_TLS_OCSP_REQUIRED = 9,\n\tMOSQ_OPT_TLS_ALPN = 10,\n\tMOSQ_OPT_TCP_NODELAY = 11,\n\tMOSQ_OPT_BIND_ADDRESS = 12,\n\tMOSQ_OPT_TLS_USE_OS_CERTS = 13,\n\tMOSQ_OPT_DISABLE_SOCKETPAIR = 14,\n\tMOSQ_OPT_TRANSPORT = 15,\n\tMOSQ_OPT_HTTP_PATH = 16,\n\tMOSQ_OPT_HTTP_HEADER_SIZE = 17,\n};\n\n/* Struct: mosquitto_message\n *\n * Contains details of a PUBLISH message.\n *\n * int mid - the message/packet ID of the PUBLISH message, assuming this is a\n *           QoS 1 or 2 message. Will be set to 0 for QoS 0 messages.\n *\n * char *topic - the topic the message was delivered on.\n *\n * void *payload - the message payload. This will be payloadlen bytes long, and\n *                 may be NULL if a zero length payload was sent.\n *\n * int payloadlen - the length of the payload, in bytes.\n *\n * int qos - the quality of service of the message, 0, 1, or 2.\n *\n * bool retain - set to true for stale retained messages.\n */\nstruct mosquitto_message {\n\tint mid;\n\tchar *topic;\n\tvoid *payload;\n\tint payloadlen;\n\tint qos;\n\tbool retain;\n};\n\nstruct mosquitto_message_v5 {\n\tvoid *payload;\n\tchar *topic;\n\tmosquitto_property *properties;\n\tuint32_t payloadlen;\n\tuint8_t qos;\n\tbool retain;\n\tuint8_t padding[2];\n};\n\n/*\n * Topic: Threads\n *\tlibmosquitto provides thread safe operation, with the exception of\n *\t<mosquitto_lib_init> which is not thread safe.\n *\n *\tIf the library has been compiled without thread support it is *not*\n *\tguaranteed to be thread safe.\n *\n *\tIf your application uses threads you must use <mosquitto_threaded_set> to\n *\ttell the library this is the case, otherwise it makes some optimisations\n *\tfor the single threaded case that may result in unexpected behaviour for\n *\tthe multi threaded case.\n */\n/***************************************************\n * Important note\n *\n * The following functions that deal with network operations will return\n * MOSQ_ERR_SUCCESS on success, but this does not mean that the operation has\n * taken place. An attempt will be made to write the network data, but if the\n * socket is not available for writing at that time then the packet will not be\n * sent. To ensure the packet is sent, call mosquitto_loop() (which must also\n * be called to process incoming network data).\n * This is especially important when disconnecting a client that has a will. If\n * the broker does not receive the DISCONNECT command, it will assume that the\n * client has disconnected unexpectedly and send the will.\n *\n * mosquitto_connect()\n * mosquitto_disconnect()\n * mosquitto_subscribe()\n * mosquitto_unsubscribe()\n * mosquitto_publish()\n ***************************************************/\n\n\n/* ======================================================================\n *\n * Section: Library version, init, and cleanup\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_lib_version\n *\n * Can be used to obtain version information for the mosquitto library.\n * This allows the application to compare the library version against the\n * version it was compiled against by using the LIBMOSQUITTO_MAJOR,\n * LIBMOSQUITTO_MINOR and LIBMOSQUITTO_REVISION defines.\n *\n * Parameters:\n *  major -    an integer pointer. If not NULL, the major version of the\n *             library will be returned in this variable.\n *  minor -    an integer pointer. If not NULL, the minor version of the\n *             library will be returned in this variable.\n *  revision - an integer pointer. If not NULL, the revision of the library will\n *             be returned in this variable.\n *\n * Returns:\n *\tLIBMOSQUITTO_VERSION_NUMBER - which is a unique number based on the major,\n *\t\tminor and revision values.\n * See Also:\n * \t<mosquitto_lib_cleanup>, <mosquitto_lib_init>\n */\nlibmosq_EXPORT int mosquitto_lib_version(int *major, int *minor, int *revision);\n\n/*\n * Function: mosquitto_lib_init\n *\n * Must be called before any other mosquitto functions.\n *\n * This function is *not* thread safe.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_UNKNOWN - on Windows, if sockets couldn't be initialized.\n *\n * See Also:\n * \t<mosquitto_lib_cleanup>, <mosquitto_lib_version>\n */\nlibmosq_EXPORT int mosquitto_lib_init(void);\n\n/*\n * Function: mosquitto_lib_cleanup\n *\n * Call to free resources associated with the library.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - always\n *\n * See Also:\n * \t<mosquitto_lib_init>, <mosquitto_lib_version>\n */\nlibmosq_EXPORT int mosquitto_lib_cleanup(void);\n\n#include <mosquitto/libmosquitto_auth.h>\n#include <mosquitto/libmosquitto_callbacks.h>\n#include <mosquitto/libmosquitto_connect.h>\n#include <mosquitto/libmosquitto_create_delete.h>\n#include <mosquitto/libmosquitto_helpers.h>\n#include <mosquitto/libmosquitto_loop.h>\n#include <mosquitto/libmosquitto_message.h>\n#include <mosquitto/libmosquitto_options.h>\n#include <mosquitto/libmosquitto_publish.h>\n#include <mosquitto/libmosquitto_socks.h>\n#include <mosquitto/libmosquitto_subscribe.h>\n#include <mosquitto/libmosquitto_tls.h>\n#include <mosquitto/libmosquitto_unsubscribe.h>\n#include <mosquitto/libmosquitto_will.h>\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_auth.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_AUTH_H\n#define MOSQUITTO_LIBMOSQUITTO_AUTH_H\n\n/*\n * File: mosquitto/libmosquitto_auth.h\n *\n * This header contains functions for setting client authentication parameters in libmosquitto.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ======================================================================\n *\n * Section: Username and password\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_username_pw_set\n *\n * Configure username and password for a mosquitto instance. By default, no\n * username or password will be sent. For v3.1 and v3.1.1 clients, if username\n * is NULL, the password argument is ignored.\n *\n * This is must be called before calling <mosquitto_connect>.\n *\n * Parameters:\n * \tmosq -     a valid mosquitto instance.\n * \tusername - the username to send as a string, or NULL to disable\n *             authentication.\n * \tpassword - the password to send as a string. Set to NULL when username is\n * \t           valid in order to send just a username.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -   if an out of memory condition occurred.\n */\nlibmosq_EXPORT int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password);\n\n/*\n * Function: mosquitto_ext_auth_continue\n *\n * Use within an on_ext_auth callback only.\n *\n * Call to continue the MQTT v5 extended authentication flow.\n *\n * Parameters:\n * \tmosq -          a valid mosquitto instance.\n * \tauth_method -   the authentication method as provided in the on_ext_auth callback\n * \tauth_data -     authentication data to send to the broker, or NULL\n * \tauth_data_len - the length of auth_data, in bytes, or 0\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -   if an out of memory condition occurred.\n */\nlibmosq_EXPORT int mosquitto_ext_auth_continue(struct mosquitto *context, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *props);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_callbacks.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_CALLBACKS_H\n#define MOSQUITTO_LIBMOSQUITTO_CALLBACKS_H\n\n/*\n * File: mosquitto/libmosquitto_callbacks.h\n *\n * This header contains functions for handling libmosquitto client callbacks.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <mosquitto/defs.h>\n#include <mosquitto/mqtt_protocol.h>\n\n/* ======================================================================\n *\n * Section: Callbacks\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_connect_callback_set\n *\n * Set the connect callback. This is called when the library receives a CONNACK\n * message in response to a connection.\n *\n * Parameters:\n *  mosq -       a valid mosquitto instance.\n *  on_connect - a callback function in the following form:\n *               void callback(struct mosquitto *mosq, void *obj, int rc)\n *\n * Callback Parameters:\n *  mosq - the mosquitto instance making the callback.\n *  obj - the user data provided in <mosquitto_new>\n *  rc -  the return code of the connection response. The values are defined by\n *        the MQTT protocol version in use.\n *        For MQTT v5.0, look at section 3.2.2.2 Connect Reason code: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html\n *        For MQTT v3.1.1, look at section 3.2.2.3 Connect Return code: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html\n *\n * See Also:\n *  <mosquitto_pre_connect_callback_set>\n */\ntypedef void (*LIBMOSQ_CB_connect)(struct mosquitto *mosq, void *obj, int rc);\nlibmosq_EXPORT void mosquitto_connect_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_connect on_connect);\n\n/*\n * Function: mosquitto_connect_with_flags_callback_set\n *\n * Set the connect callback. This is called when the library receives a CONNACK\n * message in response to a connection.\n *\n * Parameters:\n *  mosq -       a valid mosquitto instance.\n *  on_connect - a callback function in the following form:\n *               void callback(struct mosquitto *mosq, void *obj, int rc)\n *\n * Callback Parameters:\n *  mosq - the mosquitto instance making the callback.\n *  obj - the user data provided in <mosquitto_new>\n *  rc -  the return code of the connection response. The values are defined by\n *        the MQTT protocol version in use.\n *        For MQTT v5.0, look at section 3.2.2.2 Connect Reason code: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html\n *        For MQTT v3.1.1, look at section 3.2.2.3 Connect Return code: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html\n *  flags - the connect flags.\n *\n * See Also:\n *  <mosquitto_pre_connect_callback_set>\n */\ntypedef void (*LIBMOSQ_CB_connect_with_flags)(struct mosquitto *mosq, void *obj, int rc, int flags);\nlibmosq_EXPORT void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_connect_with_flags on_connect);\n\n/*\n * Function: mosquitto_connect_v5_callback_set\n *\n * Set the connect callback. This is called when the library receives a CONNACK\n * message in response to a connection.\n *\n * It is valid to set this callback for all MQTT protocol versions. If it is\n * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props`\n * argument will always be NULL.\n *\n * Parameters:\n *  mosq -       a valid mosquitto instance.\n *  on_connect - a callback function in the following form:\n *               void callback(struct mosquitto *mosq, void *obj, int rc)\n *\n * Callback Parameters:\n *  mosq - the mosquitto instance making the callback.\n *  obj - the user data provided in <mosquitto_new>\n *  rc -  the return code of the connection response. The values are defined by\n *        the MQTT protocol version in use.\n *        For MQTT v5.0, look at section 3.2.2.2 Connect Reason code: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html\n *        For MQTT v3.1.1, look at section 3.2.2.3 Connect Return code: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html\n *  flags - the connect flags.\n *  props - list of MQTT 5 properties, or NULL\n *\n * See Also:\n *  <mosquitto_pre_connect_callback_set>\n */\ntypedef void (*LIBMOSQ_CB_connect_v5)(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *props);\nlibmosq_EXPORT void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_connect_v5 on_connect);\n\n/*\n * Function: mosquitto_pre_connect_callback_set\n *\n * Set the pre-connect callback. The pre-connect callback is called just before an attempt is made to connect to the broker. This may be useful if you are using <mosquitto_loop_start>, or\n * <mosquitto_loop_forever>, because when your client disconnects the library\n * will by default automatically reconnect. Using the pre-connect callback\n * allows you to set usernames, passwords, and TLS related parameters.\n *\n * Parameters:\n *  mosq -           a valid mosquitto instance.\n *  on_pre_connect - a callback function in the following form:\n *                   void callback(struct mosquitto *mosq, void *obj)\n *\n * Callback Parameters:\n *  mosq - the mosquitto instance making the callback.\n *  obj - the user data provided in <mosquitto_new>\n */\ntypedef void (*LIBMOSQ_CB_pre_connect)(struct mosquitto *mosq, void *obj);\nlibmosq_EXPORT void mosquitto_pre_connect_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_pre_connect on_pre_connect);\n\n/*\n * Function: mosquitto_disconnect_callback_set\n *\n * Set the disconnect callback. This is called when the broker has received the\n * DISCONNECT command and has disconnected the client.\n *\n * Parameters:\n *  mosq -          a valid mosquitto instance.\n *  on_disconnect - a callback function in the following form:\n *                  void callback(struct mosquitto *mosq, void *obj)\n *\n * Callback Parameters:\n *  mosq - the mosquitto instance making the callback.\n *  obj -  the user data provided in <mosquitto_new>\n *  rc -   integer value indicating the reason for the disconnect. A value of 0\n *         means the client has called <mosquitto_disconnect>. Any other value\n *         indicates that the disconnect is unexpected.\n */\ntypedef void (*LIBMOSQ_CB_disconnect)(struct mosquitto *mosq, void *obj, int rc);\nlibmosq_EXPORT void mosquitto_disconnect_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_disconnect on_disconnect);\n\n/*\n * Function: mosquitto_disconnect_v5_callback_set\n *\n * Set the disconnect callback. This is called when the broker has received the\n * DISCONNECT command and has disconnected the client.\n *\n * It is valid to set this callback for all MQTT protocol versions. If it is\n * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props`\n * argument will always be NULL.\n *\n * Parameters:\n *  mosq -          a valid mosquitto instance.\n *  on_disconnect - a callback function in the following form:\n *                  void callback(struct mosquitto *mosq, void *obj)\n *\n * Callback Parameters:\n *  mosq - the mosquitto instance making the callback.\n *  obj -  the user data provided in <mosquitto_new>\n *  rc -   integer value indicating the reason for the disconnect. A value of 0\n *         means the client has called <mosquitto_disconnect>. Any other value\n *         indicates that the disconnect is unexpected.\n *  props - list of MQTT 5 properties, or NULL\n */\ntypedef void (*LIBMOSQ_CB_disconnect_v5)(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *props);\nlibmosq_EXPORT void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_disconnect_v5 on_disconnect);\n\n/*\n * Function: mosquitto_publish_callback_set\n *\n * Set the publish callback. This is called when a message initiated with\n * <mosquitto_publish> has been sent to the broker. \"Sent\" means different\n * things depending on the QoS of the message:\n *\n * QoS 0: The PUBLISH was passed to the local operating system for delivery,\n *        there is no guarantee that it was delivered to the remote broker.\n * QoS 1: The PUBLISH was sent to the remote broker and the corresponding\n *        PUBACK was received by the library.\n * QoS 2: The PUBLISH was sent to the remote broker and the corresponding\n *        PUBCOMP was received by the library.\n *\n * Parameters:\n *  mosq -       a valid mosquitto instance.\n *  on_publish - a callback function in the following form:\n *               void callback(struct mosquitto *mosq, void *obj, int mid)\n *\n * Callback Parameters:\n *  mosq - the mosquitto instance making the callback.\n *  obj -  the user data provided in <mosquitto_new>\n *  mid -  the message id of the sent message.\n */\ntypedef void (*LIBMOSQ_CB_publish)(struct mosquitto *mosq, void *obj, int mid);\nlibmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_publish on_publish);\n\n/*\n * Function: mosquitto_publish_v5_callback_set\n *\n * Set the publish callback. This is called when a message initiated with\n * <mosquitto_publish> has been sent to the broker. This callback will be\n * called both if the message is sent successfully, or if the broker responded\n * with an error, which will be reflected in the reason_code parameter.\n * \"Sent\" means different things depending on the QoS of the message:\n *\n * QoS 0: The PUBLISH was passed to the local operating system for delivery,\n *        there is no guarantee that it was delivered to the remote broker.\n * QoS 1: The PUBLISH was sent to the remote broker and the corresponding\n *        PUBACK was received by the library.\n * QoS 2: The PUBLISH was sent to the remote broker and the corresponding\n *        PUBCOMP was received by the library.\n *\n *\n * It is valid to set this callback for all MQTT protocol versions. If it is\n * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props`\n * argument will always be NULL.\n *\n * Parameters:\n *  mosq -       a valid mosquitto instance.\n *  on_publish - a callback function in the following form:\n *               void callback(struct mosquitto *mosq, void *obj, int mid)\n *\n * Callback Parameters:\n *  mosq - the mosquitto instance making the callback.\n *  obj -  the user data provided in <mosquitto_new>\n *  mid -  the message id of the sent message.\n *  reason_code - the MQTT 5 reason code\n *  props - list of MQTT 5 properties, or NULL\n */\ntypedef void (*LIBMOSQ_CB_publish_v5)(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *props);\nlibmosq_EXPORT void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_publish_v5 on_publish);\n\n/*\n * Function: mosquitto_message_callback_set\n *\n * Set the message callback. This is called when a message is received from the\n * broker and the required QoS flow has completed.\n *\n * Parameters:\n *  mosq -       a valid mosquitto instance.\n *  on_message - a callback function in the following form:\n *               void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)\n *\n * Callback Parameters:\n *  mosq -    the mosquitto instance making the callback.\n *  obj -     the user data provided in <mosquitto_new>\n *  message - the message data. This variable and associated memory will be\n *            freed by the library after the callback completes. The client\n *            should make copies of any of the data it requires.\n *\n * See Also:\n * \t<mosquitto_message_copy>\n */\ntypedef void (*LIBMOSQ_CB_message)(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message);\nlibmosq_EXPORT void mosquitto_message_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_message on_message);\n\n/*\n * Function: mosquitto_message_v5_callback_set\n *\n * Set the message callback. This is called when a message is received from the\n * broker and the required QoS flow has completed.\n *\n * It is valid to set this callback for all MQTT protocol versions. If it is\n * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props`\n * argument will always be NULL.\n *\n * Parameters:\n *  mosq -       a valid mosquitto instance.\n *  on_message - a callback function in the following form:\n *               void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)\n *\n * Callback Parameters:\n *  mosq -    the mosquitto instance making the callback.\n *  obj -     the user data provided in <mosquitto_new>\n *  message - the message data. This variable and associated memory will be\n *            freed by the library after the callback completes. The client\n *            should make copies of any of the data it requires.\n *  props - list of MQTT 5 properties, or NULL\n *\n * See Also:\n * \t<mosquitto_message_copy>\n */\ntypedef void (*LIBMOSQ_CB_message_v5)(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *props);\nlibmosq_EXPORT void mosquitto_message_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_message_v5 on_message);\n\n/*\n * Function: mosquitto_subscribe_callback_set\n *\n * Set the subscribe callback. This is called when the library receives a\n * SUBACK message in response to a SUBSCRIBE.\n *\n * Parameters:\n *  mosq -         a valid mosquitto instance.\n *  on_subscribe - a callback function in the following form:\n *                 void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n *\n * Callback Parameters:\n *  mosq -        the mosquitto instance making the callback.\n *  obj -         the user data provided in <mosquitto_new>\n *  mid -         the message id of the subscribe message.\n *  qos_count -   the number of granted subscriptions (size of granted_qos).\n *  granted_qos - an array of integers indicating the granted QoS for each of\n *                the subscriptions.\n */\ntypedef void (*LIBMOSQ_CB_subscribe)(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos);\nlibmosq_EXPORT void mosquitto_subscribe_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_subscribe on_subscribe);\n\n/*\n * Function: mosquitto_subscribe_v5_callback_set\n *\n * Set the subscribe callback. This is called when the library receives a\n * SUBACK message in response to a SUBSCRIBE.\n *\n * It is valid to set this callback for all MQTT protocol versions. If it is\n * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props`\n * argument will always be NULL.\n *\n * Parameters:\n *  mosq -         a valid mosquitto instance.\n *  on_subscribe - a callback function in the following form:\n *                 void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n *\n * Callback Parameters:\n *  mosq -        the mosquitto instance making the callback.\n *  obj -         the user data provided in <mosquitto_new>\n *  mid -         the message id of the subscribe message.\n *  qos_count -   the number of granted subscriptions (size of granted_qos).\n *  granted_qos - an array of integers indicating the granted QoS for each of\n *                the subscriptions.\n *  props - list of MQTT 5 properties, or NULL\n */\ntypedef void (*LIBMOSQ_CB_subscribe_v5)(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props);\nlibmosq_EXPORT void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_subscribe_v5 on_subscribe);\n\n/*\n * Function: mosquitto_unsubscribe_callback_set\n *\n * Set the unsubscribe callback. This is called when the library receives a\n * UNSUBACK message in response to an UNSUBSCRIBE.\n *\n * Parameters:\n *  mosq -           a valid mosquitto instance.\n *  on_unsubscribe - a callback function in the following form:\n *                   void callback(struct mosquitto *mosq, void *obj, int mid)\n *\n * Callback Parameters:\n *  mosq - the mosquitto instance making the callback.\n *  obj -  the user data provided in <mosquitto_new>\n *  mid -  the message id of the unsubscribe message.\n */\ntypedef void (*LIBMOSQ_CB_unsubscribe)(struct mosquitto *mosq, void *obj, int mid);\nlibmosq_EXPORT void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_unsubscribe on_unsubscribe);\n\n/*\n * Function: mosquitto_unsubscribe_v5_callback_set\n *\n * Set the unsubscribe callback. This is called when the library receives a\n * UNSUBACK message in response to an UNSUBSCRIBE.\n *\n * It is valid to set this callback for all MQTT protocol versions. If it is\n * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props`\n * argument will always be NULL.\n *\n * Parameters:\n *  mosq -           a valid mosquitto instance.\n *  on_unsubscribe - a callback function in the following form:\n *                   void callback(struct mosquitto *mosq, void *obj, int mid, const mosquitto_property *props)\n *\n * Callback Parameters:\n *  mosq - the mosquitto instance making the callback.\n *  obj -  the user data provided in <mosquitto_new>\n *  mid -  the message id of the unsubscribe message.\n *  props - list of MQTT 5 properties, or NULL\n */\ntypedef void (*LIBMOSQ_CB_unsubscribe_v5)(struct mosquitto *mosq, void *obj, int mid, const mosquitto_property *props);\nlibmosq_EXPORT void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_unsubscribe_v5 on_unsubscribe);\n\n/*\n * Function: mosquitto_unsubscribe2_v5_callback_set\n *\n * Set the unsubscribe callback. This is called when the library receives a\n * UNSUBACK message in response to an UNSUBSCRIBE.\n *\n * It is valid to set this callback for all MQTT protocol versions. If it is\n * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props`\n * argument will always be NULL.\n *\n * Parameters:\n *  mosq -           a valid mosquitto instance.\n *  on_unsubscribe - a callback function in the following form:\n *                   void callback(struct mosquitto *mosq, void *obj, int mid,\n *                   int reason_code_count, const int *reason_codes, const mosquitto_property *props)\n *\n * Callback Parameters:\n *  mosq -              the mosquitto instance making the callback.\n *  obj -               the user data provided in <mosquitto_new>\n *  mid -               the message id of the unsubscribe message.\n *  reason_code_count - the count of reason code responses\n *  reason_codes -      an array of integers indicating the reason codes for each of\n *                      the unsubscription requests.\n *  mid -               the message id of the unsubscribe message.\n *  props -             list of MQTT 5 properties, or NULL\n */\ntypedef void (*LIBMOSQ_CB_unsubscribe2_v5)(struct mosquitto *mosq, void *obj, int mid, int reason_code_count, const int *reason_codes, const mosquitto_property *props);\nlibmosq_EXPORT void mosquitto_unsubscribe2_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_unsubscribe2_v5 on_unsubscribe);\n\n/*\n * Function: mosquitto_ext_auth_callback_set\n *\n * Set the callback for extended authentication. This should be used if you\n * want to support MQTT v5.0 extended authentication.\n *\n *  mosq -        a valid mosquitto instance.\n *  on_ext_auth - a callback function in the following form:\n *                void callback(struct mosquitto *mosq, void *obj, const char *auth_method, int auth_data_len, const void *auth_data, const mosquitto_property *props)\n *\n * Callback Parameters:\n *  mosq -          the mosquitto instance making the callback.\n *  obj -           the user data provided in <mosquitto_new>\n *  auth_method -   the authentication method provided by the broker\n *  auth_data_len - the length of auth_data in bytes\n *  auth_data -     the authentication data, or NULL\n *  props -         list of MQTT 5 properties sent\n *                  note that this includes the auth-method and auth-data\n *                  properties, so you cannot use it directly with\n *                  mosquitto_ext_auth_continue and must instead create your\n *                  own property list\n *\n * Callback Return:\n *  MOSQ_ERR_SUCCESS - if you accept the authentication data\n *  MOSQ_ERR_AUTH    - if the authentication should fail\n *  MOSQ_ERR_NOMEM   - on out of memory\n *\n * See Also:\n *    <mosquitto_ext_auth_continue>\n */\ntypedef int (*LIBMOSQ_CB_ext_auth)(struct mosquitto *mosq, void *obj, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *props);\nlibmosq_EXPORT void mosquitto_ext_auth_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_ext_auth on_ext_auth);\n\n/*\n * Function: mosquitto_log_callback_set\n *\n * Set the logging callback. This should be used if you want event logging\n * information from the client library.\n *\n *  mosq -   a valid mosquitto instance.\n *  on_log - a callback function in the following form:\n *           void callback(struct mosquitto *mosq, void *obj, int level, const char *str)\n *\n * Callback Parameters:\n *  mosq -  the mosquitto instance making the callback.\n *  obj -   the user data provided in <mosquitto_new>\n *  level - the log message level from the values:\n *\t        MOSQ_LOG_INFO\n *\t        MOSQ_LOG_NOTICE\n *\t        MOSQ_LOG_WARNING\n *\t        MOSQ_LOG_ERR\n *\t        MOSQ_LOG_DEBUG\n *\tstr -   the message string.\n */\ntypedef void (*LIBMOSQ_CB_log)(struct mosquitto *mosq, void *obj, int level, const char *str);\nlibmosq_EXPORT void mosquitto_log_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_log on_log);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_connect.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_CONNECT_H\n#define MOSQUITTO_LIBMOSQUITTO_CONNECT_H\n\n/*\n * File: mosquitto/libmosquitto_connect.h\n *\n * This header contains functions for connect/disconnecting/reconnectng clients in libmosquitto.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ======================================================================\n *\n * Section: Connecting, reconnecting, disconnecting\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_connect\n *\n * Connect to an MQTT broker.\n *\n * It is valid to use this function for clients using all MQTT protocol versions.\n * If you need to set MQTT v5 CONNECT properties, use <mosquitto_connect_bind_v5>\n * instead.\n *\n * Parameters:\n * \tmosq -      a valid mosquitto instance.\n * \thost -      the hostname or ip address of the broker to connect to.\n * \tport -      the network port to connect to. Usually 1883.\n * \tkeepalive - the number of seconds after which the client should send a PING\n *              message to the broker if no other messages have been exchanged\n *              in that time.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid, which could be any of:\n * \t                   * mosq == NULL\n * \t                   * host == NULL\n * \t                   * port < 0\n * \t                   * keepalive < 5 (keepalive == 0 is allowed, for an infinite keepalive)\n * \tMOSQ_ERR_ERRNO -   if a system call returned an error. The variable errno\n *                     contains the error code, even on Windows.\n *                     Use strerror_r() where available or FormatMessage() on\n *                     Windows.\n *\n * See Also:\n * \t<mosquitto_connect_bind>, <mosquitto_connect_async>, <mosquitto_reconnect>, <mosquitto_disconnect>, <mosquitto_tls_set>\n */\nlibmosq_EXPORT int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive);\n\n/*\n * Function: mosquitto_connect_bind\n *\n * Connect to an MQTT broker. This extends the functionality of\n * <mosquitto_connect> by adding the bind_address parameter. Use this function\n * if you need to restrict network communication over a particular interface.\n *\n * Parameters:\n * \tmosq -         a valid mosquitto instance.\n * \thost -         the hostname or ip address of the broker to connect to.\n * \tport -         the network port to connect to. Usually 1883.\n * \tkeepalive -    the number of seconds after which the client should send a PING\n *                 message to the broker if no other messages have been exchanged\n *                 in that time.\n *  bind_address - the hostname or ip address of the local network interface to\n *                 bind to. If you do not want to bind to a specific interface,\n *                 set this to NULL.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_ERRNO -   if a system call returned an error. The variable errno\n *                     contains the error code, even on Windows.\n *                     Use strerror_r() where available or FormatMessage() on\n *                     Windows.\n *\n * See Also:\n * \t<mosquitto_connect>, <mosquitto_connect_async>, <mosquitto_connect_bind_async>\n */\nlibmosq_EXPORT int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address);\n\n/*\n * Function: mosquitto_connect_bind_v5\n *\n * Connect to an MQTT broker. This extends the functionality of\n * <mosquitto_connect> by adding the bind_address parameter and MQTT v5\n * properties. Use this function if you need to restrict network communication\n * over a particular interface.\n *\n * Use e.g. <mosquitto_property_add_string> and similar to create a list of\n * properties, then attach them to this publish. Properties need freeing with\n * <mosquitto_property_free_all>.\n *\n * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument\n * will be applied to the CONNECT message. For MQTT v3.1.1 and below, the\n * `properties` argument will be ignored.\n *\n * Set your client to use MQTT v5 immediately after it is created:\n *\n * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n *\n * Parameters:\n * \tmosq -         a valid mosquitto instance.\n * \thost -         the hostname or ip address of the broker to connect to.\n * \tport -         the network port to connect to. Usually 1883.\n * \tkeepalive -    the number of seconds after which the client should send a PING\n *                 message to the broker if no other messages have been exchanged\n *                 in that time.\n *  bind_address - the hostname or ip address of the local network interface to\n *                 bind to. If you do not want to bind to a specific interface,\n *                 set this to NULL.\n *  properties - the MQTT 5 properties for the connect (not for the Will).\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid, which could be any of:\n * \t                   * mosq == NULL\n * \t                   * host == NULL\n * \t                   * port < 0\n * \t                   * keepalive < 5 (keepalive == 0 is allowed, for an infinite keepalive)\n * \tMOSQ_ERR_ERRNO -   if a system call returned an error. The variable errno\n *                     contains the error code, even on Windows.\n *                     Use strerror_r() where available or FormatMessage() on\n *                     Windows.\n *\tMOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden.\n *\tMOSQ_ERR_PROTOCOL - if any property is invalid for use with CONNECT.\n *\n * See Also:\n * \t<mosquitto_connect>, <mosquitto_connect_async>, <mosquitto_connect_bind_async>\n */\nlibmosq_EXPORT int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties);\n\n/*\n * Function: mosquitto_connect_async\n *\n * Connect to an MQTT broker. This is a non-blocking call. If you use\n * <mosquitto_connect_async> your client must use the threaded interface\n * <mosquitto_loop_start>. If you need to use <mosquitto_loop>, you must use\n * <mosquitto_connect> to connect the client.\n *\n * May be called before or after <mosquitto_loop_start>.\n *\n * Parameters:\n * \tmosq -      a valid mosquitto instance.\n * \thost -      the hostname or ip address of the broker to connect to.\n * \tport -      the network port to connect to. Usually 1883.\n * \tkeepalive - the number of seconds after which the client should send a PING\n *              message to the broker if no other messages have been exchanged\n *              in that time.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_ERRNO -   if a system call returned an error. The variable errno\n *                     contains the error code, even on Windows.\n *                     Use strerror_r() where available or FormatMessage() on\n *                     Windows.\n *\n * See Also:\n * \t<mosquitto_connect_bind_async>, <mosquitto_connect>, <mosquitto_reconnect>, <mosquitto_disconnect>, <mosquitto_tls_set>\n */\nlibmosq_EXPORT int mosquitto_connect_async(struct mosquitto *mosq, const char *host, int port, int keepalive);\n\n/*\n * Function: mosquitto_connect_bind_async\n *\n * Connect to an MQTT broker. This is a non-blocking call. If you use\n * <mosquitto_connect_bind_async> your client must use the threaded interface\n * <mosquitto_loop_start>. If you need to use <mosquitto_loop>, you must use\n * <mosquitto_connect> to connect the client.\n *\n * This extends the functionality of <mosquitto_connect_async> by adding the\n * bind_address parameter. Use this function if you need to restrict network\n * communication over a particular interface.\n *\n * May be called before or after <mosquitto_loop_start>.\n *\n * Parameters:\n * \tmosq -         a valid mosquitto instance.\n * \thost -         the hostname or ip address of the broker to connect to.\n * \tport -         the network port to connect to. Usually 1883.\n * \tkeepalive -    the number of seconds after which the client should send a PING\n *                 message to the broker if no other messages have been exchanged\n *                 in that time.\n *  bind_address - the hostname or ip address of the local network interface to\n *                 bind to. If you do not want to bind to a specific interface,\n *                 set this to NULL.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid, which could be any of:\n * \t                   * mosq == NULL\n * \t                   * host == NULL\n * \t                   * port < 0\n * \t                   * keepalive < 5\n * \tMOSQ_ERR_ERRNO -   if a system call returned an error. The variable errno\n *                     contains the error code, even on Windows.\n *                     Use strerror_r() where available or FormatMessage() on\n *                     Windows.\n *\n * See Also:\n * \t<mosquitto_connect_async>, <mosquitto_connect>, <mosquitto_connect_bind>\n */\nlibmosq_EXPORT int mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address);\n\n/*\n * Function: mosquitto_connect_srv\n *\n * Connect to an MQTT broker.\n *\n * If you set `host` to `example.com`, then this call will attempt to retrieve\n * the DNS SRV record for `_secure-mqtt._tcp.example.com` or\n * `_mqtt._tcp.example.com` to discover which actual host to connect to.\n *\n * DNS SRV support is not usually compiled in to libmosquitto, use of this call\n * is not recommended.\n *\n * Parameters:\n * \tmosq -         a valid mosquitto instance.\n * \thost -         the hostname to search for an SRV record.\n * \tkeepalive -    the number of seconds after which the client should send a PING\n *                 message to the broker if no other messages have been exchanged\n *                 in that time.\n *  bind_address - the hostname or ip address of the local network interface to\n *                 bind to. If you do not want to bind to a specific interface,\n *                 set this to NULL.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid, which could be any of:\n * \t                   * mosq == NULL\n * \t                   * host == NULL\n * \t                   * port < 0\n * \t                   * keepalive < 5\n * \tMOSQ_ERR_ERRNO -   if a system call returned an error. The variable errno\n *                     contains the error code, even on Windows.\n *                     Use strerror_r() where available or FormatMessage() on\n *                     Windows.\n *\n * See Also:\n * \t<mosquitto_connect_async>, <mosquitto_connect>, <mosquitto_connect_bind>\n */\nlibmosq_EXPORT int mosquitto_connect_srv(struct mosquitto *mosq, const char *host, int keepalive, const char *bind_address);\n\n/*\n * Function: mosquitto_reconnect\n *\n * Reconnect to a broker.\n *\n * This function provides an easy way of reconnecting to a broker after a\n * connection has been lost. It uses the values that were provided in the\n * <mosquitto_connect> call. It must not be called before\n * <mosquitto_connect>.\n *\n * Parameters:\n * \tmosq - a valid mosquitto instance.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -   if an out of memory condition occurred.\n * \tMOSQ_ERR_ERRNO -   if a system call returned an error. The variable errno\n *                     contains the error code, even on Windows.\n *                     Use strerror_r() where available or FormatMessage() on\n *                     Windows.\n *\n * See Also:\n * \t<mosquitto_connect>, <mosquitto_disconnect>, <mosquitto_reconnect_async>\n */\nlibmosq_EXPORT int mosquitto_reconnect(struct mosquitto *mosq);\n\n/*\n * Function: mosquitto_reconnect_async\n *\n * Reconnect to a broker. Non blocking version of <mosquitto_reconnect>.\n *\n * This function provides an easy way of reconnecting to a broker after a\n * connection has been lost. It uses the values that were provided in the\n * <mosquitto_connect> or <mosquitto_connect_async> calls. It must not be\n * called before <mosquitto_connect>.\n *\n * Parameters:\n * \tmosq - a valid mosquitto instance.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -   if an out of memory condition occurred.\n * \tMOSQ_ERR_ERRNO -   if a system call returned an error. The variable errno\n *                     contains the error code, even on Windows.\n *                     Use strerror_r() where available or FormatMessage() on\n *                     Windows.\n *\n * See Also:\n * \t<mosquitto_connect>, <mosquitto_disconnect>\n */\nlibmosq_EXPORT int mosquitto_reconnect_async(struct mosquitto *mosq);\n\n/*\n * Function: mosquitto_disconnect\n *\n * Disconnect from the broker.\n *\n * It is valid to use this function for clients using all MQTT protocol versions.\n * If you need to set MQTT v5 DISCONNECT properties, use\n * <mosquitto_disconnect_v5> instead.\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_NO_CONN -  if the client isn't connected to a broker.\n */\nlibmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq);\n\n/*\n * Function: mosquitto_disconnect_v5\n *\n * Disconnect from the broker, with attached MQTT properties.\n *\n * Use e.g. <mosquitto_property_add_string> and similar to create a list of\n * properties, then attach them to this publish. Properties need freeing with\n * <mosquitto_property_free_all>.\n *\n * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument\n * will be applied to the DISCONNECT message. For MQTT v3.1.1 and below, the\n * `properties` argument will be ignored.\n *\n * Set your client to use MQTT v5 immediately after it is created:\n *\n * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\treason_code - the disconnect reason code.\n * \tproperties - a valid mosquitto_property list, or NULL.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_NO_CONN -  if the client isn't connected to a broker.\n *\tMOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden.\n *\tMOSQ_ERR_PROTOCOL - if any property is invalid for use with DISCONNECT.\n */\nlibmosq_EXPORT int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_create_delete.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_CREATE_DELETE_H\n#define MOSQUITTO_LIBMOSQUITTO_CREATE_DELETE_H\n\n/*\n * File: mosquitto/libmosquitto_create_delete.h\n *\n * This header contains functions for creating/deleting/reinitialising mosquitto clients.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ======================================================================\n *\n * Section: Client creation, destruction, and reinitialisation\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_new\n *\n * Create a new mosquitto client instance.\n *\n * Parameters:\n * \tid -            String to use as the client id. If NULL, a random client id\n * \t                will be generated. If id is NULL, clean_session must be true.\n * \tclean_session - set to true to instruct the broker to clean all messages\n *                  and subscriptions on disconnect, false to instruct it to\n *                  keep them. See the man page mqtt(7) for more details.\n *                  Note that a client will never discard its own outgoing\n *                  messages on disconnect. Calling <mosquitto_connect> or\n *                  <mosquitto_reconnect> will cause the messages to be resent.\n *                  Use <mosquitto_reinitialise> to reset a client to its\n *                  original state.\n *                  Must be set to true if the id parameter is NULL.\n * \tobj -           A user pointer that will be passed as an argument to any\n *                  callbacks that are specified.\n *\n * Returns:\n * \tPointer to a struct mosquitto on success.\n * \tNULL on failure. Interrogate errno to determine the cause for the failure:\n *      - ENOMEM on out of memory.\n *      - EINVAL on invalid input parameters.\n *\n * See Also:\n * \t<mosquitto_reinitialise>, <mosquitto_destroy>, <mosquitto_user_data_set>\n */\nlibmosq_EXPORT struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *obj);\n\n/*\n * Function: mosquitto_destroy\n *\n * Use to free memory associated with a mosquitto client instance.\n *\n * Parameters:\n * \tmosq - a struct mosquitto pointer to free.\n *\n * See Also:\n * \t<mosquitto_new>, <mosquitto_reinitialise>\n */\nlibmosq_EXPORT void mosquitto_destroy(struct mosquitto *mosq);\n\n/*\n * Function: mosquitto_reinitialise\n *\n * This function allows an existing mosquitto client to be reused. Call on a\n * mosquitto instance to close any open network connections, free memory\n * and reinitialise the client with the new parameters. The end result is the\n * same as the output of <mosquitto_new>.\n *\n * Parameters:\n * \tmosq -          a valid mosquitto instance.\n * \tid -            string to use as the client id. If NULL, a random client id\n * \t                will be generated. If id is NULL, clean_session must be true.\n * \tclean_session - set to true to instruct the broker to clean all messages\n *                  and subscriptions on disconnect, false to instruct it to\n *                  keep them. See the man page mqtt(7) for more details.\n *                  Must be set to true if the id parameter is NULL.\n * \tobj -           A user pointer that will be passed as an argument to any\n *                  callbacks that are specified.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS -        on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if the client id is not valid UTF-8.\n *\n * See Also:\n * \t<mosquitto_new>, <mosquitto_destroy>\n */\nlibmosq_EXPORT int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_session, void *obj);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_helpers.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_HELPERS_H\n#define MOSQUITTO_LIBMOSQUITTO_HELPERS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <mosquitto/defs.h>\n#include <mosquitto/mqtt_protocol.h>\n\n/* =============================================================================\n *\n * Section: One line client helper functions\n *\n * =============================================================================\n */\n\nstruct libmosquitto_will {\n\tchar *topic;\n\tvoid *payload;\n\tint payloadlen;\n\tint qos;\n\tbool retain;\n};\n\nstruct libmosquitto_auth {\n\tchar *username;\n\tchar *password;\n};\n\nstruct libmosquitto_tls {\n\tchar *cafile;\n\tchar *capath;\n\tchar *certfile;\n\tchar *keyfile;\n\tchar *ciphers;\n\tchar *tls_version;\n\tint (*pw_callback)(char *buf, int size, int rwflag, void *userdata);\n\tint cert_reqs;\n};\n\n/*\n * Function: mosquitto_subscribe_simple\n *\n * Helper function to make subscribing to a topic and retrieving some messages\n * very straightforward.\n *\n * This connects to a broker, subscribes to a topic, waits for msg_count\n * messages to be received, then returns after disconnecting cleanly.\n *\n * Parameters:\n *   messages - pointer to a \"struct mosquitto_message *\". The received\n *              messages will be returned here. On error, this will be set to\n *              NULL.\n *   msg_count - the number of messages to retrieve.\n *   want_retained - if set to true, stale retained messages will be treated as\n *                   normal messages with regards to msg_count. If set to\n *                   false, they will be ignored.\n *   topic - the subscription topic to use (wildcards are allowed).\n *   qos - the qos to use for the subscription.\n *   host - the broker to connect to.\n *   port - the network port the broker is listening on.\n *   clientid - the client id to use, or NULL if a random client id should be\n *               generated.\n *   keepalive - the MQTT keepalive value.\n *   clean_session - the MQTT clean session flag.\n *   username - the username string, or NULL for no username authentication.\n *   password - the password string, or NULL for an empty password.\n *   will - a libmosquitto_will struct containing will information, or NULL for\n *          no will.\n *   tls - a libmosquitto_tls struct containing TLS related parameters, or NULL\n *         for no use of TLS.\n *\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   Greater than 0 - on error.\n */\nlibmosq_EXPORT int mosquitto_subscribe_simple(\n\t\tstruct mosquitto_message **messages,\n\t\tint msg_count,\n\t\tbool want_retained,\n\t\tconst char *topic,\n\t\tint qos,\n\t\tconst char *host,\n\t\tint port,\n\t\tconst char *clientid,\n\t\tint keepalive,\n\t\tbool clean_session,\n\t\tconst char *username,\n\t\tconst char *password,\n\t\tconst struct libmosquitto_will *will,\n\t\tconst struct libmosquitto_tls *tls);\n\n\n/*\n * Function: mosquitto_subscribe_callback\n *\n * Helper function to make subscribing to a topic and processing some messages\n * very straightforward.\n *\n * This connects to a broker, subscribes to a topic, then passes received\n * messages to a user provided callback. If the callback returns a 1, it then\n * disconnects cleanly and returns.\n *\n * Parameters:\n *   callback - a callback function in the following form:\n *              int callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)\n *              Note that this is the same as the normal on_message callback,\n *              except that it returns an int.\n *   userdata - user provided pointer that will be passed to the callback.\n *   topic - the subscription topic to use (wildcards are allowed).\n *   qos - the qos to use for the subscription.\n *   host - the broker to connect to.\n *   port - the network port the broker is listening on.\n *   clientid - the client id to use, or NULL if a random client id should be\n *               generated.\n *   keepalive - the MQTT keepalive value.\n *   clean_session - the MQTT clean session flag.\n *   username - the username string, or NULL for no username authentication.\n *   password - the password string, or NULL for an empty password.\n *   will - a libmosquitto_will struct containing will information, or NULL for\n *          no will.\n *   tls - a libmosquitto_tls struct containing TLS related parameters, or NULL\n *         for no use of TLS.\n *\n *\n * Returns:\n *   MOSQ_ERR_SUCCESS - on success\n *   Greater than 0 - on error.\n */\nlibmosq_EXPORT int mosquitto_subscribe_callback(\n\t\tint (*callback)(struct mosquitto *, void *, const struct mosquitto_message *),\n\t\tvoid *userdata,\n\t\tconst char *topic,\n\t\tint qos,\n\t\tconst char *host,\n\t\tint port,\n\t\tconst char *clientid,\n\t\tint keepalive,\n\t\tbool clean_session,\n\t\tconst char *username,\n\t\tconst char *password,\n\t\tconst struct libmosquitto_will *will,\n\t\tconst struct libmosquitto_tls *tls);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_loop.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_LOOP_H\n#define MOSQUITTO_LIBMOSQUITTO_LOOP_H\n\n/*\n * File: mosquitto/libmosquitto_loop.h\n *\n * This header contains functions for handling the libmosquitto network loop.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <mosquitto/defs.h>\n#include <mosquitto/mqtt_protocol.h>\n\n/* ======================================================================\n *\n * Section: Network loop (managed by libmosquitto)\n *\n * The internal network loop must be called at a regular interval. The two\n * recommended approaches are to use either <mosquitto_loop_forever> or\n * <mosquitto_loop_start>. <mosquitto_loop_forever> is a blocking call and is\n * suitable for the situation where you only want to handle incoming messages\n * in callbacks. <mosquitto_loop_start> is a non-blocking call, it creates a\n * separate thread to run the loop for you. Use this function when you have\n * other tasks you need to run at the same time as the MQTT client, e.g.\n * reading data from a sensor.\n *\n * ====================================================================== */\n\n/*\n * Function: mosquitto_loop_forever\n *\n * This function call loop() for you in an infinite blocking loop. It is useful\n * for the case where you only want to run the MQTT client loop in your\n * program.\n *\n * It handles reconnecting in case server connection is lost. If you call\n * mosquitto_disconnect() in a callback it will return.\n *\n * Parameters:\n *  mosq - a valid mosquitto instance.\n *\ttimeout -     Maximum number of milliseconds to wait for network activity\n *\t              in the select() call before timing out. Set to 0 for instant\n *\t              return.  Set negative to use the default of 1000ms.\n *\tmax_packets - this parameter is currently unused and should be set to 1 for\n *\t              future compatibility.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -   on success.\n * \tMOSQ_ERR_INVAL -     if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -     if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -   if the client isn't connected to a broker.\n *  MOSQ_ERR_CONN_LOST - if the connection to the broker was lost.\n *\tMOSQ_ERR_PROTOCOL -  if there is a protocol error communicating with the\n *                       broker.\n * \tMOSQ_ERR_ERRNO -     if a system call returned an error. The variable errno\n *                       contains the error code, even on Windows.\n *                       Use strerror_r() where available or FormatMessage() on\n *                       Windows.\n *\n * See Also:\n *\t<mosquitto_loop>, <mosquitto_loop_start>\n */\nlibmosq_EXPORT int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets);\n\n/*\n * Function: mosquitto_loop_start\n *\n * This is part of the threaded client interface. Call this once to start a new\n * thread to process network traffic. This provides an alternative to\n * repeatedly calling <mosquitto_loop> yourself.\n *\n * Parameters:\n *  mosq - a valid mosquitto instance.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -       on success.\n * \tMOSQ_ERR_INVAL -         if the input parameters were invalid.\n *\tMOSQ_ERR_NOT_SUPPORTED - if thread support is not available.\n *\n * See Also:\n *\t<mosquitto_connect_async>, <mosquitto_loop>, <mosquitto_loop_forever>, <mosquitto_loop_stop>\n */\nlibmosq_EXPORT int mosquitto_loop_start(struct mosquitto *mosq);\n\n/*\n * Function: mosquitto_loop_stop\n *\n * This is part of the threaded client interface. Call this once to stop the\n * network thread previously created with <mosquitto_loop_start>. This call\n * will block until the network thread finishes. For the network thread to end,\n * you must have previously called <mosquitto_disconnect> or have set the force\n * parameter to true.\n *\n * Parameters:\n *  mosq - a valid mosquitto instance.\n *\tforce - set to true to force thread cancellation. If false,\n *\t        <mosquitto_disconnect> must have already been called.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -       on success.\n * \tMOSQ_ERR_INVAL -         if the input parameters were invalid.\n *\tMOSQ_ERR_NOT_SUPPORTED - if thread support is not available.\n *\n * See Also:\n *\t<mosquitto_loop>, <mosquitto_loop_start>\n */\nlibmosq_EXPORT int mosquitto_loop_stop(struct mosquitto *mosq, bool force);\n\n/*\n * Function: mosquitto_loop\n *\n * The main network loop for the client. This must be called frequently\n * to keep communications between the client and broker working. This is\n * carried out by <mosquitto_loop_forever> and <mosquitto_loop_start>, which\n * are the recommended ways of handling the network loop. You may also use this\n * function if you wish. It must not be called inside a callback.\n *\n * If incoming data is present it will then be processed. Outgoing commands,\n * from e.g.  <mosquitto_publish>, are normally sent immediately that their\n * function is called, but this is not always possible. <mosquitto_loop> will\n * also attempt to send any remaining outgoing messages, which also includes\n * commands that are part of the flow for messages with QoS>0.\n *\n * This calls select() to monitor the client network socket. If you want to\n * integrate mosquitto client operation with your own select() call, use\n * <mosquitto_socket>, <mosquitto_loop_read>, <mosquitto_loop_write> and\n * <mosquitto_loop_misc>.\n *\n * Threads:\n *\n * Parameters:\n *\tmosq -        a valid mosquitto instance.\n *\ttimeout -     Maximum number of milliseconds to wait for network activity\n *\t              in the select() call before timing out. Set to 0 for instant\n *\t              return.  Set negative to use the default of 1000ms.\n *\tmax_packets - this parameter is currently unused and should be set to 1 for\n *\t              future compatibility.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -   on success.\n * \tMOSQ_ERR_INVAL -     if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -     if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -   if the client isn't connected to a broker.\n *  MOSQ_ERR_CONN_LOST - if the connection to the broker was lost.\n *\tMOSQ_ERR_PROTOCOL -  if there is a protocol error communicating with the\n *                       broker.\n * \tMOSQ_ERR_ERRNO -     if a system call returned an error. The variable errno\n *                       contains the error code, even on Windows.\n *                       Use strerror_r() where available or FormatMessage() on\n *                       Windows.\n * See Also:\n *\t<mosquitto_loop_forever>, <mosquitto_loop_start>, <mosquitto_loop_stop>\n */\nlibmosq_EXPORT int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets);\n\n/* ======================================================================\n *\n * Section: Network loop (for use in other event loops)\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_loop_read\n *\n * Carry out network read operations.\n * This should only be used if you are not using mosquitto_loop() and are\n * monitoring the client network socket for activity yourself.\n *\n * Parameters:\n *\tmosq -        a valid mosquitto instance.\n *\tmax_packets - this parameter is currently unused and should be set to 1 for\n *\t              future compatibility.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -   on success.\n * \tMOSQ_ERR_INVAL -     if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -     if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -   if the client isn't connected to a broker.\n *  MOSQ_ERR_CONN_LOST - if the connection to the broker was lost.\n *\tMOSQ_ERR_PROTOCOL -  if there is a protocol error communicating with the\n *                       broker.\n * \tMOSQ_ERR_ERRNO -     if a system call returned an error. The variable errno\n *                       contains the error code, even on Windows.\n *                       Use strerror_r() where available or FormatMessage() on\n *                       Windows.\n *\n * See Also:\n *\t<mosquitto_socket>, <mosquitto_loop_write>, <mosquitto_loop_misc>\n */\nlibmosq_EXPORT int mosquitto_loop_read(struct mosquitto *mosq, int max_packets);\n\n/*\n * Function: mosquitto_loop_write\n *\n * Carry out network write operations.\n * This should only be used if you are not using mosquitto_loop() and are\n * monitoring the client network socket for activity yourself.\n *\n * Parameters:\n *\tmosq -        a valid mosquitto instance.\n *\tmax_packets - this parameter is currently unused and should be set to 1 for\n *\t              future compatibility.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -   on success.\n * \tMOSQ_ERR_INVAL -     if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -     if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -   if the client isn't connected to a broker.\n *  MOSQ_ERR_CONN_LOST - if the connection to the broker was lost.\n *\tMOSQ_ERR_PROTOCOL -  if there is a protocol error communicating with the\n *                       broker.\n * \tMOSQ_ERR_ERRNO -     if a system call returned an error. The variable errno\n *                       contains the error code, even on Windows.\n *                       Use strerror_r() where available or FormatMessage() on\n *                       Windows.\n *\n * See Also:\n *\t<mosquitto_socket>, <mosquitto_loop_read>, <mosquitto_loop_misc>, <mosquitto_want_write>\n */\nlibmosq_EXPORT int mosquitto_loop_write(struct mosquitto *mosq, int max_packets);\n\n/*\n * Function: mosquitto_loop_misc\n *\n * Carry out miscellaneous operations required as part of the network loop.\n * This should only be used if you are not using mosquitto_loop() and are\n * monitoring the client network socket for activity yourself.\n *\n * This function deals with handling PINGs and checking whether messages need\n * to be retried, so should be called fairly frequently, around once per second\n * is sufficient.\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -   on success.\n * \tMOSQ_ERR_INVAL -     if the input parameters were invalid.\n * \tMOSQ_ERR_NO_CONN -   if the client isn't connected to a broker.\n *\n * See Also:\n *\t<mosquitto_socket>, <mosquitto_loop_read>, <mosquitto_loop_write>\n */\nlibmosq_EXPORT int mosquitto_loop_misc(struct mosquitto *mosq);\n\n\n/* ======================================================================\n *\n * Section: Network loop (helper functions)\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_socket\n *\n * Return the socket handle for a mosquitto instance. Useful if you want to\n * include a mosquitto client in your own select() calls.\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\n * Returns:\n *\tThe socket for the mosquitto client or -1 on failure.\n */\nlibmosq_EXPORT int mosquitto_socket(struct mosquitto *mosq);\n\n/*\n * Function: mosquitto_want_write\n *\n * Returns true if there is data ready to be written on the socket.\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\n * See Also:\n *\t<mosquitto_socket>, <mosquitto_loop_read>, <mosquitto_loop_write>\n */\nlibmosq_EXPORT bool mosquitto_want_write(struct mosquitto *mosq);\n\n/*\n * Function: mosquitto_threaded_set\n *\n * Used to tell the library that your application is using threads, but not\n * using <mosquitto_loop_start>. The library operates slightly differently when\n * not in threaded mode in order to simplify its operation. If you are managing\n * your own threads and do not use this function you will experience crashes\n * due to race conditions.\n *\n * When using <mosquitto_loop_start>, this is set automatically.\n *\n * Parameters:\n *  mosq -     a valid mosquitto instance.\n *  threaded - true if your application is using threads, false otherwise.\n */\nlibmosq_EXPORT int mosquitto_threaded_set(struct mosquitto *mosq, bool threaded);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_message.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_MESSAGE_H\n#define MOSQUITTO_LIBMOSQUITTO_MESSAGE_H\n\n/*\n * File: mosquitto/libmosquitto_message.h\n *\n * This header contains functions for handling mosquitto_message structs.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ======================================================================\n *\n * Section: Struct mosquitto_message helper functions\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_message_copy\n *\n * Copy the contents of a mosquitto message to another message.\n * Useful for preserving a message received in the on_message() callback.\n *\n * Parameters:\n *\tdst - a pointer to a valid mosquitto_message struct to copy to.\n *\tsrc - a pointer to a valid mosquitto_message struct to copy from.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -   if an out of memory condition occurred.\n *\n * See Also:\n * \t<mosquitto_message_free>\n */\nlibmosq_EXPORT int mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto_message *src);\n\n/*\n * Function: mosquitto_message_free\n *\n * Completely free a mosquitto_message struct.\n *\n * Parameters:\n *\tmessage - pointer to a mosquitto_message pointer to free.\n *\n * See Also:\n * \t<mosquitto_message_copy>, <mosquitto_message_free_contents>\n */\nlibmosq_EXPORT void mosquitto_message_free(struct mosquitto_message **message);\n\n/*\n * Function: mosquitto_message_free_contents\n *\n * Free a mosquitto_message struct contents, leaving the struct unaffected.\n *\n * Parameters:\n *\tmessage - pointer to a mosquitto_message struct to free its contents.\n *\n * See Also:\n * \t<mosquitto_message_copy>, <mosquitto_message_free>\n */\nlibmosq_EXPORT void mosquitto_message_free_contents(struct mosquitto_message *message);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_options.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_OPTIONS_H\n#define MOSQUITTO_LIBMOSQUITTO_OPTIONS_H\n\n/*\n * File: mosquitto/libmosquitto_options.h\n *\n * This header contains functions for setting client options in libmosquitto.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ======================================================================\n *\n * Section: Client options\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_opts_set\n *\n * Used to set options for the client.\n *\n * This function is deprecated, the replacement <mosquitto_int_option>,\n * <mosquitto_string_option> and <mosquitto_void_option> functions should\n * be used instead.\n *\n * Parameters:\n *\tmosq -   a valid mosquitto instance.\n *\toption - the option to set.\n *\tvalue -  the option specific value.\n *\n * Options:\n *\tMOSQ_OPT_PROTOCOL_VERSION - Value must be an int, set to either\n *\t          MQTT_PROTOCOL_V31 or MQTT_PROTOCOL_V311. Must be set\n *\t          before the client connects.\n *\t          Defaults to MQTT_PROTOCOL_V31.\n *\n *\tMOSQ_OPT_SSL_CTX - Pass an openssl SSL_CTX to be used when creating\n *\t          TLS connections rather than libmosquitto creating its own.\n *\t          This must be called before connecting to have any effect.\n *\t          If you use this option, the onus is on you to ensure that\n *\t          you are using secure settings.\n *\t          Setting to NULL means that libmosquitto will use its own SSL_CTX\n *\t          if TLS is to be used.\n *\t          This option is only available for openssl 1.1.0 and higher.\n *\n *\tMOSQ_OPT_SSL_CTX_WITH_DEFAULTS - Value must be an int set to 1 or 0.\n *\t          If set to 1, then the user specified SSL_CTX passed in using\n *\t          MOSQ_OPT_SSL_CTX will have the default options applied to it.\n *\t          This means that you only need to change the values that are\n *\t          relevant to you. If you use this option then you must configure\n *\t          the TLS options as normal, i.e. you should use\n *\t          <mosquitto_tls_set> to configure the cafile/capath as a minimum.\n *\t          This option is only available for openssl 1.1.0 and higher.\n */\nlibmosq_EXPORT int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value);\n\n/*\n * Function: mosquitto_int_option\n *\n * Used to set integer options for the client.\n *\n * Parameters:\n *\tmosq -   a valid mosquitto instance.\n *\toption - the option to set.\n *\tvalue -  the option specific value.\n *\n * Options:\n *\tMOSQ_OPT_TCP_NODELAY - Set to 1 to disable Nagle's algorithm on client\n *\t          sockets. This has the effect of reducing latency of individual\n *\t          messages at the potential cost of increasing the number of\n *\t          packets being sent.\n *\t          Defaults to 0, which means Nagle remains enabled.\n *\n *\tMOSQ_OPT_PROTOCOL_VERSION - Value must be set to either MQTT_PROTOCOL_V31,\n *\t          MQTT_PROTOCOL_V311, or MQTT_PROTOCOL_V5. Must be set before the\n *\t          client connects.  Defaults to MQTT_PROTOCOL_V311.\n *\n *\tMOSQ_OPT_RECEIVE_MAXIMUM - Value can be set between 1 and 65535 inclusive,\n *\t          and represents the maximum number of incoming QoS 1 and QoS 2\n *\t          messages that this client wants to process at once. Defaults to\n *\t          20. This option is not valid for MQTT v3.1 or v3.1.1 clients.\n *\t          Note that if the MQTT_PROP_RECEIVE_MAXIMUM property is in the\n *\t          proplist passed to mosquitto_connect_v5(), then that property\n *\t          will override this option. Using this option is the recommended\n *\t          method however.\n *\n *\tMOSQ_OPT_SEND_MAXIMUM - Value can be set between 1 and 65535 inclusive,\n *\t          and represents the maximum number of outgoing QoS 1 and QoS 2\n *\t          messages that this client will attempt to have \"in flight\" at\n *\t          once. Defaults to 20.\n *\t          This option is not valid for MQTT v3.1 or v3.1.1 clients.\n *\t          Note that if the broker being connected to sends a\n *\t          MQTT_PROP_RECEIVE_MAXIMUM property that has a lower value than\n *\t          this option, then the broker provided value will be used.\n *\n *\tMOSQ_OPT_SSL_CTX_WITH_DEFAULTS - If value is set to a non zero value,\n *\t          then the user specified SSL_CTX passed in using MOSQ_OPT_SSL_CTX\n *\t          will have the default options applied to it. This means that\n *\t          you only need to change the values that are relevant to you.\n *\t          If you use this option then you must configure the TLS options\n *\t          as normal, i.e.  you should use <mosquitto_tls_set> to\n *\t          configure the cafile/capath as a minimum.\n *\t          This option is only available for openssl 1.1.0 and higher.\n *\n *\tMOSQ_OPT_TLS_OCSP_REQUIRED - Set whether OCSP checking on TLS\n *\t          connections is required. Set to 1 to enable checking,\n *\t          or 0 (the default) for no checking.\n *\n *\tMOSQ_OPT_TLS_USE_OS_CERTS - Set to 1 to instruct the client to load and\n *\t          trust OS provided CA certificates for use with TLS connections.\n *\t          Set to 0 (the default) to only use manually specified CA certs.\n *\n *\tMOSQ_OPT_DISABLE_SOCKETPAIR - By default, each client connected will create\n *            an internal pair of connected sockets to allow the network thread\n *            to be notified and woken up if another thread calls\n *            <mosquitto_publish> or other similar command. If you are\n *            operating with an external loop, this is not necessary and\n *            consumes an extra two sockets per client. Set this option to 1 to\n *            disable the use of the socket pair.\n *\n *\tMOSQ_OPT_TRANSPORT - Have the client connect with either MQTT over TCP as\n *\t          normal, or MQTT over WebSockets. Set the value to MOSQ_T_TCP or\n *\t          MOSQ_T_WEBSOCKETS.\n *\n *\tMOSQ_OPT_HTTP_HEADER_SIZE - Size the size of buffer that will be allocated\n *\t          to store the incoming HTTP header when using Websocket transport.\n *\t          Defaults to 4096. Setting to below 100 will result in a return\n*\t          value of MOSQ_ERR_INVAL. This should be set before starting the\n*\t          connection. If you try to set this when the initial http request\n*\t          is underway then it will return MOSQ_ERR_INVAL.\n */\nlibmosq_EXPORT int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value);\n\n\n/*\n * Function: mosquitto_string_option\n *\n * Used to set const char* options for the client.\n *\n * Parameters:\n *\tmosq -   a valid mosquitto instance.\n *\toption - the option to set.\n *\tvalue -  the option specific value.\n *\n * Options:\n *\tMOSQ_OPT_TLS_ENGINE - Configure the client for TLS Engine support.\n *\t          Pass a TLS Engine ID to be used when creating TLS\n *\t          connections. Must be set before <mosquitto_connect>.\n *\t          Must be a valid engine, and note that the string will not be used\n *\t          until a connection attempt is made so this function will return\n *\t          success even if an invalid engine string is passed.\n *\n *\tMOSQ_OPT_TLS_KEYFORM - Configure the client to treat the keyfile\n *\t          differently depending on its type.  Must be set\n *\t          before <mosquitto_connect>.\n *\t          Set as either \"pem\" or \"engine\", to determine from where the\n *\t          private key for a TLS connection will be obtained. Defaults to\n *\t          \"pem\", a normal private key file.\n *\n *\tMOSQ_OPT_TLS_ENGINE_KPASS_SHA1 - Where the TLS Engine requires the use of\n *\t          a password to be accessed, this option allows a hex encoded\n *\t          SHA1 hash of the private key password to be passed to the\n *\t          engine directly. Must be set before <mosquitto_connect>.\n *\n *\tMOSQ_OPT_TLS_ALPN - If the broker being connected to has multiple\n *\t          services available on a single TLS port, such as both MQTT\n *\t          and WebSockets, use this option to configure the ALPN\n *\t          option for the connection.\n *\n *\tMOSQ_OPT_BIND_ADDRESS - Set the hostname or ip address of the local network\n *\t          interface to bind to when connecting.\n */\nlibmosq_EXPORT int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, const char *value);\n\n\n/*\n * Function: mosquitto_void_option\n *\n * Used to set void* options for the client.\n *\n * Parameters:\n *\tmosq -   a valid mosquitto instance.\n *\toption - the option to set.\n *\tvalue -  the option specific value.\n *\n * Options:\n *\tMOSQ_OPT_SSL_CTX - Pass an openssl SSL_CTX to be used when creating TLS\n *\t          connections rather than libmosquitto creating its own.  This must\n *\t          be called before connecting to have any effect. If you use this\n *\t          option, the onus is on you to ensure that you are using secure\n *\t          settings.\n *\t          Setting to NULL means that libmosquitto will use its own SSL_CTX\n *\t          if TLS is to be used.\n *\t          This option is only available for openssl 1.1.0 and higher.\n */\nlibmosq_EXPORT int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value);\n\n/*\n * Function: mosquitto_reconnect_delay_set\n *\n * Control the behaviour of the client when it has unexpectedly disconnected in\n * <mosquitto_loop_forever> or after <mosquitto_loop_start>. The default\n * behaviour if this function is not used is to repeatedly attempt to reconnect\n * with a delay of 1 second until the connection succeeds.\n *\n * Use reconnect_delay parameter to change the delay between successive\n * reconnection attempts. You may also enable exponential backoff of the time\n * between reconnections by setting reconnect_exponential_backoff to true and\n * set an upper bound on the delay with reconnect_delay_max.\n *\n * Example 1:\n *\tdelay=2, delay_max=10, exponential_backoff=False\n *\tDelays would be: 2, 4, 6, 8, 10, 10, ...\n *\n * Example 2:\n *\tdelay=3, delay_max=30, exponential_backoff=True\n *\tDelays would be: 3, 6, 12, 24, 30, 30, ...\n *\n * Parameters:\n *  mosq -                          a valid mosquitto instance.\n *  reconnect_delay -               the number of seconds to wait between\n *                                  reconnects.\n *  reconnect_delay_max -           the maximum number of seconds to wait\n *                                  between reconnects.\n *  reconnect_exponential_backoff - use exponential backoff between\n *                                  reconnect attempts. Set to true to enable\n *                                  exponential backoff.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n */\nlibmosq_EXPORT int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff);\n\n/*\n * Function: mosquitto_max_inflight_messages_set\n *\n * This function is deprecated. Use the <mosquitto_int_option> function with the\n * MOSQ_OPT_SEND_MAXIMUM option instead.\n *\n * Set the number of QoS 1 and 2 messages that can be \"in flight\" at one time.\n * An in flight message is part way through its delivery flow. Attempts to send\n * further messages with <mosquitto_publish> will result in the messages being\n * queued until the number of in flight messages reduces.\n *\n * A higher number here results in greater message throughput, but if set\n * higher than the maximum in flight messages on the broker may lead to\n * delays in the messages being acknowledged.\n *\n * Set to 0 for no maximum.\n *\n * Parameters:\n *  mosq -                  a valid mosquitto instance.\n *  max_inflight_messages - the maximum number of inflight messages. Defaults\n *                          to 20.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n */\nlibmosq_EXPORT int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages);\n\n/*\n * Function: mosquitto_message_retry_set\n *\n * This function now has no effect.\n */\nlibmosq_EXPORT void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry);\n\n/*\n * Function: mosquitto_user_data_set\n *\n * When <mosquitto_new> is called, the pointer given as the \"obj\" parameter\n * will be passed to the callbacks as user data. The <mosquitto_user_data_set>\n * function allows this obj parameter to be updated at any time. This function\n * will not modify the memory pointed to by the current user data pointer. If\n * it is dynamically allocated memory you must free it yourself.\n *\n * Parameters:\n *  mosq - a valid mosquitto instance.\n * \tobj -  A user pointer that will be passed as an argument to any callbacks\n * \t       that are specified.\n */\nlibmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj);\n\n/* Function: mosquitto_userdata\n *\n * Retrieve the \"userdata\" variable for a mosquitto client.\n *\n * Parameters:\n * \tmosq - a valid mosquitto instance.\n *\n * Returns:\n *\tA pointer to the userdata member variable.\n */\nlibmosq_EXPORT void *mosquitto_userdata(struct mosquitto *mosq);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_publish.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_PUBLISH_H\n#define MOSQUITTO_LIBMOSQUITTO_PUBLISH_H\n\n/*\n * File: mosquitto/libmosquitto_publish.h\n *\n * This header contains functions for publishing with libmosquitto.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Function: mosquitto_publish\n *\n * Publish a message on a given topic.\n *\n * It is valid to use this function for clients using all MQTT protocol versions.\n * If you need to set MQTT v5 PUBLISH properties, use <mosquitto_publish_v5>\n * instead.\n *\n * Parameters:\n * \tmosq -       a valid mosquitto instance.\n * \tmid -        pointer to an int. If not NULL, the function will set this\n *               to the message id of this particular message. This can be then\n *               used with the publish callback to determine when the message\n *               has been sent.\n *               Note that although the MQTT protocol doesn't use message ids\n *               for messages with QoS=0, libmosquitto assigns them message ids\n *               so they can be tracked with this parameter.\n *  topic -      null terminated string of the topic to publish to.\n * \tpayloadlen - the size of the payload (bytes). Valid values are between 0 and\n *               268,435,455.\n * \tpayload -    pointer to the data to send. If payloadlen > 0 this must be a\n *               valid memory location.\n * \tqos -        integer value 0, 1 or 2 indicating the Quality of Service to be\n *               used for the message.\n * \tretain -     set to true to make the message retained.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS -        on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -        if the client isn't connected to a broker.\n *\tMOSQ_ERR_PROTOCOL -       if there is a protocol error communicating with the\n *                            broker.\n * \tMOSQ_ERR_PAYLOAD_SIZE -   if payloadlen is too large.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8\n *\tMOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by\n *\t                             the broker.\n *\tMOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than\n *\t                           supported by the broker.\n *\n * See Also:\n *\t<mosquitto_max_inflight_messages_set>\n */\nlibmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain);\n\n\n/*\n * Function: mosquitto_publish_v5\n *\n * Publish a message on a given topic, with attached MQTT properties.\n *\n * Use e.g. <mosquitto_property_add_string> and similar to create a list of\n * properties, then attach them to this publish. Properties need freeing with\n * <mosquitto_property_free_all>.\n *\n * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument\n * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the\n * `properties` argument will be ignored.\n *\n * Set your client to use MQTT v5 immediately after it is created:\n *\n * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n *\n * Parameters:\n * \tmosq -       a valid mosquitto instance.\n * \tmid -        pointer to an int. If not NULL, the function will set this\n *               to the message id of this particular message. This can be then\n *               used with the publish callback to determine when the message\n *               has been sent.\n *               Note that although the MQTT protocol doesn't use message ids\n *               for messages with QoS=0, libmosquitto assigns them message ids\n *               so they can be tracked with this parameter.\n *  topic -      null terminated string of the topic to publish to.\n * \tpayloadlen - the size of the payload (bytes). Valid values are between 0 and\n *               268,435,455.\n * \tpayload -    pointer to the data to send. If payloadlen > 0 this must be a\n *               valid memory location.\n * \tqos -        integer value 0, 1 or 2 indicating the Quality of Service to be\n *               used for the message.\n * \tretain -     set to true to make the message retained.\n * \tproperties - a valid mosquitto_property list, or NULL.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS -        on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -        if the client isn't connected to a broker.\n *\tMOSQ_ERR_PROTOCOL -       if there is a protocol error communicating with the\n *                            broker.\n * \tMOSQ_ERR_PAYLOAD_SIZE -   if payloadlen is too large.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8\n *\tMOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden.\n *\tMOSQ_ERR_PROTOCOL - if any property is invalid for use with PUBLISH.\n *\tMOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by\n *\t                             the broker.\n *\tMOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than\n *\t                           supported by the broker.\n */\nlibmosq_EXPORT int mosquitto_publish_v5(\n\t\tstruct mosquitto *mosq,\n\t\tint *mid,\n\t\tconst char *topic,\n\t\tint payloadlen,\n\t\tconst void *payload,\n\t\tint qos,\n\t\tbool retain,\n\t\tconst mosquitto_property *properties);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_socks.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_SOCKS_H\n#define MOSQUITTO_LIBMOSQUITTO_SOCKS_H\n\n/*\n * File: mosquitto/libmosquitto_socks.h\n *\n * This header contains functions for controlling SOCKSv5 in libmosquitto.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* =============================================================================\n *\n * Section: SOCKS5 proxy functions\n *\n * =============================================================================\n */\n\n/*\n * Function: mosquitto_socks5_set\n *\n * Configure the client to use a SOCKS5 proxy when connecting. Must be called\n * before connecting. \"None\" and \"username/password\" authentication is\n * supported.\n *\n * Parameters:\n *   mosq - a valid mosquitto instance.\n *   host - the SOCKS5 proxy host to connect to.\n *   port - the SOCKS5 proxy port to use.\n *   username - if not NULL, use this username when authenticating with the proxy.\n *   password - if not NULL and username is not NULL, use this password when\n *              authenticating with the proxy.\n */\nlibmosq_EXPORT int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, const char *username, const char *password);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_subscribe.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_SUBSCRIBE_H\n#define MOSQUITTO_LIBMOSQUITTO_SUBSCRIBE_H\n\n/*\n * File: mosquitto/libmosquitto_subscribe.h\n *\n * This header contains functions for subscribing in libmosquitto.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Function: mosquitto_subscribe\n *\n * Subscribe to a topic.\n *\n * It is valid to use this function for clients using all MQTT protocol versions.\n * If you need to set MQTT v5 SUBSCRIBE properties, use <mosquitto_subscribe_v5>\n * instead.\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\tmid -  a pointer to an int. If not NULL, the function will set this to\n *\t       the message id of this particular message. This can be then used\n *\t       with the subscribe callback to determine when the message has been\n *\t       sent.\n *\tsub -  the subscription pattern - must not be NULL or an empty string.\n *\tqos -  the requested Quality of Service for this subscription.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -        on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -        if the client isn't connected to a broker.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8\n *\tMOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than\n *\t                           supported by the broker.\n */\nlibmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos);\n\n/*\n * Function: mosquitto_subscribe_v5\n *\n * Subscribe to a topic, with attached MQTT properties.\n *\n * Use e.g. <mosquitto_property_add_string> and similar to create a list of\n * properties, then attach them to this publish. Properties need freeing with\n * <mosquitto_property_free_all>.\n *\n * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument\n * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the\n * `properties` argument will be ignored.\n *\n * Set your client to use MQTT v5 immediately after it is created:\n *\n * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\tmid -  a pointer to an int. If not NULL, the function will set this to\n *\t       the message id of this particular message. This can be then used\n *\t       with the subscribe callback to determine when the message has been\n *\t       sent.\n *\tsub -  the subscription pattern - must not be NULL or an empty string.\n *\tqos -  the requested Quality of Service for this subscription.\n *\toptions - options to apply to this subscription, OR'd together. Set to 0 to\n *\t          use the default options, otherwise choose from list of <mqtt5_sub_options>\n * \tproperties - a valid mosquitto_property list, or NULL.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -        on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -        if the client isn't connected to a broker.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8\n *\tMOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden.\n *\tMOSQ_ERR_PROTOCOL - if any property is invalid for use with SUBSCRIBE.\n *\tMOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than\n *\t                           supported by the broker.\n */\nlibmosq_EXPORT int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties);\n\n/*\n * Function: mosquitto_subscribe_multiple\n *\n * Subscribe to multiple topics.\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\tmid -  a pointer to an int. If not NULL, the function will set this to\n *\t       the message id of this particular message. This can be then used\n *\t       with the subscribe callback to determine when the message has been\n *\t       sent.\n *  sub_count - the count of subscriptions to be made\n *\tsub -  array of sub_count pointers, each pointing to a subscription string.\n *\t       The \"char *const *const\" datatype ensures that neither the array of\n *\t       pointers nor the strings that they point to are mutable. If you aren't\n *\t       familiar with this, just think of it as a safer \"char **\",\n *\t       equivalent to \"const char *\" for a simple string pointer.\n *\t       Each string must not be NULL nor an empty string.\n *\tqos -  the requested Quality of Service for each subscription.\n *\toptions - options to apply to this subscription, OR'd together. This\n *\t          argument is not used for MQTT v3 susbcriptions. Set to 0 to use\n *\t          the default options, otherwise choose from list of <mqtt5_sub_options>\n * \tproperties - a valid mosquitto_property list, or NULL. Only used with MQTT\n * \t             v5 clients.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -        on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -        if the client isn't connected to a broker.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8\n *\tMOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than\n *\t                           supported by the broker.\n */\nlibmosq_EXPORT int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_tls.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_TLS_H\n#define MOSQUITTO_LIBMOSQUITTO_TLS_H\n\n/*\n * File: mosquitto/libmosquitto_tls.h\n *\n * This header contains functions for setting TLS options in libmosquitto.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ======================================================================\n *\n * Section: TLS support\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_tls_set\n *\n * Configure the client for certificate based SSL/TLS support. Must be called\n * before <mosquitto_connect>.\n *\n * Cannot be used in conjunction with <mosquitto_tls_psk_set>.\n *\n * Define the Certificate Authority certificates to be trusted (ie. the server\n * certificate must be signed with one of these certificates) using cafile.\n *\n * If the server you are connecting to requires clients to provide a\n * certificate, define certfile and keyfile with your client certificate and\n * private key. If your private key is encrypted, provide a password callback\n * function or you will have to enter the password at the command line.\n *\n * Parameters:\n *  mosq -        a valid mosquitto instance.\n *  cafile -      path to a file containing the PEM encoded trusted CA\n *                certificate files. Either cafile or capath must not be NULL.\n *  capath -      path to a directory containing the PEM encoded trusted CA\n *                certificate files. See mosquitto.conf for more details on\n *                configuring this directory. Either cafile or capath must not\n *                be NULL.\n *  certfile -    path to a file containing the PEM encoded certificate file\n *                for this client. If NULL, keyfile must also be NULL and no\n *                client certificate will be used.\n *  keyfile -     path to a file containing the PEM encoded private key for\n *                this client. If NULL, certfile must also be NULL and no\n *                client certificate will be used.\n *  pw_callback - if keyfile is encrypted, set pw_callback to allow your client\n *                to pass the correct password for decryption. If set to NULL,\n *                the password must be entered on the command line.\n *                Your callback must write the password into \"buf\", which is\n *                \"size\" bytes long. The return value must be the length of the\n *                password. \"userdata\" will be set to the calling mosquitto\n *                instance. The mosquitto userdata member variable can be\n *                retrieved using <mosquitto_userdata>.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -   if an out of memory condition occurred.\n *\n * See Also:\n *\t<mosquitto_tls_opts_set>, <mosquitto_tls_psk_set>,\n *\t<mosquitto_tls_insecure_set>, <mosquitto_userdata>\n */\nlibmosq_EXPORT int mosquitto_tls_set(struct mosquitto *mosq,\n\t\tconst char *cafile, const char *capath,\n\t\tconst char *certfile, const char *keyfile,\n\t\tint (*pw_callback)(char *buf, int size, int rwflag, void *userdata));\n\n/*\n * Function: mosquitto_tls_insecure_set\n *\n * Configure verification of the server hostname in the server certificate. If\n * value is set to true, it is impossible to guarantee that the host you are\n * connecting to is not impersonating your server. This can be useful in\n * initial server testing, but makes it possible for a malicious third party to\n * impersonate your server through DNS spoofing, for example.\n * Do not use this function in a real system. Setting value to true makes the\n * connection encryption pointless.\n * Must be called before <mosquitto_connect>.\n *\n * Parameters:\n *  mosq -  a valid mosquitto instance.\n *  value - if set to false, the default, certificate hostname checking is\n *          performed. If set to true, no hostname checking is performed and\n *          the connection is insecure.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n *\n * See Also:\n *\t<mosquitto_tls_set>\n */\nlibmosq_EXPORT int mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value);\n\n/*\n * Function: mosquitto_tls_opts_set\n *\n * Set advanced SSL/TLS options. Must be called before <mosquitto_connect>.\n *\n * Parameters:\n *  mosq -        a valid mosquitto instance.\n *\tcert_reqs -   an integer defining the verification requirements the client\n *\t              will impose on the server. This can be one of:\n *\t              * SSL_VERIFY_NONE (0): the server will not be verified in any way.\n *\t              * SSL_VERIFY_PEER (1): the server certificate will be verified\n *\t                and the connection aborted if the verification fails.\n *\t              The default and recommended value is SSL_VERIFY_PEER. Using\n *\t              SSL_VERIFY_NONE provides no security.\n *\ttls_version - the version of the SSL/TLS protocol to use as a string. If NULL,\n *\t              the default value is used. The default value and the\n *\t              available values depend on the version of openssl that the\n *\t              library was compiled against. The available options are\n *\t              tlsv1.3 and tlsv1.2, with tlsv1.2 as the default.\n *\tciphers -     a string describing the ciphers available for use. See the\n *\t              \"openssl ciphers\" tool for more information. If NULL, the\n *\t              default ciphers will be used.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -   if an out of memory condition occurred.\n *\n * See Also:\n *\t<mosquitto_tls_set>\n */\nlibmosq_EXPORT int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tls_version, const char *ciphers);\n\n/*\n * Function: mosquitto_tls_psk_set\n *\n * Configure the client for pre-shared-key based TLS support. Must be called\n * before <mosquitto_connect>.\n *\n * Cannot be used in conjunction with <mosquitto_tls_set>.\n *\n * Parameters:\n *  mosq -     a valid mosquitto instance.\n *  psk -      the pre-shared-key in hex format with no leading \"0x\".\n *  identity - the identity of this client. May be used as the username\n *             depending on the server settings.\n *\tciphers -  a string describing the PSK ciphers available for use. See the\n *\t           \"openssl ciphers\" tool for more information. If NULL, the\n *\t           default ciphers will be used.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -   if an out of memory condition occurred.\n *\n * See Also:\n *\t<mosquitto_tls_set>\n */\nlibmosq_EXPORT int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers);\n\n\n/*\n * Function: mosquitto_ssl_get\n *\n * Retrieve a pointer to the SSL structure used for TLS connections in this\n * client. This can be used in e.g. the connect callback to carry out\n * additional verification steps.\n *\n * Parameters:\n *  mosq - a valid mosquitto instance\n *\n * Returns:\n *  A valid pointer to an openssl SSL structure - if the client is using TLS.\n *  NULL - if the client is not using TLS, or TLS support is not compiled in.\n */\nlibmosq_EXPORT void *mosquitto_ssl_get(struct mosquitto *mosq);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_unsubscribe.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_UNSUBSCRIBE_H\n#define MOSQUITTO_LIBMOSQUITTO_UNSUBSCRIBE_H\n\n/*\n * File: mosquitto/libmosquitto_unsubscribe.h\n *\n * This header contains functions for client unsubscribing in libmosquitto.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Function: mosquitto_unsubscribe\n *\n * Unsubscribe from a topic.\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\tmid -  a pointer to an int. If not NULL, the function will set this to\n *\t       the message id of this particular message. This can be then used\n *\t       with the unsubscribe callback to determine when the message has been\n *\t       sent.\n *\tsub -  the unsubscription pattern - must not by NULL or an empty string.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -        on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -        if the client isn't connected to a broker.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8\n *\tMOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than\n *\t                           supported by the broker.\n */\nlibmosq_EXPORT int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub);\n\n/*\n * Function: mosquitto_unsubscribe_v5\n *\n * Unsubscribe from a topic, with attached MQTT properties.\n *\n * It is valid to use this function for clients using all MQTT protocol versions.\n * If you need to set MQTT v5 UNSUBSCRIBE properties, use\n * <mosquitto_unsubscribe_v5> instead.\n *\n * Use e.g. <mosquitto_property_add_string> and similar to create a list of\n * properties, then attach them to this publish. Properties need freeing with\n * <mosquitto_property_free_all>.\n *\n * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument\n * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the\n * `properties` argument will be ignored.\n *\n * Set your client to use MQTT v5 immediately after it is created:\n *\n * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\tmid -  a pointer to an int. If not NULL, the function will set this to\n *\t       the message id of this particular message. This can be then used\n *\t       with the unsubscribe callback to determine when the message has been\n *\t       sent.\n *\tsub -  the unsubscription pattern - must not by NULL or an empty string.\n * \tproperties - a valid mosquitto_property list, or NULL. Only used with MQTT\n * \t             v5 clients.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -        on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -        if the client isn't connected to a broker.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8\n *\tMOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden.\n *\tMOSQ_ERR_PROTOCOL - if any property is invalid for use with UNSUBSCRIBE.\n *\tMOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than\n *\t                           supported by the broker.\n */\nlibmosq_EXPORT int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties);\n\n/*\n * Function: mosquitto_unsubscribe_multiple\n *\n * Unsubscribe from multiple topics.\n *\n * Parameters:\n *\tmosq - a valid mosquitto instance.\n *\tmid -  a pointer to an int. If not NULL, the function will set this to\n *\t       the message id of this particular message. This can be then used\n *\t       with the subscribe callback to determine when the message has been\n *\t       sent.\n *  sub_count - the count of unsubscriptions to be made\n *\tsub -  array of sub_count pointers, each pointing to an unsubscription string.\n *\t       The \"char *const *const\" datatype ensures that neither the array of\n *\t       pointers nor the strings that they point to are mutable. If you aren't\n *\t       familiar with this, just think of it as a safer \"char **\",\n *\t       equivalent to \"const char *\" for a simple string pointer.\n *\t       Each sub must not be NULL nor an empty string.\n * \tproperties - a valid mosquitto_property list, or NULL. Only used with MQTT\n * \t             v5 clients.\n *\n * Returns:\n *\tMOSQ_ERR_SUCCESS -        on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_NO_CONN -        if the client isn't connected to a broker.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8\n *\tMOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than\n *\t                           supported by the broker.\n */\nlibmosq_EXPORT int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquitto_will.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTO_WILL_H\n#define MOSQUITTO_LIBMOSQUITTO_WILL_H\n\n/*\n * File: mosquitto/libmosquitto_will.h\n *\n * This header contains functions for manipulating client Wills in libmosquitto.\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ======================================================================\n *\n * Section: Will\n *\n * ====================================================================== */\n/*\n * Function: mosquitto_will_set\n *\n * Configure will information for a mosquitto instance. By default, clients do\n * not have a will.  This must be called before calling <mosquitto_connect>.\n *\n * It is valid to use this function for clients using all MQTT protocol versions.\n * If you need to set MQTT v5 Will properties, use <mosquitto_will_set_v5> instead.\n *\n * Parameters:\n * \tmosq -       a valid mosquitto instance.\n * \ttopic -      the topic on which to publish the will.\n * \tpayloadlen - the size of the payload (bytes). Valid values are between 0 and\n *               268,435,455.\n * \tpayload -    pointer to the data to send. If payloadlen > 0 this must be a\n *               valid memory location.\n * \tqos -        integer value 0, 1 or 2 indicating the Quality of Service to be\n *               used for the will.\n * \tretain -     set to true to make the will a retained message.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS -      on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_PAYLOAD_SIZE -   if payloadlen is too large.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8.\n */\nlibmosq_EXPORT int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain);\n\n/*\n * Function: mosquitto_will_set_v5\n *\n * Configure will information for a mosquitto instance, with attached\n * properties. By default, clients do not have a will.  This must be called\n * before calling <mosquitto_connect>.\n *\n * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument\n * will be applied to the Will. For MQTT v3.1.1 and below, the `properties`\n * argument will be ignored.\n *\n * Set your client to use MQTT v5 immediately after it is created:\n *\n * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n *\n * Parameters:\n * \tmosq -       a valid mosquitto instance.\n * \ttopic -      the topic on which to publish the will.\n * \tpayloadlen - the size of the payload (bytes). Valid values are between 0 and\n *               268,435,455.\n * \tpayload -    pointer to the data to send. If payloadlen > 0 this must be a\n *               valid memory location.\n * \tqos -        integer value 0, 1 or 2 indicating the Quality of Service to be\n *               used for the will.\n * \tretain -     set to true to make the will a retained message.\n * \tproperties - list of MQTT 5 properties. Can be NULL. On success only, the\n * \t             property list becomes the property of libmosquitto once this\n * \t             function is called and will be freed by the library. The\n * \t             property list must be freed by the application on error.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS -      on success.\n * \tMOSQ_ERR_INVAL -          if the input parameters were invalid.\n * \tMOSQ_ERR_NOMEM -          if an out of memory condition occurred.\n * \tMOSQ_ERR_PAYLOAD_SIZE -   if payloadlen is too large.\n * \tMOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8.\n * \tMOSQ_ERR_NOT_SUPPORTED -  if properties is not NULL and the client is not\n * \t                          using MQTT v5\n * \tMOSQ_ERR_PROTOCOL -       if a property is invalid for use with wills.\n *\tMOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden.\n */\nlibmosq_EXPORT int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties);\n\n/*\n * Function: mosquitto_will_clear\n *\n * Remove a previously configured will. This must be called before calling\n * <mosquitto_connect>.\n *\n * Parameters:\n * \tmosq - a valid mosquitto instance.\n *\n * Returns:\n * \tMOSQ_ERR_SUCCESS - on success.\n * \tMOSQ_ERR_INVAL -   if the input parameters were invalid.\n */\nlibmosq_EXPORT int mosquitto_will_clear(struct mosquitto *mosq);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/mosquitto/libmosquittopp.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_LIBMOSQUITTOPP_H\n#define MOSQUITTO_LIBMOSQUITTOPP_H\n\n#if defined(_WIN32) && !defined(LIBMOSQUITTO_STATIC)\n#   ifdef mosquittopp_EXPORTS\n#       define mosqpp_EXPORT  __declspec(dllexport)\n#   else\n#       define mosqpp_EXPORT  __declspec(dllimport)\n#   endif\n#else\n#   define mosqpp_EXPORT\n#endif\n\n#include <cstdlib>\n#include <mosquitto.h>\n#include <time.h>\n\nnamespace mosqpp{\n\n\nmosqpp_EXPORT const char *strerror(int mosq_errno);\nmosqpp_EXPORT const char *connack_string(int connack_code);\nmosqpp_EXPORT const char *reason_string(int reason_code);\nmosqpp_EXPORT int sub_topic_tokenise(const char *subtopic, char ***topics, int *count);\nmosqpp_EXPORT int sub_topic_tokens_free(char ***topics, int count);\nmosqpp_EXPORT int lib_version(int *major, int *minor, int *revision);\nmosqpp_EXPORT int lib_init();\nmosqpp_EXPORT int lib_cleanup();\nmosqpp_EXPORT int topic_matches_sub(const char *sub, const char *topic, bool *result);\nmosqpp_EXPORT int topic_matches_sub_with_pattern(const char *sub, const char *topic, const char *clientid, const char *username, bool *result);\nmosqpp_EXPORT int sub_matches_acl(const char *acl, const char *sub, bool *result);\nmosqpp_EXPORT int sub_matches_acl_with_pattern(const char *acl, const char *sub, const char *clientid, const char *username, bool *result);\nmosqpp_EXPORT int validate_utf8(const char *str, int len);\nmosqpp_EXPORT int subscribe_simple(\n\t\tstruct mosquitto_message **messages,\n\t\tint msg_count,\n\t\tbool retained,\n\t\tconst char *topic,\n\t\tint qos=0,\n\t\tconst char *host=\"localhost\",\n\t\tint port=1883,\n\t\tconst char *clientid=NULL,\n\t\tint keepalive=60,\n\t\tbool clean_session=true,\n\t\tconst char *username=NULL,\n\t\tconst char *password=NULL,\n\t\tconst struct libmosquitto_will *will=NULL,\n\t\tconst struct libmosquitto_tls *tls=NULL);\n\nmosqpp_EXPORT int subscribe_callback(\n\t\tint (*callback)(struct mosquitto *, void *, const struct mosquitto_message *),\n\t\tvoid *userdata,\n\t\tconst char *topic,\n\t\tint qos=0,\n\t\tconst char *host=\"localhost\",\n\t\tint port=1883,\n\t\tconst char *clientid=NULL,\n\t\tint keepalive=60,\n\t\tbool clean_session=true,\n\t\tconst char *username=NULL,\n\t\tconst char *password=NULL,\n\t\tconst struct libmosquitto_will *will=NULL,\n\t\tconst struct libmosquitto_tls *tls=NULL);\n\nmosqpp_EXPORT int property_check_command(int command, int identifier);\nmosqpp_EXPORT int property_check_all(int command, const mosquitto_property *properties);\n\n/*\n * Class: mosquittopp\n *\n * A mosquitto client class. This is a C++ wrapper class for the mosquitto C\n * library. Please see mosquitto.h for details of the functions.\n */\nclass mosqpp_EXPORT mosquittopp {\nprivate:\n\tstruct mosquitto *m_mosq;\npublic:\n\tmosquittopp(const char *id=NULL, bool clean_session=true);\n\tvirtual ~mosquittopp();\n\n\tint reinitialise(const char *id, bool clean_session);\n\tint socket();\n\tint will_set(const char *topic, int payloadlen=0, const void *payload=NULL, int qos=0, bool retain=false);\n\tint will_set_v5(const char *topic, int payloadlen=0, const void *payload=NULL, int qos=0, bool retain=false, mosquitto_property *properties=NULL);\n\tint will_clear();\n\tint username_pw_set(const char *username, const char *password=NULL);\n\tint connect(const char *host, int port=1883, int keepalive=60);\n\tint connect(const char *host, int port, int keepalive, const char *bind_address);\n\tint connect_v5(const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties);\n\tint connect_async(const char *host, int port=1883, int keepalive=60);\n\tint connect_async(const char *host, int port, int keepalive, const char *bind_address);\n\tint reconnect();\n\tint reconnect_async();\n\tint disconnect();\n\tint disconnect_v5(int reason_code, const mosquitto_property *properties);\n\tint publish(int *mid, const char *topic, int payloadlen=0, const void *payload=NULL, int qos=0, bool retain=false);\n\tint publish_v5(int *mid, const char *topic, int payloadlen=0, const void *payload=NULL, int qos=0, bool retain=false, const mosquitto_property *properties=NULL);\n\tint subscribe(int *mid, const char *sub, int qos=0);\n\tint subscribe_v5(int *mid, const char *sub, int qos=0, int options=0, const mosquitto_property *properties=NULL);\n\tint unsubscribe(int *mid, const char *sub);\n\tint unsubscribe_v5(int *mid, const char *sub, const mosquitto_property *properties);\n\tvoid reconnect_delay_set(unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff);\n\tint max_inflight_messages_set(unsigned int max_inflight_messages);\n\tvoid message_retry_set(unsigned int message_retry);\n\tvoid user_data_set(void *userdata);\n\tint tls_set(const char *cafile, const char *capath=NULL, const char *certfile=NULL, const char *keyfile=NULL, int (*pw_callback)(char *buf, int size, int rwflag, void *userdata)=NULL);\n\tint ext_auth_continue(const char *auth_method, uint16_t auth_data_len=0, const void *auth_data=NULL, const mosquitto_property *properties=NULL);\n\tint tls_opts_set(int cert_reqs, const char *tls_version=NULL, const char *ciphers=NULL);\n\tint tls_insecure_set(bool value);\n\tint tls_psk_set(const char *psk, const char *identity, const char *ciphers=NULL);\n\tint opts_set(enum mosq_opt_t option, void *value);\n\tint int_option(enum mosq_opt_t option, int value);\n\tint string_option(enum mosq_opt_t option, const char *value);\n\tint void_option(enum mosq_opt_t option, void *value);\n\n\tint loop(int timeout=-1, int max_packets=1);\n\tint loop_misc();\n\tint loop_read(int max_packets=1);\n\tint loop_write(int max_packets=1);\n\tint loop_forever(int timeout=-1, int max_packets=1);\n\tint loop_start();\n\tint loop_stop(bool force=false);\n\tbool want_write();\n\tint threaded_set(bool threaded=true);\n\tint socks5_set(const char *host, int port=1080, const char *username=NULL, const char *password=NULL);\n\n\n\t// names in the functions commented to prevent unused parameter warning\n\tvirtual void MOSQ_USED on_pre_connect()\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_connect(int /*rc*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_connect_with_flags(int /*rc*/, int /*flags*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_connect_v5(int /*rc*/, int /*flags*/, const mosquitto_property * /*props*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_disconnect(int /*rc*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_disconnect_v5(int /*rc*/, const mosquitto_property * /*props*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_publish(int /*mid*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_publish_v5(int /*mid*/, int /*reason_code*/, const mosquitto_property * /*props*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_message(const struct mosquitto_message * /*message*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_message_v5(const struct mosquitto_message * /*message*/, const mosquitto_property * /*props*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_subscribe(int /*mid*/, int /*qos_count*/, const int * /*granted_qos*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_subscribe_v5(int /*mid*/, int /*qos_count*/, const int * /*granted_qos*/, const mosquitto_property * /*props*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_unsubscribe(int /*mid*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_unsubscribe_v5(int /*mid*/, const mosquitto_property * /*props*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_log(int /*level*/, const char * /*str*/)\n\t{return;}\n\n\n\tvirtual void MOSQ_USED on_error()\n\t{return;}\n\n\n\tvirtual int MOSQ_USED on_ext_auth(const char * /*auth_method*/, uint16_t /*auth_data_len*/, const void * /*auth_data*/, const mosquitto_property * /*props*/)\n\t{return MOSQ_ERR_AUTH;}\n};\n\n}\n#endif\n"
  },
  {
    "path": "include/mosquitto/mqtt_protocol.h",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MQTT_PROTOCOL_H\n#define MQTT_PROTOCOL_H\n\n#include <stdint.h>\n\n/*\n * File: mosquitto/mqtt_protocol.h\n *\n * This header contains definitions of MQTT values as defined in the specifications.\n */\n#define PROTOCOL_NAME_v31 \"MQIsdp\"\n#define PROTOCOL_VERSION_v31 3\n\n#define PROTOCOL_NAME \"MQTT\"\n\n#define PROTOCOL_VERSION_v311 4\n#define PROTOCOL_VERSION_v5 5\n\n\n/* Message types */\n#define CMD_RESERVED 0x00U\n#define CMD_CONNECT 0x10U\n#define CMD_CONNACK 0x20U\n#define CMD_PUBLISH 0x30U\n#define CMD_PUBACK 0x40U\n#define CMD_PUBREC 0x50U\n#define CMD_PUBREL 0x60U\n#define CMD_PUBCOMP 0x70U\n#define CMD_SUBSCRIBE 0x80U\n#define CMD_SUBACK 0x90U\n#define CMD_UNSUBSCRIBE 0xA0U\n#define CMD_UNSUBACK 0xB0U\n#define CMD_PINGREQ 0xC0U\n#define CMD_PINGRESP 0xD0U\n#define CMD_DISCONNECT 0xE0U\n#define CMD_AUTH 0xF0U\n\n/* Mosquitto only: for distinguishing CONNECT and WILL properties */\n#define CMD_WILL 0x100\n\n/* Enum: mqtt311_connack_codes\n *\n * The CONNACK results for MQTT v3.1.1, and v3.1.\n *\n * Values:\n *  CONNACK_ACCEPTED - 0\n *  CONNACK_REFUSED_PROTOCOL_VERSION - 1\n *  CONNACK_REFUSED_IDENTIFIER_REJECTED - 2\n *  CONNACK_REFUSED_SERVER_UNAVAILABLE - 3\n *  CONNACK_REFUSED_BAD_USERNAME_PASSWORD - 4\n *  CONNACK_REFUSED_NOT_AUTHORIZED - 5\n */\nenum mqtt311_connack_codes {\n\tCONNACK_ACCEPTED = 0,\n\tCONNACK_REFUSED_PROTOCOL_VERSION = 1,\n\tCONNACK_REFUSED_IDENTIFIER_REJECTED = 2,\n\tCONNACK_REFUSED_SERVER_UNAVAILABLE = 3,\n\tCONNACK_REFUSED_BAD_USERNAME_PASSWORD = 4,\n\tCONNACK_REFUSED_NOT_AUTHORIZED = 5,\n};\n\n/* Enum: mqtt5_return_codes\n * The reason codes returned in various MQTT commands.\n *\n * Values:\n *  MQTT_RC_SUCCESS - 0\n *  MQTT_RC_NORMAL_DISCONNECTION - 0\n *  MQTT_RC_GRANTED_QOS0 - 0\n *  MQTT_RC_GRANTED_QOS1 - 1\n *  MQTT_RC_GRANTED_QOS2 - 2\n *  MQTT_RC_DISCONNECT_WITH_WILL_MSG - 4\n *  MQTT_RC_NO_MATCHING_SUBSCRIBERS - 16\n *  MQTT_RC_NO_SUBSCRIPTION_EXISTED - 17\n *  MQTT_RC_CONTINUE_AUTHENTICATION - 24\n *  MQTT_RC_REAUTHENTICATE - 25\n *  MQTT_RC_UNSPECIFIED - 128\n *  MQTT_RC_MALFORMED_PACKET - 129\n *  MQTT_RC_PROTOCOL_ERROR - 130\n *  MQTT_RC_IMPLEMENTATION_SPECIFIC - 131\n *  MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION - 132\n *  MQTT_RC_CLIENTID_NOT_VALID - 133\n *  MQTT_RC_BAD_USERNAME_OR_PASSWORD - 134\n *  MQTT_RC_NOT_AUTHORIZED - 135\n *  MQTT_RC_SERVER_UNAVAILABLE - 136\n *  MQTT_RC_SERVER_BUSY - 137\n *  MQTT_RC_BANNED - 138\n *  MQTT_RC_SERVER_SHUTTING_DOWN - 139\n *  MQTT_RC_BAD_AUTHENTICATION_METHOD - 140\n *  MQTT_RC_KEEP_ALIVE_TIMEOUT - 141\n *  MQTT_RC_SESSION_TAKEN_OVER - 142\n *  MQTT_RC_TOPIC_FILTER_INVALID - 143\n *  MQTT_RC_TOPIC_NAME_INVALID - 144\n *  MQTT_RC_PACKET_ID_IN_USE - 145\n *  MQTT_RC_PACKET_ID_NOT_FOUND - 146\n *  MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED - 147\n *  MQTT_RC_TOPIC_ALIAS_INVALID - 148\n *  MQTT_RC_PACKET_TOO_LARGE - 149\n *  MQTT_RC_MESSAGE_RATE_TOO_HIGH - 150\n *  MQTT_RC_QUOTA_EXCEEDED - 151\n *  MQTT_RC_ADMINISTRATIVE_ACTION - 152\n *  MQTT_RC_PAYLOAD_FORMAT_INVALID - 153\n *  MQTT_RC_RETAIN_NOT_SUPPORTED - 154\n *  MQTT_RC_QOS_NOT_SUPPORTED - 155\n *  MQTT_RC_USE_ANOTHER_SERVER - 156\n *  MQTT_RC_SERVER_MOVED - 157\n *  MQTT_RC_SHARED_SUBS_NOT_SUPPORTED - 158\n *  MQTT_RC_CONNECTION_RATE_EXCEEDED - 159\n *  MQTT_RC_MAXIMUM_CONNECT_TIME - 160\n *  MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED - 161\n *  MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED - 162\n */\nenum mqtt5_return_codes {\n\tMQTT_RC_SUCCESS = 0,                        /* CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH */\n\tMQTT_RC_NORMAL_DISCONNECTION = 0,           /* DISCONNECT */\n\tMQTT_RC_GRANTED_QOS0 = 0,                   /* SUBACK */\n\tMQTT_RC_GRANTED_QOS1 = 1,                   /* SUBACK */\n\tMQTT_RC_GRANTED_QOS2 = 2,                   /* SUBACK */\n\tMQTT_RC_DISCONNECT_WITH_WILL_MSG = 4,       /* DISCONNECT */\n\tMQTT_RC_NO_MATCHING_SUBSCRIBERS = 16,       /* PUBACK, PUBREC */\n\tMQTT_RC_NO_SUBSCRIPTION_EXISTED = 17,       /* UNSUBACK */\n\tMQTT_RC_CONTINUE_AUTHENTICATION = 24,       /* AUTH */\n\tMQTT_RC_REAUTHENTICATE = 25,                /* AUTH */\n\n\tMQTT_RC_UNSPECIFIED = 128,                  /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */\n\tMQTT_RC_MALFORMED_PACKET = 129,             /* CONNACK, DISCONNECT */\n\tMQTT_RC_PROTOCOL_ERROR = 130,               /* DISCONNECT */\n\tMQTT_RC_IMPLEMENTATION_SPECIFIC = 131,      /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */\n\tMQTT_RC_UNSUPPORTED_PROTOCOL_VERSION = 132, /* CONNACK */\n\tMQTT_RC_CLIENTID_NOT_VALID = 133,           /* CONNACK */\n\tMQTT_RC_BAD_USERNAME_OR_PASSWORD = 134,     /* CONNACK */\n\tMQTT_RC_NOT_AUTHORIZED = 135,               /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */\n\tMQTT_RC_SERVER_UNAVAILABLE = 136,           /* CONNACK */\n\tMQTT_RC_SERVER_BUSY = 137,                  /* CONNACK, DISCONNECT */\n\tMQTT_RC_BANNED = 138,                       /* CONNACK */\n\tMQTT_RC_SERVER_SHUTTING_DOWN = 139,         /* DISCONNECT */\n\tMQTT_RC_BAD_AUTHENTICATION_METHOD = 140,    /* CONNACK */\n\tMQTT_RC_KEEP_ALIVE_TIMEOUT = 141,           /* DISCONNECT */\n\tMQTT_RC_SESSION_TAKEN_OVER = 142,           /* DISCONNECT */\n\tMQTT_RC_TOPIC_FILTER_INVALID = 143,         /* SUBACK, UNSUBACK, DISCONNECT */\n\tMQTT_RC_TOPIC_NAME_INVALID = 144,           /* CONNACK, PUBACK, PUBREC, DISCONNECT */\n\tMQTT_RC_PACKET_ID_IN_USE = 145,             /* PUBACK, SUBACK, UNSUBACK */\n\tMQTT_RC_PACKET_ID_NOT_FOUND = 146,          /* PUBREL, PUBCOMP */\n\tMQTT_RC_RECEIVE_MAXIMUM_EXCEEDED = 147,     /* DISCONNECT */\n\tMQTT_RC_TOPIC_ALIAS_INVALID = 148,          /* DISCONNECT */\n\tMQTT_RC_PACKET_TOO_LARGE = 149,             /* CONNACK, PUBACK, PUBREC, DISCONNECT */\n\tMQTT_RC_MESSAGE_RATE_TOO_HIGH = 150,        /* DISCONNECT */\n\tMQTT_RC_QUOTA_EXCEEDED = 151,               /* PUBACK, PUBREC, SUBACK, DISCONNECT */\n\tMQTT_RC_ADMINISTRATIVE_ACTION = 152,        /* DISCONNECT */\n\tMQTT_RC_PAYLOAD_FORMAT_INVALID = 153,       /* CONNACK, PUBACK, PUBREC, DISCONNECT */\n\tMQTT_RC_RETAIN_NOT_SUPPORTED = 154,         /* CONNACK, DISCONNECT */\n\tMQTT_RC_QOS_NOT_SUPPORTED = 155,            /* CONNACK, DISCONNECT */\n\tMQTT_RC_USE_ANOTHER_SERVER = 156,           /* CONNACK, DISCONNECT */\n\tMQTT_RC_SERVER_MOVED = 157,                 /* CONNACK, DISCONNECT */\n\tMQTT_RC_SHARED_SUBS_NOT_SUPPORTED = 158,    /* SUBACK, DISCONNECT */\n\tMQTT_RC_CONNECTION_RATE_EXCEEDED = 159,     /* CONNACK, DISCONNECT */\n\tMQTT_RC_MAXIMUM_CONNECT_TIME = 160,         /* DISCONNECT */\n\tMQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED = 161, /* SUBACK, DISCONNECT */\n\tMQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED = 162,  /* SUBACK, DISCONNECT */\n};\n\n/* Enum: mqtt5_property\n * Options for use with MQTTv5 properties.\n * Options:\n *\n *\tMQTT_PROP_PAYLOAD_FORMAT_INDICATOR - property option.\n *\tMQTT_PROP_MESSAGE_EXPIRY_INTERVAL - property option.\n *\tMQTT_PROP_CONTENT_TYPE - property option.\n *\tMQTT_PROP_RESPONSE_TOPIC - property option.\n *\tMQTT_PROP_CORRELATION_DATA - property option.\n *\tMQTT_PROP_SUBSCRIPTION_IDENTIFIER - property option.\n *\tMQTT_PROP_SESSION_EXPIRY_INTERVAL - property option.\n *\tMQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER - property option.\n *\tMQTT_PROP_SERVER_KEEP_ALIVE - property option.\n *\tMQTT_PROP_AUTHENTICATION_METHOD - property option.\n *\tMQTT_PROP_AUTHENTICATION_DATA - property option.\n *\tMQTT_PROP_REQUEST_PROBLEM_INFORMATION - property option.\n *\tMQTT_PROP_WILL_DELAY_INTERVAL - property option.\n *\tMQTT_PROP_REQUEST_RESPONSE_INFORMATION - property option.\n *\tMQTT_PROP_RESPONSE_INFORMATION - property option.\n *\tMQTT_PROP_SERVER_REFERENCE - property option.\n *\tMQTT_PROP_REASON_STRING - property option.\n *\tMQTT_PROP_RECEIVE_MAXIMUM - property option.\n *\tMQTT_PROP_TOPIC_ALIAS_MAXIMUM - property option.\n *\tMQTT_PROP_TOPIC_ALIAS - property option.\n *\tMQTT_PROP_MAXIMUM_QOS - property option.\n *\tMQTT_PROP_RETAIN_AVAILABLE - property option.\n *\tMQTT_PROP_USER_PROPERTY - property option.\n *\tMQTT_PROP_MAXIMUM_PACKET_SIZE - property option.\n *\tMQTT_PROP_WILDCARD_SUB_AVAILABLE - property option.\n *\tMQTT_PROP_SUBSCRIPTION_ID_AVAILABLE - property option.\n *\tMQTT_PROP_SHARED_SUB_AVAILABLE - property option.\n */\nenum mqtt5_property {\n\tMQTT_PROP_PAYLOAD_FORMAT_INDICATOR = 1,     /* Byte :\t\t\t\tPUBLISH, Will Properties */\n\tMQTT_PROP_MESSAGE_EXPIRY_INTERVAL = 2,      /* 4 byte int :\t\t\tPUBLISH, Will Properties */\n\tMQTT_PROP_CONTENT_TYPE = 3,                 /* UTF-8 string :\t\tPUBLISH, Will Properties */\n\tMQTT_PROP_RESPONSE_TOPIC = 8,               /* UTF-8 string :\t\tPUBLISH, Will Properties */\n\tMQTT_PROP_CORRELATION_DATA = 9,             /* Binary Data :\t\tPUBLISH, Will Properties */\n\tMQTT_PROP_SUBSCRIPTION_IDENTIFIER = 11,     /* Variable byte int :\tPUBLISH, SUBSCRIBE */\n\tMQTT_PROP_SESSION_EXPIRY_INTERVAL = 17,     /* 4 byte int :\t\t\tCONNECT, CONNACK, DISCONNECT */\n\tMQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER = 18,  /* UTF-8 string :\t\tCONNACK */\n\tMQTT_PROP_SERVER_KEEP_ALIVE = 19,           /* 2 byte int :\t\t\tCONNACK */\n\tMQTT_PROP_AUTHENTICATION_METHOD = 21,       /* UTF-8 string :\t\tCONNECT, CONNACK, AUTH */\n\tMQTT_PROP_AUTHENTICATION_DATA = 22,         /* Binary Data :\t\tCONNECT, CONNACK, AUTH */\n\tMQTT_PROP_REQUEST_PROBLEM_INFORMATION = 23, /* Byte :\t\t\t\tCONNECT */\n\tMQTT_PROP_WILL_DELAY_INTERVAL = 24,         /* 4 byte int :\t\t\tWill properties */\n\tMQTT_PROP_REQUEST_RESPONSE_INFORMATION = 25,/* Byte :\t\t\t\tCONNECT */\n\tMQTT_PROP_RESPONSE_INFORMATION = 26,        /* UTF-8 string :\t\tCONNACK */\n\tMQTT_PROP_SERVER_REFERENCE = 28,            /* UTF-8 string :\t\tCONNACK, DISCONNECT */\n\tMQTT_PROP_REASON_STRING = 31,               /* UTF-8 string :\t\tAll except Will properties */\n\tMQTT_PROP_RECEIVE_MAXIMUM = 33,             /* 2 byte int :\t\t\tCONNECT, CONNACK */\n\tMQTT_PROP_TOPIC_ALIAS_MAXIMUM = 34,         /* 2 byte int :\t\t\tCONNECT, CONNACK */\n\tMQTT_PROP_TOPIC_ALIAS = 35,                 /* 2 byte int :\t\t\tPUBLISH */\n\tMQTT_PROP_MAXIMUM_QOS = 36,                 /* Byte :\t\t\t\tCONNACK */\n\tMQTT_PROP_RETAIN_AVAILABLE = 37,            /* Byte :\t\t\t\tCONNACK */\n\tMQTT_PROP_USER_PROPERTY = 38,               /* UTF-8 string pair :\tAll */\n\tMQTT_PROP_MAXIMUM_PACKET_SIZE = 39,         /* 4 byte int :\t\t\tCONNECT, CONNACK */\n\tMQTT_PROP_WILDCARD_SUB_AVAILABLE = 40,      /* Byte :\t\t\t\tCONNACK */\n\tMQTT_PROP_SUBSCRIPTION_ID_AVAILABLE = 41,   /* Byte :\t\t\t\tCONNACK */\n\tMQTT_PROP_SHARED_SUB_AVAILABLE = 42,        /* Byte :\t\t\t\tCONNACK */\n};\n\nenum mqtt5_property_type {\n\tMQTT_PROP_TYPE_BYTE = 1,\n\tMQTT_PROP_TYPE_INT16 = 2,\n\tMQTT_PROP_TYPE_INT32 = 3,\n\tMQTT_PROP_TYPE_VARINT = 4,\n\tMQTT_PROP_TYPE_BINARY = 5,\n\tMQTT_PROP_TYPE_STRING = 6,\n\tMQTT_PROP_TYPE_STRING_PAIR = 7,\n};\n\n/* Enum: mqtt5_sub_options\n * Options for use with MQTTv5 subscriptions.\n *\n * MQTT_SUB_OPT_NO_LOCAL - with this option set, if this client publishes to\n * a topic to which it is subscribed, the broker will not publish the\n * message back to the client.\n *\n * MQTT_SUB_OPT_RETAIN_AS_PUBLISHED - with this option set, messages\n * published for this subscription will keep the retain flag as was set by\n * the publishing client. The default behaviour without this option set has\n * the retain flag indicating whether a message is fresh/stale.\n *\n * MQTT_SUB_OPT_SEND_RETAIN_ALWAYS - with this option set, pre-existing\n * retained messages are sent as soon as the subscription is made, even\n * if the subscription already exists. This is the default behaviour, so\n * it is not necessary to set this option.\n *\n * MQTT_SUB_OPT_SEND_RETAIN_NEW - with this option set, pre-existing retained\n * messages for this subscription will be sent when the subscription is made,\n * but only if the subscription does not already exist.\n *\n * MQTT_SUB_OPT_SEND_RETAIN_NEVER - with this option set, pre-existing\n * retained messages will never be sent for this subscription.\n */\nenum mqtt5_sub_options {\n\tMQTT_SUB_OPT_NO_LOCAL = 0x04,\n\tMQTT_SUB_OPT_RETAIN_AS_PUBLISHED = 0x08,\n\tMQTT_SUB_OPT_SEND_RETAIN_ALWAYS = 0x00,\n\tMQTT_SUB_OPT_SEND_RETAIN_NEW = 0x10,\n\tMQTT_SUB_OPT_SEND_RETAIN_NEVER = 0x20,\n};\n\n#define MQTT_MAX_PAYLOAD 268435455U\n\n#define MQTT_SUB_OPT_GET_QOS(opt) ((opt) & 0x03)\n#define MQTT_SUB_OPT_GET_NO_LOCAL(opt) ((opt)&MQTT_SUB_OPT_NO_LOCAL)\n#define MQTT_SUB_OPT_GET_RETAIN_AS_PUBLISHED(opt) ((opt)&MQTT_SUB_OPT_RETAIN_AS_PUBLISHED)\n#define MQTT_SUB_OPT_GET_SEND_RETAIN(opt) ((opt) & (MQTT_SUB_OPT_SEND_RETAIN_NEW | MQTT_SUB_OPT_SEND_RETAIN_NEVER))\n#define MQTT_SUB_OPT_GET_RETAIN_HANDLING(opt) ((opt) & (MQTT_SUB_OPT_SEND_RETAIN_NEW | MQTT_SUB_OPT_SEND_RETAIN_NEVER))\n\n#define MQTT_SUB_OPT_SET_QOS(opt, qos) ((opt) = ((opt) & 0xFC) | ((qos) & 0x03))\n#define MQTT_SUB_OPT_SET_NO_LOCAL(opt, qos) ((opt) = ((opt) & 0xFC) | ((qos) & 0x03))\n#define MQTT_SUB_OPT_SET_RETAIN_HANDLING(opt, rh) ((opt) = ((opt) & 0xCF) | ((rh) & 0x30))\n\n#define MQTT_SUB_OPT_SET(opt, val) ((opt) |= val)\n#define MQTT_SUB_OPT_CLEAR(opt, val) ((opt) = (opt) & !val)\n\n#define MQTT_SESSION_EXPIRY_IMMEDIATE 0\n#define MQTT_SESSION_EXPIRY_NEVER UINT32_MAX\n#endif\n"
  },
  {
    "path": "include/mosquitto.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef MOSQUITTO_H\n#define MOSQUITTO_H\n\n#ifndef _MSC_VER\n#  define MOSQ_USED __attribute__((used))\n#else\n#  define MOSQ_USED\n#endif\n\n#include <mosquitto/mqtt_protocol.h>\n\n#include <mosquitto/libmosquitto.h>\n#include <mosquitto/libcommon.h>\n\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_control.h>\n#include <mosquitto/broker_plugin.h>\n\n#endif\n"
  },
  {
    "path": "include/mosquitto_broker.h",
    "content": "#include <mosquitto/broker.h>\n#warning \"Please replace '#include <mosquitto_broker.h> with #include <mosquitto.h>\"\n"
  },
  {
    "path": "include/mosquitto_plugin.h",
    "content": "#include <mosquitto/broker_plugin.h>\n#warning \"Please replace '#include <broker_plugin.h> with #include <mosquitto.h>\"\n"
  },
  {
    "path": "include/mosquittopp.h",
    "content": "#include <mosquitto/libmosquittopp.h>\n#warning \"Please replace '#include <mosquittopp.h> with #include <mosquitto/libmosquittopp.h>\"\n"
  },
  {
    "path": "include/mqtt_protocol.h",
    "content": "#include <mosquitto/mqtt_protocol.h>\n#warning \"Please replace '#include <mqtt_protocol.h> with #include <mosquitto.h>\"\n"
  },
  {
    "path": "installer/mosquitto.nsi",
    "content": "; NSIS installer script for mosquitto\nUnicode True\nSetCompressor /SOLID lzma\n\n!include \"MUI2.nsh\"\n!include \"nsDialogs.nsh\"\n!include \"LogicLib.nsh\"\n\n; For environment variable code\n!include \"WinMessages.nsh\"\n!define env_hklm 'HKLM \"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\"'\n\nName \"Eclipse Mosquitto\"\n!define VERSION 2.1.2\nOutFile \"mosquitto-${VERSION}-install-windows-x86.exe\"\n\nInstallDir \"$PROGRAMFILES\\Mosquitto\"\n\n;--------------------------------\n; Installer pages\n!insertmacro MUI_PAGE_WELCOME\n\n!insertmacro MUI_PAGE_COMPONENTS\n!insertmacro MUI_PAGE_DIRECTORY\n!insertmacro MUI_PAGE_INSTFILES\n!insertmacro MUI_PAGE_FINISH\n\n\n;--------------------------------\n; Uninstaller pages\n!insertmacro MUI_UNPAGE_WELCOME\n!insertmacro MUI_UNPAGE_CONFIRM\n!insertmacro MUI_UNPAGE_INSTFILES\n!insertmacro MUI_UNPAGE_FINISH\n\n;--------------------------------\n; Languages\n!insertmacro MUI_LANGUAGE \"English\"\n\n;--------------------------------\n; Installer sections\n\nSection \"Files\" SecInstall\n\tSectionIn RO\n\n\tExecWait 'sc stop mosquitto'\n\tSleep 1000\n\n\tSetOutPath \"$INSTDIR\"\n\tFile \"..\\logo\\mosquitto.ico\"\n\tFile \"..\\build\\src\\Release\\mosquitto.exe\"\n\tFile \"..\\build\\apps\\db_dump\\Release\\mosquitto_db_dump.exe\"\n\tFile \"..\\build\\apps\\mosquitto_ctrl\\Release\\mosquitto_ctrl.exe\"\n\tFile \"..\\build\\apps\\mosquitto_passwd\\Release\\mosquitto_passwd.exe\"\n\tFile \"..\\build\\apps\\mosquitto_signal\\Release\\mosquitto_signal.exe\"\n\tFile \"..\\build\\client\\Release\\mosquitto_pub.exe\"\n\tFile \"..\\build\\client\\Release\\mosquitto_sub.exe\"\n\tFile \"..\\build\\client\\Release\\mosquitto_rr.exe\"\n\tFile \"..\\build\\libcommon\\Release\\mosquitto_common.dll\"\n\tFile \"..\\build\\lib\\Release\\mosquitto.dll\"\n\tFile \"..\\build\\lib\\cpp\\Release\\mosquittopp.dll\"\n\tFile \"..\\build\\plugins\\acl-file\\Release\\mosquitto_acl_file.dll\"\n\tFile \"..\\build\\plugins\\dynamic-security\\Release\\mosquitto_dynamic_security.dll\"\n\tFile \"..\\build\\plugins\\password-file\\Release\\mosquitto_password_file.dll\"\n\tFile \"..\\build\\plugins\\persist-sqlite\\Release\\mosquitto_persist_sqlite.dll\"\n\tFile \"..\\build\\plugins\\sparkplug-aware\\Release\\mosquitto_sparkplug_aware.dll\"\n\tFile \"..\\aclfile.example\"\n\tFile \"..\\ChangeLog.txt\"\n\tFile \"..\\NOTICE.md\"\n\tFile \"..\\pskfile.example\"\n\tFile \"..\\pwfile.example\"\n\tFile \"..\\README.md\"\n\tFile \"..\\README-windows.txt\"\n\tFile \"..\\README-letsencrypt.md\"\n\tFile \"..\\SECURITY.md\"\n\tFile \"..\\edl-v10\"\n\tFile \"..\\epl-v20\"\n\n\tSetOverwrite off\n\tFile \"..\\mosquitto.conf\"\n\tSetOverwrite on\n\n\tFile \"..\\build\\vcpkg_installed\\x86-windows\\bin\\cjson.dll\"\n\tFile \"..\\build\\vcpkg_installed\\x86-windows\\bin\\libcrypto-3.dll\"\n\tFile \"..\\build\\vcpkg_installed\\x86-windows\\bin\\libmicrohttpd-dll.dll\"\n\tFile \"..\\build\\vcpkg_installed\\x86-windows\\bin\\libssl-3.dll\"\n\tFile \"..\\build\\vcpkg_installed\\x86-windows\\bin\\pthreadVC3.dll\"\n\tFile \"..\\build\\vcpkg_installed\\x86-windows\\bin\\sqlite3.dll\"\n\n\tSetOutPath \"$INSTDIR\\devel\"\n\tFile \"..\\build\\lib\\Release\\mosquitto.lib\"\n\tFile \"..\\build\\lib\\cpp\\Release\\mosquittopp.lib\"\n\tFile \"..\\build\\src\\Release\\mosquitto_broker.lib\"\n\tFile \"..\\include\\mosquitto.h\"\n\tFile \"..\\include\\mosquitto_broker.h\"\n\tFile \"..\\include\\mosquitto_plugin.h\"\n\tFile \"..\\include\\mosquittopp.h\"\n\tfile \"..\\include\\mqtt_protocol.h\"\n\n\tSetOutPath \"$INSTDIR\\devel\\mosquitto\"\n\tFile \"..\\include\\mosquitto\\broker.h\"\n\tFile \"..\\include\\mosquitto\\broker_control.h\"\n\tFile \"..\\include\\mosquitto\\broker_plugin.h\"\n\tFile \"..\\include\\mosquitto\\defs.h\"\n\tFile \"..\\include\\mosquitto\\libcommon.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_base64.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_cjson.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_file.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_memory.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_password.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_properties.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_random.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_string.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_string.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_time.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_topic.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_utf8.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_auth.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_callbacks.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_connect.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_create_delete.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_helpers.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_loop.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_message.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_options.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_publish.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_socks.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_subscribe.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_tls.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_unsubscribe.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_will.h\"\n\tFile \"..\\include\\mosquitto\\libmosquittopp.h\"\n\tFile \"..\\include\\mosquitto\\mqtt_protocol.h\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\"\n\tFile \"..\\dashboard\\src\\index.html\"\n\tFile \"..\\dashboard\\src\\listeners.html\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\app\"\n\tFile \"..\\dashboard\\src\\app\\consts.js\"\n\tFile \"..\\dashboard\\src\\app\\dashboard.js\"\n\tFile \"..\\dashboard\\src\\app\\index.js\"\n\tFile \"..\\dashboard\\src\\app\\listeners.js\"\n\tFile \"..\\dashboard\\src\\app\\sidebar.js\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\css\"\n\tFile \"..\\dashboard\\src\\css\\styles.css\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\lib\"\n\tFile \"..\\dashboard\\src\\lib\\chart.umd.js\"\n\tFile \"..\\dashboard\\src\\lib\\chartjs-plugin-zoom.min.js\"\n\tFile \"..\\dashboard\\src\\lib\\hammer.min.js\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\media\"\n\tFile \"..\\dashboard\\src\\media\\banner.svg\"\n\tFile \"..\\dashboard\\src\\media\\favicon-16x16.png\"\n\tFile \"..\\dashboard\\src\\media\\favicon-32x32.png\"\n\tFile \"..\\dashboard\\src\\media\\mosquitto-logo.png\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\tailwind\"\n\tFile \"..\\dashboard\\src\\tailwind.config.js\"\n\tFile \"..\\dashboard\\src\\tailwind\\styles.css\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\utils\"\n\tFile \"..\\dashboard\\src\\utils\\assert.js\"\n\tFile \"..\\dashboard\\src\\utils\\queue.js\"\n\tFile \"..\\dashboard\\src\\utils\\utils.js\"\n\n\tWriteUninstaller \"$INSTDIR\\Uninstall.exe\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto\" \"DisplayName\" \"Eclipse Mosquitto MQTT broker (32 bit)\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto\" \"DisplayIcon\" \"$INSTDIR\\mosquitto.ico\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto\" \"UninstallString\" \"$\\\"$INSTDIR\\Uninstall.exe$\\\"\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto\" \"QuietUninstallString\" \"$\\\"$INSTDIR\\Uninstall.exe$\\\" /S\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto\" \"HelpLink\" \"https://mosquitto.org/\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto\" \"URLInfoAbout\" \"https://mosquitto.org/\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto\" \"DisplayVersion\" \"${VERSION}\"\n\tWriteRegDWORD HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto\" \"NoModify\" \"1\"\n\tWriteRegDWORD HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto\" \"NoRepair\" \"1\"\n\n\tWriteRegExpandStr ${env_hklm} MOSQUITTO_DIR $INSTDIR\n\tSendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 \"STR:Environment\" /TIMEOUT=5000\nSectionEnd\n\nSection \"Visual Studio Runtime\"\n  SetOutPath \"$INSTDIR\"\n  File \"VC_redist.x86.exe\"\n  ExecWait '\"$INSTDIR\\VC_redist.x86.exe\" /quiet /norestart'\n  Delete \"$INSTDIR\\VC_redist.x86.exe\"\nSectionEnd\n\nSection \"Service\" SecService\n\tExecWait '\"$INSTDIR\\mosquitto.exe\" install'\n\tExecWait 'sc start mosquitto'\nSectionEnd\n\nSection \"Uninstall\"\n\tExecWait 'sc stop mosquitto'\n\tSleep 1000\n\tExecWait '\"$INSTDIR\\mosquitto.exe\" uninstall'\n\tSleep 1000\n\n\tDelete \"$INSTDIR\\mosquitto.dll\"\n\tDelete \"$INSTDIR\\mosquitto.exe\"\n\tDelete \"$INSTDIR\\mosquitto_common.dll\"\n\tDelete \"$INSTDIR\\mosquitto_ctrl.exe\"\n\tDelete \"$INSTDIR\\mosquitto_db_dump.exe\"\n\tDelete \"$INSTDIR\\mosquitto_passwd.exe\"\n\tDelete \"$INSTDIR\\mosquitto_pub.exe\"\n\tDelete \"$INSTDIR\\mosquitto_rr.exe\"\n\tDelete \"$INSTDIR\\mosquitto_signal.exe\"\n\tDelete \"$INSTDIR\\mosquitto_sub.exe\"\n\tDelete \"$INSTDIR\\mosquittopp.dll\"\n\tDelete \"$INSTDIR\\mosquitto_acl_file.dll\"\n\tDelete \"$INSTDIR\\mosquitto_dynamic_security.dll\"\n\tDelete \"$INSTDIR\\mosquitto_password_file.dll\"\n\tDelete \"$INSTDIR\\mosquitto_persist_sqlite.dll\"\n\tDelete \"$INSTDIR\\mosquitto_sparkplug_aware.dll\"\n\tDelete \"$INSTDIR\\aclfile.example\"\n\tDelete \"$INSTDIR\\ChangeLog.txt\"\n\tDelete \"$INSTDIR\\mosquitto.conf\"\n\tDelete \"$INSTDIR\\pskfile.example\"\n\tDelete \"$INSTDIR\\pwfile.example\"\n\tDelete \"$INSTDIR\\NOTICE.md\"\n\tDelete \"$INSTDIR\\README.md\"\n\tDelete \"$INSTDIR\\README-windows.txt\"\n\tDelete \"$INSTDIR\\README-letsencrypt.md\"\n\tDelete \"$INSTDIR\\SECURITY.md\"\n\tDelete \"$INSTDIR\\edl-v10\"\n\tDelete \"$INSTDIR\\epl-v20\"\n\tDelete \"$INSTDIR\\mosquitto.ico\"\n\n\tDelete \"$INSTDIR\\argon2.dll\"\n\tDelete \"$INSTDIR\\cjson.dll\"\n\tDelete \"$INSTDIR\\libcrypto-3.dll\"\n\tDelete \"$INSTDIR\\libmicrohttpd-dll.dll\"\n\tDelete \"$INSTDIR\\libssl-3.dll\"\n\tDelete \"$INSTDIR\\pthreadVC3.dll\"\n\tDelete \"$INSTDIR\\sqlite3.dll\"\n\n\tDelete \"$INSTDIR\\devel\\mosquitto.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\broker.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\broker_control.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\broker_plugin.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\defs.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_base64.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_cjson.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_file.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_memory.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_password.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_properties.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_random.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_string.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_time.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_topic.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_utf8.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_auth.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_callbacks.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_connect.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_create_delete.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_helpers.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_loop.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_message.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_options.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_publish.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_socks.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_subscribe.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_tls.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_unsubscribe.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_will.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquittopp.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\mqtt_protocol.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto_broker.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto_plugin.h\"\n\tDelete \"$INSTDIR\\devel\\mosquittopp.h\"\n\tDelete \"$INSTDIR\\devel\\mqtt_protocol.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto.lib\"\n\tDelete \"$INSTDIR\\devel\\mosquitto_broker.lib\"\n\tDelete \"$INSTDIR\\devel\\mosquittopp.lib\"\n\tRMDir \"$INSTDIR\\devel\\mosquitto\"\n\tRMDir \"$INSTDIR\\devel\"\n\n\tDelete \"$INSTDIR\\dashboard\\app\\consts.js\"\n\tDelete \"$INSTDIR\\dashboard\\app\\dashboard.js\"\n\tDelete \"$INSTDIR\\dashboard\\app\\index.js\"\n\tDelete \"$INSTDIR\\dashboard\\app\\listeners.js\"\n\tDelete \"$INSTDIR\\dashboard\\app\\sidebar.js\"\n\tRMDir \"$INSTDIR\\dashboard\\app\"\n\tDelete \"$INSTDIR\\dashboard\\css\\styles.css\"\n\tRMDir \"$INSTDIR\\dashboard\\css\"\n\tDelete \"$INSTDIR\\dashboard\\index.html\"\n\tDelete \"$INSTDIR\\dashboard\\lib\\chart.umd.js\"\n\tDelete \"$INSTDIR\\dashboard\\lib\\chartjs-plugin-zoom.min.js\"\n\tDelete \"$INSTDIR\\dashboard\\lib\\hammer.min.js\"\n\tRMDir \"$INSTDIR\\dashboard\\lib\"\n\tDelete \"$INSTDIR\\dashboard\\listeners.html\"\n\tDelete \"$INSTDIR\\dashboard\\media\\banner.svg\"\n\tDelete \"$INSTDIR\\dashboard\\media\\favicon-16x16.png\"\n\tDelete \"$INSTDIR\\dashboard\\media\\favicon-32x32.png\"\n\tDelete \"$INSTDIR\\dashboard\\media\\mosquitto-logo.png\"\n\tRMDir \"$INSTDIR\\dashboard\\media\"\n\tDelete \"$INSTDIR\\dashboard\\tailwind.config.js\"\n\tDelete \"$INSTDIR\\dashboard\\tailwind\\styles.css\"\n\tRMDir \"$INSTDIR\\dashboard\\tailwind\"\n\tDelete \"$INSTDIR\\dashboard\\utils\\assert.js\"\n\tDelete \"$INSTDIR\\dashboard\\utils\\queue.js\"\n\tDelete \"$INSTDIR\\dashboard\\utils\\utils.js\"\n\tRMDir \"$INSTDIR\\dashboard\\utils\"\n\tRMDir \"$INSTDIR\\dashboard\"\n\n\tDelete \"$INSTDIR\\Uninstall.exe\"\n\tRMDir \"$INSTDIR\"\n\tDeleteRegKey HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto\"\n\n\tDeleteRegValue ${env_hklm} MOSQUITTO_DIR\n\tSendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 \"STR:Environment\" /TIMEOUT=5000\nSectionEnd\n\nLangString DESC_SecInstall ${LANG_ENGLISH} \"The main installation.\"\nLangString DESC_SecService ${LANG_ENGLISH} \"Install mosquitto as a Windows service?\"\n\n!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n\t!insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall)\n\t!insertmacro MUI_DESCRIPTION_TEXT ${SecService} $(DESC_SecService)\n!insertmacro MUI_FUNCTION_DESCRIPTION_END\n"
  },
  {
    "path": "installer/mosquitto64.nsi",
    "content": "; NSIS installer script for mosquitto\nUnicode True\nSetCompressor /SOLID lzma\n\n!include \"MUI2.nsh\"\n!include \"nsDialogs.nsh\"\n!include \"LogicLib.nsh\"\n\n; For environment variable code\n!include \"WinMessages.nsh\"\n!define env_hklm 'HKLM \"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\"'\n\nName \"Eclipse Mosquitto\"\n!define VERSION 2.1.2\nOutFile \"mosquitto-${VERSION}-install-windows-x64.exe\"\n\n!include \"x64.nsh\"\nInstallDir \"$PROGRAMFILES64\\Mosquitto\"\n\n;--------------------------------\n; Installer pages\n!insertmacro MUI_PAGE_WELCOME\n\n!insertmacro MUI_PAGE_COMPONENTS\n!insertmacro MUI_PAGE_DIRECTORY\n!insertmacro MUI_PAGE_INSTFILES\n!insertmacro MUI_PAGE_FINISH\n\n\n;--------------------------------\n; Uninstaller pages\n!insertmacro MUI_UNPAGE_WELCOME\n!insertmacro MUI_UNPAGE_CONFIRM\n!insertmacro MUI_UNPAGE_INSTFILES\n!insertmacro MUI_UNPAGE_FINISH\n\n;--------------------------------\n; Languages\n!insertmacro MUI_LANGUAGE \"English\"\n\n;--------------------------------\n; Installer sections\n\nSection \"Files\" SecInstall\n\tSectionIn RO\n\n\tExecWait 'sc stop mosquitto'\n\tSleep 1000\n\n\tSetOutPath \"$INSTDIR\"\n\tFile \"..\\logo\\mosquitto.ico\"\n\tFile \"..\\build64\\src\\Release\\mosquitto.exe\"\n\tFile \"..\\build64\\apps\\db_dump\\Release\\mosquitto_db_dump.exe\"\n\tFile \"..\\build64\\apps\\mosquitto_ctrl\\Release\\mosquitto_ctrl.exe\"\n\tFile \"..\\build64\\apps\\mosquitto_passwd\\Release\\mosquitto_passwd.exe\"\n\tFile \"..\\build64\\apps\\mosquitto_signal\\Release\\mosquitto_signal.exe\"\n\tFile \"..\\build64\\client\\Release\\mosquitto_pub.exe\"\n\tFile \"..\\build64\\client\\Release\\mosquitto_sub.exe\"\n\tFile \"..\\build64\\client\\Release\\mosquitto_rr.exe\"\n\tFile \"..\\build64\\libcommon\\Release\\mosquitto_common.dll\"\n\tFile \"..\\build64\\lib\\Release\\mosquitto.dll\"\n\tFile \"..\\build64\\lib\\cpp\\Release\\mosquittopp.dll\"\n\tFile \"..\\build64\\plugins\\acl-file\\Release\\mosquitto_acl_file.dll\"\n\tFile \"..\\build64\\plugins\\dynamic-security\\Release\\mosquitto_dynamic_security.dll\"\n\tFile \"..\\build64\\plugins\\password-file\\Release\\mosquitto_password_file.dll\"\n\tFile \"..\\build64\\plugins\\persist-sqlite\\Release\\mosquitto_persist_sqlite.dll\"\n\tFile \"..\\build64\\plugins\\sparkplug-aware\\Release\\mosquitto_sparkplug_aware.dll\"\n\tFile \"..\\aclfile.example\"\n\tFile \"..\\ChangeLog.txt\"\n\tFile \"..\\NOTICE.md\"\n\tFile \"..\\pskfile.example\"\n\tFile \"..\\pwfile.example\"\n\tFile \"..\\README.md\"\n\tFile \"..\\README-windows.txt\"\n\tFile \"..\\README-letsencrypt.md\"\n\tFile \"..\\SECURITY.md\"\n\tFile \"..\\edl-v10\"\n\tFile \"..\\epl-v20\"\n\n\tSetOverwrite off\n\tFile \"..\\mosquitto.conf\"\n\tSetOverwrite on\n\n\tFile \"..\\build64\\vcpkg_installed\\x64-windows-release\\bin\\cjson.dll\"\n\tFile \"..\\build64\\vcpkg_installed\\x64-windows-release\\bin\\libcrypto-3-x64.dll\"\n\tFile \"..\\build64\\vcpkg_installed\\x64-windows-release\\bin\\libmicrohttpd-dll.dll\"\n\tFile \"..\\build64\\vcpkg_installed\\x64-windows-release\\bin\\libssl-3-x64.dll\"\n\tFile \"..\\build64\\vcpkg_installed\\x64-windows-release\\bin\\pthreadVC3.dll\"\n\tFile \"..\\build64\\vcpkg_installed\\x64-windows-release\\bin\\sqlite3.dll\"\n\n\tSetOutPath \"$INSTDIR\\devel\"\n\tFile \"..\\build64\\lib\\Release\\mosquitto.lib\"\n\tFile \"..\\build64\\lib\\cpp\\Release\\mosquittopp.lib\"\n\tFile \"..\\build64\\src\\Release\\mosquitto_broker.lib\"\n\tFile \"..\\include\\mosquitto.h\"\n\tFile \"..\\include\\mosquitto_broker.h\"\n\tFile \"..\\include\\mosquitto_plugin.h\"\n\tFile \"..\\include\\mosquittopp.h\"\n\tfile \"..\\include\\mqtt_protocol.h\"\n\n\tSetOutPath \"$INSTDIR\\devel\\mosquitto\"\n\tFile \"..\\include\\mosquitto\\broker.h\"\n\tFile \"..\\include\\mosquitto\\broker_control.h\"\n\tFile \"..\\include\\mosquitto\\broker_plugin.h\"\n\tFile \"..\\include\\mosquitto\\defs.h\"\n\tFile \"..\\include\\mosquitto\\libcommon.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_base64.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_cjson.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_file.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_memory.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_password.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_properties.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_random.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_string.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_string.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_time.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_topic.h\"\n\tFile \"..\\include\\mosquitto\\libcommon_utf8.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_auth.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_callbacks.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_connect.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_create_delete.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_helpers.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_loop.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_message.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_options.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_publish.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_socks.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_subscribe.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_tls.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_unsubscribe.h\"\n\tFile \"..\\include\\mosquitto\\libmosquitto_will.h\"\n\tFile \"..\\include\\mosquitto\\libmosquittopp.h\"\n\tFile \"..\\include\\mosquitto\\mqtt_protocol.h\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\"\n\tFile \"..\\dashboard\\src\\index.html\"\n\tFile \"..\\dashboard\\src\\listeners.html\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\app\"\n\tFile \"..\\dashboard\\src\\app\\consts.js\"\n\tFile \"..\\dashboard\\src\\app\\dashboard.js\"\n\tFile \"..\\dashboard\\src\\app\\index.js\"\n\tFile \"..\\dashboard\\src\\app\\listeners.js\"\n\tFile \"..\\dashboard\\src\\app\\sidebar.js\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\css\"\n\tFile \"..\\dashboard\\src\\css\\styles.css\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\lib\"\n\tFile \"..\\dashboard\\src\\lib\\chart.umd.js\"\n\tFile \"..\\dashboard\\src\\lib\\chartjs-plugin-zoom.min.js\"\n\tFile \"..\\dashboard\\src\\lib\\hammer.min.js\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\media\"\n\tFile \"..\\dashboard\\src\\media\\banner.svg\"\n\tFile \"..\\dashboard\\src\\media\\favicon-16x16.png\"\n\tFile \"..\\dashboard\\src\\media\\favicon-32x32.png\"\n\tFile \"..\\dashboard\\src\\media\\mosquitto-logo.png\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\tailwind\"\n\tFile \"..\\dashboard\\src\\tailwind.config.js\"\n\tFile \"..\\dashboard\\src\\tailwind\\styles.css\"\n\n\tSetOutPath \"$INSTDIR\\dashboard\\utils\"\n\tFile \"..\\dashboard\\src\\utils\\assert.js\"\n\tFile \"..\\dashboard\\src\\utils\\queue.js\"\n\tFile \"..\\dashboard\\src\\utils\\utils.js\"\n\n\tWriteUninstaller \"$INSTDIR\\Uninstall.exe\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto64\" \"DisplayName\" \"Eclipse Mosquitto MQTT broker (64 bit)\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto64\" \"DisplayIcon\" \"$INSTDIR\\mosquitto.ico\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto64\" \"UninstallString\" \"$\\\"$INSTDIR\\Uninstall.exe$\\\"\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto64\" \"QuietUninstallString\" \"$\\\"$INSTDIR\\Uninstall.exe$\\\" /S\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto64\" \"HelpLink\" \"https://mosquitto.org/\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto64\" \"URLInfoAbout\" \"https://mosquitto.org/\"\n\tWriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto64\" \"DisplayVersion\" \"${VERSION}\"\n\tWriteRegDWORD HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto64\" \"NoModify\" \"1\"\n\tWriteRegDWORD HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto64\" \"NoRepair\" \"1\"\n\n\tWriteRegExpandStr ${env_hklm} MOSQUITTO_DIR $INSTDIR\n\tSendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 \"STR:Environment\" /TIMEOUT=5000\nSectionEnd\n\nSection \"Visual Studio Runtime\"\n  SetOutPath \"$INSTDIR\"\n  File \"VC_redist.x64.exe\"\n  ExecWait '\"$INSTDIR\\VC_redist.x64.exe\" /quiet /norestart'\n  Delete \"$INSTDIR\\VC_redist.x64.exe\"\nSectionEnd\n\nSection \"Service\" SecService\n\tExecWait '\"$INSTDIR\\mosquitto.exe\" install'\n\tExecWait 'sc start mosquitto'\nSectionEnd\n\nSection \"Uninstall\"\n\tExecWait 'sc stop mosquitto'\n\tSleep 1000\n\tExecWait '\"$INSTDIR\\mosquitto.exe\" uninstall'\n\tSleep 1000\n\n\tDelete \"$INSTDIR\\mosquitto.dll\"\n\tDelete \"$INSTDIR\\mosquitto.exe\"\n\tDelete \"$INSTDIR\\mosquitto_common.dll\"\n\tDelete \"$INSTDIR\\mosquitto_ctrl.exe\"\n\tDelete \"$INSTDIR\\mosquitto_db_dump.exe\"\n\tDelete \"$INSTDIR\\mosquitto_passwd.exe\"\n\tDelete \"$INSTDIR\\mosquitto_pub.exe\"\n\tDelete \"$INSTDIR\\mosquitto_rr.exe\"\n\tDelete \"$INSTDIR\\mosquitto_signal.exe\"\n\tDelete \"$INSTDIR\\mosquitto_sub.exe\"\n\tDelete \"$INSTDIR\\mosquittopp.dll\"\n\tDelete \"$INSTDIR\\mosquitto_acl_file.dll\"\n\tDelete \"$INSTDIR\\mosquitto_dynamic_security.dll\"\n\tDelete \"$INSTDIR\\mosquitto_password_file.dll\"\n\tDelete \"$INSTDIR\\mosquitto_persist_sqlite.dll\"\n\tDelete \"$INSTDIR\\mosquitto_sparkplug_aware.dll\"\n\tDelete \"$INSTDIR\\aclfile.example\"\n\tDelete \"$INSTDIR\\ChangeLog.txt\"\n\tDelete \"$INSTDIR\\mosquitto.conf\"\n\tDelete \"$INSTDIR\\pskfile.example\"\n\tDelete \"$INSTDIR\\pwfile.example\"\n\tDelete \"$INSTDIR\\NOTICE.md\"\n\tDelete \"$INSTDIR\\README.md\"\n\tDelete \"$INSTDIR\\README-windows.txt\"\n\tDelete \"$INSTDIR\\README-letsencrypt.md\"\n\tDelete \"$INSTDIR\\SECURITY.md\"\n\tDelete \"$INSTDIR\\edl-v10\"\n\tDelete \"$INSTDIR\\epl-v20\"\n\tDelete \"$INSTDIR\\mosquitto.ico\"\n\n\tDelete \"$INSTDIR\\argon2.dll\"\n\tDelete \"$INSTDIR\\cjson.dll\"\n\tDelete \"$INSTDIR\\libcrypto-3-x64.dll\"\n\tDelete \"$INSTDIR\\libmicrohttpd-dll.dll\"\n\tDelete \"$INSTDIR\\libssl-3-x64.dll\"\n\tDelete \"$INSTDIR\\pthreadVC3.dll\"\n\tDelete \"$INSTDIR\\sqlite3.dll\"\n\n\tDelete \"$INSTDIR\\devel\\mosquitto.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\broker.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\broker_control.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\broker_plugin.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\defs.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_base64.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_cjson.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_file.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_memory.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_password.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_properties.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_random.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_string.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_time.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_topic.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libcommon_utf8.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_auth.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_callbacks.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_connect.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_create_delete.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_helpers.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_loop.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_message.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_options.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_publish.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_socks.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_subscribe.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_tls.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_unsubscribe.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquitto_will.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\libmosquittopp.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto\\mqtt_protocol.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto_broker.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto_plugin.h\"\n\tDelete \"$INSTDIR\\devel\\mosquittopp.h\"\n\tDelete \"$INSTDIR\\devel\\mqtt_protocol.h\"\n\tDelete \"$INSTDIR\\devel\\mosquitto.lib\"\n\tDelete \"$INSTDIR\\devel\\mosquitto_broker.lib\"\n\tDelete \"$INSTDIR\\devel\\mosquittopp.lib\"\n\tRMDir \"$INSTDIR\\devel\\mosquitto\"\n\tRMDir \"$INSTDIR\\devel\"\n\n\tDelete \"$INSTDIR\\dashboard\\app\\consts.js\"\n\tDelete \"$INSTDIR\\dashboard\\app\\dashboard.js\"\n\tDelete \"$INSTDIR\\dashboard\\app\\index.js\"\n\tDelete \"$INSTDIR\\dashboard\\app\\listeners.js\"\n\tDelete \"$INSTDIR\\dashboard\\app\\sidebar.js\"\n\tRMDir \"$INSTDIR\\dashboard\\app\"\n\tDelete \"$INSTDIR\\dashboard\\css\\styles.css\"\n\tRMDir \"$INSTDIR\\dashboard\\css\"\n\tDelete \"$INSTDIR\\dashboard\\index.html\"\n\tDelete \"$INSTDIR\\dashboard\\lib\\chart.umd.js\"\n\tDelete \"$INSTDIR\\dashboard\\lib\\chartjs-plugin-zoom.min.js\"\n\tDelete \"$INSTDIR\\dashboard\\lib\\hammer.min.js\"\n\tRMDir \"$INSTDIR\\dashboard\\lib\"\n\tDelete \"$INSTDIR\\dashboard\\listeners.html\"\n\tDelete \"$INSTDIR\\dashboard\\media\\banner.svg\"\n\tDelete \"$INSTDIR\\dashboard\\media\\favicon-16x16.png\"\n\tDelete \"$INSTDIR\\dashboard\\media\\favicon-32x32.png\"\n\tDelete \"$INSTDIR\\dashboard\\media\\mosquitto-logo.png\"\n\tRMDir \"$INSTDIR\\dashboard\\media\"\n\tDelete \"$INSTDIR\\dashboard\\tailwind.config.js\"\n\tDelete \"$INSTDIR\\dashboard\\tailwind\\styles.css\"\n\tRMDir \"$INSTDIR\\dashboard\\tailwind\"\n\tDelete \"$INSTDIR\\dashboard\\utils\\assert.js\"\n\tDelete \"$INSTDIR\\dashboard\\utils\\queue.js\"\n\tDelete \"$INSTDIR\\dashboard\\utils\\utils.js\"\n\tRMDir \"$INSTDIR\\dashboard\\utils\"\n\tRMDir \"$INSTDIR\\dashboard\"\n\n\tDelete \"$INSTDIR\\Uninstall.exe\"\n\tRMDir \"$INSTDIR\"\n\tDeleteRegKey HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Mosquitto64\"\n\n\tDeleteRegValue ${env_hklm} MOSQUITTO_DIR\n\tSendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 \"STR:Environment\" /TIMEOUT=5000\nSectionEnd\n\nLangString DESC_SecInstall ${LANG_ENGLISH} \"The main installation.\"\nLangString DESC_SecService ${LANG_ENGLISH} \"Install mosquitto as a Windows service?\"\n\n!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n\t!insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall)\n\t!insertmacro MUI_DESCRIPTION_TEXT ${SecService} $(DESC_SecService)\n!insertmacro MUI_FUNCTION_DESCRIPTION_END\n\n"
  },
  {
    "path": "lib/CMakeLists.txt",
    "content": "if(WITH_LIB_CPP)\n\tadd_subdirectory(cpp)\nendif()\n\nset(C_SRC\n\tactions_publish.c\n\tactions_subscribe.c\n\tactions_unsubscribe.c\n\talias_mosq.c alias_mosq.h\n\tcallbacks.c\n\tconnect.c\n\textended_auth.c\n\thandle_auth.c\n\thandle_connack.c\n\thandle_disconnect.c\n\thandle_ping.c\n\thandle_pubackcomp.c\n\thandle_publish.c\n\thandle_pubrec.c\n\thandle_pubrel.c\n\thandle_suback.c\n\thandle_unsuback.c\n\thelpers.c\n\thttp_client.c http_client.h\n\tlibmosquitto.c\n\tlogging_mosq.c logging_mosq.h\n\tloop.c\n\tmessages_mosq.c messages_mosq.h\n\tmosquitto_internal.h\n\t../include/mosquitto.h\n\t../include/mosquitto/mqtt_protocol.h\n\tnet_mosq_ocsp.c net_mosq.c net_mosq.h\n\tnet_ws.c\n\toptions.c\n\tpacket_datatypes.c\n\tpacket_mosq.c packet_mosq.h\n\tproperty_mosq.c property_mosq.h\n\tread_handle.c read_handle.h\n\tsend_connect.c\n\tsend_disconnect.c\n\tsend_mosq.c\n\tsend_publish.c\n\tsend_subscribe.c\n\tsend_unsubscribe.c\n\tsend_mosq.c send_mosq.h\n\tsocks_mosq.c\n\tsrv_mosq.c\n\tthread_mosq.c\n\ttls_mosq.c\n\tutil_mosq.c util_mosq.h\n\twill_mosq.c will_mosq.h)\n\nset(LIBRARIES common-options libmosquitto_common)\n\nif(WITH_TLS)\n\tset (LIBRARIES ${LIBRARIES} OpenSSL::SSL)\nendif()\n\nif(WIN32)\n\tset (LIBRARIES ${LIBRARIES} ws2_32)\nendif()\n\nif(WITH_SRV)\n\t# Simple detect c-ares\n\tfind_path(ARES_HEADER ares.h)\n\tif(ARES_HEADER)\n\t\tadd_definitions(\"-DWITH_SRV\")\n\t\tset (LIBRARIES ${LIBRARIES} cares)\n\telse()\n\t\tmessage(WARNING \"c-ares library not found.\")\n\tendif()\nendif()\n\nif(WITH_WEBSOCKETS AND WITH_WEBSOCKETS_BUILTIN)\n\tadd_definitions(\"-DWITH_WEBSOCKETS=WS_IS_BUILTIN\")\n\tset(C_SRC ${C_SRC}\n\t\t../deps/picohttpparser/picohttpparser.c\n\t\thttp_client.c http_client.h\n\t\tnet_ws.c\n\t\t)\nendif()\n\nadd_library(libmosquitto SHARED\n\t${C_SRC}\n)\n\nif(WITH_WEBSOCKETS AND WITH_WEBSOCKETS_BUILTIN)\n\ttarget_include_directories(libmosquitto PRIVATE\n\t\t\"${mosquitto_SOURCE_DIR}/deps/picohttpparser\")\nendif()\n\ntarget_include_directories(libmosquitto\n\tPUBLIC\n\t\t\"${mosquitto_SOURCE_DIR}/include\"\n\t\t\"${mosquitto_SOURCE_DIR}/lib\"\n\tPRIVATE\n\t\t\"${mosquitto_SOURCE_DIR}/common\"\n\t\t\"${mosquitto_SOURCE_DIR}/libcommon\"\n\t\t\"${OPENSSL_INCLUDE_DIR}\" # Required for cross compilation\n)\n\nif(WITH_THREADING)\n\tif(WIN32)\n\t\tset (LIBRARIES ${LIBRARIES} PThreads4W::PThreads4W)\n\telse()\n\t\tset(THREADS_PREFER_PTHREAD_FLAG ON)\n\t\tfind_package(Threads REQUIRED)\n\n\t\tset (LIBRARIES ${LIBRARIES} Threads::Threads)\n\tendif()\nendif()\n\ntarget_link_libraries(libmosquitto PUBLIC ${LIBRARIES})\n\nset_target_properties(libmosquitto PROPERTIES\n\tOUTPUT_NAME mosquitto\n\tVERSION ${VERSION}\n\tSOVERSION 1\n\tPOSITION_INDEPENDENT_CODE 1\n)\n\nif(UNIX AND NOT APPLE AND NOT AIX)\n\tset_target_properties(libmosquitto PROPERTIES\n\t\tLINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/linker.version\n\t\tLINK_FLAGS \"-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linker.version\"\n\t)\nendif()\n\ninstall(TARGETS libmosquitto\n\tRUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n\tARCHIVE DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n\tLIBRARY DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n)\n\nif(WITH_STATIC_LIBRARIES)\n\tadd_library(libmosquitto_static STATIC ${C_SRC})\n\tif(WITH_PIC)\n\t\tset_target_properties(libmosquitto_static PROPERTIES\n\t\t\tPOSITION_INDEPENDENT_CODE 1\n\t\t)\n\tendif()\n\n\tif(WITH_WEBSOCKETS AND WITH_WEBSOCKETS_BUILTIN)\n\t\ttarget_include_directories(libmosquitto_static PRIVATE\n\t\t\t\"${mosquitto_SOURCE_DIR}/deps/picohttpparser\")\n\tendif()\n\n\n\ttarget_link_libraries(libmosquitto_static PRIVATE ${LIBRARIES})\n\n\ttarget_include_directories(libmosquitto_static PRIVATE\n\t\t\"${mosquitto_SOURCE_DIR}\"\n\t\t\"${mosquitto_SOURCE_DIR}/include\"\n\t\t\"${mosquitto_SOURCE_DIR}/lib\"\n\t\t\"${mosquitto_SOURCE_DIR}/libcommon\"\n\t)\n\n\tset_target_properties(libmosquitto_static PROPERTIES\n\t\tOUTPUT_NAME mosquitto_static\n\t\tVERSION ${VERSION}\n\t)\n\n\ttarget_compile_definitions(libmosquitto_static PUBLIC \"LIBMOSQUITTO_STATIC\")\n\tinstall(TARGETS libmosquitto_static\n\t\tARCHIVE DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n\t)\nendif()\n"
  },
  {
    "path": "lib/Makefile",
    "content": "R=..\ninclude ${R}/config.mk\n\nLOCAL_CFLAGS+=-fPIC\nLOCAL_CPPFLAGS+=-I${R}/libcommon\nLOCAL_LDFLAGS+=-Wl,--version-script=linker.version -Wl,-soname,libmosquitto.so.$(SOVERSION) -fPIC -shared\nLOCAL_LIBADD+=-lcjson -lc ${LIBMOSQ_COMMON}\nSTATIC_LIB_DEPS:=\n\n# ------------------------------------------\n#  Compile time options\n# ------------------------------------------\n\nifeq ($(WITH_SOCKS),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_SOCKS\nendif\n\nifeq ($(WITH_SRV),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_SRV\n\tLOCAL_LIBADD+=-lcares\n\tSTATIC_LIB_DEPS+=-lcares\nendif\n\nifeq ($(WITH_THREADING),yes)\n\tLOCAL_CFLAGS+=-pthread\n\tLOCAL_CPPFLAGS+=-DWITH_THREADING\n\tLOCAL_LDFLAGS+=-pthread\n\tSTATIC_LIB_DEPS+=-pthread\nendif\n\nifeq ($(WITH_TLS),yes)\n\tLOCAL_LIBADD+=-lssl -lcrypto\n\tSTATIC_LIB_DEPS:=$(STATIC_LIB_DEPS) -lssl -lcrypto\nendif\n\n# ------------------------------------------\n#  Targets\n# ------------------------------------------\n.PHONY : really clean install\n\nOBJS= \\\n\tlibmosquitto.o \\\n\tactions_publish.o \\\n\tactions_subscribe.o \\\n\tactions_unsubscribe.o \\\n\talias_mosq.o \\\n\tcallbacks.o \\\n\tconnect.o \\\n\textended_auth.o \\\n\thandle_auth.o \\\n\thandle_connack.o \\\n\thandle_disconnect.o \\\n\thandle_ping.o \\\n\thandle_pubackcomp.o \\\n\thandle_publish.o \\\n\thandle_pubrec.o \\\n\thandle_pubrel.o \\\n\thandle_suback.o \\\n\thandle_unsuback.o \\\n\thelpers.o \\\n\thttp_client.o \\\n\tlogging_mosq.o \\\n\tloop.o \\\n\tmessages_mosq.o \\\n\tnet_mosq_ocsp.o \\\n\tnet_mosq.o \\\n\tnet_ws.o \\\n\toptions.o \\\n\tpacket_datatypes.o \\\n\tpacket_mosq.o \\\n\tproperty_mosq.o \\\n\tread_handle.o \\\n\tsend_connect.o \\\n\tsend_disconnect.o \\\n\tsend_mosq.o \\\n\tsend_publish.o \\\n\tsend_subscribe.o \\\n\tsend_unsubscribe.o \\\n\tsocks_mosq.o \\\n\tsrv_mosq.o \\\n\tthread_mosq.o \\\n\ttls_mosq.o \\\n\tutil_mosq.o \\\n\twill_mosq.o\n\nOBJS_EXTERNAL=\n\nifeq ($(WITH_WEBSOCKETS),yes)\n\tOBJS_EXTERNAL+=${R}/deps/picohttpparser/picohttpparser.o\nendif\n\nALL_DEPS:=\n\nifeq ($(WITH_SHARED_LIBRARIES),yes)\n\tALL_DEPS+=libmosquitto.so.${SOVERSION}\nendif\n\nifeq ($(WITH_STATIC_LIBRARIES),yes)\n\tALL_DEPS+=libmosquitto.a\nendif\n\nall : ${ALL_DEPS}\nifeq ($(WITH_SHARED_LIBRARIES),yes)\n\t$(MAKE) -C cpp\nendif\n\ninstall : all\n\t$(INSTALL) -d \"${DESTDIR}${libdir}/\"\nifeq ($(WITH_SHARED_LIBRARIES),yes)\n\t$(INSTALL) ${STRIP_OPTS} libmosquitto.so.${SOVERSION} \"${DESTDIR}${libdir}/libmosquitto.so.${SOVERSION}\"\n\tln -sf libmosquitto.so.${SOVERSION} \"${DESTDIR}${libdir}/libmosquitto.so\"\nendif\nifeq ($(WITH_STATIC_LIBRARIES),yes)\n\t$(INSTALL) ${STRIP_OPTS} libmosquitto.a \"${DESTDIR}${libdir}/libmosquitto.a\"\nendif\n\t$(INSTALL) -d \"${DESTDIR}${libdir}/pkgconfig\"\n\t$(INSTALL) -m644 ${R}/libmosquitto.pc.in \"${DESTDIR}${libdir}/pkgconfig/libmosquitto.pc\"\n\tsed ${SEDINPLACE} -e \"s#@CMAKE_INSTALL_PREFIX@#${prefix}#\" -e \"s#@CMAKE_INSTALL_LIBDIR@#lib${LIB_SUFFIX}#\" -e \"s#@VERSION@#${VERSION}#\" \"${DESTDIR}${libdir}/pkgconfig/libmosquitto.pc\"\nifeq ($(WITH_SHARED_LIBRARIES),yes)\n\t$(MAKE) -C cpp install\nendif\n\nuninstall :\n\t-rm -f \"${DESTDIR}${libdir}/libmosquitto.so.${SOVERSION}\"\n\t-rm -f \"${DESTDIR}${libdir}/libmosquitto.so\"\n\t-rm -f \"${DESTDIR}${libdir}/libmosquitto.a\"\n\t-rm -f \"${DESTDIR}${incdir}/mosquitto.h\"\n\nreallyclean : clean\n\nclean :\n\t-rm -f ${OBJS} ${OBJS_EXTERNAL} libmosquitto.so.${SOVERSION} libmosquitto.so libmosquitto.a *.gcno *.gcda\n\t$(MAKE) -C cpp clean\n\nlibmosquitto.so.${SOVERSION} : ${OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}$(CC) $(LOCAL_LDFLAGS) $^ -o $@ ${LOCAL_LIBADD}\n\nlibmosquitto.a : ${OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}$(AR) cr $@ $^\n\n${OBJS} : %.o: %.c ${R}/include/mosquitto.h mosquitto_internal.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n${R}/deps/picohttpparser/picohttpparser.o : ${R}/deps/picohttpparser/picohttpparser.c\n\t${CROSS_COMPILE}$(CC) $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n"
  },
  {
    "path": "lib/actions_publish.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdbool.h>\n#include <string.h>\n\n#include \"messages_mosq.h\"\n#include \"mosquitto_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"property_common.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain)\n{\n\treturn mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, NULL);\n}\n\n\nint mosquitto_publish_v5(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, const mosquitto_property *properties)\n{\n\tstruct mosquitto_message_all *message;\n\tuint16_t local_mid;\n\tconst mosquitto_property *p;\n\tconst mosquitto_property *outgoing_properties = NULL;\n\tmosquitto_property *properties_copy = NULL;\n\tmosquitto_property local_property;\n\tbool have_topic_alias;\n\tint rc;\n\tsize_t tlen = 0;\n\tuint32_t remaining_length;\n\n\tif(!mosq || qos<0 || qos>2){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosq->protocol != mosq_p_mqtt5 && properties){\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}\n\tif(qos > mosq->max_qos){\n\t\treturn MOSQ_ERR_QOS_NOT_SUPPORTED;\n\t}\n\n\tif(!mosq->retain_available){\n\t\tretain = false;\n\t}\n\n\tif(properties){\n\t\tif(properties->client_generated){\n\t\t\toutgoing_properties = properties;\n\t\t}else{\n\t\t\tmemcpy(&local_property, properties, sizeof(mosquitto_property));\n\t\t\tlocal_property.client_generated = true;\n\t\t\tlocal_property.next = NULL;\n\t\t\toutgoing_properties = &local_property;\n\t\t}\n\t\trc = mosquitto_property_check_all(CMD_PUBLISH, outgoing_properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tif(!topic || STREMPTY(topic)){\n\t\tif(topic){\n\t\t\ttopic = NULL;\n\t\t}\n\n\t\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t\tp = outgoing_properties;\n\t\t\thave_topic_alias = false;\n\t\t\twhile(p){\n\t\t\t\tif(mosquitto_property_identifier(p) == MQTT_PROP_TOPIC_ALIAS){\n\t\t\t\t\thave_topic_alias = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tp = mosquitto_property_next(p);\n\t\t\t}\n\t\t\tif(have_topic_alias == false){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}else{\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}else{\n\t\ttlen = strlen(topic);\n\t\tif(mosquitto_validate_utf8(topic, (int)tlen)){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t\tif(payloadlen < 0 || payloadlen > (int)MQTT_MAX_PAYLOAD){\n\t\t\treturn MOSQ_ERR_PAYLOAD_SIZE;\n\t\t}\n\t\tif(mosquitto_pub_topic_check(topic) != MOSQ_ERR_SUCCESS){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\n\tif(mosq->maximum_packet_size > 0){\n\t\tremaining_length = 1 + 2+(uint32_t)tlen + (uint32_t)payloadlen + mosquitto_property_get_length_all(outgoing_properties);\n\t\tif(qos > 0){\n\t\t\tremaining_length++;\n\t\t}\n\t\tif(packet__check_oversize(mosq, remaining_length)){\n\t\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t\t}\n\t}\n\n\tlocal_mid = mosquitto__mid_generate(mosq);\n\tif(mid){\n\t\t*mid = local_mid;\n\t}\n\n\tif(qos == 0){\n\t\treturn send__publish(mosq, local_mid, topic, (uint32_t)payloadlen, payload, (uint8_t)qos, retain, false, 0, outgoing_properties, 0);\n\t}else{\n\t\tif(outgoing_properties){\n\t\t\trc = mosquitto_property_copy_all(&properties_copy, outgoing_properties);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t\tmessage = mosquitto_calloc(1, sizeof(struct mosquitto_message_all));\n\t\tif(!message){\n\t\t\tmosquitto_property_free_all(&properties_copy);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tmessage->next = NULL;\n\t\tmessage->msg.mid = local_mid;\n\t\tif(topic){\n\t\t\tmessage->msg.topic = mosquitto_strdup(topic);\n\t\t\tif(!message->msg.topic){\n\t\t\t\tmessage__cleanup(&message);\n\t\t\t\tmosquitto_property_free_all(&properties_copy);\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t\tif(payloadlen){\n\t\t\tmessage->msg.payloadlen = payloadlen;\n\t\t\tmessage->msg.payload = mosquitto_malloc((unsigned int)payloadlen*sizeof(uint8_t));\n\t\t\tif(!message->msg.payload){\n\t\t\t\tmessage__cleanup(&message);\n\t\t\t\tmosquitto_property_free_all(&properties_copy);\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tmemcpy(message->msg.payload, payload, (uint32_t)payloadlen*sizeof(uint8_t));\n\t\t}else{\n\t\t\tmessage->msg.payloadlen = 0;\n\t\t\tmessage->msg.payload = NULL;\n\t\t}\n\t\tmessage->msg.qos = (uint8_t)qos;\n\t\tmessage->msg.retain = retain;\n\t\tmessage->dup = false;\n\t\tmessage->properties = properties_copy;\n\n\t\tCOMPAT_pthread_mutex_lock(&mosq->msgs_out.mutex);\n\t\tmessage->state = mosq_ms_invalid;\n\t\trc = message__queue(mosq, message, mosq_md_out);\n\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n\t\treturn rc;\n\t}\n}\n"
  },
  {
    "path": "lib/actions_subscribe.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <string.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_common.h\"\n#include \"send_mosq.h\"\n\n\nint mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos)\n{\n\treturn mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, 0, NULL);\n}\n\n\nint mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties)\n{\n\treturn mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, options, properties);\n}\n\n\nint mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties)\n{\n\tconst mosquitto_property *outgoing_properties = NULL;\n\tmosquitto_property local_property;\n\tint i;\n\tint rc;\n\tuint32_t remaining_length = 0;\n\tint slen;\n\n\tif(!mosq || !sub_count || !sub){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosq->protocol != mosq_p_mqtt5 && properties){\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}\n\tif(qos < 0 || qos > 2){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif((options & 0x30) == 0x30 || (options & 0xC0) != 0){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!net__is_connected(mosq)){\n\t\treturn MOSQ_ERR_NO_CONN;\n\t}\n\n\tif(properties){\n\t\tif(properties->client_generated){\n\t\t\toutgoing_properties = properties;\n\t\t}else{\n\t\t\tmemcpy(&local_property, properties, sizeof(mosquitto_property));\n\t\t\tlocal_property.client_generated = true;\n\t\t\tlocal_property.next = NULL;\n\t\t\toutgoing_properties = &local_property;\n\t\t}\n\t\trc = mosquitto_property_check_all(CMD_SUBSCRIBE, outgoing_properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tfor(i=0; i<sub_count; i++){\n\t\tif(mosquitto_sub_topic_check(sub[i])){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tslen = (int)strlen(sub[i]);\n\t\tif(slen == 0){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(mosquitto_validate_utf8(sub[i], slen)){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t\tremaining_length += 2+(uint32_t)slen + 1;\n\t}\n\n\tif(mosq->maximum_packet_size > 0){\n\t\tremaining_length += 2 + mosquitto_property_get_length_all(outgoing_properties);\n\t\tif(packet__check_oversize(mosq, remaining_length)){\n\t\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t\t}\n\t}\n\tif(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){\n\t\toptions = 0;\n\t}\n\n\treturn send__subscribe(mosq, mid, sub_count, sub, qos|options, outgoing_properties);\n}\n"
  },
  {
    "path": "lib/actions_unsubscribe.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdint.h>\n#include <string.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"property_common.h\"\n#include \"send_mosq.h\"\n\n\nint mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub)\n{\n\treturn mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, NULL);\n}\n\n\nint mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties)\n{\n\treturn mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, properties);\n}\n\n\nint mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties)\n{\n\tconst mosquitto_property *outgoing_properties = NULL;\n\tmosquitto_property local_property;\n\tint rc;\n\tint i;\n\tuint32_t remaining_length = 0;\n\tint slen;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosq->protocol != mosq_p_mqtt5 && properties){\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}\n\tif(!net__is_connected(mosq)){\n\t\treturn MOSQ_ERR_NO_CONN;\n\t}\n\n\tif(properties){\n\t\tif(properties->client_generated){\n\t\t\toutgoing_properties = properties;\n\t\t}else{\n\t\t\tmemcpy(&local_property, properties, sizeof(mosquitto_property));\n\t\t\tlocal_property.client_generated = true;\n\t\t\tlocal_property.next = NULL;\n\t\t\toutgoing_properties = &local_property;\n\t\t}\n\t\trc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, outgoing_properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tfor(i=0; i<sub_count; i++){\n\t\tif(mosquitto_sub_topic_check(sub[i])){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tslen = (int)strlen(sub[i]);\n\t\tif(slen == 0){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(mosquitto_validate_utf8(sub[i], slen)){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t\tremaining_length += 2U + (uint32_t)slen;\n\t}\n\n\tif(mosq->maximum_packet_size > 0){\n\t\tremaining_length += 2U + mosquitto_property_get_length_all(outgoing_properties);\n\t\tif(packet__check_oversize(mosq, remaining_length)){\n\t\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t\t}\n\t}\n\n\treturn send__unsubscribe(mosq, mid, sub_count, sub, outgoing_properties);\n}\n"
  },
  {
    "path": "lib/alias_mosq.c",
    "content": "/*\nCopyright (c) 2019-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <string.h>\n\n#include \"mosquitto.h\"\n#include \"alias_mosq.h\"\n\nstatic void alias__free_r2l(struct mosquitto *mosq);\nstatic void alias__free_l2r(struct mosquitto *mosq);\n\n\nint alias__add_l2r(struct mosquitto *mosq, const char *topic, uint16_t *alias)\n{\n\tstruct mosquitto__alias *aliases_new;\n\n\tif(mosq->alias_count_l2r < mosq->alias_max_l2r){\n\t\taliases_new = mosquitto_realloc(mosq->aliases_l2r, sizeof(struct mosquitto__alias)*(size_t)(mosq->alias_count_l2r+1));\n\t\tif(!aliases_new){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tmosq->aliases_l2r = aliases_new;\n\t\tmosq->alias_count_l2r++;\n\t\t*alias = mosq->alias_count_l2r;\n\n\t\tmosq->aliases_l2r[mosq->alias_count_l2r-1].alias = *alias;\n\t\tmosq->aliases_l2r[mosq->alias_count_l2r-1].topic = mosquitto_strdup(topic);\n\t\tif(!mosq->aliases_l2r[mosq->alias_count_l2r-1].topic){\n\t\t\t*alias = 0;\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\t*alias = 0;\n\treturn MOSQ_ERR_INVAL;\n}\n\n\nint alias__add_r2l(struct mosquitto *mosq, const char *topic, uint16_t alias)\n{\n\tint i;\n\tstruct mosquitto__alias *aliases_new;\n\n\tfor(i=0; i<mosq->alias_count_r2l; i++){\n\t\tif(mosq->aliases_r2l[i].alias == alias){\n\t\t\tmosquitto_FREE(mosq->aliases_r2l[i].topic);\n\t\t\tmosq->aliases_r2l[i].topic = mosquitto_strdup(topic);\n\t\t\tif(mosq->aliases_r2l[i].topic){\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* New alias */\n\taliases_new = mosquitto_realloc(mosq->aliases_r2l, sizeof(struct mosquitto__alias)*(size_t)(mosq->alias_count_r2l+1));\n\tif(!aliases_new){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tmosq->aliases_r2l = aliases_new;\n\tmosq->alias_count_r2l++;\n\n\tmosq->aliases_r2l[mosq->alias_count_r2l-1].alias = alias;\n\tmosq->aliases_r2l[mosq->alias_count_r2l-1].topic = mosquitto_strdup(topic);\n\tif(!mosq->aliases_r2l[mosq->alias_count_r2l-1].topic){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint alias__find_by_alias(struct mosquitto *mosq, int direction, uint16_t alias, char **topic)\n{\n\tint i;\n\tstruct mosquitto__alias *aliases;\n\tint alias_count;\n\n\tif(direction == ALIAS_DIR_R2L){\n\t\taliases = mosq->aliases_r2l;\n\t\talias_count = mosq->alias_count_r2l;\n\t}else{\n\t\taliases = mosq->aliases_l2r;\n\t\talias_count = mosq->alias_count_l2r;\n\t}\n\n\tfor(i=0; i<alias_count; i++){\n\t\tif(aliases[i].alias == alias){\n\t\t\t*topic = mosquitto_strdup(aliases[i].topic);\n\t\t\tif(*topic){\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_INVAL;\n}\n\n\nint alias__find_by_topic(struct mosquitto *mosq, int direction, const char *topic, uint16_t *alias)\n{\n\tint i;\n\tstruct mosquitto__alias *aliases;\n\tint alias_count;\n\n\tif(direction == ALIAS_DIR_R2L){\n\t\taliases = mosq->aliases_r2l;\n\t\talias_count = mosq->alias_count_r2l;\n\t}else{\n\t\taliases = mosq->aliases_l2r;\n\t\talias_count = mosq->alias_count_l2r;\n\t}\n\n\tfor(i=0; i<alias_count; i++){\n\t\tif(aliases[i].topic && !strcmp(aliases[i].topic, topic)){\n\t\t\t*alias = aliases[i].alias;\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\treturn MOSQ_ERR_INVAL;\n}\n\n\nstatic void alias__free_r2l(struct mosquitto *mosq)\n{\n\tint i;\n\n\tfor(i=0; i<mosq->alias_count_r2l; i++){\n\t\tmosquitto_FREE(mosq->aliases_r2l[i].topic);\n\t}\n\tmosquitto_FREE(mosq->aliases_r2l);\n\tmosq->alias_count_r2l = 0;\n}\n\n\nstatic void alias__free_l2r(struct mosquitto *mosq)\n{\n\tint i;\n\n\tfor(i=0; i<mosq->alias_count_l2r; i++){\n\t\tmosquitto_FREE(mosq->aliases_l2r[i].topic);\n\t}\n\tmosquitto_FREE(mosq->aliases_l2r);\n\tmosq->alias_count_l2r = 0;\n}\n\n\nvoid alias__free_all(struct mosquitto *mosq)\n{\n\talias__free_r2l(mosq);\n\talias__free_l2r(mosq);\n}\n"
  },
  {
    "path": "lib/alias_mosq.h",
    "content": "/*\nCopyright (c) 2019-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef ALIAS_MOSQ_H\n#define ALIAS_MOSQ_H\n\n#include \"mosquitto_internal.h\"\n\nint alias__add_r2l(struct mosquitto *mosq, const char *topic, uint16_t alias);\nint alias__add_l2r(struct mosquitto *mosq, const char *topic, uint16_t *alias);\nint alias__find_by_alias(struct mosquitto *mosq, int direction, uint16_t alias, char **topic);\nint alias__find_by_topic(struct mosquitto *mosq, int direction, const char *topic, uint16_t *alias);\nvoid alias__free_all(struct mosquitto *mosq);\n\n#endif\n"
  },
  {
    "path": "lib/callbacks.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"callbacks.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n\n\nvoid mosquitto_connect_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_connect on_connect)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_connect = on_connect;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_connect_with_flags on_connect)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_connect_with_flags = on_connect;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_connect_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_connect_v5 on_connect)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_connect_v5 = on_connect;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_pre_connect_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_pre_connect on_pre_connect)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_pre_connect = on_pre_connect;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_disconnect_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_disconnect on_disconnect)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_disconnect = on_disconnect;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_disconnect_v5 on_disconnect)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_disconnect_v5 = on_disconnect;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_publish_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_publish on_publish)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_publish = on_publish;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_publish_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_publish_v5 on_publish)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_publish_v5 = on_publish;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_message_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_message on_message)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_message = on_message;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_message_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_message_v5 on_message)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_message_v5 = on_message;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_subscribe_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_subscribe on_subscribe)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_subscribe = on_subscribe;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_subscribe_v5 on_subscribe)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_subscribe_v5 = on_subscribe;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_unsubscribe on_unsubscribe)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_unsubscribe = on_unsubscribe;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_unsubscribe_v5 on_unsubscribe)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_unsubscribe_v5 = on_unsubscribe;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_unsubscribe2_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_unsubscribe2_v5 on_unsubscribe)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_unsubscribe2_v5 = on_unsubscribe;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid mosquitto_log_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_log on_log)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->log_callback_mutex);\n\tmosq->on_log = on_log;\n\tCOMPAT_pthread_mutex_unlock(&mosq->log_callback_mutex);\n}\n\n\nvoid mosquitto_ext_auth_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_ext_auth on_ext_auth)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\tmosq->on_ext_auth = on_ext_auth;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n}\n\n\nvoid callback__on_pre_connect(struct mosquitto *mosq)\n{\n\tLIBMOSQ_CB_pre_connect on_pre_connect;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\ton_pre_connect = mosq->on_pre_connect;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n\n\tmosq->callback_depth++;\n\tif(on_pre_connect){\n\t\ton_pre_connect(mosq, mosq->userdata);\n\t}\n\tmosq->callback_depth--;\n}\n\n\nvoid callback__on_connect(struct mosquitto *mosq, uint8_t reason_code, uint8_t connect_flags, const mosquitto_property *properties)\n{\n\tLIBMOSQ_CB_connect on_connect;\n\tLIBMOSQ_CB_connect_with_flags on_connect_with_flags;\n\tLIBMOSQ_CB_connect_v5 on_connect_v5;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\ton_connect = mosq->on_connect;\n\ton_connect_with_flags = mosq->on_connect_with_flags;\n\ton_connect_v5 = mosq->on_connect_v5;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n\n\tmosq->callback_depth++;\n\tif(on_connect){\n\t\ton_connect(mosq, mosq->userdata, reason_code);\n\t}\n\tif(on_connect_with_flags){\n\t\ton_connect_with_flags(mosq, mosq->userdata, reason_code, connect_flags);\n\t}\n\tif(on_connect_v5){\n\t\ton_connect_v5(mosq, mosq->userdata, reason_code, connect_flags, properties);\n\t}\n\tmosq->callback_depth--;\n}\n\n\nvoid callback__on_publish(struct mosquitto *mosq, int mid, int reason_code, const mosquitto_property *properties)\n{\n\tLIBMOSQ_CB_publish on_publish;\n\tLIBMOSQ_CB_publish_v5 on_publish_v5;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\ton_publish = mosq->on_publish;\n\ton_publish_v5 = mosq->on_publish_v5;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n\n\tmosq->callback_depth++;\n\tif(on_publish){\n\t\ton_publish(mosq, mosq->userdata, mid);\n\t}\n\tif(on_publish_v5){\n\t\ton_publish_v5(mosq, mosq->userdata, mid, reason_code, properties);\n\t}\n\tmosq->callback_depth--;\n}\n\n\nvoid callback__on_message(struct mosquitto *mosq, const struct mosquitto_message *message, const mosquitto_property *properties)\n{\n\tLIBMOSQ_CB_message on_message;\n\tLIBMOSQ_CB_message_v5 on_message_v5;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\ton_message = mosq->on_message;\n\ton_message_v5 = mosq->on_message_v5;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n\n\tmosq->callback_depth++;\n\tif(on_message){\n\t\ton_message(mosq, mosq->userdata, message);\n\t}\n\tif(on_message_v5){\n\t\ton_message_v5(mosq, mosq->userdata, message, properties);\n\t}\n\tmosq->callback_depth--;\n}\n\n\nvoid callback__on_subscribe(struct mosquitto *mosq, int mid, int qos_count, const int *granted_qos, const mosquitto_property *properties)\n{\n\tLIBMOSQ_CB_subscribe on_subscribe;\n\tLIBMOSQ_CB_subscribe_v5 on_subscribe_v5;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\ton_subscribe = mosq->on_subscribe;\n\ton_subscribe_v5 = mosq->on_subscribe_v5;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n\n\tmosq->callback_depth++;\n\tif(on_subscribe){\n\t\ton_subscribe(mosq, mosq->userdata, mid, qos_count, granted_qos);\n\t}\n\tif(on_subscribe_v5){\n\t\ton_subscribe_v5(mosq, mosq->userdata, mid, qos_count, granted_qos, properties);\n\t}\n\tmosq->callback_depth--;\n}\n\n\nvoid callback__on_unsubscribe(struct mosquitto *mosq, int mid, int reason_code_count, const int *reason_codes, const mosquitto_property *properties)\n{\n\tLIBMOSQ_CB_unsubscribe on_unsubscribe;\n\tLIBMOSQ_CB_unsubscribe_v5 on_unsubscribe_v5;\n\tLIBMOSQ_CB_unsubscribe2_v5 on_unsubscribe2_v5;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\ton_unsubscribe = mosq->on_unsubscribe;\n\ton_unsubscribe_v5 = mosq->on_unsubscribe_v5;\n\ton_unsubscribe2_v5 = mosq->on_unsubscribe2_v5;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n\n\tmosq->callback_depth++;\n\tif(on_unsubscribe){\n\t\ton_unsubscribe(mosq, mosq->userdata, mid);\n\t}\n\tif(on_unsubscribe_v5){\n\t\ton_unsubscribe_v5(mosq, mosq->userdata, mid, properties);\n\t}\n\tif(on_unsubscribe2_v5){\n\t\ton_unsubscribe2_v5(mosq, mosq->userdata, mid, reason_code_count, reason_codes, properties);\n\t}\n\tmosq->callback_depth--;\n}\n\n\nvoid callback__on_disconnect(struct mosquitto *mosq, int rc, const mosquitto_property *properties)\n{\n\tLIBMOSQ_CB_disconnect on_disconnect;\n\tLIBMOSQ_CB_disconnect_v5 on_disconnect_v5;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\ton_disconnect = mosq->on_disconnect;\n\ton_disconnect_v5 = mosq->on_disconnect_v5;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n\n\tmosq->callback_depth++;\n\tif(on_disconnect){\n\t\ton_disconnect(mosq, mosq->userdata, rc);\n\t}\n\tif(on_disconnect_v5){\n\t\ton_disconnect_v5(mosq, mosq->userdata, rc, properties);\n\t}\n\tmosq->callback_depth--;\n}\n\n\nint callback__on_ext_auth(struct mosquitto *mosq, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *properties)\n{\n\tint rc = MOSQ_ERR_AUTH;\n\tLIBMOSQ_CB_ext_auth on_ext_auth;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->callback_mutex);\n\ton_ext_auth = mosq->on_ext_auth;\n\tCOMPAT_pthread_mutex_unlock(&mosq->callback_mutex);\n\n\tmosq->callback_depth++;\n\tif(on_ext_auth){\n\t\trc = on_ext_auth(mosq, mosq->userdata, auth_method, auth_data_len, auth_data, properties);\n\t}\n\tmosq->callback_depth--;\n\treturn rc;\n}\n"
  },
  {
    "path": "lib/callbacks.h",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef CALLBACKS_H\n#define CALLBACKS_H\n\n#include \"mosquitto.h\"\n\nvoid callback__on_pre_connect(struct mosquitto *mosq);\nvoid callback__on_connect(struct mosquitto *mosq, uint8_t reason_code, uint8_t connect_flags, const mosquitto_property *properties);\nvoid callback__on_publish(struct mosquitto *mosq, int mid, int reason_code, const mosquitto_property *properties);\nvoid callback__on_message(struct mosquitto *mosq, const struct mosquitto_message *message, const mosquitto_property *properties);\nvoid callback__on_subscribe(struct mosquitto *mosq, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props);\nvoid callback__on_unsubscribe(struct mosquitto *mosq, int mid, int reason_code_count, const int *reason_codes, const mosquitto_property *props);\nvoid callback__on_disconnect(struct mosquitto *mosq, int rc, const mosquitto_property *props);\nint callback__on_ext_auth(struct mosquitto *mosq, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *properties);\n\n#endif\n"
  },
  {
    "path": "lib/connect.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <string.h>\n\n#ifdef WIN32\n#  include <winsock2.h>\n#  include <ws2tcpip.h>\n#else\n#  include <arpa/inet.h>\n#  include <netinet/in.h>\n#endif\n\n#include \"callbacks.h\"\n#include \"http_client.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"logging_mosq.h\"\n#include \"messages_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_common.h\"\n#include \"net_mosq.h\"\n#include \"send_mosq.h\"\n#include \"socks_mosq.h\"\n#include \"util_mosq.h\"\n\nstatic char alphanum[] = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\n\nstatic int mosquitto__reconnect(struct mosquitto *mosq, bool blocking);\nstatic int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive);\n\n\nstatic int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive)\n{\n\tint i;\n\tint rc;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!host || port < 0 || port > UINT16_MAX){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(keepalive != 0 && (keepalive < 5 || keepalive > UINT16_MAX)){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* Only MQTT v3.1 requires a client id to be sent */\n\tif(mosq->id == NULL && (mosq->protocol == mosq_p_mqtt31)){\n\t\tmosq->id = (char *)mosquitto_calloc(24, sizeof(char));\n\t\tif(!mosq->id){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tmosq->id[0] = 'm';\n\t\tmosq->id[1] = 'o';\n\t\tmosq->id[2] = 's';\n\t\tmosq->id[3] = 'q';\n\t\tmosq->id[4] = '-';\n\n\t\trc = mosquitto_getrandom(&mosq->id[5], 18);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n\t\tfor(i=5; i<23; i++){\n\t\t\tmosq->id[i] = alphanum[(mosq->id[i]&0x7F)%(sizeof(alphanum)-1)];\n\t\t}\n\t}\n\n\tmosquitto_FREE(mosq->host);\n\tmosq->host = mosquitto_strdup(host);\n\tif(!mosq->host){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tmosq->port = (uint16_t)port;\n\n\tmosq->keepalive = (uint16_t)keepalive;\n\tmosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum;\n\tmosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;\n\tmosq->retain_available = 1;\n\tmosquitto__set_request_disconnect(mosq, false);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive)\n{\n\treturn mosquitto_connect_bind(mosq, host, port, keepalive, NULL);\n}\n\n\nint mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address)\n{\n\treturn mosquitto_connect_bind_v5(mosq, host, port, keepalive, bind_address, NULL);\n}\n\n\nint mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties)\n{\n\tint rc;\n\n\tif(bind_address){\n\t\trc = mosquitto_string_option(mosq, MOSQ_OPT_BIND_ADDRESS, bind_address);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tmosquitto_property_free_all(&mosq->connect_properties);\n\tif(properties){\n\t\trc = mosquitto_property_check_all(CMD_CONNECT, properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n\t\trc = mosquitto_property_copy_all(&mosq->connect_properties, properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\tmosq->connect_properties->client_generated = true;\n\t}\n\n\trc = mosquitto__connect_init(mosq, host, port, keepalive);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tmosquitto__set_state(mosq, mosq_cs_new);\n\n\treturn mosquitto__reconnect(mosq, true);\n}\n\n\nint mosquitto_connect_async(struct mosquitto *mosq, const char *host, int port, int keepalive)\n{\n\treturn mosquitto_connect_bind_async(mosq, host, port, keepalive, NULL);\n}\n\n\nint mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address)\n{\n\tint rc;\n\n\tif(bind_address){\n\t\trc = mosquitto_string_option(mosq, MOSQ_OPT_BIND_ADDRESS, bind_address);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\trc = mosquitto__connect_init(mosq, host, port, keepalive);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\treturn mosquitto__reconnect(mosq, false);\n}\n\n\nint mosquitto_reconnect_async(struct mosquitto *mosq)\n{\n\treturn mosquitto__reconnect(mosq, false);\n}\n\n\nint mosquitto_reconnect(struct mosquitto *mosq)\n{\n\treturn mosquitto__reconnect(mosq, true);\n}\n\n\nint get_address(mosq_sock_t sock, char *buf, size_t len, uint16_t *remote_port)\n{\n\tstruct sockaddr_storage addr;\n\tsocklen_t addrlen;\n\n\tmemset(&addr, 0, sizeof(struct sockaddr_storage));\n\taddrlen = sizeof(addr);\n\tif(!getpeername(sock, (struct sockaddr *)&addr, &addrlen)){\n\t\tif(addr.ss_family == AF_INET){\n\t\t\tif(remote_port){\n\t\t\t\t*remote_port = ntohs(((struct sockaddr_in *)&addr)->sin_port);\n\t\t\t}\n\t\t\tif(inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr.s_addr, buf, (socklen_t)len)){\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}else if(addr.ss_family == AF_INET6){\n\t\t\tif(remote_port){\n\t\t\t\t*remote_port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port);\n\t\t\t}\n\t\t\tif(inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, buf, (socklen_t)len)){\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\treturn 1;\n}\n\n\nstatic int mosquitto__reconnect(struct mosquitto *mosq, bool blocking)\n{\n\tconst mosquitto_property *outgoing_properties = NULL;\n\tmosquitto_property local_property;\n\tint rc;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!mosq->host){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(mosq->connect_properties){\n\t\tif(mosq->protocol != mosq_p_mqtt5){\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t\t}\n\n\t\tif(mosq->connect_properties->client_generated){\n\t\t\toutgoing_properties = mosq->connect_properties;\n\t\t}else{\n\t\t\tmemcpy(&local_property, mosq->connect_properties, sizeof(mosquitto_property));\n\t\t\tlocal_property.client_generated = true;\n\t\t\tlocal_property.next = NULL;\n\t\t\toutgoing_properties = &local_property;\n\t\t}\n\t\trc = mosquitto_property_check_all(CMD_CONNECT, outgoing_properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tCOMPAT_pthread_mutex_lock(&mosq->msgtime_mutex);\n\tmosq->last_msg_in = mosquitto_time();\n\tmosq->next_msg_out = mosq->last_msg_in + mosq->keepalive;\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgtime_mutex);\n\n\tmosq->ping_t = 0;\n\n\tpacket__cleanup(&mosq->in_packet);\n\n\tpacket__cleanup_all(mosq);\n\n\tmessage__reconnect_reset(mosq, false);\n\n\tif(net__is_connected(mosq)){\n\t\tnet__socket_close(mosq);\n\t}\n\n\tcallback__on_pre_connect(mosq);\n\n#ifdef WITH_SOCKS\n\tif(mosq->socks5_host){\n\t\trc = net__socket_connect(mosq, mosq->socks5_host, mosq->socks5_port, mosq->bind_address, blocking);\n\t}else\n#endif\n\t{\n\t\trc = net__socket_connect(mosq, mosq->host, mosq->port, mosq->bind_address, blocking);\n\t}\n\tchar address[1024];\n\tuint16_t port;\n\tget_address(mosq->sock, address, 1024, &port);\n\tif(rc>0){\n\t\tmosquitto__set_state(mosq, mosq_cs_connect_pending);\n\t\treturn rc;\n\t}\n\n#ifdef WITH_SOCKS\n\tif(mosq->socks5_host){\n\t\tmosquitto__set_state(mosq, mosq_cs_socks5_new);\n\t\treturn socks5__send(mosq);\n\t}else\n#endif\n\t{\n\t\tmosquitto__set_state(mosq, mosq_cs_connected);\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\tif(mosq->transport == mosq_t_ws){\n\t\t\thttp_c__context_init(mosq);\n\t\t}else\n#endif\n\t\t{\n\t\t\trc = send__connect(mosq, mosq->keepalive, mosq->clean_start, outgoing_properties);\n\t\t\tif(rc){\n\t\t\t\tpacket__cleanup_all(mosq);\n\t\t\t\tnet__socket_close(mosq);\n\t\t\t}\n\t\t}\n\t\treturn rc;\n\t}\n}\n\n\nint mosquitto_disconnect(struct mosquitto *mosq)\n{\n\treturn mosquitto_disconnect_v5(mosq, 0, NULL);\n}\n\n\nint mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties)\n{\n\tconst mosquitto_property *outgoing_properties = NULL;\n\tmosquitto_property local_property;\n\tint rc;\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosq->protocol != mosq_p_mqtt5 && properties){\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}\n\tif(reason_code < 0 || reason_code > UINT8_MAX){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(properties){\n\t\tif(properties->client_generated){\n\t\t\toutgoing_properties = properties;\n\t\t}else{\n\t\t\tmemcpy(&local_property, properties, sizeof(mosquitto_property));\n\t\t\tlocal_property.client_generated = true;\n\t\t\tlocal_property.next = NULL;\n\t\t\toutgoing_properties = &local_property;\n\t\t}\n\t\trc = mosquitto_property_check_all(CMD_DISCONNECT, outgoing_properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tmosquitto__set_state(mosq, mosq_cs_disconnected);\n\tmosquitto__set_request_disconnect(mosq, true);\n\tif(!net__is_connected(mosq)){\n\t\treturn MOSQ_ERR_NO_CONN;\n\t}else{\n\t\treturn send__disconnect(mosq, (uint8_t)reason_code, outgoing_properties);\n\t}\n}\n\n\nvoid do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties)\n{\n\tmosquitto__set_state(mosq, mosq_cs_disconnected);\n\tnet__socket_close(mosq);\n\n\t/* Free data and reset values */\n\tpacket__cleanup_all(mosq);\n\n\tCOMPAT_pthread_mutex_lock(&mosq->msgtime_mutex);\n\tmosq->next_msg_out = mosquitto_time() + mosq->keepalive;\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgtime_mutex);\n\n\tcallback__on_disconnect(mosq, reason_code, properties);\n}\n\n"
  },
  {
    "path": "lib/cpp/CMakeLists.txt",
    "content": "set(CPP_SRC mosquittopp.cpp ../../include/mosquitto/libmosquittopp.h)\n\nadd_library(mosquittopp SHARED\n\t${CPP_SRC}\n)\n\nset_target_properties(mosquittopp PROPERTIES\n\tPOSITION_INDEPENDENT_CODE 1\n)\n\ntarget_include_directories(mosquittopp\n\tPUBLIC\n\t\t\"${mosquitto_SOURCE_DIR}/include\"\n)\n\ntarget_link_libraries(mosquittopp\n\tPUBLIC libmosquitto\n\tPRIVATE common-options\n)\n\nif (WITH_THREADING AND NOT WIN32)\n\tset(THREADS_PREFER_PTHREAD_FLAG ON)\n\tfind_package(Threads REQUIRED)\n\n\ttarget_link_libraries(mosquittopp PRIVATE Threads::Threads)\nendif()\n\nset_target_properties(mosquittopp PROPERTIES\n\tVERSION ${VERSION}\n\tSOVERSION 1\n)\ninstall(TARGETS mosquittopp\n\tRUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n\tARCHIVE DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n\tLIBRARY DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n)\n\nif(WITH_STATIC_LIBRARIES)\n\tadd_library(mosquittopp_static STATIC\n\t\t${C_SRC}\n\t\t${CPP_SRC}\n\t)\n\tif(WITH_PIC)\n\t\tset_target_properties(mosquittopp_static PROPERTIES\n\t\t\tPOSITION_INDEPENDENT_CODE 1\n\t\t)\n\tendif()\n\n\ttarget_include_directories(mosquittopp_static PRIVATE\n\t\t\"${mosquitto_SOURCE_DIR}/include\"\n\t\t\"${mosquitto_SOURCE_DIR}/lib\"\n\t\t\"${mosquitto_SOURCE_DIR}/lib/cpp\"\n\t)\n\n\ttarget_link_libraries(mosquittopp_static PRIVATE ${LIBRARIES})\n\n\tset_target_properties(mosquittopp_static PROPERTIES\n\t\tOUTPUT_NAME mosquittopp_static\n\t\tVERSION ${VERSION}\n\t)\n\n\ttarget_compile_definitions(mosquittopp_static PUBLIC \"LIBMOSQUITTO_STATIC\")\n\tinstall(TARGETS mosquittopp_static\n\t\tARCHIVE DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n\t)\nendif()\n"
  },
  {
    "path": "lib/cpp/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\nifeq ($(or $(findstring $(UNAME),SunOS), $(findstring $(UNAME),AIX)),)\nLOCAL_LDFLAGS+=-Wl,-soname,libmosquittopp.so.${SOVERSION}\nendif\nLOCAL_CPPFLAGS+=\nLOCAL_CXXFLAGS+=-fPIC\nLOCAL_LIBADD+=\n\n.PHONY : clean install\n\nALL_DEPS=libmosquittopp.so.${SOVERSION}\n\nifeq ($(WITH_STATIC_LIBRARIES),yes)\n    ALL_DEPS+=libmosquittopp.a\nendif\n\nall : ${ALL_DEPS}\n\ninstall : all\n\t$(INSTALL) -d \"${DESTDIR}${libdir}/\"\n\t$(INSTALL) ${STRIP_OPTS} libmosquittopp.so.${SOVERSION} \"${DESTDIR}${libdir}/libmosquittopp.so.${SOVERSION}\"\n\tln -sf libmosquittopp.so.${SOVERSION} \"${DESTDIR}${libdir}/libmosquittopp.so\"\nifeq ($(WITH_STATIC_LIBRARIES),yes)\n\t$(INSTALL) libmosquittopp.a \"${DESTDIR}${libdir}/libmosquittopp.a\"\nifneq ($(UNAME),AIX)\n\t${CROSS_COMPILE}${STRIP} -g --strip-unneeded \"${DESTDIR}${libdir}/libmosquittopp.a\"\nendif\nendif\n\t$(INSTALL) -d \"${DESTDIR}${libdir}/pkgconfig/\"\n\t$(INSTALL) -m644 ${R}/libmosquittopp.pc.in \"${DESTDIR}${libdir}/pkgconfig/libmosquittopp.pc\"\n\tsed ${SEDINPLACE} -e \"s#@CMAKE_INSTALL_PREFIX@#${prefix}#\" -e \"s#@CMAKE_INSTALL_LIBDIR@#lib${LIB_SUFFIX}#\" -e \"s#@VERSION@#${VERSION}#\" \"${DESTDIR}${libdir}/pkgconfig/libmosquittopp.pc\"\n\nuninstall :\n\t-rm -f \"${DESTDIR}${libdir}/libmosquittopp.so.${SOVERSION}\"\n\t-rm -f \"${DESTDIR}${libdir}/libmosquittopp.so\"\n\t-rm -f \"${DESTDIR}${libdir}/libmosquittopp.a\"\n\nclean :\n\t-rm -f *.o libmosquittopp.so.${SOVERSION} libmosquittopp.a\n\nlibmosquittopp.so.${SOVERSION} : mosquittopp.o\n\t${CROSS_COMPILE}$(CXX) -shared $(LOCAL_LDFLAGS) $< -o $@ ../libmosquitto.so.${SOVERSION} $(LOCAL_LIDADD)\n\nlibmosquittopp.a : mosquittopp.o\n\t${CROSS_COMPILE}$(AR) cr $@ $^\n\nmosquittopp.o : mosquittopp.cpp ${R}/include/mosquitto/libmosquittopp.h\n\t${CROSS_COMPILE}$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_CXXFLAGS) -c $< -o $@\n"
  },
  {
    "path": "lib/cpp/mosquittopp.cpp",
    "content": "/*\nCopyright (c) 2010-2019 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cstdlib>\n#include <mosquitto.h>\n#include <mosquitto/libmosquittopp.h>\n\n#define UNUSED(A) (void)(A)\n\nnamespace mosqpp{\n\n\nstatic void on_pre_connect_wrapper(struct mosquitto *mosq, void *userdata)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\n\tUNUSED(mosq);\n\n\tm->on_pre_connect();\n}\n\n\nstatic void on_connect_wrapper(struct mosquitto *mosq, void *userdata, int rc)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\n\tUNUSED(mosq);\n\n\tm->on_connect(rc);\n}\n\n\nstatic void on_connect_with_flags_wrapper(struct mosquitto *mosq, void *userdata, int rc, int flags)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_connect_with_flags(rc, flags);\n}\n\n\nstatic void on_connect_v5_wrapper(struct mosquitto *mosq, void *userdata, int rc, int flags, const mosquitto_property *props)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_connect_v5(rc, flags, props);\n}\n\n\nstatic void on_disconnect_wrapper(struct mosquitto *mosq, void *userdata, int rc)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_disconnect(rc);\n}\n\n\nstatic void on_disconnect_v5_wrapper(struct mosquitto *mosq, void *userdata, int rc, const mosquitto_property *props)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_disconnect_v5(rc, props);\n}\n\n\nstatic void on_publish_wrapper(struct mosquitto *mosq, void *userdata, int mid)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_publish(mid);\n}\n\n\nstatic void on_publish_v5_wrapper(struct mosquitto *mosq, void *userdata, int mid, int reason_code, const mosquitto_property *props)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_publish_v5(mid, reason_code, props);\n}\n\n\nstatic void on_message_wrapper(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_message(message);\n}\n\n\nstatic void on_message_v5_wrapper(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message, const mosquitto_property *props)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_message_v5(message, props);\n}\n\n\nstatic void on_subscribe_wrapper(struct mosquitto *mosq, void *userdata, int mid, int qos_count, const int *granted_qos)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_subscribe(mid, qos_count, granted_qos);\n}\n\n\nstatic void on_subscribe_v5_wrapper(struct mosquitto *mosq, void *userdata, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_subscribe_v5(mid, qos_count, granted_qos, props);\n}\n\n\nstatic void on_unsubscribe_wrapper(struct mosquitto *mosq, void *userdata, int mid)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_unsubscribe(mid);\n}\n\n\nstatic void on_unsubscribe_v5_wrapper(struct mosquitto *mosq, void *userdata, int mid, const mosquitto_property *props)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_unsubscribe_v5(mid, props);\n}\n\n\nstatic int on_ext_auth_wrapper(struct mosquitto *mosq, void *userdata, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *props)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\treturn m->on_ext_auth(auth_method, auth_data_len, auth_data, props);\n}\n\n\nstatic void on_log_wrapper(struct mosquitto *mosq, void *userdata, int level, const char *str)\n{\n\tclass mosquittopp *m = (class mosquittopp *) userdata;\n\tUNUSED(mosq);\n\tm->on_log(level, str);\n}\n\n\nint lib_version(int *major, int *minor, int *revision)\n{\n\tif(major){\n\t\t*major = LIBMOSQUITTO_MAJOR;\n\t}\n\tif(minor){\n\t\t*minor = LIBMOSQUITTO_MINOR;\n\t}\n\tif(revision){\n\t\t*revision = LIBMOSQUITTO_REVISION;\n\t}\n\treturn LIBMOSQUITTO_VERSION_NUMBER;\n}\n\n\nint lib_init()\n{\n\treturn mosquitto_lib_init();\n}\n\n\nint lib_cleanup()\n{\n\treturn mosquitto_lib_cleanup();\n}\n\n\nconst char *strerror(int mosq_errno)\n{\n\treturn mosquitto_strerror(mosq_errno);\n}\n\n\nconst char *connack_string(int connack_code)\n{\n\treturn mosquitto_connack_string(connack_code);\n}\n\n\nint property_check_command(int command, int identifier)\n{\n\treturn mosquitto_property_check_command(command, identifier);\n}\n\n\nint property_check_all(int command, const mosquitto_property *properties)\n{\n\treturn mosquitto_property_check_all(command, properties);\n}\n\n\nconst char *reason_string(int reason_code)\n{\n\treturn mosquitto_reason_string(reason_code);\n}\n\n\nint sub_topic_tokenise(const char *subtopic, char ***topics, int *count)\n{\n\treturn mosquitto_sub_topic_tokenise(subtopic, topics, count);\n}\n\n\nint sub_topic_tokens_free(char ***topics, int count)\n{\n\treturn mosquitto_sub_topic_tokens_free(topics, count);\n}\n\n\nint topic_matches_sub(const char *sub, const char *topic, bool *result)\n{\n\treturn mosquitto_topic_matches_sub(sub, topic, result);\n}\n\n\nint topic_matches_sub_with_pattern(const char *sub, const char *topic, const char *clientid, const char *username, bool *result)\n{\n\treturn mosquitto_topic_matches_sub_with_pattern(sub, topic, clientid, username, result);\n}\n\n\nint sub_matches_acl(const char *acl, const char *sub, bool *result)\n{\n\treturn mosquitto_sub_matches_acl(acl, sub, result);\n}\n\n\nint sub_matches_acl_with_pattern(const char *acl, const char *sub, const char *clientid, const char *username, bool *result)\n{\n\treturn mosquitto_sub_matches_acl_with_pattern(acl, sub, clientid, username, result);\n}\n\n\nint validate_utf8(const char *str, int len)\n{\n\treturn mosquitto_validate_utf8(str, len);\n}\n\n\nint subscribe_simple(\n\t\tstruct mosquitto_message **messages,\n\t\tint msg_count,\n\t\tbool retained,\n\t\tconst char *topic,\n\t\tint qos,\n\t\tconst char *host,\n\t\tint port,\n\t\tconst char *clientid,\n\t\tint keepalive,\n\t\tbool clean_session,\n\t\tconst char *username,\n\t\tconst char *password,\n\t\tconst struct libmosquitto_will *will,\n\t\tconst struct libmosquitto_tls *tls)\n{\n\treturn mosquitto_subscribe_simple(\n\t\t\tmessages, msg_count, retained,\n\t\t\ttopic, qos,\n\t\t\thost, port, clientid, keepalive, clean_session,\n\t\t\tusername, password,\n\t\t\twill, tls);\n}\n\n\nmosqpp_EXPORT int subscribe_callback(\n\t\tint (*callback)(struct mosquitto *, void *, const struct mosquitto_message *),\n\t\tvoid *userdata,\n\t\tconst char *topic,\n\t\tint qos,\n\t\tconst char *host,\n\t\tint port,\n\t\tconst char *clientid,\n\t\tint keepalive,\n\t\tbool clean_session,\n\t\tconst char *username,\n\t\tconst char *password,\n\t\tconst struct libmosquitto_will *will,\n\t\tconst struct libmosquitto_tls *tls)\n{\n\treturn mosquitto_subscribe_callback(\n\t\t\tcallback, userdata,\n\t\t\ttopic, qos,\n\t\t\thost, port, clientid, keepalive, clean_session,\n\t\t\tusername, password,\n\t\t\twill, tls);\n}\n\nnamespace {\n\n\nvoid mosquitto_callbacks_set(struct mosquitto *mosq)\n{\n\tmosquitto_pre_connect_callback_set(mosq, on_pre_connect_wrapper);\n\tmosquitto_connect_callback_set(mosq, on_connect_wrapper);\n\tmosquitto_connect_with_flags_callback_set(mosq, on_connect_with_flags_wrapper);\n\tmosquitto_connect_v5_callback_set(mosq, on_connect_v5_wrapper);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect_wrapper);\n\tmosquitto_disconnect_v5_callback_set(mosq, on_disconnect_v5_wrapper);\n\tmosquitto_publish_callback_set(mosq, on_publish_wrapper);\n\tmosquitto_publish_v5_callback_set(mosq, on_publish_v5_wrapper);\n\tmosquitto_message_callback_set(mosq, on_message_wrapper);\n\tmosquitto_message_v5_callback_set(mosq, on_message_v5_wrapper);\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe_wrapper);\n\tmosquitto_subscribe_v5_callback_set(mosq, on_subscribe_v5_wrapper);\n\tmosquitto_unsubscribe_callback_set(mosq, on_unsubscribe_wrapper);\n\tmosquitto_unsubscribe_v5_callback_set(mosq, on_unsubscribe_v5_wrapper);\n\tmosquitto_ext_auth_callback_set(mosq, on_ext_auth_wrapper);\n\tmosquitto_log_callback_set(mosq, on_log_wrapper);\n}\n}\n\nmosquittopp::mosquittopp(const char *id, bool clean_session)\n{\n\tm_mosq = mosquitto_new(id, clean_session, this);\n\tif(m_mosq){\n\t\tmosquitto_callbacks_set(m_mosq);\n\t}\n}\n\nmosquittopp::~mosquittopp()\n{\n\tmosquitto_destroy(m_mosq);\n}\n\n\nint mosquittopp::reinitialise(const char *id, bool clean_session)\n{\n\tint rc;\n\trc = mosquitto_reinitialise(m_mosq, id, clean_session, this);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tmosquitto_callbacks_set(m_mosq);\n\t}\n\treturn rc;\n}\n\n\nint mosquittopp::connect(const char *host, int port, int keepalive)\n{\n\treturn mosquitto_connect(m_mosq, host, port, keepalive);\n}\n\n\nint mosquittopp::connect(const char *host, int port, int keepalive, const char *bind_address)\n{\n\treturn mosquitto_connect_bind(m_mosq, host, port, keepalive, bind_address);\n}\n\n\nint mosquittopp::connect_v5(const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties)\n{\n\treturn mosquitto_connect_bind_v5(m_mosq, host, port, keepalive, bind_address, properties);\n}\n\n\nint mosquittopp::connect_async(const char *host, int port, int keepalive)\n{\n\treturn mosquitto_connect_async(m_mosq, host, port, keepalive);\n}\n\n\nint mosquittopp::connect_async(const char *host, int port, int keepalive, const char *bind_address)\n{\n\treturn mosquitto_connect_bind_async(m_mosq, host, port, keepalive, bind_address);\n}\n\n\nint mosquittopp::reconnect()\n{\n\treturn mosquitto_reconnect(m_mosq);\n}\n\n\nint mosquittopp::reconnect_async()\n{\n\treturn mosquitto_reconnect_async(m_mosq);\n}\n\n\nint mosquittopp::disconnect()\n{\n\treturn mosquitto_disconnect(m_mosq);\n}\n\n\nint mosquittopp::disconnect_v5(int reason_code, const mosquitto_property *properties)\n{\n\treturn mosquitto_disconnect_v5(m_mosq, reason_code, properties);\n}\n\n\nint mosquittopp::ext_auth_continue(const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *properties)\n{\n\treturn mosquitto_ext_auth_continue(m_mosq, auth_method, auth_data_len, auth_data, properties);\n}\n\n\nint mosquittopp::socket()\n{\n\treturn mosquitto_socket(m_mosq);\n}\n\n\nint mosquittopp::will_set(const char *topic, int payloadlen, const void *payload, int qos, bool retain)\n{\n\treturn mosquitto_will_set(m_mosq, topic, payloadlen, payload, qos, retain);\n}\n\n\nint mosquittopp::will_set_v5(const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties)\n{\n\treturn mosquitto_will_set_v5(m_mosq, topic, payloadlen, payload, qos, retain, properties);\n}\n\n\nint mosquittopp::will_clear()\n{\n\treturn mosquitto_will_clear(m_mosq);\n}\n\n\nint mosquittopp::username_pw_set(const char *username, const char *password)\n{\n\treturn mosquitto_username_pw_set(m_mosq, username, password);\n}\n\n\nint mosquittopp::publish(int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain)\n{\n\treturn mosquitto_publish(m_mosq, mid, topic, payloadlen, payload, qos, retain);\n}\n\n\nint mosquittopp::publish_v5(int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, const mosquitto_property *properties)\n{\n\treturn mosquitto_publish_v5(m_mosq, mid, topic, payloadlen, payload, qos, retain, properties);\n}\n\n\nvoid mosquittopp::reconnect_delay_set(unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff)\n{\n\tmosquitto_reconnect_delay_set(m_mosq, reconnect_delay, reconnect_delay_max, reconnect_exponential_backoff);\n}\n\n\nint mosquittopp::max_inflight_messages_set(unsigned int max_inflight_messages)\n{\n\treturn mosquitto_max_inflight_messages_set(m_mosq, max_inflight_messages);\n}\n\n\nvoid mosquittopp::message_retry_set(unsigned int message_retry)\n{\n\tmosquitto_message_retry_set(m_mosq, message_retry);\n}\n\n\nint mosquittopp::subscribe(int *mid, const char *sub, int qos)\n{\n\treturn mosquitto_subscribe(m_mosq, mid, sub, qos);\n}\n\n\nint mosquittopp::subscribe_v5(int *mid, const char *sub, int qos, int options, const mosquitto_property *properties)\n{\n\treturn mosquitto_subscribe_v5(m_mosq, mid, sub, qos, options, properties);\n}\n\n\nint mosquittopp::unsubscribe(int *mid, const char *sub)\n{\n\treturn mosquitto_unsubscribe(m_mosq, mid, sub);\n}\n\n\nint mosquittopp::unsubscribe_v5(int *mid, const char *sub, const mosquitto_property *properties)\n{\n\treturn mosquitto_unsubscribe_v5(m_mosq, mid, sub, properties);\n}\n\n\nint mosquittopp::loop(int timeout, int max_packets)\n{\n\treturn mosquitto_loop(m_mosq, timeout, max_packets);\n}\n\n\nint mosquittopp::loop_misc()\n{\n\treturn mosquitto_loop_misc(m_mosq);\n}\n\n\nint mosquittopp::loop_read(int max_packets)\n{\n\treturn mosquitto_loop_read(m_mosq, max_packets);\n}\n\n\nint mosquittopp::loop_write(int max_packets)\n{\n\treturn mosquitto_loop_write(m_mosq, max_packets);\n}\n\n\nint mosquittopp::loop_forever(int timeout, int max_packets)\n{\n\treturn mosquitto_loop_forever(m_mosq, timeout, max_packets);\n}\n\n\nint mosquittopp::loop_start()\n{\n\treturn mosquitto_loop_start(m_mosq);\n}\n\n\nint mosquittopp::loop_stop(bool force)\n{\n\treturn mosquitto_loop_stop(m_mosq, force);\n}\n\n\nbool mosquittopp::want_write()\n{\n\treturn mosquitto_want_write(m_mosq);\n}\n\n\nint mosquittopp::opts_set(enum mosq_opt_t option, void *value)\n{\n\treturn mosquitto_opts_set(m_mosq, option, value);\n}\n\n\nint mosquittopp::int_option(enum mosq_opt_t option, int value)\n{\n\treturn mosquitto_int_option(m_mosq, option, value);\n}\n\n\nint mosquittopp::string_option(enum mosq_opt_t option, const char *value)\n{\n\treturn mosquitto_string_option(m_mosq, option, value);\n}\n\n\nint mosquittopp::void_option(enum mosq_opt_t option, void *value)\n{\n\treturn mosquitto_void_option(m_mosq, option, value);\n}\n\n\nint mosquittopp::threaded_set(bool threaded)\n{\n\treturn mosquitto_threaded_set(m_mosq, threaded);\n}\n\n\nvoid mosquittopp::user_data_set(void *userdata)\n{\n\tmosquitto_user_data_set(m_mosq, userdata);\n}\n\n\nint mosquittopp::socks5_set(const char *host, int port, const char *username, const char *password)\n{\n\treturn mosquitto_socks5_set(m_mosq, host, port, username, password);\n}\n\n\nint mosquittopp::tls_set(const char *cafile, const char *capath, const char *certfile, const char *keyfile, int (*pw_callback)(char *buf, int size, int rwflag, void *userdata))\n{\n\treturn mosquitto_tls_set(m_mosq, cafile, capath, certfile, keyfile, pw_callback);\n}\n\n\nint mosquittopp::tls_opts_set(int cert_reqs, const char *tls_version, const char *ciphers)\n{\n\treturn mosquitto_tls_opts_set(m_mosq, cert_reqs, tls_version, ciphers);\n}\n\n\nint mosquittopp::tls_insecure_set(bool value)\n{\n\treturn mosquitto_tls_insecure_set(m_mosq, value);\n}\n\n\nint mosquittopp::tls_psk_set(const char *psk, const char *identity, const char *ciphers)\n{\n\treturn mosquitto_tls_psk_set(m_mosq, psk, identity, ciphers);\n}\n\n}\n"
  },
  {
    "path": "lib/extended_auth.c",
    "content": "/*\nCopyright (c) 2019-2024 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint mosquitto_ext_auth_continue(struct mosquitto *context, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *input_props)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tint rc;\n\tuint32_t remaining_length;\n\tmosquitto_property *properties = NULL;\n\n\trc = mosquitto_property_copy_all(&properties, input_props);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(!context || context->protocol != mosq_p_mqtt5 || !auth_method){\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tremaining_length = 1;\n\n\trc = mosquitto_property_add_string(&properties, MQTT_PROP_AUTHENTICATION_METHOD, auth_method);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\tif(auth_data != NULL && auth_data_len > 0){\n\t\trc = mosquitto_property_add_binary(&properties, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len);\n\t\tif(rc){\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\tremaining_length += mosquitto_property_get_remaining_length(properties);\n\n\trc = packet__check_oversize(context, remaining_length);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\trc = packet__alloc(&packet, CMD_AUTH, remaining_length);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\tpacket__write_byte(packet, MQTT_RC_CONTINUE_AUTHENTICATION);\n\tproperty__write_all(packet, properties, true);\n\tmosquitto_property_free_all(&properties);\n\n\treturn packet__queue(context, packet);\nerror:\n\tmosquitto_property_free_all(&properties);\n\treturn rc;\n}\n"
  },
  {
    "path": "lib/handle_auth.c",
    "content": "/*\nCopyright (c) 2018-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"callbacks.h\"\n#include \"logging_mosq.h\"\n#include \"mosquitto_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"read_handle.h\"\n\n\nint handle__auth(struct mosquitto *mosq)\n{\n\tint rc = 0;\n\tuint8_t reason_code;\n\tchar *auth_method = NULL;\n\tvoid *auth_data = NULL;\n\tuint16_t auth_data_len = 0;\n\tmosquitto_property *properties = NULL;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received AUTH\", SAFE_PRINT(mosq->id));\n\n\tif(mosq->protocol != mosq_p_mqtt5){\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosq->in_packet.command != CMD_AUTH){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tif(packet__read_byte(&mosq->in_packet, &reason_code)){\n\t\treturn 1;\n\t}\n\n\trc = property__read_all(CMD_AUTH, &mosq->in_packet, &properties);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tmosquitto_property_read_string(properties, MQTT_PROP_AUTHENTICATION_METHOD, &auth_method, false);\n\tmosquitto_property_read_binary(properties, MQTT_PROP_AUTHENTICATION_DATA, &auth_data, &auth_data_len, false);\n\trc = callback__on_ext_auth(mosq, auth_method, auth_data_len, auth_data, properties);\n\tmosquitto_property_free_all(&properties);\n\n\treturn rc;\n}\n"
  },
  {
    "path": "lib/handle_connack.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n\n#include \"callbacks.h\"\n#include \"logging_mosq.h\"\n#include \"messages_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"read_handle.h\"\n#include \"util_mosq.h\"\n\n\nint handle__connack(struct mosquitto *mosq)\n{\n\tuint8_t connect_flags;\n\tuint8_t reason_code;\n\tint rc;\n\tmosquitto_property *properties = NULL;\n\tchar *clientid = NULL;\n\tenum mosquitto_client_state state;\n\n\tassert(mosq);\n\tstate = mosquitto__get_state(mosq);\n\tif(state != mosq_cs_new && state != mosq_cs_connected){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received duplicate CONNACK\", mosq->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(mosq->in_packet.command != CMD_CONNACK\n\t\t\t|| ((mosq->protocol == 3 || mosq->protocol == 4) && mosq->in_packet.remaining_length != 2)){\n\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\trc = packet__read_byte(&mosq->in_packet, &connect_flags);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tif((mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt5) && (connect_flags & 0xFE)){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received CONNACK with invalid connect flags (%d)\", mosq->id, connect_flags);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosq->clean_start && connect_flags){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received CONNACK with session present when clean start was set\", mosq->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\trc = packet__read_byte(&mosq->in_packet, &reason_code);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\trc = property__read_all(CMD_CONNACK, &mosq->in_packet, &properties);\n\n\t\tif(rc == MOSQ_ERR_PROTOCOL && reason_code == CONNACK_REFUSED_PROTOCOL_VERSION){\n\t\t\t/* This could occur because we are connecting to a v3.x broker and\n\t\t\t * it has replied with \"unacceptable protocol version\", but with a\n\t\t\t * v3 CONNACK. */\n\n\t\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received CONNACK (%d)\", mosq->id, reason_code);\n\t\t\tcallback__on_connect(mosq, MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION, connect_flags, NULL);\n\t\t\treturn rc;\n\t\t}else if(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tmosquitto_property_read_string(properties, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, &clientid, false);\n\tif(clientid){\n\t\tif(mosq->id){\n\t\t\t/* We've been sent a client identifier but already have one. This\n\t\t\t * shouldn't happen. */\n\t\t\tmosquitto_FREE(clientid);\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}else{\n\t\t\tmosq->id = clientid;\n\t\t\tclientid = NULL;\n\t\t}\n\t}\n\n\tmosquitto_property_read_byte(properties, MQTT_PROP_RETAIN_AVAILABLE, &mosq->retain_available, false);\n\tmosquitto_property_read_byte(properties, MQTT_PROP_MAXIMUM_QOS, &mosq->max_qos, false);\n\tmosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &mosq->msgs_out.inflight_maximum, false);\n\tmosquitto_property_read_int16(properties, MQTT_PROP_SERVER_KEEP_ALIVE, &mosq->keepalive, false);\n\tmosquitto_property_read_int16(properties, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, &mosq->alias_max_l2r, false);\n\tmosquitto_property_read_int32(properties, MQTT_PROP_MAXIMUM_PACKET_SIZE, &mosq->maximum_packet_size, false);\n\n\tmosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;\n\tmessage__reconnect_reset(mosq, true);\n\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received CONNACK (%d)\", mosq->id, reason_code);\n\tif(reason_code == MQTT_RC_SUCCESS){\n\t\tmosq->reconnects = 0;\n\t}\n\tcallback__on_connect(mosq, reason_code, connect_flags, properties);\n\tmosquitto_property_free_all(&properties);\n\n\tswitch(reason_code){\n\t\tcase 0:\n\t\t\tCOMPAT_pthread_mutex_lock(&mosq->state_mutex);\n\t\t\tif(mosq->state != mosq_cs_disconnecting){\n\t\t\t\tmosq->state = mosq_cs_active;\n\t\t\t}\n\t\t\tCOMPAT_pthread_mutex_unlock(&mosq->state_mutex);\n\t\t\tmessage__retry_check(mosq);\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\tcase 1:\n\t\tcase 2:\n\t\tcase 3:\n\t\tcase 4:\n\t\tcase 5:\n\t\t\treturn MOSQ_ERR_CONN_REFUSED;\n\t\tdefault:\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n}\n\n"
  },
  {
    "path": "lib/handle_disconnect.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"logging_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint handle__disconnect(struct mosquitto *mosq)\n{\n\tint rc;\n\tuint8_t reason_code;\n\tmosquitto_property *properties = NULL;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(mosq->protocol != mosq_p_mqtt5){\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosq->in_packet.command != CMD_DISCONNECT){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\trc = packet__read_byte(&mosq->in_packet, &reason_code);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(mosq->in_packet.remaining_length > 2){\n\t\trc = property__read_all(CMD_DISCONNECT, &mosq->in_packet, &properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Received DISCONNECT (%d)\", reason_code);\n\n\tdo_client_disconnect(mosq, reason_code, properties);\n\n\tmosquitto_property_free_all(&properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n"
  },
  {
    "path": "lib/handle_ping.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#endif\n\n#include \"mosquitto.h\"\n#include \"logging_mosq.h\"\n#include \"messages_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint handle__pingreq(struct mosquitto *mosq)\n{\n\tassert(mosq);\n\n\tif(mosquitto__get_state(mosq) != mosq_cs_active){\n#ifdef WITH_BROKER\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PINGREQ before session is active.\", mosq->id);\n#endif\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosq->in_packet.command != CMD_PINGREQ || mosq->in_packet.remaining_length != 0){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n#ifdef WITH_BROKER\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received PINGREQ from %s\", SAFE_PRINT(mosq->id));\n\treturn send__pingresp(mosq);\n#else\n\treturn MOSQ_ERR_PROTOCOL;\n#endif\n}\n\n\nint handle__pingresp(struct mosquitto *mosq)\n{\n\tassert(mosq);\n\n\tif(mosquitto__get_state(mosq) != mosq_cs_active){\n#ifdef WITH_BROKER\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PINGRESP before session is active.\", mosq->id);\n#endif\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosq->in_packet.command != CMD_PINGRESP || mosq->in_packet.remaining_length != 0){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tmosq->ping_t = 0; /* No longer waiting for a PINGRESP. */\n#ifdef WITH_BROKER\n\tif(mosq->bridge == NULL){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PINGRESP when not a bridge.\", mosq->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received PINGRESP from %s\", SAFE_PRINT(mosq->id));\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received PINGRESP\", SAFE_PRINT(mosq->id));\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n"
  },
  {
    "path": "lib/handle_pubackcomp.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#endif\n\n#include \"callbacks.h\"\n#include \"mosquitto.h\"\n#include \"logging_mosq.h\"\n#include \"messages_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint handle__pubackcomp(struct mosquitto *mosq, const char *type)\n{\n\tuint8_t reason_code = 0;\n\tuint16_t mid;\n\tint rc;\n\tmosquitto_property *properties = NULL;\n\tint qos;\n\n\tassert(mosq);\n\n\tif(mosquitto__get_state(mosq) != mosq_cs_active){\n#ifdef WITH_BROKER\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: %s before session is active.\", mosq->id, type);\n#endif\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosq->protocol != mosq_p_mqtt31){\n\t\tif((mosq->in_packet.command&0x0F) != 0x00){\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t}\n\n\tCOMPAT_pthread_mutex_lock(&mosq->msgs_out.mutex);\n\tutil__increment_send_quota(mosq);\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n\n\trc = packet__read_uint16(&mosq->in_packet, &mid);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tif(type[3] == 'A'){ /* pubAck or pubComp */\n\t\tif(mosq->in_packet.command != CMD_PUBACK){\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t\tqos = 1;\n\t}else{\n\t\tif(mosq->in_packet.command != CMD_PUBCOMP){\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t\tqos = 2;\n\t}\n\tif(mid == 0){\n#ifdef WITH_BROKER\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: %s with mid = 0.\", mosq->id, type);\n#endif\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){\n\t\trc = packet__read_byte(&mosq->in_packet, &reason_code);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n\t\tif(mosq->in_packet.remaining_length > 3){\n\t\t\trc = property__read_all(CMD_PUBACK, &mosq->in_packet, &properties);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t\tif(type[3] == 'A'){ /* pubAck or pubComp */\n\t\t\tif(reason_code != MQTT_RC_SUCCESS\n\t\t\t\t\t&& reason_code != MQTT_RC_NO_MATCHING_SUBSCRIBERS\n\t\t\t\t\t&& reason_code != MQTT_RC_UNSPECIFIED\n\t\t\t\t\t&& reason_code != MQTT_RC_IMPLEMENTATION_SPECIFIC\n\t\t\t\t\t&& reason_code != MQTT_RC_NOT_AUTHORIZED\n\t\t\t\t\t&& reason_code != MQTT_RC_TOPIC_NAME_INVALID\n\t\t\t\t\t&& reason_code != MQTT_RC_PACKET_ID_IN_USE\n\t\t\t\t\t&& reason_code != MQTT_RC_QUOTA_EXCEEDED\n\t\t\t\t\t&& reason_code != MQTT_RC_PAYLOAD_FORMAT_INVALID\n\t\t\t\t\t){\n\n\t\t\t\tmosquitto_property_free_all(&properties);\n#ifdef WITH_BROKER\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: %s with reason code = %d.\",\n\t\t\t\t\t\tmosq->id, type, reason_code);\n#endif\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t}else{\n\t\t\tif(reason_code != MQTT_RC_SUCCESS\n\t\t\t\t\t&& reason_code != MQTT_RC_PACKET_ID_NOT_FOUND\n\t\t\t\t\t){\n\n\t\t\t\tmosquitto_property_free_all(&properties);\n#ifdef WITH_BROKER\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: %s with reason code = %d.\",\n\t\t\t\t\t\tmosq->id, type, reason_code);\n#endif\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t}\n\t}\n\tif(mosq->in_packet.pos < mosq->in_packet.remaining_length){\n\t\tmosquitto_property_free_all(&properties);\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n#ifdef WITH_BROKER\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received %s from %s (Mid: %d, RC:%d)\", type, SAFE_PRINT(mosq->id), mid, reason_code);\n\n\t/* Immediately free, we don't do anything with Reason String or User Property at the moment */\n\tmosquitto_property_free_all(&properties);\n\n\trc = db__message_delete_outgoing(mosq, mid, mosq_ms_wait_for_pubcomp, qos);\n\tif(rc == MOSQ_ERR_NOT_FOUND){\n\t\tlog__printf(mosq, MOSQ_LOG_WARNING, \"Warning: Received %s from %s for an unknown packet identifier %d.\", type, SAFE_PRINT(mosq->id), mid);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn rc;\n\t}\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received %s (Mid: %d, RC:%d)\", SAFE_PRINT(mosq->id), type, mid, reason_code);\n\n\trc = message__delete(mosq, mid, mosq_md_out, qos);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t/* Only inform the client the message has been sent once. */\n\t\tcallback__on_publish(mosq, mid, reason_code, properties);\n\t\tmosquitto_property_free_all(&properties);\n\t}else{\n\t\tmosquitto_property_free_all(&properties);\n\n\t\tif(rc != MOSQ_ERR_NOT_FOUND){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tCOMPAT_pthread_mutex_lock(&mosq->msgs_out.mutex);\n\tmessage__release_to_inflight(mosq, mosq_md_out);\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n\n\treturn MOSQ_ERR_SUCCESS;\n#endif\n}\n"
  },
  {
    "path": "lib/handle_publish.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <string.h>\n\n#include \"alias_mosq.h\"\n#include \"callbacks.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"logging_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"messages_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nstatic int property__process_publish(struct mosquitto *mosq, mosquitto_property *props, struct mosquitto_message_all *message, uint16_t *topic_alias)\n{\n\twhile(props){\n\t\tswitch(mosquitto_property_identifier(props)){\n\t\t\tcase MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:\n\t\t\tcase MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:\n\t\t\tcase MQTT_PROP_CONTENT_TYPE:\n\t\t\tcase MQTT_PROP_RESPONSE_TOPIC: //\n\t\t\tcase MQTT_PROP_CORRELATION_DATA:\n\t\t\tcase MQTT_PROP_USER_PROPERTY:\n\t\t\t\t/* Allowed */\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_SUBSCRIPTION_IDENTIFIER:\n\t\t\t\t/* Allowed */\n\t\t\t\tif(mosquitto_property_varint_value(props) == 0){\n\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_TOPIC_ALIAS:\n\t\t\t\t{\n\t\t\t\t\t*topic_alias = mosquitto_property_int16_value(props);\n\t\t\t\t\tif(*topic_alias == 0 || *topic_alias > mosq->alias_max_l2r){\n\t\t\t\t\t\treturn MOSQ_ERR_TOPIC_ALIAS_INVALID;\n\t\t\t\t\t}\n\t\t\t\t\tif(message->msg.topic){\n\t\t\t\t\t\t/* Set a new topic alias */\n\t\t\t\t\t\tif(alias__add_r2l(mosq, message->msg.topic, *topic_alias)){\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\t/* Retrieve an existing topic alias */\n\t\t\t\t\t\tmosquitto_FREE(message->msg.topic);\n\t\t\t\t\t\tif(alias__find_by_alias(mosq, ALIAS_DIR_R2L, *topic_alias, &message->msg.topic)){\n\t\t\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t\tprops = mosquitto_property_next(props);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint handle__publish(struct mosquitto *mosq)\n{\n\tuint8_t header;\n\tstruct mosquitto_message_all *message;\n\tint rc = 0;\n\tuint16_t mid = 0;\n\tuint16_t slen;\n\tmosquitto_property *properties = NULL;\n\tuint16_t topic_alias = 0;\n\n\tassert(mosq);\n\n\tif(mosquitto__get_state(mosq) != mosq_cs_active){\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tmessage = mosquitto_calloc(1, sizeof(struct mosquitto_message_all));\n\tif(!message){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\theader = mosq->in_packet.command;\n\n\tmessage->dup = (header & 0x08)>>3;\n\tmessage->msg.qos = (header & 0x06)>>1;\n\tmessage->msg.retain = (header & 0x01);\n\n\trc = packet__read_string(&mosq->in_packet, &message->msg.topic, &slen);\n\tif(rc){\n\t\tmessage__cleanup(&message);\n\t\treturn rc;\n\t}\n\tif(mosq->protocol != mosq_p_mqtt5 && slen == 0){\n\t\tmessage__cleanup(&message);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(message->msg.qos > 0){\n\t\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t\tif(mosq->msgs_in.inflight_quota == 0){\n\t\t\t\tmessage__cleanup(&message);\n\t\t\t\t/* FIXME - should send a DISCONNECT here */\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t}\n\n\t\trc = packet__read_uint16(&mosq->in_packet, &mid);\n\t\tif(rc){\n\t\t\tmessage__cleanup(&message);\n\t\t\treturn rc;\n\t\t}\n\t\tif(mid == 0){\n\t\t\tmessage__cleanup(&message);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t\tmessage->msg.mid = (int)mid;\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\trc = property__read_all(CMD_PUBLISH, &mosq->in_packet, &properties);\n\t\tif(rc){\n\t\t\tmessage__cleanup(&message);\n\t\t\treturn rc;\n\t\t}\n\n\t\trc = property__process_publish(mosq, properties, message, &topic_alias);\n\t\tif(rc){\n\t\t\tmessage__cleanup(&message);\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\treturn rc;\n\t\t}\n\t}\n\t/* If we haven't got a topic at this point, it's a protocol error. */\n\tif(topic_alias == 0 && message->msg.topic == NULL){\n\t\tmessage__cleanup(&message);\n\t\tmosquitto_property_free_all(&properties);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosquitto_pub_topic_check(message->msg.topic) != MOSQ_ERR_SUCCESS){\n\t\tmessage__cleanup(&message);\n\t\tmosquitto_property_free_all(&properties);\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tmessage->msg.payloadlen = (int)(mosq->in_packet.remaining_length - mosq->in_packet.pos);\n\tif(message->msg.payloadlen){\n\t\tmessage->msg.payload = mosquitto_calloc((size_t)message->msg.payloadlen+1, sizeof(uint8_t));\n\t\tif(!message->msg.payload){\n\t\t\tmessage__cleanup(&message);\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\trc = packet__read_bytes(&mosq->in_packet, message->msg.payload, (uint32_t)message->msg.payloadlen);\n\t\tif(rc){\n\t\t\tmessage__cleanup(&message);\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\treturn rc;\n\t\t}\n\t}\n\tlog__printf(mosq, MOSQ_LOG_DEBUG,\n\t\t\t\"Client %s received PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))\",\n\t\t\tSAFE_PRINT(mosq->id), message->dup, message->msg.qos, message->msg.retain,\n\t\t\tmessage->msg.mid, message->msg.topic,\n\t\t\t(long)message->msg.payloadlen);\n\n\tswitch(message->msg.qos){\n\t\tcase 0:\n\t\t\tcallback__on_message(mosq, &message->msg, properties);\n\t\t\tmessage__cleanup(&message);\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\tcase 1:\n\t\t\tutil__decrement_receive_quota(mosq);\n\t\t\trc = send__puback(mosq, mid, 0, NULL);\n\t\t\tcallback__on_message(mosq, &message->msg, properties);\n\t\t\tmessage__cleanup(&message);\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\treturn rc;\n\t\tcase 2:\n\t\t\tmessage->properties = properties;\n\t\t\tutil__decrement_receive_quota(mosq);\n\t\t\trc = send__pubrec(mosq, mid, 0, NULL);\n\t\t\tCOMPAT_pthread_mutex_lock(&mosq->msgs_in.mutex);\n\t\t\tmessage->state = mosq_ms_wait_for_pubrel;\n\t\t\tmessage__queue(mosq, message, mosq_md_in);\n\t\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_in.mutex);\n\t\t\treturn rc;\n\t\tdefault:\n\t\t\tmessage__cleanup(&message);\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n}\n\n"
  },
  {
    "path": "lib/handle_pubrec.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#endif\n\n#include \"callbacks.h\"\n#include \"mosquitto.h\"\n#include \"logging_mosq.h\"\n#include \"messages_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint handle__pubrec(struct mosquitto *mosq)\n{\n\tuint8_t reason_code = 0;\n\tuint16_t mid;\n\tint rc;\n\tmosquitto_property *properties = NULL;\n\n\tassert(mosq);\n\n\tif(mosquitto__get_state(mosq) != mosq_cs_active){\n#ifdef WITH_BROKER\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBREC before session is active.\", mosq->id);\n#endif\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosq->in_packet.command != CMD_PUBREC){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\trc = packet__read_uint16(&mosq->in_packet, &mid);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tif(mid == 0){\n#ifdef WITH_BROKER\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBREC with mid = 0.\",\n\t\t\t\tmosq->id);\n#endif\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){\n\t\trc = packet__read_byte(&mosq->in_packet, &reason_code);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n\t\tif(reason_code != MQTT_RC_SUCCESS\n\t\t\t\t&& reason_code != MQTT_RC_NO_MATCHING_SUBSCRIBERS\n\t\t\t\t&& reason_code != MQTT_RC_UNSPECIFIED\n\t\t\t\t&& reason_code != MQTT_RC_IMPLEMENTATION_SPECIFIC\n\t\t\t\t&& reason_code != MQTT_RC_NOT_AUTHORIZED\n\t\t\t\t&& reason_code != MQTT_RC_TOPIC_NAME_INVALID\n\t\t\t\t&& reason_code != MQTT_RC_PACKET_ID_IN_USE\n\t\t\t\t&& reason_code != MQTT_RC_QUOTA_EXCEEDED\n\t\t\t\t&& reason_code != MQTT_RC_PAYLOAD_FORMAT_INVALID){\n\n#ifdef WITH_BROKER\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBREC with reason code = %d.\",\n\t\t\t\t\tmosq->id, reason_code);\n#endif\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\n\t\tif(mosq->in_packet.remaining_length > 3){\n\t\t\trc = property__read_all(CMD_PUBREC, &mosq->in_packet, &properties);\n\t\t\tif(rc){\n\t\t\t\tif(rc == MOSQ_ERR_PROTOCOL){\n#ifdef WITH_BROKER\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBREC with invalid properties.\", mosq->id);\n#endif\n\t\t\t\t}\n\t\t\t\treturn rc;\n\t\t\t}\n\n\t\t\t/* Immediately free, we don't do anything with Reason String or User Property at the moment */\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t}\n\t}\n\n\tif(mosq->in_packet.pos < mosq->in_packet.remaining_length){\n#ifdef WITH_BROKER\n\t\tmosquitto_property_free_all(&properties);\n#endif\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n#ifdef WITH_BROKER\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received PUBREC from %s (Mid: %d)\", SAFE_PRINT(mosq->id), mid);\n\n\tif(reason_code < 0x80){\n\t\trc = db__message_update_outgoing(mosq, mid, mosq_ms_wait_for_pubcomp, 2, true);\n\t}else{\n\t\treturn db__message_delete_outgoing(mosq, mid, mosq_ms_wait_for_pubrec, 2);\n\t}\n#else\n\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received PUBREC (Mid: %d)\", SAFE_PRINT(mosq->id), mid);\n\n\tif(reason_code < 0x80 || mosq->protocol != mosq_p_mqtt5){\n\t\trc = message__out_update(mosq, mid, mosq_ms_wait_for_pubcomp, 2);\n\t}else{\n\t\tif(!message__delete(mosq, mid, mosq_md_out, 2)){\n\t\t\t/* Only inform the client the message has been sent once. */\n\t\t\tcallback__on_publish(mosq, mid, reason_code, properties);\n\t\t}\n\t\tutil__increment_send_quota(mosq);\n\t\tCOMPAT_pthread_mutex_lock(&mosq->msgs_out.mutex);\n\t\tmessage__release_to_inflight(mosq, mosq_md_out);\n\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n#endif\n\tif(rc == MOSQ_ERR_NOT_FOUND){\n\t\tlog__printf(mosq, MOSQ_LOG_WARNING, \"Warning: Received PUBREC from %s for an unknown packet identifier %d.\", SAFE_PRINT(mosq->id), mid);\n\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\trc = send__pubrel(mosq, mid, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "lib/handle_pubrel.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#endif\n\n#include \"callbacks.h\"\n#include \"mosquitto.h\"\n#include \"logging_mosq.h\"\n#include \"messages_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint handle__pubrel(struct mosquitto *mosq)\n{\n\tuint8_t reason_code;\n\tuint16_t mid;\n#ifndef WITH_BROKER\n\tstruct mosquitto_message_all *message = NULL;\n#endif\n\tint rc;\n\tmosquitto_property *properties = NULL;\n\n\tassert(mosq);\n\n\tif(mosquitto__get_state(mosq) != mosq_cs_active){\n#ifdef WITH_BROKER\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBREL before session is active.\", mosq->id);\n#endif\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosq->protocol != mosq_p_mqtt31 && mosq->in_packet.command != (CMD_PUBREL|2)){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tif(mosq->protocol != mosq_p_mqtt31){\n\t\tif((mosq->in_packet.command&0x0F) != 0x02){\n#ifdef WITH_BROKER\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBREL with non-zero reserved flags (%02X).\",\n\t\t\t\t\tmosq->id, mosq->in_packet.command);\n#endif\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t}\n\trc = packet__read_uint16(&mosq->in_packet, &mid);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tif(mid == 0){\n#ifdef WITH_BROKER\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBREL with mid = 0.\",\n\t\t\t\tmosq->id);\n#endif\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){\n\t\trc = packet__read_byte(&mosq->in_packet, &reason_code);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n\t\tif(reason_code != MQTT_RC_SUCCESS && reason_code != MQTT_RC_PACKET_ID_NOT_FOUND){\n#ifdef WITH_BROKER\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBREL with reason code = %d.\",\n\t\t\t\t\tmosq->id, reason_code);\n#endif\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\n\t\tif(mosq->in_packet.remaining_length > 3){\n\t\t\trc = property__read_all(CMD_PUBREL, &mosq->in_packet, &properties);\n\t\t\t/* Immediately free, we don't do anything with Reason String or\n\t\t\t * User Property at the moment */\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\tif(rc){\n\t\t\t\tif(rc == MOSQ_ERR_PROTOCOL){\n#ifdef WITH_BROKER\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBREL with invalid properties.\", mosq->id);\n#endif\n\t\t\t\t}\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(mosq->in_packet.pos < mosq->in_packet.remaining_length){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n#ifdef WITH_BROKER\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received PUBREL from %s (Mid: %d)\", SAFE_PRINT(mosq->id), mid);\n\n\trc = db__message_release_incoming(mosq, mid);\n\tif(rc == MOSQ_ERR_NOT_FOUND){\n\t\t/* Message not found. Still send a PUBCOMP anyway because this could be\n\t\t * due to a repeated PUBREL after a client has reconnected. */\n\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\trc = send__pubcomp(mosq, mid, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received PUBREL (Mid: %d)\", SAFE_PRINT(mosq->id), mid);\n\n\trc = send__pubcomp(mosq, mid, NULL);\n\tif(rc){\n\t\tmessage__remove(mosq, mid, mosq_md_in, &message, 2);\n\t\treturn rc;\n\t}\n\n\trc = message__remove(mosq, mid, mosq_md_in, &message, 2);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t/* Only pass the message on if we have removed it from the queue - this\n\t\t * prevents multiple callbacks for the same message. */\n\t\tcallback__on_message(mosq, &message->msg, message->properties);\n\t\tmessage__cleanup(&message);\n\t}else if(rc == MOSQ_ERR_NOT_FOUND){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn rc;\n\t}\n#endif\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "lib/handle_suback.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#endif\n\n#include \"callbacks.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"logging_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"read_handle.h\"\n#include \"util_mosq.h\"\n\n\nint handle__suback(struct mosquitto *mosq)\n{\n\tuint16_t mid;\n\tuint8_t qos;\n\tint *granted_qos;\n\tint qos_count;\n\tint i = 0;\n\tint rc;\n\tmosquitto_property *properties = NULL;\n\n\tassert(mosq);\n\n\tif(mosquitto__get_state(mosq) != mosq_cs_active){\n#ifdef WITH_BROKER\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: SUBACK before session is active.\", mosq->id);\n#endif\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosq->in_packet.command != CMD_SUBACK){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n#ifdef WITH_BROKER\n\tif(mosq->bridge == NULL){\n\t\t/* Client is not a bridge, so shouldn't be sending SUBACK */\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: SUBACK when not a bridge.\", mosq->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received SUBACK from %s\", SAFE_PRINT(mosq->id));\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received SUBACK\", SAFE_PRINT(mosq->id));\n#endif\n\trc = packet__read_uint16(&mosq->in_packet, &mid);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tif(mid == 0){\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\trc = property__read_all(CMD_SUBACK, &mosq->in_packet, &properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tqos_count = (int)(mosq->in_packet.remaining_length - mosq->in_packet.pos);\n\tif(qos_count == 0){\n\t\tmosquitto_property_free_all(&properties);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tgranted_qos = mosquitto_malloc((size_t)qos_count*sizeof(int));\n\tif(!granted_qos){\n\t\tmosquitto_property_free_all(&properties);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\twhile(mosq->in_packet.pos < mosq->in_packet.remaining_length){\n\t\trc = packet__read_byte(&mosq->in_packet, &qos);\n\t\tif(rc){\n\t\t\tmosquitto_FREE(granted_qos);\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\treturn rc;\n\t\t}\n\t\tgranted_qos[i] = (int)qos;\n\t\ti++;\n\t}\n#ifdef WITH_BROKER\n\t/* Immediately free, we don't do anything with Reason String or User Property at the moment */\n\tmosquitto_property_free_all(&properties);\n#else\n\tcallback__on_subscribe(mosq, mid, qos_count, granted_qos, properties);\n\tmosquitto_property_free_all(&properties);\n#endif\n\tmosquitto_FREE(granted_qos);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "lib/handle_unsuback.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#endif\n\n#include \"callbacks.h\"\n#include \"mosquitto.h\"\n#include \"logging_mosq.h\"\n#include \"messages_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint handle__unsuback(struct mosquitto *mosq)\n{\n\tuint16_t mid;\n\tint rc;\n\tmosquitto_property *properties = NULL;\n\tint *reason_codes = NULL;\n\tint reason_code_count = 0;\n\n\tassert(mosq);\n\n\tif(mosquitto__get_state(mosq) != mosq_cs_active){\n#ifdef WITH_BROKER\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: UNSUBACK before session is active.\", mosq->id);\n#endif\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(mosq->in_packet.command != CMD_UNSUBACK){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n#ifdef WITH_BROKER\n\tif(mosq->bridge == NULL){\n\t\t/* Client is not a bridge, so shouldn't be sending SUBACK */\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: UNSUBACK when not a bridge.\", mosq->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received UNSUBACK from %s\", SAFE_PRINT(mosq->id));\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s received UNSUBACK\", SAFE_PRINT(mosq->id));\n#endif\n\trc = packet__read_uint16(&mosq->in_packet, &mid);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tif(mid == 0){\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\trc = property__read_all(CMD_UNSUBACK, &mosq->in_packet, &properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n\t\tuint8_t byte;\n\t\treason_code_count = (int)(mosq->in_packet.remaining_length - mosq->in_packet.pos);\n\t\treason_codes = mosquitto_malloc((size_t)reason_code_count*sizeof(int));\n\t\tif(!reason_codes){\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tfor(int i=0; i<reason_code_count; i++){\n\t\t\trc = packet__read_byte(&mosq->in_packet, &byte);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_FREE(reason_codes);\n\t\t\t\tmosquitto_property_free_all(&properties);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\treason_codes[i] = (int)byte;\n\t\t}\n\t}\n\n#ifndef WITH_BROKER\n\tcallback__on_unsubscribe(mosq, mid, reason_code_count, reason_codes, properties);\n#endif\n\tmosquitto_property_free_all(&properties);\n\tmosquitto_FREE(reason_codes);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "lib/helpers.c",
    "content": "/*\nCopyright (c) 2016-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <stdbool.h>\n\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n\nstruct userdata__callback {\n\tconst char *topic;\n\tint (*callback)(struct mosquitto *, void *, const struct mosquitto_message *);\n\tvoid *userdata;\n\tint qos;\n};\n\nstruct userdata__simple {\n\tstruct mosquitto_message *messages;\n\tint max_msg_count;\n\tint message_count;\n\tbool want_retained;\n};\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\tstruct userdata__callback *userdata = obj;\n\n\tUNUSED(rc);\n\n\tmosquitto_subscribe(mosq, NULL, userdata->topic, userdata->qos);\n}\n\n\nstatic void on_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)\n{\n\tint rc;\n\tstruct userdata__callback *userdata = obj;\n\n\trc = userdata->callback(mosq, userdata->userdata, message);\n\tif(rc){\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic int on_message_simple(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)\n{\n\tstruct userdata__simple *userdata = obj;\n\tint rc;\n\n\tif(userdata->max_msg_count == 0){\n\t\treturn 0;\n\t}\n\n\t/* Don't process stale retained messages if 'want_retained' was false */\n\tif(!userdata->want_retained && message->retain){\n\t\treturn 0;\n\t}\n\n\tuserdata->max_msg_count--;\n\n\trc = mosquitto_message_copy(&userdata->messages[userdata->message_count], message);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tuserdata->message_count++;\n\tif(userdata->max_msg_count == 0){\n\t\tmosquitto_disconnect(mosq);\n\t}\n\treturn 0;\n}\n\n\nlibmosq_EXPORT int mosquitto_subscribe_simple(\n\t\tstruct mosquitto_message **messages,\n\t\tint msg_count,\n\t\tbool want_retained,\n\t\tconst char *topic,\n\t\tint qos,\n\t\tconst char *host,\n\t\tint port,\n\t\tconst char *clientid,\n\t\tint keepalive,\n\t\tbool clean_session,\n\t\tconst char *username,\n\t\tconst char *password,\n\t\tconst struct libmosquitto_will *will,\n\t\tconst struct libmosquitto_tls *tls)\n{\n\tstruct userdata__simple userdata;\n\tint rc;\n\tint i;\n\n\tif(!topic || msg_count < 1 || !messages){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t*messages = NULL;\n\n\tuserdata.messages = mosquitto_calloc((size_t)msg_count, sizeof(struct mosquitto_message));\n\tif(!userdata.messages){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tuserdata.message_count = 0;\n\tuserdata.max_msg_count = msg_count;\n\tuserdata.want_retained = want_retained;\n\n\trc = mosquitto_subscribe_callback(\n\t\t\ton_message_simple, &userdata,\n\t\t\ttopic, qos,\n\t\t\thost, port,\n\t\t\tclientid, keepalive, clean_session,\n\t\t\tusername, password,\n\t\t\twill, tls);\n\n\tif(!rc && userdata.max_msg_count == 0){\n\t\t*messages = userdata.messages;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tfor(i=0; i<msg_count; i++){\n\t\t\tmosquitto_message_free_contents(&userdata.messages[i]);\n\t\t}\n\t\tmosquitto_FREE(userdata.messages);\n\t\treturn rc;\n\t}\n}\n\n\nlibmosq_EXPORT int mosquitto_subscribe_callback(\n\t\tint (*callback)(struct mosquitto *, void *, const struct mosquitto_message *),\n\t\tvoid *userdata,\n\t\tconst char *topic,\n\t\tint qos,\n\t\tconst char *host,\n\t\tint port,\n\t\tconst char *clientid,\n\t\tint keepalive,\n\t\tbool clean_session,\n\t\tconst char *username,\n\t\tconst char *password,\n\t\tconst struct libmosquitto_will *will,\n\t\tconst struct libmosquitto_tls *tls)\n{\n\tstruct mosquitto *mosq;\n\tstruct userdata__callback cb_userdata;\n\tint rc;\n\n\tif(!callback || !topic){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcb_userdata.topic = topic;\n\tcb_userdata.qos = qos;\n\tcb_userdata.userdata = userdata;\n\tcb_userdata.callback = callback;\n\n\tmosq = mosquitto_new(clientid, clean_session, &cb_userdata);\n\tif(!mosq){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(will){\n\t\trc = mosquitto_will_set(mosq, will->topic, will->payloadlen, will->payload, will->qos, will->retain);\n\t\tif(rc){\n\t\t\tmosquitto_destroy(mosq);\n\t\t\treturn rc;\n\t\t}\n\t}\n\tif(username){\n\t\trc = mosquitto_username_pw_set(mosq, username, password);\n\t\tif(rc){\n\t\t\tmosquitto_destroy(mosq);\n\t\t\treturn rc;\n\t\t}\n\t}\n\tif(tls){\n\t\trc = mosquitto_tls_set(mosq, tls->cafile, tls->capath, tls->certfile, tls->keyfile, tls->pw_callback);\n\t\tif(rc){\n\t\t\tmosquitto_destroy(mosq);\n\t\t\treturn rc;\n\t\t}\n\t\trc = mosquitto_tls_opts_set(mosq, tls->cert_reqs, tls->tls_version, tls->ciphers);\n\t\tif(rc){\n\t\t\tmosquitto_destroy(mosq);\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_message_callback_set(mosq, on_message_callback);\n\n\trc = mosquitto_connect(mosq, host, port, keepalive);\n\tif(rc){\n\t\tmosquitto_destroy(mosq);\n\t\treturn rc;\n\t}\n\trc = mosquitto_loop_forever(mosq, -1, 1);\n\tmosquitto_destroy(mosq);\n\treturn rc;\n}\n\n"
  },
  {
    "path": "lib/http_client.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\n#include <assert.h>\n#include <errno.h>\n#include <string.h>\n\n#include \"mosquitto_internal.h\"\n#include \"http_client.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n#include \"picohttpparser.h\"\n\n\nstatic int create_request_key(char **encoded)\n{\n\tuint8_t bytes[16];\n\tmosquitto_getrandom(bytes, sizeof(bytes));\n\treturn mosquitto_base64_encode(bytes, sizeof(bytes), encoded);\n}\n\n\nint http_c__context_init(struct mosquitto *context)\n{\n\tstruct mosquitto__packet *packet;\n\tchar *key;\n\tconst char *path;\n\n\tcontext->transport = mosq_t_http;\n\tcontext->http_request = mosquitto_calloc(1, (size_t)context->wsd.http_header_size + 1);\n\tif(context->http_request == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(create_request_key(&key)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tif(ws__create_accept_key(key, strlen(key), &context->wsd.accept_key)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tpacket = mosquitto_calloc(1, sizeof(struct mosquitto__packet) + 1024 + WS_PACKET_OFFSET);\n\tif(!packet){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tpath = context->wsd.http_path?context->wsd.http_path:\"/mqtt\";\n\n\tpacket->packet_length = (uint32_t )snprintf((char *)&packet->payload[WS_PACKET_OFFSET], 1024,\n\t\t\t\"GET %s HTTP/1.1\\r\\n\"\n\t\t\t\"Host: %s\\r\\n\"\n\t\t\t\"Upgrade: websocket\\r\\n\"\n\t\t\t\"Connection: Upgrade\\r\\n\"\n\t\t\t\"Sec-WebSocket-Key: %s\\r\\n\"\n\t\t\t\"Sec-WebSocket-Protocol: mqtt\\r\\n\"\n\t\t\t\"Sec-WebSocket-Version: 13\\r\\n\"\n\t\t\t\"\\r\\n\", path, context->host, key);\n\tmosquitto_FREE(key);\n\tpacket->packet_length += WS_PACKET_OFFSET;\n\tpacket->to_process = packet->packet_length;\n\tcontext->http_request[0] = '\\0';\n\treturn packet__queue(context, packet);\n}\n\n\nint http_c__context_cleanup(struct mosquitto *context)\n{\n\tmosquitto_FREE(context->wsd.accept_key);\n\tmosquitto_FREE(context->http_request);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint http_c__read(struct mosquitto *mosq)\n{\n\tssize_t read_length;\n\tenum mosquitto_client_state state;\n\tsize_t hlen;\n\tint http_status;\n\tconst char *http_msg;\n\tsize_t http_msg_len;\n\tint http_minor_version;\n\tsize_t http_header_count = 100;\n\tstruct phr_header http_headers[100];\n\tconst char *client_key = NULL;\n\tsize_t client_key_len = 0;\n\tsize_t i;\n\tbool header_have_upgrade;\n\tbool header_have_connection;\n\tbool header_have_subprotocol;\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosq->sock == INVALID_SOCKET){\n\t\treturn MOSQ_ERR_NO_CONN;\n\t}\n\n\tstate = mosquitto__get_state(mosq);\n\tif(state == mosq_cs_connect_pending){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\thlen = strlen(mosq->http_request);\n\tread_length = net__read(mosq, &mosq->http_request[hlen], (size_t)mosq->wsd.http_header_size-hlen);\n\tif(read_length <= 0){\n\t\tif(read_length == 0){\n\t\t\treturn MOSQ_ERR_CONN_LOST; /* EOF */\n\t\t}\n\t\tWINDOWS_SET_ERRNO_RW();\n\t\tif(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\tswitch(errno){\n\t\t\t\tcase COMPAT_ECONNRESET:\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\tcase COMPAT_EINTR:\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\tdefault:\n\t\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t}\n\t\t}\n\t}\n\thlen += (size_t)read_length;\n\tmosq->http_request[hlen] = '\\0';\n\n\tread_length = phr_parse_response(mosq->http_request, hlen,\n\t\t\t&http_minor_version, &http_status,\n\t\t\t&http_msg, &http_msg_len,\n\t\t\thttp_headers, &http_header_count,\n\t\t\t0);\n\tif(read_length == -2){\n\t\t// Partial read\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(read_length == -1){\n\t\t// Error\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(http_status != 101){\n\t\tmosquitto_FREE(mosq->http_request);\n\t\t/* FIXME Not supported - send 501 response */\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\theader_have_upgrade = false;\n\theader_have_connection = false;\n\theader_have_subprotocol = false;\n\n\tfor(i=0; i<http_header_count; i++){\n\t\tif(!strncasecmp(http_headers[i].name, \"Upgrade\", http_headers[i].name_len)){\n\t\t\tif(!strncasecmp(http_headers[i].value, \"websocket\", http_headers[i].value_len)){\n\t\t\t\theader_have_upgrade = true;\n\t\t\t}\n\t\t}else if(!strncasecmp(http_headers[i].name, \"Connection\", http_headers[i].name_len)){\n\t\t\t/* Check for \"upgrade\" */\n\t\t\tconst char *str = http_headers[i].value;\n\t\t\tsize_t start = 0;\n\t\t\tsize_t j = 0;\n\t\t\tfor(j=0; j<http_headers[i].value_len; j++){\n\t\t\t\tif(str[j] == ','){\n\t\t\t\t\tif(!strncasecmp(&str[start], \"upgrade\", http_headers[i].value_len-j)){\n\t\t\t\t\t\theader_have_connection = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tstart = j+1;\n\t\t\t\t\t}\n\t\t\t\t}else if(str[j] == ' '){\n\t\t\t\t\tstart = j+1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(!strncasecmp(&str[start], \"upgrade\", http_headers[i].value_len-j)){\n\t\t\t\theader_have_connection = true;\n\t\t\t}\n\t\t}else if(!strncasecmp(http_headers[i].name, \"Sec-WebSocket-Accept\", http_headers[i].name_len)){\n\t\t\tclient_key = http_headers[i].value;\n\t\t\tclient_key_len = http_headers[i].value_len;\n\t\t}else if(!strncasecmp(http_headers[i].name, \"Sec-WebSocket-Version\", http_headers[i].name_len)){\n\t\t\t/* Check for \"13\" */\n\t\t\tif(http_headers[i].value_len != 2\n\t\t\t\t\t|| http_headers[i].value[0] != '1'\n\t\t\t\t\t|| http_headers[i].value[1] != '3'\n\t\t\t\t\t){\n\n\t\t\t\t/* FIXME - not supported */\n\t\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t\t\t}\n\t\t}else if(!strncasecmp(http_headers[i].name, \"Sec-WebSocket-Protocol\", http_headers[i].name_len)){\n\t\t\t/* Check for \"mqtt\" */\n\t\t\tif(!strncmp(http_headers[i].value, \"mqtt\", http_headers[i].value_len)){\n\t\t\t\theader_have_subprotocol = true;\n\t\t\t}\n\t\t}else{\n\t\t\t/* Unknown header */\n\t\t}\n\t}\n\n\tif(header_have_upgrade == false || header_have_connection == false || header_have_subprotocol == false\n\t\t\t|| client_key == NULL || client_key_len == 0){\n\n\t\t// FIXME - 404\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tif(strncmp(mosq->wsd.accept_key, client_key, client_key_len)){\n\t\t// FIXME - 50x\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\thttp_c__context_cleanup(mosq);\n\tws__context_init(mosq);\n\n\t//* FIXME outgoing properites\n\trc = send__connect(mosq, mosq->keepalive, mosq->clean_start, NULL);\n\tif(rc){\n\t\tpacket__cleanup_all(mosq);\n\t\tnet__socket_close(mosq);\n\t\tmosquitto__set_state(mosq, mosq_cs_new);\n\t}\n\treturn rc;\n}\n\n#endif\n"
  },
  {
    "path": "lib/http_client.h",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef HTTP_CLIENT_H\n#define HTTP_CLIENT_H\n\n#include \"config.h\"\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\n#include \"mosquitto_internal.h\"\n\n\nint http_c__context_init(struct mosquitto *context);\nint http_c__context_cleanup(struct mosquitto *context);\nint http_c__read(struct mosquitto *mosq);\n\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/libmosquitto.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <signal.h>\n#include <string.h>\n#ifndef WIN32\n#include <sys/time.h>\n#include <strings.h>\n#endif\n\n#if defined(__APPLE__)\n#  include <mach/mach_time.h>\n#endif\n\n#include \"logging_mosq.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"messages_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"will_mosq.h\"\n\nstatic unsigned int init_refcount = 0;\n\nvoid mosquitto__destroy(struct mosquitto *mosq);\n\n\nint mosquitto_lib_version(int *major, int *minor, int *revision)\n{\n\tif(major){\n\t\t*major = LIBMOSQUITTO_MAJOR;\n\t}\n\tif(minor){\n\t\t*minor = LIBMOSQUITTO_MINOR;\n\t}\n\tif(revision){\n\t\t*revision = LIBMOSQUITTO_REVISION;\n\t}\n\treturn LIBMOSQUITTO_VERSION_NUMBER;\n}\n\n\nint mosquitto_lib_init(void)\n{\n\tint rc;\n\n\tif(init_refcount == 0){\n\t\tmosquitto_time_init();\n\n\t\trc = net__init();\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tinit_refcount++;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_lib_cleanup(void)\n{\n\tif(init_refcount == 1){\n\t\tnet__cleanup();\n\t}\n\n\tif(init_refcount > 0){\n\t\t--init_refcount;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int alloc_packet_buffer(struct mosquitto *mosq)\n{\n\tmosq->in_packet.packet_buffer_size = 4096;\n\tmosq->in_packet.packet_buffer = mosquitto_calloc(1, mosq->in_packet.packet_buffer_size);\n\treturn !mosq->in_packet.packet_buffer;\n}\n\nstruct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata)\n{\n\tstruct mosquitto *mosq = NULL;\n\tint rc;\n\n\tif(clean_start == false && id == NULL){\n\t\terrno = EINVAL;\n\t\treturn NULL;\n\t}\n\n\tmosq = (struct mosquitto *)mosquitto_calloc(1, sizeof(struct mosquitto));\n\tif(mosq){\n\t\tmosq->sock = INVALID_SOCKET;\n#ifdef WITH_THREADING\n#  ifndef WIN32\n\t\t/* Windows doesn't have pthread_cancel, so no need to record self */\n\t\tmosq->thread_id = pthread_self();\n#  endif\n#endif\n\t\tmosq->sockpairR = INVALID_SOCKET;\n\t\tmosq->sockpairW = INVALID_SOCKET;\n\t\trc = mosquitto_reinitialise(mosq, id, clean_start, userdata);\n\t\tif(rc){\n\t\t\tmosquitto_destroy(mosq);\n\t\t\tif(rc == MOSQ_ERR_INVAL){\n\t\t\t\terrno = EINVAL;\n\t\t\t}else if(rc == MOSQ_ERR_NOMEM){\n\t\t\t\terrno = ENOMEM;\n\t\t\t}\n\t\t\treturn NULL;\n\t\t}\n\t}else{\n\t\terrno = ENOMEM;\n\t}\n\treturn mosq;\n}\n\n\nint mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_start, void *userdata)\n{\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(clean_start == false && id == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmosquitto__destroy(mosq);\n\tmemset(mosq, 0, sizeof(struct mosquitto));\n\n#ifdef WITH_THREADING\n\tCOMPAT_pthread_mutex_init(&mosq->callback_mutex, NULL);\n\tCOMPAT_pthread_mutex_init(&mosq->log_callback_mutex, NULL);\n\tCOMPAT_pthread_mutex_init(&mosq->state_mutex, NULL);\n\tCOMPAT_pthread_mutex_init(&mosq->out_packet_mutex, NULL);\n\tCOMPAT_pthread_mutex_init(&mosq->msgtime_mutex, NULL);\n\tCOMPAT_pthread_mutex_init(&mosq->msgs_in.mutex, NULL);\n\tCOMPAT_pthread_mutex_init(&mosq->msgs_out.mutex, NULL);\n\tCOMPAT_pthread_mutex_init(&mosq->mid_mutex, NULL);\n\tmosq->thread_id = pthread_self();\n#endif\n\n\tif(userdata){\n\t\tmosq->userdata = userdata;\n\t}else{\n\t\tmosq->userdata = mosq;\n\t}\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\tmemset(&mosq->wsd, 0, sizeof(mosq->wsd));\n\tmosq->wsd.opcode = UINT8_MAX;\n\tmosq->wsd.mask = UINT8_MAX;\n\tmosq->wsd.disconnect_reason = 0xE8;\n\tmosq->wsd.is_client = true;\n\tmosq->wsd.http_header_size = 4096;\n#endif\n\tif(alloc_packet_buffer(mosq)){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tmosq->transport = mosq_t_tcp;\n\tmosq->protocol = mosq_p_mqtt311;\n\tmosq->sock = INVALID_SOCKET;\n\tmosq->sockpairR = INVALID_SOCKET;\n\tmosq->sockpairW = INVALID_SOCKET;\n\tmosq->keepalive = 60;\n\tmosq->clean_start = clean_start;\n\tif(id){\n\t\tif(STREMPTY(id)){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(mosquitto_validate_utf8(id, (int)strlen(id))){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t\tmosq->id = mosquitto_strdup(id);\n\t\tif(!mosq->id){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\tpacket__cleanup(&mosq->in_packet);\n\tmosq->out_packet = NULL;\n\tmosq->out_packet_count = 0;\n\tmosq->out_packet_bytes = 0;\n\tmosq->last_msg_in = mosquitto_time();\n\tmosq->next_msg_out = mosquitto_time() + mosq->keepalive;\n\tmosq->ping_t = 0;\n\tmosq->last_mid = 0;\n\tmosq->state = mosq_cs_new;\n\tmosq->max_qos = 2;\n\tmosq->msgs_in.inflight_maximum = 20;\n\tmosq->msgs_out.inflight_maximum = 20;\n\tmosq->msgs_in.inflight_quota = 20;\n\tmosq->msgs_out.inflight_quota = 20;\n\tmosq->will = NULL;\n\tmosq->on_connect = NULL;\n\tmosq->on_publish = NULL;\n\tmosq->on_message = NULL;\n\tmosq->on_subscribe = NULL;\n\tmosq->on_unsubscribe = NULL;\n\tmosq->host = NULL;\n\tmosq->port = 1883;\n\tmosq->reconnect_delay = 1;\n\tmosq->reconnect_delay_max = 1;\n\tmosq->reconnect_exponential_backoff = false;\n\tmosq->threaded = mosq_ts_none;\n#ifdef WITH_TLS\n\tmosq->ssl = NULL;\n\tmosq->ssl_ctx = NULL;\n\tmosq->ssl_ctx_defaults = true;\n#ifndef WITH_BROKER\n\tmosq->user_ssl_ctx = NULL;\n#endif\n\tmosq->tls_cert_reqs = SSL_VERIFY_PEER;\n\tmosq->tls_insecure = false;\n\tmosq->want_write = false;\n\tmosq->tls_ocsp_required = false;\n#endif\n\tif(mosq->disable_socketpair == false){\n\t\t/* This must be after pthread_mutex_init(), otherwise the log mutex may be\n\t\t* used before being initialised. */\n\t\tif(net__socketpair(&mosq->sockpairR, &mosq->sockpairW)){\n\t\t\tlog__printf(mosq, MOSQ_LOG_WARNING,\n\t\t\t\t\t\"Warning: Unable to open socket pair, outgoing publish commands may be delayed.\");\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid mosquitto__destroy(struct mosquitto *mosq)\n{\n\tif(!mosq){\n\t\treturn;\n\t}\n\n#ifdef WITH_THREADING\n#  ifdef HAVE_PTHREAD_CANCEL\n\tif(mosq->threaded == mosq_ts_self && !pthread_equal(mosq->thread_id, pthread_self())){\n\t\tCOMPAT_pthread_cancel(mosq->thread_id);\n\t\tCOMPAT_pthread_join(mosq->thread_id, NULL);\n\t\tmosq->threaded = mosq_ts_none;\n\t}\n#  endif\n\n\tCOMPAT_pthread_mutex_destroy(&mosq->callback_mutex);\n\tCOMPAT_pthread_mutex_destroy(&mosq->log_callback_mutex);\n\tCOMPAT_pthread_mutex_destroy(&mosq->state_mutex);\n\tCOMPAT_pthread_mutex_destroy(&mosq->out_packet_mutex);\n\tCOMPAT_pthread_mutex_destroy(&mosq->msgtime_mutex);\n\tCOMPAT_pthread_mutex_destroy(&mosq->msgs_in.mutex);\n\tCOMPAT_pthread_mutex_destroy(&mosq->msgs_out.mutex);\n\tCOMPAT_pthread_mutex_destroy(&mosq->mid_mutex);\n#endif\n\tif(net__is_connected(mosq)){\n\t\tnet__socket_close(mosq);\n\t}\n\tmessage__cleanup_all(mosq);\n\twill__clear(mosq);\n#ifdef WITH_TLS\n\tif(mosq->ssl){\n\t\tSSL_free(mosq->ssl);\n\t}\n#ifndef WITH_BROKER\n\tif(mosq->user_ssl_ctx){\n\t\tSSL_CTX_free(mosq->user_ssl_ctx);\n\t}else if(mosq->ssl_ctx){\n\t\tSSL_CTX_free(mosq->ssl_ctx);\n\t}\n#else\n\tif(mosq->ssl_ctx){\n\t\tSSL_CTX_free(mosq->ssl_ctx);\n\t}\n#endif\n\tmosquitto_FREE(mosq->tls_cafile);\n\tmosquitto_FREE(mosq->tls_capath);\n\tmosquitto_FREE(mosq->tls_certfile);\n\tmosquitto_FREE(mosq->tls_keyfile);\n\tmosq->tls_pw_callback = NULL;\n\tmosquitto_FREE(mosq->tls_version);\n\tmosquitto_FREE(mosq->tls_ciphers);\n\tmosquitto_FREE(mosq->tls_psk);\n\tmosquitto_FREE(mosq->tls_psk_identity);\n\tmosquitto_FREE(mosq->tls_alpn);\n#ifndef OPENSSL_NO_ENGINE\n\tmosquitto_FREE(mosq->tls_engine);\n#endif\n#endif\n\n\tmosquitto_FREE(mosq->address);\n\tmosquitto_FREE(mosq->id);\n\tmosquitto_FREE(mosq->username);\n\tmosquitto_FREE(mosq->password);\n\tmosquitto_FREE(mosq->host);\n\tmosquitto_FREE(mosq->bind_address);\n\tmosquitto_FREE(mosq->in_packet.packet_buffer);\n\tmosq->in_packet.packet_buffer_size = 4096;\n\n\tmosquitto_property_free_all(&mosq->connect_properties);\n\n\tpacket__cleanup_all_no_locks(mosq);\n\n\tpacket__cleanup(&mosq->in_packet);\n\tif(mosq->sockpairR != INVALID_SOCKET){\n\t\tCOMPAT_CLOSE(mosq->sockpairR);\n\t\tmosq->sockpairR = INVALID_SOCKET;\n\t}\n\tif(mosq->sockpairW != INVALID_SOCKET){\n\t\tCOMPAT_CLOSE(mosq->sockpairW);\n\t\tmosq->sockpairW = INVALID_SOCKET;\n\t}\n}\n\n\nvoid mosquitto_destroy(struct mosquitto *mosq)\n{\n\tif(!mosq){\n\t\treturn;\n\t}\n\n\tmosquitto__destroy(mosq);\n\tmosquitto_FREE(mosq);\n}\n\n\nint mosquitto_socket(struct mosquitto *mosq)\n{\n\tif(!mosq){\n\t\treturn INVALID_SOCKET;\n\t}\n\treturn mosq->sock;\n}\n\n\nbool mosquitto_want_write(struct mosquitto *mosq)\n{\n\treturn mosq->out_packet || mosq->want_write;\n}\n"
  },
  {
    "path": "lib/linker.version",
    "content": "/* Linker version script - currently used here primarily to control which\n * symbols are exported.\n */\n\nMOSQ_1.0 {\n\tglobal:\n\t\tmosquitto_lib_version;\n\t\tmosquitto_lib_init;\n\t\tmosquitto_lib_cleanup;\n\t\tmosquitto_new;\n\t\tmosquitto_destroy;\n\t\tmosquitto_reinitialise;\n\t\tmosquitto_will_set;\n\t\tmosquitto_will_clear;\n\t\tmosquitto_username_pw_set;\n\t\tmosquitto_connect;\n\t\tmosquitto_connect_async;\n\t\tmosquitto_reconnect;\n\t\tmosquitto_disconnect;\n\t\tmosquitto_publish;\n\t\tmosquitto_subscribe;\n\t\tmosquitto_unsubscribe;\n\t\tmosquitto_message_copy;\n\t\tmosquitto_message_free;\n\t\tmosquitto_loop;\n\t\tmosquitto_socket;\n\t\tmosquitto_loop_start;\n\t\tmosquitto_loop_stop;\n\t\tmosquitto_loop_read;\n\t\tmosquitto_loop_write;\n\t\tmosquitto_loop_misc;\n\t\tmosquitto_connect_callback_set;\n\t\tmosquitto_disconnect_callback_set;\n\t\tmosquitto_publish_callback_set;\n\t\tmosquitto_message_callback_set;\n\t\tmosquitto_subscribe_callback_set;\n\t\tmosquitto_unsubscribe_callback_set;\n\t\tmosquitto_log_callback_set;\n\t\tmosquitto_message_retry_set;\n\t\tmosquitto_want_write;\n\t\tmosquitto_user_data_set;\n\t\tmosquitto_strerror;\n\t\tmosquitto_connack_string;\n\t\tmosquitto_tls_set;\n\t\tmosquitto_tls_opts_set;\n\t\tmosquitto_tls_psk_set;\n\t\tmosquitto_sub_topic_tokenise;\n\t\tmosquitto_sub_topic_tokens_free;\n\t\tmosquitto_topic_matches_sub;\n\tlocal: *;\n};\n\nMOSQ_1.1 {\n\tglobal:\n\t\tmosquitto_loop_forever;\n} MOSQ_1.0;\n\nMOSQ_1.2 {\n\tglobal:\n\t\tmosquitto_connect_bind;\n\t\tmosquitto_connect_bind_async;\n\t\tmosquitto_max_inflight_messages_set;\n\t\tmosquitto_reconnect_delay_set;\n\t\tmosquitto_reconnect_async;\n\t\tmosquitto_tls_insecure_set;\n} MOSQ_1.1;\n\nMOSQ_1.3 {\n\tglobal:\n\t\tmosquitto_connect_srv;\n} MOSQ_1.2;\n\nMOSQ_1.4 {\n\tglobal:\n\t\tmosquitto_threaded_set;\n\t\tmosquitto_opts_set;\n\t\tmosquitto_pub_topic_check;\n\t\tmosquitto_sub_topic_check;\n\t\tmosquitto_socks5_set;\n} MOSQ_1.3;\n\nMOSQ_1.5 {\n\tglobal:\n\t\tmosquitto_subscribe_simple;\n\t\tmosquitto_subscribe_callback;\n\t\tmosquitto_message_free_contents;\n\t\tmosquitto_validate_utf8;\n\t\tmosquitto_userdata;\n\t\tmosquitto_pub_topic_check2;\n\t\tmosquitto_sub_topic_check2;\n\t\tmosquitto_topic_matches_sub2;\n\t\tmosquitto_connect_with_flags_callback_set;\n} MOSQ_1.4;\n\nMOSQ_1.6 {\n\tglobal:\n\t\tmosquitto_connect_bind_v5;\n\t\tmosquitto_connect_v5_callback_set;\n\t\tmosquitto_disconnect_v5;\n\t\tmosquitto_disconnect_v5_callback_set;\n\t\tmosquitto_int_option;\n\t\tmosquitto_message_v5_callback_set;\n\t\tmosquitto_property_add_binary;\n\t\tmosquitto_property_add_byte;\n\t\tmosquitto_property_add_int16;\n\t\tmosquitto_property_add_int32;\n\t\tmosquitto_property_add_string;\n\t\tmosquitto_property_add_string_pair;\n\t\tmosquitto_property_add_varint;\n\t\tmosquitto_property_check_all;\n\t\tmosquitto_property_check_command;\n\t\tmosquitto_property_copy_all;\n\t\tmosquitto_property_free_all;\n\t\tmosquitto_property_read_binary;\n\t\tmosquitto_property_read_byte;\n\t\tmosquitto_property_read_int16;\n\t\tmosquitto_property_read_int32;\n\t\tmosquitto_property_read_string;\n\t\tmosquitto_property_read_string_pair;\n\t\tmosquitto_property_read_varint;\n\t\tmosquitto_publish_v5;\n\t\tmosquitto_publish_v5_callback_set;\n\t\tmosquitto_reason_string;\n\t\tmosquitto_string_option;\n\t\tmosquitto_string_to_command;\n\t\tmosquitto_string_to_property_info;\n\t\tmosquitto_subscribe_multiple;\n\t\tmosquitto_subscribe_v5;\n\t\tmosquitto_subscribe_v5_callback_set;\n\t\tmosquitto_unsubscribe_multiple;\n\t\tmosquitto_unsubscribe_v5;\n\t\tmosquitto_unsubscribe_v5_callback_set;\n\t\tmosquitto_void_option;\n\t\tmosquitto_will_set_v5;\n} MOSQ_1.5;\n\nMOSQ_1.7 {\n\tglobal:\n\t\tmosquitto_property_identifier;\n\t\tmosquitto_property_identifier_to_string;\n\t\tmosquitto_property_next;\n\t\tmosquitto_ssl_get;\n} MOSQ_1.6;\n\nMOSQ_2.1 {\n\tglobal:\n\t\tmosquitto_ext_auth_callback_set;\n\t\tmosquitto_ext_auth_continue;\n\t\tmosquitto_pre_connect_callback_set;\n\t\tmosquitto_topic_matches_sub_with_pattern;\n\t\tmosquitto_sub_matches_acl;\n\t\tmosquitto_sub_matches_acl_with_pattern;\n\t\tmosquitto_unsubscribe2_v5_callback_set;\n\t\tmosquitto_property_remove;\n\t\tmosquitto_property_type;\n\t\tmosquitto_property_byte_value;\n\t\tmosquitto_property_int16_value;\n\t\tmosquitto_property_int32_value;\n\t\tmosquitto_property_varint_value;\n\t\tmosquitto_property_binary_value;\n\t\tmosquitto_property_binary_value_length;\n\t\tmosquitto_property_string_value;\n\t\tmosquitto_property_string_value_length;\n\t\tmosquitto_property_string_name;\n\t\tmosquitto_property_string_name_length;\n} MOSQ_1.7;\n"
  },
  {
    "path": "lib/logging_mosq.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"logging_mosq.h\"\n#include \"mosquitto_internal.h\"\n#include \"mosquitto.h\"\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\tva_list va;\n\tchar *s;\n\tsize_t len;\n\tvoid (*on_log)(struct mosquitto *, void *userdata, int level, const char *str);\n\n\tassert(mosq);\n\tassert(fmt);\n\n\tCOMPAT_pthread_mutex_lock(&mosq->log_callback_mutex);\n\ton_log = mosq->on_log;\n\tCOMPAT_pthread_mutex_unlock(&mosq->log_callback_mutex);\n\n\tif(on_log){\n\t\tlen = strlen(fmt) + 500;\n\t\ts = mosquitto_malloc(len*sizeof(char));\n\t\tif(!s){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tva_start(va, fmt);\n\t\tvsnprintf(s, len, fmt, va);\n\t\tva_end(va);\n\t\ts[len-1] = '\\0'; /* Ensure string is null terminated. */\n\n\t\ton_log(mosq, mosq->userdata, (int)priority, s);\n\n\t\tmosquitto_FREE(s);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "lib/logging_mosq.h",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef LOGGING_MOSQ_H\n#define LOGGING_MOSQ_H\n\n#include \"mosquitto.h\"\n\n#ifndef __GNUC__\n#define __attribute__(attrib)\n#endif\n\nint log__printf(struct mosquitto *mosq, unsigned int level, const char *fmt, ...) __attribute__((format(printf, 3, 4)));\n\n#endif\n"
  },
  {
    "path": "lib/loop.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#ifndef WIN32\n#include <sys/select.h>\n#include <time.h>\n#endif\n\n#include \"callbacks.h\"\n#include \"http_client.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"socks_mosq.h\"\n#include \"tls_mosq.h\"\n#include \"util_mosq.h\"\n\n#if !defined(WIN32) && !defined(__QNX__)\n#define HAVE_PSELECT\n#endif\n\n\nint mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets)\n{\n#ifdef HAVE_PSELECT\n\tstruct timespec local_timeout;\n#else\n\tstruct timeval local_timeout;\n#endif\n\tfd_set readfds, writefds;\n\tint fdcount;\n\tint rc;\n\tchar pairbuf;\n\tint maxfd = 0;\n\ttime_t now;\n\ttime_t timeout_ms;\n\n\tif(!mosq || max_packets < 1){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n#ifndef WIN32\n\tif(mosq->sock >= FD_SETSIZE || mosq->sockpairR >= FD_SETSIZE){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n#endif\n\n\tFD_ZERO(&readfds);\n\tFD_ZERO(&writefds);\n\tif(net__is_connected(mosq)){\n\t\tmaxfd = mosq->sock;\n\t\tFD_SET(mosq->sock, &readfds);\n\t\tif(mosq->want_write){\n\t\t\tFD_SET(mosq->sock, &writefds);\n\t\t}else{\n#ifdef WITH_TLS\n\t\t\tif(mosq->ssl == NULL || SSL_is_init_finished(mosq->ssl))\n#endif\n\t\t\t{\n\t\t\t\tCOMPAT_pthread_mutex_lock(&mosq->out_packet_mutex);\n\t\t\t\tif(mosq->out_packet){\n\t\t\t\t\tFD_SET(mosq->sock, &writefds);\n\t\t\t\t}\n\t\t\t\tCOMPAT_pthread_mutex_unlock(&mosq->out_packet_mutex);\n\t\t\t}\n\t\t}\n\t}else{\n#ifdef WITH_SRV\n\t\tif(mosq->achan){\n\t\t\tif(mosquitto__get_state(mosq) == mosq_cs_connect_srv){\n\t\t\t\trc = ares_fds(mosq->achan, &readfds, &writefds);\n\t\t\t\tif(rc > maxfd){\n\t\t\t\t\tmaxfd = rc;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_NO_CONN;\n\t\t\t}\n\t\t}\n#else\n\t\treturn MOSQ_ERR_NO_CONN;\n#endif\n\t}\n\tif(mosq->sockpairR != INVALID_SOCKET){\n\t\t/* sockpairR is used to break out of select() before the timeout, on a\n\t\t * call to publish() etc. */\n\t\tFD_SET(mosq->sockpairR, &readfds);\n\t\tif((int)mosq->sockpairR > maxfd){\n\t\t\tmaxfd = mosq->sockpairR;\n\t\t}\n\t}\n\n\ttimeout_ms = timeout;\n\tif(timeout_ms < 0){\n\t\ttimeout_ms = 1000;\n\t}\n\n\tnow = mosquitto_time();\n\tCOMPAT_pthread_mutex_lock(&mosq->msgtime_mutex);\n\tif(mosq->next_msg_out && now + timeout_ms/1000 > mosq->next_msg_out){\n\t\ttimeout_ms = (mosq->next_msg_out - now)*1000;\n\t}\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgtime_mutex);\n\n\tif(timeout_ms < 0){\n\t\t/* There has been a delay somewhere which means we should have already\n\t\t * sent a message. */\n\t\ttimeout_ms = 0;\n\t}\n\n\tlocal_timeout.tv_sec = timeout_ms/1000;\n#ifdef HAVE_PSELECT\n\tlocal_timeout.tv_nsec = (timeout_ms-local_timeout.tv_sec*1000)*1000000;\n#else\n\tlocal_timeout.tv_usec = (timeout_ms-local_timeout.tv_sec*1000)*1000;\n#endif\n\n#ifdef HAVE_PSELECT\n\tfdcount = pselect(maxfd+1, &readfds, &writefds, NULL, &local_timeout, NULL);\n#else\n\tfdcount = select(maxfd+1, &readfds, &writefds, NULL, &local_timeout);\n#endif\n\tif(fdcount == -1){\n\t\tWINDOWS_SET_ERRNO();\n\t\tif(errno == EINTR){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t}\n\t}else{\n\t\tif(net__is_connected(mosq)){\n\t\t\tif(FD_ISSET(mosq->sock, &readfds)){\n\t\t\t\trc = mosquitto_loop_read(mosq, max_packets);\n\t\t\t\tif(rc || mosq->sock == INVALID_SOCKET){\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(mosq->sockpairR != INVALID_SOCKET && FD_ISSET(mosq->sockpairR, &readfds)){\n#ifndef WIN32\n\t\t\t\tif(read(mosq->sockpairR, &pairbuf, 1) == 0){\n\t\t\t\t}\n#else\n\t\t\t\trecv(mosq->sockpairR, &pairbuf, 1, 0);\n#endif\n\t\t\t\t/* Fake write possible, to stimulate output write even though\n\t\t\t\t * we didn't ask for it, because at that point the publish or\n\t\t\t\t * other command wasn't present. */\n\t\t\t\tif(net__is_connected(mosq)){\n\t\t\t\t\tFD_SET(mosq->sock, &writefds);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(net__is_connected(mosq) && FD_ISSET(mosq->sock, &writefds)){\n\t\t\t\trc = mosquitto_loop_write(mosq, max_packets);\n\t\t\t\tif(rc || !net__is_connected(mosq)){\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#ifdef WITH_SRV\n\t\tif(mosq->achan){\n\t\t\tares_process(mosq->achan, &readfds, &writefds);\n\t\t}\n#endif\n\t}\n\treturn mosquitto_loop_misc(mosq);\n}\n\n\nstatic int interruptible_sleep(struct mosquitto *mosq, time_t reconnect_delay)\n{\n#ifdef HAVE_PSELECT\n\tstruct timespec local_timeout;\n#else\n\tstruct timeval local_timeout;\n#endif\n\tfd_set readfds;\n\tint fdcount;\n\tchar pairbuf;\n\tint maxfd = 0;\n\n#ifndef WIN32\n\twhile(mosq->sockpairR != INVALID_SOCKET && read(mosq->sockpairR, &pairbuf, 1) > 0){\n\t}\n#else\n\twhile(mosq->sockpairR != INVALID_SOCKET && recv(mosq->sockpairR, &pairbuf, 1, 0) > 0){\n\t}\n#endif\n\n\tlocal_timeout.tv_sec = reconnect_delay;\n#ifdef HAVE_PSELECT\n\tlocal_timeout.tv_nsec = 0;\n#else\n\tlocal_timeout.tv_usec = 0;\n#endif\n\tFD_ZERO(&readfds);\n\tmaxfd = 0;\n\tif(mosq->sockpairR != INVALID_SOCKET){\n\t\t/* sockpairR is used to break out of select() before the\n\t\t * timeout, when mosquitto_loop_stop() is called */\n\t\tFD_SET(mosq->sockpairR, &readfds);\n\t\tmaxfd = mosq->sockpairR;\n\t}\n#ifdef HAVE_PSELECT\n\tfdcount = pselect(maxfd+1, &readfds, NULL, NULL, &local_timeout, NULL);\n#else\n\tfdcount = select(maxfd+1, &readfds, NULL, NULL, &local_timeout);\n#endif\n\tif(fdcount == -1){\n\t\tWINDOWS_SET_ERRNO();\n\t\tif(errno == EINTR){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t}\n\t}else if(mosq->sockpairR != INVALID_SOCKET && FD_ISSET(mosq->sockpairR, &readfds)){\n#ifndef WIN32\n\t\tif(read(mosq->sockpairR, &pairbuf, 1) == 0){\n\t\t}\n#else\n\t\trecv(mosq->sockpairR, &pairbuf, 1, 0);\n#endif\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\tunsigned long reconnect_delay;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmosq->reconnects = 0;\n\tmosq->run = true;\n\n\twhile(mosq->run){\n\t\tdo{\n#ifdef HAVE_PTHREAD_CANCEL\n\t\t\tCOMPAT_pthread_testcancel();\n#endif\n\t\t\trc = mosquitto_loop(mosq, timeout, max_packets);\n\t\t}while(mosq->run && rc == MOSQ_ERR_SUCCESS);\n\t\t/* Quit after fatal errors. */\n\t\tswitch(rc){\n\t\t\tcase MOSQ_ERR_NOMEM:\n\t\t\tcase MOSQ_ERR_PROTOCOL:\n\t\t\tcase MOSQ_ERR_INVAL:\n\t\t\tcase MOSQ_ERR_NOT_FOUND:\n\t\t\tcase MOSQ_ERR_TLS:\n\t\t\tcase MOSQ_ERR_PAYLOAD_SIZE:\n\t\t\tcase MOSQ_ERR_NOT_SUPPORTED:\n\t\t\tcase MOSQ_ERR_AUTH:\n\t\t\tcase MOSQ_ERR_ACL_DENIED:\n\t\t\tcase MOSQ_ERR_UNKNOWN:\n\t\t\tcase MOSQ_ERR_EAI:\n\t\t\tcase MOSQ_ERR_PROXY:\n\t\t\t\treturn rc;\n\t\t\tcase MOSQ_ERR_ERRNO:\n\t\t\t\tbreak;\n\t\t}\n\t\tif(errno == EPROTO){\n\t\t\treturn rc;\n\t\t}\n\t\tdo{\n#ifdef HAVE_PTHREAD_CANCEL\n\t\t\tCOMPAT_pthread_testcancel();\n#endif\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t\tif(mosquitto__get_request_disconnect(mosq)){\n\t\t\t\tmosq->run = false;\n\t\t\t}else{\n\t\t\t\tif(mosq->reconnect_delay_max > mosq->reconnect_delay){\n\t\t\t\t\tif(mosq->reconnect_exponential_backoff){\n\t\t\t\t\t\treconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1)*(mosq->reconnects+1);\n\t\t\t\t\t}else{\n\t\t\t\t\t\treconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1);\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\treconnect_delay = mosq->reconnect_delay;\n\t\t\t\t}\n\n\t\t\t\tif(reconnect_delay > mosq->reconnect_delay_max){\n\t\t\t\t\treconnect_delay = mosq->reconnect_delay_max;\n\t\t\t\t}else{\n\t\t\t\t\tmosq->reconnects++;\n\t\t\t\t}\n\n\t\t\t\trc = interruptible_sleep(mosq, (time_t)reconnect_delay);\n\t\t\t\tif(rc){\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\n\t\t\t\tif(mosquitto__get_request_disconnect(mosq)){\n\t\t\t\t\tmosq->run = false;\n\t\t\t\t}else{\n\t\t\t\t\trc = mosquitto_reconnect(mosq);\n\t\t\t\t}\n\t\t\t}\n\t\t}while(mosq->run && rc != MOSQ_ERR_SUCCESS);\n\t}\n\treturn rc;\n}\n\n\nint mosquitto_loop_misc(struct mosquitto *mosq)\n{\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!net__is_connected(mosq)){\n\t\treturn MOSQ_ERR_NO_CONN;\n\t}\n\n\treturn mosquitto__check_keepalive(mosq);\n}\n\n\nstatic int mosquitto__loop_rc_handle(struct mosquitto *mosq, int rc)\n{\n\tenum mosquitto_client_state state;\n\n\tif(rc){\n\t\tnet__socket_close(mosq);\n\t\tstate = mosquitto__get_state(mosq);\n\t\tif(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}\n\t\tcallback__on_disconnect(mosq, rc, NULL);\n\t}\n\treturn rc;\n}\n\n\nint mosquitto_loop_read(struct mosquitto *mosq, int max_packets)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\tint i;\n\tif(max_packets < 1){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tCOMPAT_pthread_mutex_lock(&mosq->msgs_out.mutex);\n\tmax_packets = mosq->msgs_out.queue_len;\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n\n\tCOMPAT_pthread_mutex_lock(&mosq->msgs_in.mutex);\n\tmax_packets += mosq->msgs_in.queue_len;\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_in.mutex);\n\n\tif(max_packets < 1){\n\t\tmax_packets = 1;\n\t}\n\t/* Queue len here tells us how many messages are awaiting processing and\n\t * have QoS > 0. We should try to deal with that many in this loop in order\n\t * to keep up. */\n\tfor(i=0; i<max_packets || SSL_DATA_PENDING(mosq); i++){\n#ifdef WITH_SOCKS\n\t\tif(mosq->socks5_host){\n\t\t\trc = socks5__read(mosq);\n\t\t}else\n#endif\n\t\t{\n\t\t\tswitch(mosq->transport){\n\t\t\t\tcase mosq_t_tcp:\n\t\t\t\tcase mosq_t_ws:\n\t\t\t\t\trc = packet__read(mosq);\n\t\t\t\t\tbreak;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\t\tcase mosq_t_http:\n\t\t\t\t\trc = http_c__read(mosq);\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tdefault:\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}\n\t\tif(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){\n\t\t\treturn mosquitto__loop_rc_handle(mosq, rc);\n\t\t}\n\t}\n\treturn rc;\n}\n\n\nint mosquitto_loop_write(struct mosquitto *mosq, int max_packets)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\tint i;\n\tif(max_packets < 1){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tfor(i=0; i<max_packets; i++){\n\t\trc = packet__write(mosq);\n\t\tif(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){\n\t\t\treturn mosquitto__loop_rc_handle(mosq, rc);\n\t\t}\n\t}\n\treturn rc;\n}\n"
  },
  {
    "path": "lib/messages_mosq.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdlib.h>\n#include <string.h>\n#include <utlist.h>\n\n#include \"mosquitto_internal.h\"\n#include \"mosquitto.h\"\n#include \"messages_mosq.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nvoid message__cleanup(struct mosquitto_message_all **message)\n{\n\tstruct mosquitto_message_all *msg;\n\n\tif(!message || !*message){\n\t\treturn;\n\t}\n\n\tmsg = *message;\n\n\tmosquitto_FREE(msg->msg.topic);\n\tmosquitto_FREE(msg->msg.payload);\n\tmosquitto_property_free_all(&msg->properties);\n\tmosquitto_FREE(msg);\n}\n\n\nvoid message__cleanup_all(struct mosquitto *mosq)\n{\n\tstruct mosquitto_message_all *tail, *tmp;\n\n\tassert(mosq);\n\n\tDL_FOREACH_SAFE(mosq->msgs_in.inflight, tail, tmp){\n\t\tDL_DELETE(mosq->msgs_in.inflight, tail);\n\t\tmessage__cleanup(&tail);\n\t}\n\tDL_FOREACH_SAFE(mosq->msgs_out.inflight, tail, tmp){\n\t\tDL_DELETE(mosq->msgs_out.inflight, tail);\n\t\tmessage__cleanup(&tail);\n\t}\n}\n\n\nint mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto_message *src)\n{\n\tif(!dst || !src){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tdst->mid = src->mid;\n\tdst->topic = mosquitto_strdup(src->topic);\n\tif(!dst->topic){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tdst->qos = src->qos;\n\tdst->retain = src->retain;\n\tif(src->payloadlen){\n\t\tdst->payload = mosquitto_calloc((unsigned int)src->payloadlen+1, sizeof(uint8_t));\n\t\tif(!dst->payload){\n\t\t\tmosquitto_FREE(dst->topic);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tmemcpy(dst->payload, src->payload, (unsigned int)src->payloadlen);\n\t\tdst->payloadlen = src->payloadlen;\n\t}else{\n\t\tdst->payloadlen = 0;\n\t\tdst->payload = NULL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos)\n{\n\tstruct mosquitto_message_all *message;\n\tint rc;\n\tassert(mosq);\n\n\trc = message__remove(mosq, mid, dir, &message, qos);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tmessage__cleanup(&message);\n\t}\n\treturn rc;\n}\n\n\nvoid mosquitto_message_free(struct mosquitto_message **message)\n{\n\tstruct mosquitto_message *msg;\n\n\tif(!message || !*message){\n\t\treturn;\n\t}\n\n\tmsg = *message;\n\n\tmosquitto_FREE(msg->topic);\n\tmosquitto_FREE(msg->payload);\n\tmosquitto_FREE(msg);\n}\n\n\nvoid mosquitto_message_free_contents(struct mosquitto_message *message)\n{\n\tif(!message){\n\t\treturn;\n\t}\n\n\tmosquitto_FREE(message->topic);\n\tmosquitto_FREE(message->payload);\n}\n\n\nint message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir)\n{\n\t/* mosq->*_message_mutex should be locked before entering this function */\n\tassert(mosq);\n\tassert(message);\n\tassert(message->msg.qos != 0);\n\n\tif(dir == mosq_md_out){\n\t\tDL_APPEND(mosq->msgs_out.inflight, message);\n\t\tmosq->msgs_out.queue_len++;\n\t}else{\n\t\tDL_APPEND(mosq->msgs_in.inflight, message);\n\t\tmosq->msgs_in.queue_len++;\n\t}\n\n\treturn message__release_to_inflight(mosq, dir);\n}\n\n\nvoid message__reconnect_reset(struct mosquitto *mosq, bool update_quota_only)\n{\n\tstruct mosquitto_message_all *message, *tmp;\n\tassert(mosq);\n\n\tCOMPAT_pthread_mutex_lock(&mosq->msgs_in.mutex);\n\tmosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum;\n\tmosq->msgs_in.queue_len = 0;\n\tDL_FOREACH_SAFE(mosq->msgs_in.inflight, message, tmp){\n\t\tmosq->msgs_in.queue_len++;\n\t\tif(message->msg.qos != 2){\n\t\t\tDL_DELETE(mosq->msgs_in.inflight, message);\n\t\t\tmessage__cleanup(&message);\n\t\t}else{\n\t\t\t/* Message state can be preserved here because it should match\n\t\t\t* whatever the client has got. */\n\t\t\tutil__decrement_receive_quota(mosq);\n\t\t}\n\t}\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_in.mutex);\n\n\n\tCOMPAT_pthread_mutex_lock(&mosq->msgs_out.mutex);\n\tmosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;\n\tmosq->msgs_out.queue_len = 0;\n\tDL_FOREACH_SAFE(mosq->msgs_out.inflight, message, tmp){\n\t\tmosq->msgs_out.queue_len++;\n\n\t\tif(mosq->msgs_out.inflight_quota != 0){\n\t\t\tutil__decrement_send_quota(mosq);\n\t\t\tif(update_quota_only == false){\n\t\t\t\tif(message->msg.qos == 1){\n\t\t\t\t\tmessage->state = mosq_ms_publish_qos1;\n\t\t\t\t}else if(message->msg.qos == 2){\n\t\t\t\t\tif(message->state == mosq_ms_wait_for_pubrec){\n\t\t\t\t\t\tmessage->state = mosq_ms_publish_qos2;\n\t\t\t\t\t}else if(message->state == mosq_ms_wait_for_pubcomp){\n\t\t\t\t\t\tmessage->state = mosq_ms_resend_pubrel;\n\t\t\t\t\t}\n\t\t\t\t\t/* Should be able to preserve state. */\n\t\t\t\t}\n\t\t\t}\n\t\t}else{\n\t\t\tmessage->state = mosq_ms_invalid;\n\t\t}\n\t}\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n}\n\n\nint message__release_to_inflight(struct mosquitto *mosq, enum mosquitto_msg_direction dir)\n{\n\t/* mosq->*_message_mutex should be locked before entering this function */\n\tstruct mosquitto_message_all *cur, *tmp;\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tif(dir == mosq_md_out){\n\t\tDL_FOREACH_SAFE(mosq->msgs_out.inflight, cur, tmp){\n\t\t\tif(mosq->msgs_out.inflight_quota > 0){\n\t\t\t\tif(cur->msg.qos > 0 && cur->state == mosq_ms_invalid){\n\t\t\t\t\tif(cur->msg.qos == 1){\n\t\t\t\t\t\tcur->state = mosq_ms_wait_for_puback;\n\t\t\t\t\t}else if(cur->msg.qos == 2){\n\t\t\t\t\t\tcur->state = mosq_ms_wait_for_pubrec;\n\t\t\t\t\t}\n\t\t\t\t\trc = send__publish(mosq, (uint16_t)cur->msg.mid, cur->msg.topic, (uint32_t)cur->msg.payloadlen, cur->msg.payload, (uint8_t)cur->msg.qos, cur->msg.retain, cur->dup, 0, cur->properties, 0);\n\t\t\t\t\tif(rc){\n\t\t\t\t\t\treturn rc;\n\t\t\t\t\t}\n\t\t\t\t\tutil__decrement_send_quota(mosq);\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\nint message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos)\n{\n\tstruct mosquitto_message_all *cur, *tmp;\n\tbool found = false;\n\tassert(mosq);\n\tassert(message);\n\n\tif(dir == mosq_md_out){\n\t\tCOMPAT_pthread_mutex_lock(&mosq->msgs_out.mutex);\n\n\t\tDL_FOREACH_SAFE(mosq->msgs_out.inflight, cur, tmp){\n\t\t\tif(found == false && cur->msg.mid == mid){\n\t\t\t\tif(cur->msg.qos != qos){\n\t\t\t\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t}\n\t\t\t\tDL_DELETE(mosq->msgs_out.inflight, cur);\n\n\t\t\t\t*message = cur;\n\t\t\t\tmosq->msgs_out.queue_len--;\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n\t\tif(found){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_NOT_FOUND;\n\t\t}\n\t}else{\n\t\tCOMPAT_pthread_mutex_lock(&mosq->msgs_in.mutex);\n\t\tDL_FOREACH_SAFE(mosq->msgs_in.inflight, cur, tmp){\n\t\t\tif(cur->msg.mid == mid){\n\t\t\t\tif(cur->msg.qos != qos){\n\t\t\t\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_in.mutex);\n\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t}\n\t\t\t\tDL_DELETE(mosq->msgs_in.inflight, cur);\n\t\t\t\t*message = cur;\n\t\t\t\tmosq->msgs_in.queue_len--;\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_in.mutex);\n\t\tif(found){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_NOT_FOUND;\n\t\t}\n\t}\n}\n\n\nvoid message__retry_check(struct mosquitto *mosq)\n{\n\tstruct mosquitto_message_all *msg;\n\tassert(mosq);\n\n#ifdef WITH_THREADING\n\tCOMPAT_pthread_mutex_lock(&mosq->msgs_out.mutex);\n#endif\n\n\tDL_FOREACH(mosq->msgs_out.inflight, msg){\n\t\tswitch(msg->state){\n\t\t\tcase mosq_ms_publish_qos1:\n\t\t\tcase mosq_ms_publish_qos2:\n\t\t\t\tmsg->dup = true;\n\t\t\t\tsend__publish(mosq, (uint16_t)msg->msg.mid, msg->msg.topic, (uint32_t)msg->msg.payloadlen, msg->msg.payload, (uint8_t)msg->msg.qos, msg->msg.retain, msg->dup, 0, msg->properties, 0);\n\t\t\t\tbreak;\n\t\t\tcase mosq_ms_wait_for_pubrel:\n\t\t\t\tmsg->dup = true;\n\t\t\t\tsend__pubrec(mosq, (uint16_t)msg->msg.mid, 0, NULL);\n\t\t\t\tbreak;\n\t\t\tcase mosq_ms_resend_pubrel:\n\t\t\tcase mosq_ms_wait_for_pubcomp:\n\t\t\t\tmsg->dup = true;\n\t\t\t\tsend__pubrel(mosq, (uint16_t)msg->msg.mid, NULL);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n#ifdef WITH_THREADING\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n#endif\n}\n\n\nvoid mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry)\n{\n\tUNUSED(mosq);\n\tUNUSED(message_retry);\n}\n\n\nint message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos)\n{\n\tstruct mosquitto_message_all *message, *tmp;\n\tassert(mosq);\n\n\tCOMPAT_pthread_mutex_lock(&mosq->msgs_out.mutex);\n\tDL_FOREACH_SAFE(mosq->msgs_out.inflight, message, tmp){\n\t\tif(message->msg.mid == mid){\n\t\t\tif(message->msg.qos != qos){\n\t\t\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tmessage->state = state;\n\t\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgs_out.mutex);\n\treturn MOSQ_ERR_NOT_FOUND;\n}\n\n\nint mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages)\n{\n\treturn mosquitto_int_option(mosq, MOSQ_OPT_SEND_MAXIMUM, (int)max_inflight_messages);\n}\n\n"
  },
  {
    "path": "lib/messages_mosq.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef MESSAGES_MOSQ_H\n#define MESSAGES_MOSQ_H\n\n#include \"mosquitto_internal.h\"\n#include \"mosquitto.h\"\n\nvoid message__cleanup_all(struct mosquitto *mosq);\nvoid message__cleanup(struct mosquitto_message_all **message);\nint message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos);\nint message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir);\nvoid message__reconnect_reset(struct mosquitto *mosq, bool update_quota_only);\nint message__release_to_inflight(struct mosquitto *mosq, enum mosquitto_msg_direction dir);\nint message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos);\nvoid message__retry_check(struct mosquitto *mosq);\nint message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos);\n\n#endif\n"
  },
  {
    "path": "lib/mosquitto_internal.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n   Tatsuzo Osawa - Add epoll.\n*/\n\n#ifndef MOSQUITTO_INTERNAL_H\n#define MOSQUITTO_INTERNAL_H\n\n#include \"config.h\"\n\n#ifdef WIN32\n#  include <winsock2.h>\n#endif\n\n#ifdef WITH_TLS\n#  include <openssl/ssl.h>\n#else\n#  include <time.h>\n#endif\n#include <stdlib.h>\n\n#include <pthread_compat.h>\n\n#ifdef WITH_SRV\n#  include <ares.h>\n#endif\n\n#ifdef WIN32\n#   if _MSC_VER < 1600\ntypedef unsigned char uint8_t;\ntypedef unsigned short uint16_t;\ntypedef unsigned int uint32_t;\ntypedef unsigned long long uint64_t;\n#   else\n#       include <stdint.h>\n#   endif\n#else\n#   include <stdint.h>\n#endif\n\n#include \"mosquitto.h\"\n#include \"mosquitto/libcommon_time.h\"\n#ifdef WITH_BROKER\n#  ifdef __linux__\n#    include <netdb.h>\n#  endif\n#  include \"uthash.h\"\nstruct mosquitto__client_msg;\n#endif\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n#  include <libwebsockets.h>\n#  define WS_PACKET_OFFSET LWS_PRE\n#else\n#  define WS_PACKET_OFFSET 16\n#endif\n\n#ifdef WIN32\ntypedef SOCKET mosq_sock_t;\n#else\ntypedef int mosq_sock_t;\n#endif\n\n#ifdef WIN32\n#  define WINDOWS_SET_ERRNO() \\\n\t\tdo{ \\\n\t\t\terrno = WSAGetLastError(); \\\n\t\t}while(0)\n#  define WINDOWS_SET_ERRNO_RW() \\\n\t\tif(errno != EAGAIN){ \\\n\t\t\terrno = WSAGetLastError(); \\\n\t\t}\n#else\n#  define WINDOWS_SET_ERRNO()\n#  define WINDOWS_SET_ERRNO_RW()\n#endif\n\n#define SAFE_PRINT(A) (A)?(A):\"null\"\n#define SAFE_FREE(A) do{ free(A); (A) = NULL;}while(0)\n\n#define MSG_EXPIRY_INFINITE UINT32_MAX\n\nenum mosquitto_msg_direction {\n\tmosq_md_in = 0,\n\tmosq_md_out = 1,\n};\n\nenum mosquitto_msg_state {\n\tmosq_ms_invalid = 0,\n\tmosq_ms_publish_qos0 = 1,\n\tmosq_ms_publish_qos1 = 2,\n\tmosq_ms_wait_for_puback = 3,\n\tmosq_ms_publish_qos2 = 4,\n\tmosq_ms_wait_for_pubrec = 5,\n\tmosq_ms_resend_pubrel = 6,\n\tmosq_ms_wait_for_pubrel = 7,\n\tmosq_ms_resend_pubcomp = 8,\n\tmosq_ms_wait_for_pubcomp = 9,\n\tmosq_ms_send_pubrec = 10,\n\tmosq_ms_queued = 11,\n\n\tmosq_ms_any = 255,\n\t/* max value allowed is 255 */\n};\n\nenum mosquitto_client_state {\n\tmosq_cs_new = 0,\n\tmosq_cs_connected = 1,\n\tmosq_cs_disconnecting = 2,\n\tmosq_cs_active = 3,\n\tmosq_cs_connect_pending = 4,\n\tmosq_cs_connect_srv = 5,\n\tmosq_cs_disconnect_ws = 6,\n\tmosq_cs_disconnected = 7,\n\tmosq_cs_socks5_new = 8,\n\tmosq_cs_socks5_start = 9,\n\tmosq_cs_socks5_request = 10,\n\tmosq_cs_socks5_reply = 11,\n\tmosq_cs_socks5_auth_ok = 12,\n\tmosq_cs_socks5_userpass_reply = 13,\n\tmosq_cs_socks5_send_userpass = 14,\n\tmosq_cs_expiring = 15,\n\tmosq_cs_duplicate = 17, /* client that has been taken over by another with the same id */\n\tmosq_cs_disconnect_with_will = 18,\n\tmosq_cs_disused = 19, /* client that has been added to the disused list to be freed */\n\tmosq_cs_authenticating = 20, /* Client has sent CONNECT but is still undergoing extended authentication */\n\tmosq_cs_reauthenticating = 21, /* Client is undergoing reauthentication and shouldn't do anything else until complete */\n\tmosq_cs_delayed_auth = 22, /* Client is awaiting an authentication result from a plugin */\n};\n\nenum mosquitto__protocol {\n\tmosq_p_invalid = 0,\n\tmosq_p_mqtts = 1,\n\tmosq_p_mqtt31 = 3,\n\tmosq_p_mqtt311 = 4,\n\tmosq_p_mqtt5 = 5,\n};\n\nenum mosquitto__threaded_state {\n\tmosq_ts_none,       /* No threads in use */\n\tmosq_ts_self,       /* Threads started by libmosquitto */\n\tmosq_ts_external,   /* Threads started by external code */\n};\n\nenum mosquitto__transport {\n\tmosq_t_invalid = 0,\n\tmosq_t_tcp = 1,\n\tmosq_t_ws = 2,\n\tmosq_t_sctp = 3,\n\tmosq_t_http = 4, /* not valid for MQTT, just as a ws precursor */\n\tmosq_t_proxy_v2 = 5, /* not valid for MQTT, just as a PROXY protocol v2 precursor */\n\tmosq_t_proxy_v1 = 6, /* not valid for MQTT, just as a PROXY protocol v1 precursor */\n};\n\n/* Alias direction - local <-> remote */\n#define ALIAS_DIR_L2R 1\n#define ALIAS_DIR_R2L 2\n\nstruct mosquitto__alias {\n\tchar *topic;\n\tuint16_t alias;\n};\n\nstruct session_expiry_list {\n\tstruct mosquitto *context;\n\tstruct session_expiry_list *prev;\n\tstruct session_expiry_list *next;\n};\n\nstruct mosquitto__packet {\n\tstruct mosquitto__packet *next;\n\tuint32_t remaining_length;\n\tuint32_t packet_length;\n\tuint32_t to_process;\n\tuint32_t pos;\n\tuint16_t mid;\n\tuint8_t command;\n\tint8_t remaining_count;\n\tuint8_t payload[];\n};\n\nstruct mosquitto__packet_in {\n\tuint8_t *payload;\n\tuint32_t remaining_mult;\n\tuint32_t remaining_length;\n\tuint32_t packet_length;\n\tuint32_t to_process;\n\tuint32_t pos;\n\tuint16_t packet_buffer_to_process;\n\tuint16_t packet_buffer_pos;\n\tuint16_t packet_buffer_size;\n\tuint8_t command;\n\tuint8_t *packet_buffer;\n\tint8_t remaining_count;\n};\n\nstruct mosquitto_message_all {\n\tstruct mosquitto_message_all *next;\n\tstruct mosquitto_message_all *prev;\n\tmosquitto_property *properties;\n\tenum mosquitto_msg_state state;\n\tbool dup;\n\tstruct mosquitto_message msg;\n\tuint32_t expiry_interval;\n};\n\n#ifdef WITH_TLS\nenum mosquitto__keyform {\n\tmosq_k_pem = 0,\n\tmosq_k_engine = 1,\n};\n#endif\n\nstruct will_delay_list {\n\tstruct mosquitto *context;\n\tstruct will_delay_list *prev;\n\tstruct will_delay_list *next;\n};\n\nstruct mosquitto_msg_data {\n#ifdef WITH_BROKER\n\tstruct mosquitto__client_msg *inflight;\n\tstruct mosquitto__client_msg *queued;\n\tlong inflight_bytes;\n\tlong inflight_bytes12;\n\tint inflight_count;\n\tint inflight_count12;\n\tlong queued_bytes;\n\tlong queued_bytes12;\n\tint queued_count;\n\tint queued_count12;\n#else\n\tstruct mosquitto_message_all *inflight;\n\tint queue_len;\n#  ifdef WITH_THREADING\n\tpthread_mutex_t mutex;\n#  endif\n#endif\n\tint inflight_quota;\n\tuint16_t inflight_maximum;\n};\n\n\n#define WS_CONTINUATION 0x00\n#define WS_TEXT 0x01\n#define WS_BINARY 0x02\n#define WS_CLOSE 0x08\n#define WS_PING 0x09\n#define WS_PONG 0x0A\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\nstruct ws_data {\n\tstruct mosquitto__packet *out_packet;\n\tchar *http_path;\n\tchar *accept_key;\n\tuint64_t payloadlen;\n\tssize_t pos;\n\tint http_header_size;\n\tuint8_t maskingkey[4];\n\tuint8_t disconnect_reason;\n\tuint8_t opcode;\n\tuint8_t mask;\n\tuint8_t mask_bytes;\n\tuint8_t payloadlen_bytes;\n\tbool is_client;\n};\n#endif\n\nstruct proxy_data {\n\tuint8_t *buf;\n\tchar *cipher;\n\tchar *tls_version;\n\tuint16_t len;\n\tuint16_t pos;\n\tint8_t cmd;\n\tuint8_t fam;\n\tbool have_tls;\n};\n\nstruct client_stats {\n\tuint64_t messages_received;\n\tuint64_t messages_sent;\n\tuint64_t messages_dropped;\n};\n\nstruct mosquitto {\n#if defined(WITH_BROKER) && (defined(WITH_EPOLL) || defined(WITH_KQUEUE))\n\t/* This *must* be the first element in the struct. */\n\tint ident;\n#endif\n\tmosq_sock_t sock;\n#ifndef WITH_BROKER\n\tmosq_sock_t sockpairR, sockpairW;\n#endif\n\tuint32_t maximum_packet_size;\n#if defined(__GLIBC__) && defined(WITH_ADNS)\n\tstruct gaicb *adns; /* For getaddrinfo_a */\n#endif\n\tuint64_t last_cmsg_id;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\tstruct ws_data wsd;\n#endif\n\tenum mosquitto__protocol protocol;\n\tchar *address;\n\tchar *id;\n\tchar *username;\n\tchar *password;\n\tunsigned id_hashv;\n\tuint16_t keepalive;\n\tuint16_t last_mid;\n\tuint16_t password_len;\n\tenum mosquitto_client_state state;\n\tuint8_t transport;\n\ttime_t last_msg_in;\n\ttime_t next_msg_out;\n\ttime_t ping_t;\n\tstruct mosquitto__packet_in in_packet;\n\tstruct mosquitto__packet *out_packet;\n\tstruct mosquitto_message_all *will;\n\tstruct mosquitto__alias *aliases_l2r;\n\tstruct mosquitto__alias *aliases_r2l;\n\tstruct will_delay_list *will_delay_entry;\n\tuint16_t alias_count_l2r;\n\tuint16_t alias_count_r2l;\n\tuint16_t alias_max_l2r;\n\tuint32_t will_delay_interval;\n\tint out_packet_count;\n\tint64_t out_packet_bytes;\n\ttime_t will_delay_time;\n#ifdef WITH_TLS\n\tSSL *ssl;\n\tSSL_CTX *ssl_ctx;\n#ifndef WITH_BROKER\n\tSSL_CTX *user_ssl_ctx;\n#endif\n\tchar *tls_cafile;\n\tchar *tls_capath;\n\tchar *tls_certfile;\n\tchar *tls_keyfile;\n\tint (*tls_pw_callback)(char *buf, int size, int rwflag, void *userdata);\n\tchar *tls_version;\n\tchar *tls_ciphers;\n\tchar *tls_13_ciphers;\n\tchar *tls_psk;\n\tchar *tls_psk_identity;\n\tchar *tls_engine;\n\tchar *tls_engine_kpass_sha1;\n\tchar *tls_alpn;\n\tint tls_cert_reqs;\n\tbool tls_insecure;\n\tbool ssl_ctx_defaults;\n\tbool tls_ocsp_required;\n\tbool tls_use_os_certs;\n\tenum mosquitto__keyform tls_keyform;\n#endif\n\tbool want_write;\n\tbool run;\n#if defined(WITH_THREADING) && !defined(WITH_BROKER)\n\tpthread_mutex_t callback_mutex;\n\tpthread_mutex_t log_callback_mutex;\n\tpthread_mutex_t msgtime_mutex;\n\tpthread_mutex_t out_packet_mutex;\n\tpthread_mutex_t state_mutex;\n\tpthread_mutex_t mid_mutex;\n\tpthread_t thread_id;\n#endif\n\tbool clean_start;\n\ttime_t session_expiry_time;\n\tuint32_t session_expiry_interval;\n#ifdef WITH_BROKER\n\tbool in_by_id;\n\tbool is_dropping;\n\tbool is_bridge;\n\tbool is_persisted;\n\tstruct mosquitto__bridge *bridge;\n\tstruct mosquitto_msg_data msgs_in;\n\tstruct mosquitto_msg_data msgs_out;\n\tstruct mosquitto__acl_user *acl_list;\n\tstruct mosquitto__listener *listener;\n\tstruct mosquitto__packet *out_packet_last;\n\tstruct mosquitto__subleaf **subs;\n\tchar *auth_method;\n\tint subs_capacity; /* allocated size of the subs instance */\n\tint subs_count; /* number of currently active subscriptions */\n#  ifndef WITH_EPOLL\n\tint pollfd_index;\n#  endif\n#  ifdef WITH_WEBSOCKETS\n#    if WITH_WEBSOCKETS == WS_IS_LWS\n\tstruct lws *wsi;\n#    endif\n#  endif\n\tbool assigned_id;\n#else\n#  ifdef WITH_SOCKS\n\tchar *socks5_host;\n\tuint16_t socks5_port;\n\tchar *socks5_username;\n\tchar *socks5_password;\n#  endif\n\tvoid *userdata;\n\tstruct mosquitto_msg_data msgs_in;\n\tstruct mosquitto_msg_data msgs_out;\n\tLIBMOSQ_CB_pre_connect on_pre_connect;\n\tLIBMOSQ_CB_connect on_connect;\n\tLIBMOSQ_CB_connect_with_flags on_connect_with_flags;\n\tLIBMOSQ_CB_connect_v5 on_connect_v5;\n\tLIBMOSQ_CB_disconnect on_disconnect;\n\tLIBMOSQ_CB_disconnect_v5 on_disconnect_v5;\n\tLIBMOSQ_CB_publish on_publish;\n\tLIBMOSQ_CB_publish_v5 on_publish_v5;\n\tLIBMOSQ_CB_message on_message;\n\tLIBMOSQ_CB_message_v5 on_message_v5;\n\tLIBMOSQ_CB_subscribe on_subscribe;\n\tLIBMOSQ_CB_subscribe_v5 on_subscribe_v5;\n\tLIBMOSQ_CB_unsubscribe on_unsubscribe;\n\tLIBMOSQ_CB_unsubscribe_v5 on_unsubscribe_v5;\n\tLIBMOSQ_CB_unsubscribe2_v5 on_unsubscribe2_v5;\n\tLIBMOSQ_CB_ext_auth on_ext_auth;\n\tLIBMOSQ_CB_log on_log;\n\t/*void (*on_error)();*/\n\tchar *host;\n\tchar *bind_address;\n\tunsigned int reconnects;\n\tunsigned int reconnect_delay;\n\tunsigned int reconnect_delay_max;\n\tint callback_depth;\n\tuint16_t port;\n\tbool disable_socketpair;\n\tbool reconnect_exponential_backoff;\n\tbool request_disconnect;\n\tchar threaded;\n\tstruct mosquitto__packet *out_packet_last;\n\tmosquitto_property *connect_properties;\n#  ifdef WITH_SRV\n\tares_channel achan;\n#  endif\n#endif\n\tuint8_t max_qos;\n\tuint8_t retain_available;\n\tbool tcp_nodelay;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\tchar *http_request;\n#endif\n\n#ifdef WITH_BROKER\n\tUT_hash_handle hh_id;\n\tUT_hash_handle hh_sock;\n\tstruct mosquitto *for_free_next;\n\tstruct session_expiry_list *expiry_list_item;\n\tuint16_t remote_port;\n#  ifndef WITH_OLD_KEEPALIVE\n\tstruct mosquitto *keepalive_next;\n\tstruct mosquitto *keepalive_prev;\n\ttime_t keepalive_add_time;\n#  endif\n\tstruct client_stats stats;\n#endif\n#ifdef WITH_EPOLL\n\tuint32_t events;\n#elif defined(WITH_KQUEUE)\n\tshort events;\n#else\n\tuint32_t events;\n#endif\n\tstruct proxy_data proxy;\n};\n\n#define STREMPTY(str) (str[0] == '\\0')\n\nvoid do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties);\n\n#endif\n"
  },
  {
    "path": "lib/net_mosq.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#define _GNU_SOURCE\n#include \"config.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#ifndef WIN32\n#define _GNU_SOURCE\n#include <netdb.h>\n#include <netinet/tcp.h>\n#include <sys/socket.h>\n#include <unistd.h>\n#else\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#endif\n\n#ifdef __ANDROID__\n#include <linux/in.h>\n#include <linux/in6.h>\n#include <sys/endian.h>\n#endif\n\n#ifdef HAVE_NETINET_IN_H\n#  include <netinet/in.h>\n#endif\n\n#ifdef WITH_UNIX_SOCKETS\n#  ifdef WIN32\n#    include <afunix.h>\n#  else\n#    include <sys/un.h>\n#  endif\n#endif\n\n#ifdef __QNX__\n#include <net/netbyte.h>\n#endif\n\n#ifdef WITH_TLS\n#include <openssl/conf.h>\n#include <openssl/engine.h>\n#include <openssl/err.h>\n#include <openssl/ui.h>\n#include <tls_mosq.h>\n#endif\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#  if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n#    include <libwebsockets.h>\n#  endif\n#else\n#  include \"read_handle.h\"\n#endif\n\n#include \"logging_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"util_mosq.h\"\n\n#ifdef WITH_TLS\nint tls_ex_index_mosq = -1;\nUI_METHOD *_ui_method = NULL;\n\nstatic bool is_tls_initialized = false;\n\n\n/* Functions taken from OpenSSL s_server/s_client */\nstatic int ui_open(UI *ui)\n{\n\treturn UI_method_get_opener(UI_OpenSSL())(ui);\n}\n\n\nstatic int ui_read(UI *ui, UI_STRING *uis)\n{\n\treturn UI_method_get_reader(UI_OpenSSL())(ui, uis);\n}\n\n\nstatic int ui_write(UI *ui, UI_STRING *uis)\n{\n\treturn UI_method_get_writer(UI_OpenSSL())(ui, uis);\n}\n\n\nstatic int ui_close(UI *ui)\n{\n\treturn UI_method_get_closer(UI_OpenSSL())(ui);\n}\n\n\nstatic void setup_ui_method(void)\n{\n\t_ui_method = UI_create_method(\"OpenSSL application user interface\");\n\tUI_method_set_opener(_ui_method, ui_open);\n\tUI_method_set_reader(_ui_method, ui_read);\n\tUI_method_set_writer(_ui_method, ui_write);\n\tUI_method_set_closer(_ui_method, ui_close);\n}\n\n\nstatic void cleanup_ui_method(void)\n{\n\tif(_ui_method){\n\t\tUI_destroy_method(_ui_method);\n\t\t_ui_method = NULL;\n\t}\n}\n\n\nUI_METHOD *net__get_ui_method(void)\n{\n\tif(!_ui_method){\n\t\tsetup_ui_method();\n\t}\n\treturn _ui_method;\n}\n\n#endif\n\n\nint net__init(void)\n{\n#ifdef WIN32\n\tWSADATA wsaData;\n\tif(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n#endif\n\n#ifdef WITH_SRV\n\tares_library_init(ARES_LIB_INIT_ALL);\n#endif\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid net__cleanup(void)\n{\n#ifdef WITH_TLS\n\tcleanup_ui_method();\n\tif(is_tls_initialized){\n\t\tis_tls_initialized = false;\n\t}\n#endif\n\n#ifdef WITH_SRV\n\tares_library_cleanup();\n#endif\n\n#ifdef WIN32\n\tWSACleanup();\n#endif\n}\n\n#ifdef WITH_TLS\n\n\nvoid net__init_tls(void)\n{\n\tif(is_tls_initialized){\n\t\treturn;\n\t}\n\n#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\tENGINE_load_builtin_engines();\n#endif\n\tif(tls_ex_index_mosq == -1){\n\t\ttls_ex_index_mosq = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);\n\t}\n\n\tis_tls_initialized = true;\n}\n#endif\n\n\nbool net__is_connected(struct mosquitto *mosq)\n{\n#if defined(WITH_BROKER) && defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\treturn mosq->sock != INVALID_SOCKET || mosq->wsi != NULL;\n#else\n\treturn mosq->sock != INVALID_SOCKET;\n#endif\n}\n\n\n/* Close a socket associated with a context and set it to -1.\n * Returns 1 on failure (context is NULL)\n * Returns 0 on success.\n */\nint net__socket_close(struct mosquitto *mosq)\n{\n\tint rc = 0;\n#ifdef WITH_BROKER\n\tstruct mosquitto *mosq_found;\n#endif\n\n\tassert(mosq);\n#ifdef WITH_TLS\n#if defined(WITH_BROKER) && defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tif(!mosq->wsi)\n#endif\n\t{\n\t\tif(mosq->ssl){\n\t\t\tif(!SSL_in_init(mosq->ssl)){\n\t\t\t\tSSL_shutdown(mosq->ssl);\n\t\t\t}\n\t\t\tSSL_free(mosq->ssl);\n\t\t\tmosq->ssl = NULL;\n\t\t}\n\t}\n#endif\n\n#if defined(WITH_BROKER) && defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tif(mosq->wsi){\n\t\tif(mosq->state != mosq_cs_disconnecting){\n\t\t\tmosquitto__set_state(mosq, mosq_cs_disconnect_ws);\n\t\t}\n\t\tlws_callback_on_writable(mosq->wsi);\n\t\tif(mosq->listener){\n\t\t\tmosq->listener->client_count--;\n\t\t}\n\t}else\n#endif\n\t{\n\t\tif(net__is_connected(mosq)){\n#ifdef WITH_BROKER\n\t\t\tHASH_FIND(hh_sock, db.contexts_by_sock, &mosq->sock, sizeof(mosq->sock), mosq_found);\n\t\t\tif(mosq_found){\n\t\t\t\tHASH_DELETE(hh_sock, db.contexts_by_sock, mosq_found);\n\t\t\t}\n\t\t\tif(mosq->listener && mosq->state != mosq_cs_disused){\n\t\t\t\tmosq->listener->client_count--;\n\t\t\t}\n#endif\n\t\t\trc = COMPAT_CLOSE(mosq->sock);\n\t\t\tmosq->sock = INVALID_SOCKET;\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\n/* Shutdown a socket.\n * Returns 1 on failure (context is NULL)\n * Returns 0 on success.\n */\nint net__socket_shutdown(struct mosquitto *mosq)\n{\n\tint rc = 0;\n\n\tassert(mosq);\n\n\tif(net__is_connected(mosq)){\n\t\trc = COMPAT_SHUTDOWN(mosq->sock);\n\t}\n\n\treturn rc;\n}\n\n\n#ifdef FINAL_WITH_TLS_PSK\n\n\nstatic unsigned int psk_client_callback(SSL *ssl, const char *hint,\n\t\tchar *identity, unsigned int max_identity_len,\n\t\tunsigned char *psk, unsigned int max_psk_len)\n{\n\tstruct mosquitto *mosq;\n\tint len;\n\n\tUNUSED(hint);\n\n\tmosq = SSL_get_ex_data(ssl, tls_ex_index_mosq);\n\tif(!mosq){\n\t\treturn 0;\n\t}\n\n\tsnprintf(identity, max_identity_len, \"%s\", mosq->tls_psk_identity);\n\n\tlen = mosquitto__hex2bin(mosq->tls_psk, psk, (int)max_psk_len);\n\tif(len < 0){\n\t\treturn 0;\n\t}\n\treturn (unsigned int)len;\n}\n#endif\n\n#if defined(WITH_BROKER) && defined(__GLIBC__) && defined(WITH_ADNS)\n\n\n/* Async connect, part 1 (dns lookup) */\nint net__try_connect_step1(struct mosquitto *mosq, const char *host)\n{\n\tint s;\n\tvoid *sevp = NULL;\n\tstruct addrinfo *hints;\n\tstruct addrinfo *ar_request;\n\n\tif(mosq->adns){\n\t\tgai_cancel(mosq->adns);\n\n\t\tar_request = (struct addrinfo *)mosq->adns->ar_request;\n\t\tmosquitto_FREE(ar_request);\n\t\tmosquitto_FREE(mosq->adns);\n\t}\n\tmosq->adns = mosquitto_calloc(1, sizeof(struct gaicb));\n\tif(!mosq->adns){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\thints = mosquitto_calloc(1, sizeof(struct addrinfo));\n\tif(!hints){\n\t\tmosquitto_FREE(mosq->adns);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\thints->ai_family = AF_UNSPEC;\n\thints->ai_socktype = SOCK_STREAM;\n\n\tmosq->adns->ar_name = host;\n\tmosq->adns->ar_request = hints;\n\n\ts = getaddrinfo_a(GAI_NOWAIT, &mosq->adns, 1, sevp);\n\tif(s){\n\t\terrno = s;\n\t\tif(mosq->adns){\n\t\t\tar_request = (struct addrinfo *)mosq->adns->ar_request;\n\t\t\tmosquitto_FREE(ar_request);\n\t\t\tmosquitto_FREE(mosq->adns);\n\t\t}\n\t\treturn MOSQ_ERR_EAI;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* Async connect part 2, the connection. */\nint net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock)\n{\n\tstruct addrinfo *ainfo, *rp;\n\tstruct addrinfo *ar_request;\n\tint rc;\n\n\tainfo = mosq->adns->ar_result;\n\n\tfor(rp = ainfo; rp != NULL; rp = rp->ai_next){\n\t\t*sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n\t\tif(*sock == INVALID_SOCKET){\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(rp->ai_family == AF_INET){\n\t\t\t((struct sockaddr_in *)rp->ai_addr)->sin_port = htons(port);\n\t\t}else if(rp->ai_family == AF_INET6){\n\t\t\t((struct sockaddr_in6 *)rp->ai_addr)->sin6_port = htons(port);\n\t\t}else{\n\t\t\tCOMPAT_CLOSE(*sock);\n\t\t\t*sock = INVALID_SOCKET;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* Set non-blocking */\n\t\tif(net__socket_nonblock(sock)){\n\t\t\tcontinue;\n\t\t}\n\n\t\trc = connect(*sock, rp->ai_addr, rp->ai_addrlen);\n\t\tWINDOWS_SET_ERRNO();\n\t\tif(rc == 0 || errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK){\n\t\t\tif(rc < 0 && (errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK)){\n\t\t\t\trc = MOSQ_ERR_CONN_PENDING;\n\t\t\t}\n\n\t\t\t/* Set non-blocking */\n\t\t\tif(net__socket_nonblock(sock)){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tCOMPAT_CLOSE(*sock);\n\t\t*sock = INVALID_SOCKET;\n\t}\n\tfreeaddrinfo(mosq->adns->ar_result);\n\tmosq->adns->ar_result = NULL;\n\n\tar_request = (struct addrinfo *)mosq->adns->ar_request;\n\tmosquitto_FREE(ar_request);\n\tmosquitto_FREE(mosq->adns);\n\n\tif(!rp){\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n\n\treturn rc;\n}\n\n#endif\n\n\nstatic int net__try_connect_tcp(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking)\n{\n\tstruct addrinfo hints;\n\tstruct addrinfo *ainfo, *rp;\n\tstruct addrinfo *ainfo_bind, *rp_bind;\n\tint s;\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tainfo_bind = NULL;\n\n\t*sock = INVALID_SOCKET;\n\tmemset(&hints, 0, sizeof(struct addrinfo));\n\thints.ai_family = AF_UNSPEC;\n\thints.ai_socktype = SOCK_STREAM;\n\n\ts = getaddrinfo(host, NULL, &hints, &ainfo);\n\tif(s){\n\t\terrno = s;\n\t\treturn MOSQ_ERR_EAI;\n\t}\n\n\tif(bind_address){\n\t\ts = getaddrinfo(bind_address, NULL, &hints, &ainfo_bind);\n\t\tif(s){\n\t\t\tfreeaddrinfo(ainfo);\n\t\t\terrno = s;\n\t\t\treturn MOSQ_ERR_EAI;\n\t\t}\n\t}\n\n\tfor(rp = ainfo; rp != NULL; rp = rp->ai_next){\n\t\t*sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n\t\tif(*sock == INVALID_SOCKET){\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(rp->ai_family == AF_INET){\n\t\t\t((struct sockaddr_in *)rp->ai_addr)->sin_port = htons(port);\n\t\t}else if(rp->ai_family == AF_INET6){\n\t\t\t((struct sockaddr_in6 *)rp->ai_addr)->sin6_port = htons(port);\n\t\t}else{\n\t\t\tCOMPAT_CLOSE(*sock);\n\t\t\t*sock = INVALID_SOCKET;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(bind_address){\n\t\t\tfor(rp_bind = ainfo_bind; rp_bind != NULL; rp_bind = rp_bind->ai_next){\n\t\t\t\tif(bind(*sock, rp_bind->ai_addr, rp_bind->ai_addrlen) == 0){\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(!rp_bind){\n\t\t\t\tCOMPAT_CLOSE(*sock);\n\t\t\t\t*sock = INVALID_SOCKET;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif(!blocking){\n\t\t\t/* Set non-blocking */\n\t\t\tif(net__socket_nonblock(sock)){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\trc = connect(*sock, rp->ai_addr, rp->ai_addrlen);\n\t\tWINDOWS_SET_ERRNO();\n\t\tif(rc == 0 || errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK){\n\t\t\tif(rc < 0 && (errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK)){\n\t\t\t\trc = MOSQ_ERR_CONN_PENDING;\n\t\t\t}\n\n\t\t\tif(blocking){\n\t\t\t\t/* Set non-blocking */\n\t\t\t\tif(net__socket_nonblock(sock)){\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tCOMPAT_CLOSE(*sock);\n\t\t*sock = INVALID_SOCKET;\n\t}\n\tfreeaddrinfo(ainfo);\n\tif(bind_address){\n\t\tfreeaddrinfo(ainfo_bind);\n\t}\n\tif(!rp){\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n\treturn rc;\n}\n\n\n#ifdef WITH_UNIX_SOCKETS\n\n\nstatic int net__try_connect_unix(const char *host, mosq_sock_t *sock)\n{\n\tstruct sockaddr_un addr;\n\tmosq_sock_t s;\n\tint rc;\n\n\tif(host == NULL || strlen(host) == 0 || strlen(host) > sizeof(addr.sun_path)-1){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmemset(&addr, 0, sizeof(struct sockaddr_un));\n\taddr.sun_family = AF_UNIX;\n\tstrncpy(addr.sun_path, host, sizeof(addr.sun_path)-1);\n\n\ts = socket(AF_UNIX, SOCK_STREAM, 0);\n\tif(s < 0){\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n#ifndef WIN32\n\trc = net__socket_nonblock(&s);\n\tif(rc){\n\t\treturn rc;\n\t}\n#endif\n\n\trc = connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un));\n\tif(rc < 0){\n\t\tCOMPAT_CLOSE(s);\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n\n\t*sock = s;\n\n\treturn 0;\n}\n#endif\n\n\nint net__try_connect(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking)\n{\n\tif(port == 0){\n#ifdef WITH_UNIX_SOCKETS\n\t\treturn net__try_connect_unix(host, sock);\n#else\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t}else{\n\t\treturn net__try_connect_tcp(host, port, sock, bind_address, blocking);\n\t}\n}\n\n\n#ifdef WITH_TLS\n\n\nvoid net__print_ssl_error(struct mosquitto *mosq, const char *msg)\n{\n\tchar ebuf[256];\n\tunsigned long e;\n\tint num = 0;\n\tconst char *msg_disp = msg == NULL ? \"\" : msg;\n\te = ERR_get_error();\n\twhile(e){\n\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"OpenSSL Error %s[%d]: %s\", msg_disp, num, ERR_error_string(e, ebuf));\n\t\te = ERR_get_error();\n\t\tnum++;\n\t}\n}\n\n\nint net__socket_connect_tls(struct mosquitto *mosq)\n{\n\tlong res;\n\n\tERR_clear_error();\n\tif(mosq->tls_ocsp_required){\n\t\t/* Note: OCSP is available in all currently supported OpenSSL versions. */\n\t\tif((res=SSL_set_tlsext_status_type(mosq->ssl, TLSEXT_STATUSTYPE_ocsp)) != 1){\n\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Could not activate OCSP (error: %ld)\", res);\n\t\t\treturn MOSQ_ERR_OCSP;\n\t\t}\n\t\tif((res=SSL_CTX_set_tlsext_status_cb(mosq->ssl_ctx, mosquitto__verify_ocsp_status_cb)) != 1){\n\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Could not activate OCSP (error: %ld)\", res);\n\t\t\treturn MOSQ_ERR_OCSP;\n\t\t}\n\t\tif((res=SSL_CTX_set_tlsext_status_arg(mosq->ssl_ctx, mosq)) != 1){\n\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Could not activate OCSP (error: %ld)\", res);\n\t\t\treturn MOSQ_ERR_OCSP;\n\t\t}\n\t}\n\tSSL_set_connect_state(mosq->ssl);\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\n#ifdef WITH_TLS\n\n\nstatic int net__tls_load_ca(struct mosquitto *mosq)\n{\n\tint ret;\n\n\tif(mosq->tls_use_os_certs){\n\t\tSSL_CTX_set_default_verify_paths(mosq->ssl_ctx);\n\t}\n#if OPENSSL_VERSION_NUMBER < 0x30000000L\n\tif(mosq->tls_cafile || mosq->tls_capath){\n\t\tret = SSL_CTX_load_verify_locations(mosq->ssl_ctx, mosq->tls_cafile, mosq->tls_capath);\n\t\tif(ret == 0){\n#  ifdef WITH_BROKER\n\t\t\tif(mosq->tls_cafile && mosq->tls_capath){\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates, check bridge_cafile \\\"%s\\\" and bridge_capath \\\"%s\\\".\", mosq->tls_cafile, mosq->tls_capath);\n\t\t\t}else if(mosq->tls_cafile){\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates, check bridge_cafile \\\"%s\\\".\", mosq->tls_cafile);\n\t\t\t}else{\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates, check bridge_capath \\\"%s\\\".\", mosq->tls_capath);\n\t\t\t}\n#  else\n\t\t\tif(mosq->tls_cafile && mosq->tls_capath){\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates, check cafile \\\"%s\\\" and capath \\\"%s\\\".\", mosq->tls_cafile, mosq->tls_capath);\n\t\t\t}else if(mosq->tls_cafile){\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates, check cafile \\\"%s\\\".\", mosq->tls_cafile);\n\t\t\t}else{\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates, check capath \\\"%s\\\".\", mosq->tls_capath);\n\t\t\t}\n#  endif\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t}\n#else\n\tif(mosq->tls_cafile){\n\t\tret = SSL_CTX_load_verify_file(mosq->ssl_ctx, mosq->tls_cafile);\n\t\tif(ret == 0){\n#  ifdef WITH_BROKER\n\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates, check bridge_cafile \\\"%s\\\".\", mosq->tls_cafile);\n#  else\n\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates, check cafile \\\"%s\\\".\", mosq->tls_cafile);\n#  endif\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t}\n\tif(mosq->tls_capath){\n\t\tret = SSL_CTX_load_verify_dir(mosq->ssl_ctx, mosq->tls_capath);\n\t\tif(ret == 0){\n#  ifdef WITH_BROKER\n\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates, check bridge_capath \\\"%s\\\".\", mosq->tls_capath);\n#  else\n\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates, check capath \\\"%s\\\".\", mosq->tls_capath);\n#  endif\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t}\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int net__init_ssl_ctx(struct mosquitto *mosq)\n{\n\tint ret;\n#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\tENGINE *engine = NULL;\n\tEVP_PKEY *pkey;\n#endif\n\tuint8_t tls_alpn_wire[256];\n\tuint8_t tls_alpn_len;\n\n\tnet__init_tls();\n\n\tnet__init_tls();\n\n#ifndef WITH_BROKER\n\tif(mosq->user_ssl_ctx){\n\t\tmosq->ssl_ctx = mosq->user_ssl_ctx;\n\t\tif(!mosq->ssl_ctx_defaults){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else if(!mosq->tls_cafile && !mosq->tls_capath && !mosq->tls_psk){\n\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: If you use MOSQ_OPT_SSL_CTX then MOSQ_OPT_SSL_CTX_WITH_DEFAULTS must be true, or at least one of cafile, capath or psk must be specified.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n#endif\n\n\t/* Apply default SSL_CTX settings. This is only used if MOSQ_OPT_SSL_CTX\n\t * has not been set, or if both of MOSQ_OPT_SSL_CTX and\n\t * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS are set. */\n\tif(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk || mosq->tls_use_os_certs){\n\t\tif(!mosq->ssl_ctx){\n\n\t\t\tmosq->ssl_ctx = SSL_CTX_new(TLS_client_method());\n\n\t\t\tif(!mosq->ssl_ctx){\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to create TLS context.\");\n\t\t\t\tnet__print_ssl_error(mosq, \"while trying to create a SSL ctx\");\n\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t}\n\t\t}\n\n#ifdef SSL_OP_NO_TLSv1_3\n\t\tif(mosq->tls_psk){\n\t\t\tSSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_TLSv1_3);\n\t\t}\n#endif\n\n\t\tif(!mosq->tls_version){\n\t\t\tSSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);\n#ifdef SSL_OP_NO_TLSv1_3\n\t\t}else if(!strcmp(mosq->tls_version, \"tlsv1.3\")){\n\t\t\tSSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);\n#endif\n\t\t}else if(!strcmp(mosq->tls_version, \"tlsv1.2\")){\n\t\t\tSSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);\n\t\t}else{\n\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Protocol %s not supported.\", mosq->tls_version);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\t/* Allow use of DHE ciphers */\n\t\tSSL_CTX_set_dh_auto(mosq->ssl_ctx, 1);\n\n\t\t/* Disable compression */\n\t\tSSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_COMPRESSION);\n\n\t\t/* Set ALPN */\n\t\tif(mosq->tls_alpn){\n\t\t\ttls_alpn_len = (uint8_t)strnlen(mosq->tls_alpn, 254);\n\t\t\ttls_alpn_wire[0] = tls_alpn_len;  /* first byte is length of string */\n\t\t\tmemcpy(tls_alpn_wire + 1, mosq->tls_alpn, tls_alpn_len);\n\t\t\tSSL_CTX_set_alpn_protos(mosq->ssl_ctx, tls_alpn_wire, tls_alpn_len + 1U);\n\t\t}\n\n#ifdef SSL_MODE_RELEASE_BUFFERS\n\t\t/* Use even less memory per SSL connection. */\n\t\tSSL_CTX_set_mode(mosq->ssl_ctx, SSL_MODE_RELEASE_BUFFERS);\n#endif\n\n#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\t\tif(mosq->tls_engine){\n\t\t\tengine = ENGINE_by_id(mosq->tls_engine);\n\t\t\tif(!engine){\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error loading %s engine\\n\", mosq->tls_engine);\n\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t}\n\t\t\tif(!ENGINE_init(engine)){\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Failed engine initialisation\\n\");\n\t\t\t\tENGINE_free(engine);\n\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t}\n\t\t\tENGINE_set_default(engine, ENGINE_METHOD_ALL);\n\t\t\tENGINE_free(engine); /* release the structural reference from ENGINE_by_id() */\n\t\t}\n#endif\n\n\t\tif(mosq->tls_ciphers){\n\t\t\tret = SSL_CTX_set_cipher_list(mosq->ssl_ctx, mosq->tls_ciphers);\n\t\t\tif(ret == 0){\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to set TLS ciphers. Check cipher list \\\"%s\\\".\", mosq->tls_ciphers);\n#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\t\t\t\tENGINE_FINISH(engine);\n#endif\n\t\t\t\tnet__print_ssl_error(mosq, \"while trying to set the cipher list\");\n\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t}\n\t\t}\n\n\t\tif(mosq->tls_13_ciphers){\n\t\t\tret = SSL_CTX_set_ciphersuites(mosq->ssl_ctx, mosq->tls_13_ciphers);\n\t\t\tif(ret == 0){\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to set TLS 1.3 ciphersuites. Check cipher_tls13 list \\\"%s\\\".\", mosq->tls_13_ciphers);\n\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t}\n\t\t}\n\n\t\tif(mosq->tls_cafile || mosq->tls_capath || mosq->tls_use_os_certs){\n\t\t\tret = net__tls_load_ca(mosq);\n\t\t\tif(ret != MOSQ_ERR_SUCCESS){\n#  if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\t\t\t\tENGINE_FINISH(engine);\n#  endif\n\t\t\t\tnet__print_ssl_error(mosq, \"while trying to load the ca\");\n\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t}\n\t\t\tif(mosq->tls_cert_reqs == 0){\n\t\t\t\tSSL_CTX_set_verify(mosq->ssl_ctx, SSL_VERIFY_NONE, NULL);\n\t\t\t}else{\n\t\t\t\tSSL_CTX_set_verify(mosq->ssl_ctx, SSL_VERIFY_PEER, mosquitto__server_certificate_verify);\n\t\t\t}\n\n\t\t\tif(mosq->tls_pw_callback){\n\t\t\t\tSSL_CTX_set_default_passwd_cb(mosq->ssl_ctx, mosq->tls_pw_callback);\n\t\t\t\tSSL_CTX_set_default_passwd_cb_userdata(mosq->ssl_ctx, mosq);\n\t\t\t}\n\n\t\t\tif(mosq->tls_certfile){\n\t\t\t\tret = SSL_CTX_use_certificate_chain_file(mosq->ssl_ctx, mosq->tls_certfile);\n\t\t\t\tif(ret != 1){\n#ifdef WITH_BROKER\n\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load client certificate, check bridge_certfile \\\"%s\\\".\", mosq->tls_certfile);\n#else\n\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load client certificate \\\"%s\\\".\", mosq->tls_certfile);\n#endif\n#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\t\t\t\t\tENGINE_FINISH(engine);\n#endif\n\t\t\t\t\tnet__print_ssl_error(mosq, \"while trying to use the certificate chain file\");\n\t\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(mosq->tls_keyfile){\n\t\t\t\tif(mosq->tls_keyform == mosq_k_engine){\n#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\t\t\t\t\tUI_METHOD *ui_method = net__get_ui_method();\n\t\t\t\t\tif(mosq->tls_engine_kpass_sha1){\n\t\t\t\t\t\tif(!ENGINE_ctrl_cmd(engine, ENGINE_SECRET_MODE, ENGINE_SECRET_MODE_SHA, NULL, NULL, 0)){\n\t\t\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to set engine secret mode sha1\");\n\t\t\t\t\t\t\tENGINE_FINISH(engine);\n\t\t\t\t\t\t\tnet__print_ssl_error(mosq, \"while trying to set the engine ctrl cmd SECRET_MODE\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(!ENGINE_ctrl_cmd(engine, ENGINE_PIN, 0, mosq->tls_engine_kpass_sha1, NULL, 0)){\n\t\t\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to set engine pin\");\n\t\t\t\t\t\t\tENGINE_FINISH(engine);\n\t\t\t\t\t\t\tnet__print_ssl_error(mosq, \"while trying to set the engine ctrl cmd PIN\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tui_method = NULL;\n\t\t\t\t\t}\n\t\t\t\t\tpkey = ENGINE_load_private_key(engine, mosq->tls_keyfile, ui_method, NULL);\n\t\t\t\t\tif(!pkey){\n\t\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load engine private key file \\\"%s\\\".\", mosq->tls_keyfile);\n\t\t\t\t\t\tENGINE_FINISH(engine);\n\t\t\t\t\t\tnet__print_ssl_error(mosq, \"while trying to load the private key\");\n\t\t\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t\t\t}\n\t\t\t\t\tif(SSL_CTX_use_PrivateKey(mosq->ssl_ctx, pkey) <= 0){\n\t\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to use engine private key file \\\"%s\\\".\", mosq->tls_keyfile);\n\t\t\t\t\t\tENGINE_FINISH(engine);\n\t\t\t\t\t\tnet__print_ssl_error(mosq, \"while trying to use the private key\");\n\t\t\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t\t\t}\n#endif\n\t\t\t\t}else{\n\t\t\t\t\tret = SSL_CTX_use_PrivateKey_file(mosq->ssl_ctx, mosq->tls_keyfile, SSL_FILETYPE_PEM);\n\t\t\t\t\tif(ret != 1){\n#ifdef WITH_BROKER\n\t\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load client key file, check bridge_keyfile \\\"%s\\\".\", mosq->tls_keyfile);\n#else\n\t\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unable to load client key file \\\"%s\\\".\", mosq->tls_keyfile);\n#endif\n#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\t\t\t\t\t\tENGINE_FINISH(engine);\n#endif\n\t\t\t\t\t\tnet__print_ssl_error(mosq, \"while trying to use the private key file\");\n\t\t\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tret = SSL_CTX_check_private_key(mosq->ssl_ctx);\n\t\t\t\tif(ret != 1){\n\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Client certificate/key are inconsistent.\");\n#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\t\t\t\t\tENGINE_FINISH(engine);\n#endif\n\t\t\t\t\tnet__print_ssl_error(mosq, \"while trying to check the private key\");\n\t\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t\t}\n\t\t\t}\n#ifdef FINAL_WITH_TLS_PSK\n\t\t}else if(mosq->tls_psk){\n\t\t\tSSL_CTX_set_psk_client_callback(mosq->ssl_ctx, psk_client_callback);\n\t\t\tif(mosq->tls_ciphers == NULL){\n\t\t\t\tSSL_CTX_set_cipher_list(mosq->ssl_ctx, \"PSK\");\n\t\t\t}\n#endif\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\nint net__socket_connect_step3(struct mosquitto *mosq, const char *host)\n{\n#ifdef WITH_TLS\n\tBIO *bio;\n\n\tint rc = net__init_ssl_ctx(mosq);\n\tif(rc){\n\t\tnet__socket_close(mosq);\n\t\treturn rc;\n\t}\n\n\tif(mosq->ssl_ctx){\n\t\tif(mosq->ssl){\n\t\t\tSSL_free(mosq->ssl);\n\t\t}\n\t\tmosq->ssl = SSL_new(mosq->ssl_ctx);\n\t\tif(!mosq->ssl){\n\t\t\tnet__socket_close(mosq);\n\t\t\tnet__print_ssl_error(mosq, \"while creating a SSL object\");\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\n\t\tif(!SSL_set_ex_data(mosq->ssl, tls_ex_index_mosq, mosq)){\n\t\t\tnet__socket_close(mosq);\n\t\t\tnet__print_ssl_error(mosq, \"while setting SSL ex data\");\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t\tbio = BIO_new_socket(mosq->sock, BIO_NOCLOSE);\n\t\tif(!bio){\n\t\t\tnet__socket_close(mosq);\n\t\t\tnet__print_ssl_error(mosq, \"while trying to create a new socket\");\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t\tSSL_set_bio(mosq->ssl, bio, bio);\n\n\t\t/*\n\t\t * required for the SNI resolving\n\t\t */\n\t\tif(SSL_set_tlsext_host_name(mosq->ssl, host) != 1){\n\t\t\tnet__socket_close(mosq);\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t\tif(tls__set_verify_hostname(mosq, host)){\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\n\t\tif(net__socket_connect_tls(mosq)){\n\t\t\tnet__socket_close(mosq);\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\n\t}\n#else\n\tUNUSED(mosq);\n\tUNUSED(host);\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* Create a socket and connect it to 'ip' on port 'port'.  */\nint net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking)\n{\n\tint rc, rc2;\n\n\tif(!mosq || !host){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trc = net__try_connect(host, port, &mosq->sock, bind_address, blocking);\n\tif(rc > 0){\n\t\treturn rc;\n\t}\n\n\tif(mosq->tcp_nodelay && port){\n\t\tint flag = 1;\n\t\tif(setsockopt(mosq->sock, IPPROTO_TCP, TCP_NODELAY, (const void *)&flag, sizeof(int)) != 0){\n\t\t\tlog__printf(mosq, MOSQ_LOG_WARNING, \"Warning: Unable to set TCP_NODELAY.\");\n\t\t}\n\t}\n\n#if defined(WITH_SOCKS) && !defined(WITH_BROKER)\n\tif(!mosq->socks5_host)\n#endif\n\t{\n\t\trc2 = net__socket_connect_step3(mosq, host);\n\t\tif(rc2){\n\t\t\treturn rc2;\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\n#ifdef WITH_TLS\n\n\nstatic void net__handle_ssl(struct mosquitto *mosq, int ret)\n{\n\tint err;\n\n\terr = SSL_get_error(mosq->ssl, ret);\n\tif(err == SSL_ERROR_WANT_READ){\n\t\terrno = EAGAIN;\n\t}else if(err == SSL_ERROR_WANT_WRITE){\n#ifdef WITH_BROKER\n\t\tmux__add_out(mosq);\n#else\n\t\tmosq->want_write = true;\n#endif\n\t\terrno = EAGAIN;\n\t}else if(err == SSL_ERROR_SSL){\n\t\tnet__print_ssl_error(mosq, \"while trying to get the error\");\n\t\terrno = EPROTO;\n\t\t/* else if SSL_ERROR_SYSCALL leave errno alone */\n\t}\n\tERR_clear_error();\n#ifdef WIN32\n\tWSASetLastError(errno);\n#endif\n}\n#endif\n\n\nssize_t net__read(struct mosquitto *mosq, void *buf, size_t count)\n{\n#ifdef WITH_TLS\n\tint ret;\n#endif\n\tassert(mosq);\n\terrno = 0;\n#ifdef WITH_TLS\n\tif(mosq->ssl){\n\t\tERR_clear_error();\n\t\tret = SSL_read(mosq->ssl, buf, (int)count);\n\t\tif(ret <= 0){\n\t\t\tnet__handle_ssl(mosq, ret);\n\t\t}\n\t\treturn (ssize_t )ret;\n\t}else\n#endif\n\t{\n\t\t/* Call normal read/recv */\n\n#ifndef WIN32\n\t\treturn read(mosq->sock, buf, count);\n#else\n\t\treturn recv(mosq->sock, buf, count, 0);\n#endif\n\t}\n}\n\n\nssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count)\n{\n#ifdef WITH_TLS\n\tint ret;\n#endif\n\tassert(mosq);\n\n\terrno = 0;\n#ifdef WITH_TLS\n\tif(mosq->ssl){\n\t\tERR_clear_error();\n\t\tmosq->want_write = false;\n\t\tret = SSL_write(mosq->ssl, buf, (int)count);\n\t\tif(ret < 0){\n\t\t\tnet__handle_ssl(mosq, ret);\n\t\t}\n\t\treturn (ssize_t )ret;\n\t}else\n\t/* Call normal write/send */\n#endif\n\t{\n\t\treturn send(mosq->sock, buf, count, MSG_NOSIGNAL);\n\t}\n}\n\n\nint net__socket_nonblock(mosq_sock_t *sock)\n{\n#ifndef WIN32\n\tint opt;\n\t/* Set non-blocking */\n\topt = fcntl(*sock, F_GETFL, 0);\n\tif(opt == -1){\n\t\tCOMPAT_CLOSE(*sock);\n\t\t*sock = INVALID_SOCKET;\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n\tif(fcntl(*sock, F_SETFL, opt | O_NONBLOCK) == -1){\n\t\t/* If either fcntl fails, don't want to allow this client to connect. */\n\t\tCOMPAT_CLOSE(*sock);\n\t\t*sock = INVALID_SOCKET;\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n#else\n\tunsigned long opt = 1;\n\tif(ioctlsocket(*sock, FIONBIO, &opt)){\n\t\tCOMPAT_CLOSE(*sock);\n\t\t*sock = INVALID_SOCKET;\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n#ifndef WITH_BROKER\n\n\nint net__socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW)\n{\n#ifdef WIN32\n\tint family[2] = {AF_INET, AF_INET6};\n\tint i;\n\tstruct sockaddr_storage ss;\n\tstruct sockaddr_in *sa = (struct sockaddr_in *)&ss;\n\tstruct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&ss;\n\tsocklen_t ss_len;\n\tmosq_sock_t spR, spW;\n\n\tmosq_sock_t listensock;\n\n\t*pairR = INVALID_SOCKET;\n\t*pairW = INVALID_SOCKET;\n\n\tfor(i=0; i<2; i++){\n\t\tmemset(&ss, 0, sizeof(ss));\n\t\tif(family[i] == AF_INET){\n\t\t\tsa->sin_family = family[i];\n\t\t\tsa->sin_addr.s_addr = htonl(INADDR_LOOPBACK);\n\t\t\tsa->sin_port = 0;\n\t\t\tss_len = sizeof(struct sockaddr_in);\n\t\t}else if(family[i] == AF_INET6){\n\t\t\tsa6->sin6_family = family[i];\n\t\t\tsa6->sin6_addr = in6addr_loopback;\n\t\t\tsa6->sin6_port = 0;\n\t\t\tss_len = sizeof(struct sockaddr_in6);\n\t\t}else{\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\tlistensock = socket(family[i], SOCK_STREAM, IPPROTO_TCP);\n\t\tif(listensock == -1){\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(bind(listensock, (struct sockaddr *)&ss, ss_len) == -1){\n\t\t\tCOMPAT_CLOSE(listensock);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(listen(listensock, 1) == -1){\n\t\t\tCOMPAT_CLOSE(listensock);\n\t\t\tcontinue;\n\t\t}\n\t\tmemset(&ss, 0, sizeof(ss));\n\t\tss_len = sizeof(ss);\n\t\tif(getsockname(listensock, (struct sockaddr *)&ss, &ss_len) < 0){\n\t\t\tCOMPAT_CLOSE(listensock);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(family[i] == AF_INET){\n\t\t\tsa->sin_family = family[i];\n\t\t\tsa->sin_addr.s_addr = htonl(INADDR_LOOPBACK);\n\t\t\tss_len = sizeof(struct sockaddr_in);\n\t\t}else if(family[i] == AF_INET6){\n\t\t\tsa6->sin6_family = family[i];\n\t\t\tsa6->sin6_addr = in6addr_loopback;\n\t\t\tss_len = sizeof(struct sockaddr_in6);\n\t\t}\n\n\t\tspR = socket(family[i], SOCK_STREAM, IPPROTO_TCP);\n\t\tif(spR == -1){\n\t\t\tCOMPAT_CLOSE(listensock);\n\t\t\tcontinue;\n\t\t}\n\t\tif(net__socket_nonblock(&spR)){\n\t\t\tCOMPAT_CLOSE(listensock);\n\t\t\tcontinue;\n\t\t}\n\t\tif(connect(spR, (struct sockaddr *)&ss, ss_len) < 0){\n\t\t\tWINDOWS_SET_ERRNO();\n\t\t\tif(errno != EINPROGRESS && errno != COMPAT_EWOULDBLOCK){\n\t\t\t\tCOMPAT_CLOSE(spR);\n\t\t\t\tCOMPAT_CLOSE(listensock);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tspW = accept(listensock, NULL, 0);\n\t\tif(spW == -1){\n\t\t\tWINDOWS_SET_ERRNO();\n\t\t\tif(errno != EINPROGRESS && errno != COMPAT_EWOULDBLOCK){\n\t\t\t\tCOMPAT_CLOSE(spR);\n\t\t\t\tCOMPAT_CLOSE(listensock);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif(net__socket_nonblock(&spW)){\n\t\t\tCOMPAT_CLOSE(spR);\n\t\t\tCOMPAT_CLOSE(listensock);\n\t\t\tcontinue;\n\t\t}\n\t\tCOMPAT_CLOSE(listensock);\n\n\t\t*pairR = spR;\n\t\t*pairW = spW;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\treturn MOSQ_ERR_UNKNOWN;\n#else\n\tint sv[2];\n\n\t*pairR = INVALID_SOCKET;\n\t*pairW = INVALID_SOCKET;\n\n\tif(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1){\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n\tif(net__socket_nonblock(&sv[0])){\n\t\tCOMPAT_CLOSE(sv[1]);\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n\tif(net__socket_nonblock(&sv[1])){\n\t\tCOMPAT_CLOSE(sv[0]);\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n\t*pairR = sv[0];\n\t*pairW = sv[1];\n\treturn MOSQ_ERR_SUCCESS;\n#endif\n}\n#endif\n\n#ifndef WITH_BROKER\n\n\nvoid *mosquitto_ssl_get(struct mosquitto *mosq)\n{\n#ifdef WITH_TLS\n\treturn mosq->ssl;\n#else\n\tUNUSED(mosq);\n\n\treturn NULL;\n#endif\n}\n#endif\n"
  },
  {
    "path": "lib/net_mosq.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef NET_MOSQ_H\n#define NET_MOSQ_H\n\n#ifndef WIN32\n#  include <sys/socket.h>\n#  include <unistd.h>\n#else\n#  include <winsock2.h>\n#  ifndef _SSIZE_T_DEFINED\ntypedef SSIZE_T ssize_t;\n#    define _SSIZE_T_DEFINED\n#  endif\n#endif\n\n#include \"mosquitto_internal.h\"\n#include \"mosquitto.h\"\n\n#ifdef WIN32\n#  define COMPAT_CLOSE(a) closesocket(a)\n#  define COMPAT_SHUTDOWN(a) shutdown(a, SD_SEND)\n#  define COMPAT_ECONNRESET WSAECONNRESET\n#  define COMPAT_EINTR WSAEINTR\n#  define COMPAT_EWOULDBLOCK WSAEWOULDBLOCK\n#  ifndef EINPROGRESS\n#    define EINPROGRESS WSAEINPROGRESS\n#  endif\n#else\n#  define COMPAT_CLOSE(a) close(a)\n#  define COMPAT_SHUTDOWN(a) shutdown(a, SHUT_WR)\n#  define COMPAT_ECONNRESET ECONNRESET\n#  define COMPAT_EINTR EINTR\n#  define COMPAT_EWOULDBLOCK EWOULDBLOCK\n#endif\n\n/* For when not using winsock libraries. */\n#ifndef INVALID_SOCKET\n#define INVALID_SOCKET -1\n#endif\n\n#ifndef MSG_NOSIGNAL\n#  define MSG_NOSIGNAL 0\n#endif\n\n/* Macros for accessing the MSB and LSB of a uint16_t */\n#define MOSQ_MSB(A) (uint8_t)((A & 0xFF00) >> 8)\n#define MOSQ_LSB(A) (uint8_t)(A & 0x00FF)\n\nint net__init(void);\nvoid net__cleanup(void);\n\n#ifdef WITH_TLS\nvoid net__init_tls(void);\n#endif\n\nint net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking);\nint net__socket_close(struct mosquitto *mosq);\nint net__socket_shutdown(struct mosquitto *mosq);\nint net__try_connect(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking);\nint net__try_connect_step1(struct mosquitto *mosq, const char *host);\nint net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock);\nint net__socket_connect_step3(struct mosquitto *mosq, const char *host);\nint net__socket_nonblock(mosq_sock_t *sock);\nint net__socketpair(mosq_sock_t *sp1, mosq_sock_t *sp2);\nbool net__is_connected(struct mosquitto *mosq);\n\nssize_t net__read(struct mosquitto *mosq, void *buf, size_t count);\nssize_t net__read_ws(struct mosquitto *mosq, void *buf, size_t count);\nssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count);\n\n#ifdef WITH_TLS\nvoid net__print_ssl_error(struct mosquitto *mosq, const char *msg);\nint net__socket_apply_tls(struct mosquitto *mosq);\nint net__socket_connect_tls(struct mosquitto *mosq);\nint mosquitto__verify_ocsp_status_cb(SSL *ssl, void *arg);\nUI_METHOD *net__get_ui_method(void);\n#define ENGINE_FINISH(e) if(e) ENGINE_finish(e)\n#define ENGINE_SECRET_MODE \"SECRET_MODE\"\n#define ENGINE_SECRET_MODE_SHA 0x1000\n#define ENGINE_PIN \"PIN\"\n#endif\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\nvoid ws__context_init(struct mosquitto *mosq);\nvoid ws__prepare_packet(struct mosquitto *mosq, struct mosquitto__packet *packet);\nint ws__create_accept_key(const char *client_key, size_t client_key_len, char **encoded);\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/net_mosq_ocsp.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\nCopyright (c) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG), Dr. Lars Voelker <lars.voelker@bmw.de>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Dr. Lars Voelker, BMW AG\n*/\n\n/*\nCOPYRIGHT AND PERMISSION NOTICE of curl on which the ocsp code is based:\n\nCopyright (c) 1996 - 2016, Daniel Stenberg, <daniel@haxx.se>, and many\ncontributors, see the THANKS file.\n\nAll rights reserved.\n\nPermission to use, copy, modify, and distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright\nnotice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN\nNO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\nOR OTHER DEALINGS IN THE SOFTWARE.\n\nExcept as contained in this notice, the name of a copyright holder shall not\nbe used in advertising or otherwise to promote the sale, use or other dealings\nin this Software without prior written authorization of the copyright holder.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_TLS\n#include <logging_mosq.h>\n#include <mosquitto_internal.h>\n#include <net_mosq.h>\n\n#include <openssl/safestack.h>\n#include <openssl/tls1.h>\n#include <openssl/ssl.h>\n#include <openssl/ocsp.h>\n\n\nint mosquitto__verify_ocsp_status_cb(SSL *ssl, void *arg)\n{\n\tstruct mosquitto *mosq = (struct mosquitto *)arg;\n\tint ocsp_status, result2, i;\n\tunsigned char *p;\n\tconst unsigned char *cp;\n\tOCSP_RESPONSE *rsp = NULL;\n\tOCSP_BASICRESP *br = NULL;\n\tX509_STORE     *st = NULL;\n\tSTACK_OF(X509) *ch = NULL;\n\tlong len;\n\n\tUNUSED(ssl);\n\n\tlen = SSL_get_tlsext_status_ocsp_resp(mosq->ssl, &p);\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"OCSP: SSL_get_tlsext_status_ocsp_resp returned %ld bytes\", len);\n\n\t/* the following functions expect a const pointer */\n\tcp = (const unsigned char *)p;\n\n\tif(!cp || len <= 0){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"OCSP: no response\");\n\t\tgoto end;\n\t}\n\n\n\trsp = d2i_OCSP_RESPONSE(NULL, &cp, len);\n\tif(rsp==NULL){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"OCSP: invalid response\");\n\t\tgoto end;\n\t}\n\n\tocsp_status = OCSP_response_status(rsp);\n\tif(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"OCSP: invalid status: %s (%d)\",\n\t\t\t\tOCSP_response_status_str(ocsp_status), ocsp_status);\n\t\tgoto end;\n\t}\n\n\tbr = OCSP_response_get1_basic(rsp);\n\tif(!br){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"OCSP: invalid response\");\n\t\tgoto end;\n\t}\n\n\tch = SSL_get_peer_cert_chain(mosq->ssl);\n\tif(sk_X509_num(ch) <= 0){\n\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"OCSP: we did not receive certificates of the server (num: %d)\", sk_X509_num(ch));\n\t\tgoto end;\n\t}\n\n\tst = SSL_CTX_get_cert_store(mosq->ssl_ctx);\n\n\t/* Note:\n\t * Other checkers often fix problems in OpenSSL before 1.0.2a (e.g. libcurl).\n\t * For all currently supported versions of the OpenSSL project, this is not needed anymore.\n\t */\n\n\tif((result2=OCSP_basic_verify(br, ch, st, 0)) <= 0){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"OCSP: response verification failed (error: %d)\", result2);\n\t\tgoto end;\n\t}\n\n\tfor(i = 0; i < OCSP_resp_count(br); i++){\n\t\tint cert_status, crl_reason;\n\t\tOCSP_SINGLERESP *single = NULL;\n\n\t\tASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;\n\n\t\tsingle = OCSP_resp_get0(br, i);\n\t\tif(!single){\n\t\t\tcontinue;\n\t\t}\n\n\t\tcert_status = OCSP_single_get0_status(single, &crl_reason, &rev, &thisupd, &nextupd);\n\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"OCSP: SSL certificate status: %s (%d)\",\n\t\t\t\tOCSP_cert_status_str(cert_status), cert_status);\n\n\t\tswitch(cert_status){\n\t\t\tcase V_OCSP_CERTSTATUS_GOOD:\n\t\t\t\t/* Note: A OCSP stapling result will be accepted up to 5 minutes after it expired! */\n\t\t\t\tif(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)){\n\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"OCSP: OCSP response has expired\");\n\t\t\t\t\tgoto end;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase V_OCSP_CERTSTATUS_REVOKED:\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"OCSP: SSL certificate revocation reason: %s (%d)\",\n\t\t\t\t\t\tOCSP_crl_reason_str(crl_reason), crl_reason);\n\t\t\t\tgoto end;\n\n\t\t\tcase V_OCSP_CERTSTATUS_UNKNOWN:\n\t\t\t\tgoto end;\n\n\t\t\tdefault:\n\t\t\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"OCSP: SSL certificate revocation status unknown\");\n\t\t\t\tgoto end;\n\t\t}\n\t}\n\n\tif(br!=NULL){\n\t\tOCSP_BASICRESP_free(br);\n\t}\n\tif(rsp!=NULL){\n\t\tOCSP_RESPONSE_free(rsp);\n\t}\n\treturn 1; /* OK */\n\nend:\n\tif(br!=NULL){\n\t\tOCSP_BASICRESP_free(br);\n\t}\n\tif(rsp!=NULL){\n\t\tOCSP_RESPONSE_free(rsp);\n\t}\n\treturn 0; /* Not OK */\n}\n#endif\n"
  },
  {
    "path": "lib/net_ws.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n#ifndef WITH_TLS\n#  error \"Builtin websockets support requires WITH_TLS=yes and openssl to be available\"\n#endif\n\n#include <errno.h>\n#include <stddef.h>\n#include <string.h>\n\n#include \"mosquitto_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"util_mosq.h\"\n\n\nvoid ws__context_init(struct mosquitto *mosq)\n{\n\tmosq->transport = mosq_t_ws;\n\tmosq->state = mosq_cs_new;\n}\n\n\nvoid ws__prepare_packet(struct mosquitto *mosq, struct mosquitto__packet *packet)\n{\n\tuint8_t opcode;\n\tuint32_t masking_offset = mosq->wsd.is_client?4:0;\n\n\tpacket->next = NULL;\n\n\tif(mosq->wsd.opcode == UINT8_MAX){\n\t\topcode = WS_BINARY;\n\t}else if(mosq->wsd.opcode == WS_PING){\n\t\topcode = WS_PONG;\n\t}else{\n\t\topcode = mosq->wsd.opcode;\n\t}\n\tif(packet->packet_length - WS_PACKET_OFFSET < 126){\n\t\tif(mosq->wsd.is_client){\n\t\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-1] = (uint8_t)(packet->packet_length-WS_PACKET_OFFSET) | 0x80;\n\t\t}else{\n\t\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-1] = (uint8_t)(packet->packet_length-WS_PACKET_OFFSET);\n\t\t}\n\t\tpacket->payload[WS_PACKET_OFFSET - masking_offset-2] = 0x80 | opcode;\n\t\tpacket->pos = WS_PACKET_OFFSET - masking_offset - 2;\n\t\tpacket->to_process = packet->packet_length - WS_PACKET_OFFSET + masking_offset + 2;\n\t}else if(packet->packet_length - WS_PACKET_OFFSET < 65536){\n\t\tuint16_t plen = (uint16_t )(packet->packet_length - WS_PACKET_OFFSET);\n\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-1] = MOSQ_LSB(plen);\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-2] = MOSQ_MSB(plen);;\n\t\tif(mosq->wsd.is_client){\n\t\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-3] = 126 | 0x80;\n\t\t}else{\n\t\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-3] = 126;\n\t\t}\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-4] = 0x80 | opcode;\n\t\tpacket->pos = WS_PACKET_OFFSET-masking_offset - 4;\n\t\tpacket->to_process = packet->packet_length - WS_PACKET_OFFSET + masking_offset + 4;\n\t}else{\n\t\tuint64_t plen = packet->packet_length - WS_PACKET_OFFSET;\n\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-1] = (uint8_t)((plen & 0x00000000000000FF) >> 0);\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-2] = (uint8_t)((plen & 0x000000000000FF00) >> 8);\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-3] = (uint8_t)((plen & 0x0000000000FF0000) >> WS_PACKET_OFFSET);\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-4] = (uint8_t)((plen & 0x00000000FF000000) >> 24);\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-5] = (uint8_t)((plen & 0x000000FF00000000) >> 32);\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-6] = (uint8_t)((plen & 0x0000FF0000000000) >> 40);\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-7] = (uint8_t)((plen & 0x00FF000000000000) >> 48);\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-8] = (uint8_t)((plen & 0xFF00000000000000) >> 56);\n\t\tif(mosq->wsd.is_client){\n\t\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-9] = 127 | 0x80;\n\t\t}else{\n\t\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-9] = 127;\n\t\t}\n\t\tpacket->payload[WS_PACKET_OFFSET-masking_offset-10] = 0x80 | opcode;\n\t\tpacket->pos = WS_PACKET_OFFSET-masking_offset - 10;\n\t\tpacket->to_process = packet->packet_length - WS_PACKET_OFFSET + masking_offset + 10;\n\t}\n\tif(mosq->wsd.is_client){\n\t\tmosquitto_getrandom(&packet->payload[WS_PACKET_OFFSET-sizeof(uint32_t)], sizeof(uint32_t));\n\t\tfor(uint32_t i=0; i<packet->packet_length - WS_PACKET_OFFSET; i++){\n\t\t\tpacket->payload[WS_PACKET_OFFSET + i] ^= packet->payload[WS_PACKET_OFFSET-sizeof(uint32_t)+(i%4)];\n\t\t}\n\t}\n}\n\n\nstatic ssize_t read_ws_opcode(struct mosquitto *mosq)\n{\n\tssize_t len;\n\tuint8_t opcode;\n\tuint8_t fin;\n\tuint8_t hbuf;\n\n\tmosq->wsd.mask_bytes = 4;\n\tmosq->wsd.pos = 0;\n\tmosq->wsd.mask = UINT8_MAX;\n\tmosq->wsd.payloadlen_bytes = UINT8_MAX;\n\n\tlen = net__read(mosq, &hbuf, 1);\n\tif(len <= 0){\n\t\treturn len;\n\t}\n\n\tif((hbuf & 0x70) != 0x00){\n\t\tmosq->wsd.disconnect_reason = 0xEA;\n\t\terrno = EPROTO;\n\t\treturn -1;\n\t}\n\topcode = (hbuf & 0x0F);\n\tfin = (hbuf & 0x80);\n\tswitch(opcode){\n\t\tcase WS_CONTINUATION:\n\t\tcase WS_BINARY:\n\t\tcase WS_PING:\n\t\tcase WS_PONG:\n\t\tcase WS_CLOSE:\n\t\t\tmosq->wsd.opcode = opcode;\n\t\t\tbreak;\n\n\t\tcase WS_TEXT:\n\t\t\tmosq->wsd.disconnect_reason = 0xEB;\n\t\t\terrno = EPROTO;\n\t\t\treturn -1;\n\n\t\tdefault:\n\t\t\tmosq->wsd.disconnect_reason = 0xEA;\n\t\t\terrno = EPROTO;\n\t\t\treturn -1;\n\t\t\tbreak;\n\t}\n\tif((opcode & 0x08) && fin == 0){\n\t\t/* Control packets may not be fragmented */\n\t\tmosq->wsd.disconnect_reason = 0xEA;\n\t\terrno = EPROTO;\n\t\treturn -1;\n\t}\n\n\treturn len;\n}\n\n\nstatic ssize_t read_ws_payloadlen_short(struct mosquitto *mosq)\n{\n\tssize_t len;\n\tuint8_t hbuf;\n\tuint8_t plen;\n\n\tlen = net__read(mosq, &hbuf, 1);\n\tif(len <= 0){\n\t\treturn len;\n\t}\n\n\tmosq->wsd.mask = (hbuf & 0x80) >> 7;\n\tplen = hbuf & 0x7F;\n\n\tif(plen == 126){\n\t\tmosq->wsd.payloadlen_bytes = 2;\n\t\tmosq->wsd.payloadlen = 0;\n\t}else if(plen == 127){\n\t\tmosq->wsd.payloadlen_bytes = 8;\n\t\tmosq->wsd.payloadlen = 0;\n\t}else{\n\t\tmosq->wsd.payloadlen_bytes = 0;\n\t\tmosq->wsd.payloadlen = plen;\n\t}\n\n\treturn len;\n}\n\n\nstatic ssize_t read_ws_payloadlen_extended(struct mosquitto *mosq)\n{\n\tuint8_t hbuf[8];\n\tssize_t len;\n\n\tlen = net__read(mosq, hbuf, mosq->wsd.payloadlen_bytes);\n\tif(len <= 0){\n\t\treturn len;\n\t}\n\tfor(ssize_t i=0; i<len; i++){\n\t\tmosq->wsd.payloadlen = (mosq->wsd.payloadlen << 8) + hbuf[i];\n\t}\n\tmosq->wsd.payloadlen_bytes = (uint8_t)(mosq->wsd.payloadlen_bytes - len);\n\n\treturn len;\n}\n\n\nstatic ssize_t read_ws_mask(struct mosquitto *mosq)\n{\n\tssize_t len;\n\n\tlen = net__read(mosq, &mosq->wsd.maskingkey[4-mosq->wsd.mask_bytes], mosq->wsd.mask_bytes);\n\tif(len <= 0){\n\t\treturn len;\n\t}\n\tmosq->wsd.mask_bytes = (uint8_t)(mosq->wsd.mask_bytes - len);\n\tif(mosq->wsd.mask_bytes > 0){\n\t\terrno = EAGAIN;\n\t\treturn -1;\n\t}\n\n\treturn len;\n}\n\n\nssize_t net__read_ws(struct mosquitto *mosq, void *buf, size_t count)\n{\n\tssize_t len = 0;\n\n\tif(mosq->wsd.payloadlen == 0){\n\t\tif(mosq->wsd.opcode == UINT8_MAX){\n\t\t\tlen = read_ws_opcode(mosq);\n\t\t\tif(len <= 0){\n\t\t\t\treturn len;\n\t\t\t}\n\t\t}\n\n\t\tif(mosq->wsd.mask == UINT8_MAX){\n\t\t\tlen = read_ws_payloadlen_short(mosq);\n\t\t\tif(len <= 0){\n\t\t\t\treturn len;\n\t\t\t}\n\t\t}\n\n\t\tif(mosq->wsd.payloadlen_bytes > 0){\n\t\t\tlen = read_ws_payloadlen_extended(mosq);\n\t\t\tif(len <= 0){\n\t\t\t\treturn len;\n\t\t\t}\n\t\t}\n\n\t\tif(mosq->wsd.mask == 1 && mosq->wsd.mask_bytes > 0){\n\t\t\tlen = read_ws_mask(mosq);\n\t\t\tif(len <= 0){\n\t\t\t\treturn len;\n\t\t\t}\n\t\t}\n\n\t\tif(mosq->wsd.opcode == WS_CLOSE && mosq->wsd.payloadlen == 1){\n\t\t\tmosq->wsd.disconnect_reason = 0xEA;\n\t\t\terrno = EPROTO;\n\t\t\treturn -1;\n\t\t}else if(mosq->wsd.payloadlen > 125 && mosq->wsd.opcode != WS_BINARY && mosq->wsd.opcode != WS_CONTINUATION){\n\t\t\tmosq->wsd.disconnect_reason = 0xEA;\n\t\t\terrno = EPROTO;\n\t\t\treturn -1;\n\t\t}\n\n\t\tif(mosq->wsd.payloadlen > MQTT_MAX_PAYLOAD){\n\t\t\terrno = EPROTO;\n\t\t\treturn -1;\n\t\t}\n\n#ifndef WS_TESTING\n\t\tif(mosq->wsd.opcode == WS_PING || (mosq->wsd.opcode == WS_CLOSE && mosq->wsd.payloadlen >= 2))\n\t\t/* Always allocate payload for testing case, otherwise just for pings */\n#endif\n\t\t{\n\t\t\tmosq->wsd.out_packet = mosquitto_calloc(1, sizeof(struct mosquitto__packet) + WS_PACKET_OFFSET + mosq->wsd.payloadlen + 1);\n\t\t\tif(mosq->wsd.out_packet == NULL){\n\t\t\t\terrno = ENOMEM;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tmosq->wsd.out_packet->packet_length = (uint32_t)mosq->wsd.payloadlen + WS_PACKET_OFFSET;\n\t\t}\n\t}\n\n\tif(mosq->wsd.out_packet){\n\t\t/* This means we are either dealing with protocol level messages (and\n\t\t * hence won't be returning MQTT data to the context), or we are\n\t\t * testing and should be echoing data back to the client.\n\t\t * So ignore what data is being asked for, and try and read the whole\n\t\t * lot at once. */\n\t\tcount = mosq->wsd.payloadlen - (uint64_t)mosq->wsd.pos;\n\t\tbuf = &mosq->wsd.out_packet->payload[WS_PACKET_OFFSET + mosq->wsd.pos];\n\t}\n\n\tif(mosq->wsd.payloadlen > 0){\n\t\tif(count > mosq->wsd.payloadlen - (uint64_t)mosq->wsd.pos){\n\t\t\tcount = mosq->wsd.payloadlen - (uint64_t)mosq->wsd.pos;\n\t\t}\n\t\tlen = net__read(mosq, buf, count);\n\t\tif(len > 0){\n\t\t\tfor(ssize_t i=0; i<len; i++){\n\t\t\t\t((uint8_t *)buf)[i] ^= mosq->wsd.maskingkey[(i+mosq->wsd.pos)%4];\n\t\t\t}\n\t\t\tmosq->wsd.pos += len;\n\t\t}\n\t}\n\n\tif(mosq->wsd.pos == (ssize_t)mosq->wsd.payloadlen){\n\t\tif(mosq->wsd.opcode == WS_CLOSE){\n\t\t\tmosquitto_FREE(mosq->wsd.out_packet);\n\n\t\t\t/* Testing or PING - so we haven't read any data for the application yet. */\n\t\t\tlen = -1;\n\t\t\terrno = EAGAIN;\n\t\t}else if(mosq->wsd.opcode == WS_PONG){\n\t\t\tmosquitto_FREE(mosq->wsd.out_packet);\n\t\t\t/* Testing or PING - so we haven't read any data for the application yet. */\n\t\t\tlen = -1;\n\t\t\terrno = EAGAIN;\n\t\t}else if(mosq->wsd.out_packet){\n\t\t\tpacket__queue(mosq, mosq->wsd.out_packet);\n\t\t\tmosq->wsd.out_packet = NULL;\n\n\t\t\t/* Testing or PING - so we haven't read any data for the application yet.\n\t\t\t* Simulate that situation. This has to be done *after* the call to\n\t\t\t* packet__queue. */\n\t\t\tlen = -1;\n\t\t\terrno = EAGAIN;\n\t\t}\n\t\tmosq->wsd.payloadlen = 0;\n\t\tmosq->wsd.opcode = UINT8_MAX;\n\t\tmosq->wsd.mask = UINT8_MAX;\n\t}else if(mosq->wsd.out_packet){\n\t\t/* Testing or PING - so we haven't read any data for the application yet.\n\t\t* Simulate that situation.*/\n\t\tlen = -1;\n\t\terrno = EAGAIN;\n\t}\n\treturn len;\n}\n\n\nint ws__create_accept_key(const char *client_key, size_t client_key_len, char **encoded)\n{\n\tconst EVP_MD *digest;\n\tEVP_MD_CTX *evp = NULL;\n\tuint8_t accept_key_hash[EVP_MAX_MD_SIZE];\n\tunsigned int accept_key_hash_len;\n\n\tdigest = EVP_get_digestbyname(\"sha1\");\n\tif(!digest){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tevp = EVP_MD_CTX_new();\n\tif(evp && EVP_DigestInit_ex(evp, digest, NULL) != 0){\n\t\tif(EVP_DigestUpdate(evp, client_key, client_key_len) != 0){\n\t\t\tif(EVP_DigestUpdate(evp, \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\",\n\t\t\t\t\tstrlen(\"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\")) != 0){\n\n\t\t\t\tif(EVP_DigestFinal_ex(evp, accept_key_hash, &accept_key_hash_len) != 0){\n\t\t\t\t\tEVP_MD_CTX_free(evp);\n\t\t\t\t\treturn mosquitto_base64_encode(accept_key_hash, accept_key_hash_len, encoded);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tEVP_MD_CTX_free(evp);\n\treturn MOSQ_ERR_UNKNOWN;\n}\n\n\n#endif\n"
  },
  {
    "path": "lib/options.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifndef WIN32\n#  include <strings.h>\n#endif\n\n#include <string.h>\n\n#ifdef WITH_TLS\n#  ifdef WIN32\n#    include <winsock2.h>\n#  endif\n#  include <openssl/engine.h>\n#endif\n\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"util_mosq.h\"\n#include \"will_mosq.h\"\n\n\nint mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain)\n{\n\treturn mosquitto_will_set_v5(mosq, topic, payloadlen, payload, qos, retain, NULL);\n}\n\n\nint mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties)\n{\n\tint rc;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(properties){\n\t\trc = mosquitto_property_check_all(CMD_WILL, properties);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\treturn will__set(mosq, topic, payloadlen, payload, qos, retain, properties);\n}\n\n\nint mosquitto_will_clear(struct mosquitto *mosq)\n{\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn will__clear(mosq);\n}\n\n\nint mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password)\n{\n\tsize_t slen;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){\n\t\tif(password != NULL && username == NULL){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\n\tmosquitto_FREE(mosq->username);\n\tmosquitto_FREE(mosq->password);\n\n\tif(username){\n\t\tslen = strlen(username);\n\t\tif(slen > UINT16_MAX){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(mosquitto_validate_utf8(username, (int)slen)){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t\tmosq->username = mosquitto_strdup(username);\n\t\tif(!mosq->username){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\tif(password){\n\t\tmosq->password = mosquitto_strdup(password);\n\t\tif(!mosq->password){\n\t\t\tmosquitto_FREE(mosq->username);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff)\n{\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(reconnect_delay == 0){\n\t\treconnect_delay = 1;\n\t}\n\n\tmosq->reconnect_delay = reconnect_delay;\n\tmosq->reconnect_delay_max = reconnect_delay_max;\n\tmosq->reconnect_exponential_backoff = reconnect_exponential_backoff;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *capath, const char *certfile, const char *keyfile, int (*pw_callback)(char *buf, int size, int rwflag, void *userdata))\n{\n#ifdef WITH_TLS\n\tFILE *fptr;\n\n\tif(!mosq || (!cafile && !capath) || (certfile && !keyfile) || (!certfile && keyfile)){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmosquitto_FREE(mosq->tls_cafile);\n\tif(cafile){\n\t\tfptr = mosquitto_fopen(cafile, \"rt\", false);\n\t\tif(fptr){\n\t\t\tfclose(fptr);\n\t\t}else{\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tmosq->tls_cafile = mosquitto_strdup(cafile);\n\n\t\tif(!mosq->tls_cafile){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\tmosquitto_FREE(mosq->tls_capath);\n\tif(capath){\n\t\tmosq->tls_capath = mosquitto_strdup(capath);\n\t\tif(!mosq->tls_capath){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\tmosquitto_FREE(mosq->tls_certfile);\n\tif(certfile){\n\t\tfptr = mosquitto_fopen(certfile, \"rt\", false);\n\t\tif(fptr){\n\t\t\tfclose(fptr);\n\t\t}else{\n\t\t\tmosquitto_FREE(mosq->tls_cafile);\n\t\t\tmosquitto_FREE(mosq->tls_capath);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tmosq->tls_certfile = mosquitto_strdup(certfile);\n\t\tif(!mosq->tls_certfile){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\tmosquitto_FREE(mosq->tls_keyfile);\n\tif(keyfile){\n\t\tif(mosq->tls_keyform == mosq_k_pem){\n\t\t\tfptr = mosquitto_fopen(keyfile, \"rt\", false);\n\t\t\tif(fptr){\n\t\t\t\tfclose(fptr);\n\t\t\t}else{\n\t\t\t\tmosquitto_FREE(mosq->tls_cafile);\n\t\t\t\tmosq->tls_cafile = NULL;\n\n\t\t\t\tmosquitto_FREE(mosq->tls_capath);\n\t\t\t\tmosq->tls_capath = NULL;\n\n\t\t\t\tmosquitto_FREE(mosq->tls_certfile);\n\t\t\t\tmosq->tls_certfile = NULL;\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}\n\t\tmosq->tls_keyfile = mosquitto_strdup(keyfile);\n\t\tif(!mosq->tls_keyfile){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\tmosq->tls_pw_callback = pw_callback;\n\n\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\tUNUSED(mosq);\n\tUNUSED(cafile);\n\tUNUSED(capath);\n\tUNUSED(certfile);\n\tUNUSED(keyfile);\n\tUNUSED(pw_callback);\n\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n\n#endif\n}\n\n\nint mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tls_version, const char *ciphers)\n{\n#ifdef WITH_TLS\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmosq->tls_cert_reqs = cert_reqs;\n\tif(tls_version){\n\t\tif(!strcasecmp(tls_version, \"tlsv1.3\")\n\t\t\t\t|| !strcasecmp(tls_version, \"tlsv1.2\")){\n\n\t\t\tmosquitto_FREE(mosq->tls_version);\n\t\t\tmosq->tls_version = mosquitto_strdup(tls_version);\n\t\t\tif(!mosq->tls_version){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}else{\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}else{\n\t\tmosquitto_FREE(mosq->tls_version);\n\t\tmosq->tls_version = mosquitto_strdup(\"tlsv1.2\");\n\t\tif(!mosq->tls_version){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\tif(ciphers){\n\t\tmosquitto_FREE(mosq->tls_ciphers);\n\t\tmosq->tls_ciphers = mosquitto_strdup(ciphers);\n\t\tif(!mosq->tls_ciphers){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}else{\n\t\tmosquitto_FREE(mosq->tls_ciphers);\n\t\tmosq->tls_ciphers = NULL;\n\t}\n\n\tmosquitto_FREE(mosq->tls_ciphers);\n\tmosquitto_FREE(mosq->tls_13_ciphers);\n\n\tif(ciphers){\n\t\tif(!strcasecmp(mosq->tls_version, \"tlsv1.3\")){\n\t\t\tmosq->tls_13_ciphers = mosquitto_strdup(ciphers);\n\t\t\tif(!mosq->tls_13_ciphers){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}else{\n\t\t\tmosq->tls_ciphers = mosquitto_strdup(ciphers);\n\t\t\tif(!mosq->tls_ciphers){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\tUNUSED(mosq);\n\tUNUSED(cert_reqs);\n\tUNUSED(tls_version);\n\tUNUSED(ciphers);\n\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nint mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value)\n{\n#ifdef WITH_TLS\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tmosq->tls_insecure = value;\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\tUNUSED(mosq);\n\tUNUSED(value);\n\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nint mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, const char *value)\n{\n#if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\tENGINE *eng;\n\tchar *str;\n#endif\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tswitch(option){\n\t\tcase MOSQ_OPT_TLS_ENGINE:\n#if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\t\t\tmosquitto_FREE(mosq->tls_engine);\n\t\t\tif(value){\n\t\t\t\t/* The \"Dynamic\" OpenSSL engine is not initialized by default but\n\t\t\t\t   is required by ENGINE_by_id() to find dynamically loadable engines */\n\t\t\t\tOPENSSL_init_crypto(OPENSSL_INIT_ENGINE_DYNAMIC, NULL);\n\t\t\t\teng = ENGINE_by_id(value);\n\t\t\t\tif(!eng){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\tENGINE_free(eng); /* release the structural reference from ENGINE_by_id() */\n\t\t\t\tmosq->tls_engine = mosquitto_strdup(value);\n\t\t\t\tif(!mosq->tls_engine){\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn MOSQ_ERR_SUCCESS;\n#else\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\t\tbreak;\n\n\t\tcase MOSQ_OPT_TLS_KEYFORM:\n#if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\t\t\tif(!value){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tif(!strcasecmp(value, \"pem\")){\n\t\t\t\tmosq->tls_keyform = mosq_k_pem;\n\t\t\t}else if(!strcasecmp(value, \"engine\")){\n\t\t\t\tmosq->tls_keyform = mosq_k_engine;\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\treturn MOSQ_ERR_SUCCESS;\n#else\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\t\tbreak;\n\n\n\t\tcase MOSQ_OPT_TLS_ENGINE_KPASS_SHA1:\n#if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\t\t\tmosquitto_FREE(mosq->tls_engine_kpass_sha1);\n\t\t\tif(mosquitto__hex2bin_sha1(value, (unsigned char **)&str) != MOSQ_ERR_SUCCESS){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tmosq->tls_engine_kpass_sha1 = str;\n\t\t\treturn MOSQ_ERR_SUCCESS;\n#else\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\t\tbreak;\n\n\t\tcase MOSQ_OPT_TLS_ALPN:\n#ifdef WITH_TLS\n\t\t\tmosquitto_FREE(mosq->tls_alpn);\n\t\t\tmosq->tls_alpn = mosquitto_strdup(value);\n\t\t\tif(!mosq->tls_alpn){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\treturn MOSQ_ERR_SUCCESS;\n#else\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\t\tbreak;\n\n\t\tcase MOSQ_OPT_BIND_ADDRESS:\n\t\t\tmosquitto_FREE(mosq->bind_address);\n\t\t\tif(value){\n\t\t\t\tmosq->bind_address = mosquitto_strdup(value);\n\t\t\t\tif(mosq->bind_address){\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}else{\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\n\t\tcase MOSQ_OPT_HTTP_PATH:\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\tmosquitto_FREE(mosq->wsd.http_path);\n\t\t\tif(value){\n\t\t\t\tmosq->wsd.http_path = mosquitto_strdup(value);\n\t\t\t\tif(mosq->wsd.http_path){\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}else{\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n#else\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\n\t\tdefault:\n\t\t\treturn MOSQ_ERR_INVAL;\n\t}\n}\n\n\nint mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers)\n{\n#ifdef FINAL_WITH_TLS_PSK\n\tif(!mosq || !psk || !identity){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* Check for hex only digits */\n\tif(strspn(psk, \"0123456789abcdefABCDEF\") < strlen(psk)){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tmosq->tls_psk = mosquitto_strdup(psk);\n\tif(!mosq->tls_psk){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tmosq->tls_psk_identity = mosquitto_strdup(identity);\n\tif(!mosq->tls_psk_identity){\n\t\tmosquitto_FREE(mosq->tls_psk);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tif(ciphers){\n\t\tmosq->tls_ciphers = mosquitto_strdup(ciphers);\n\t\tif(!mosq->tls_ciphers){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}else{\n\t\tmosq->tls_ciphers = NULL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\tUNUSED(mosq);\n\tUNUSED(psk);\n\tUNUSED(identity);\n\tUNUSED(ciphers);\n\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nint mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value)\n{\n\tint ival;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tswitch(option){\n\t\tcase MOSQ_OPT_PROTOCOL_VERSION:\n\t\t\tif(value == NULL){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tival = *((int *)value);\n\t\t\treturn mosquitto_int_option(mosq, option, ival);\n\t\tcase MOSQ_OPT_SSL_CTX:\n\t\t\treturn mosquitto_void_option(mosq, option, value);\n\t\tdefault:\n\t\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value)\n{\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tswitch(option){\n\t\tcase MOSQ_OPT_DISABLE_SOCKETPAIR:\n\t\t\tmosq->disable_socketpair = (bool)value;\n\t\t\tbreak;\n\n\t\tcase MOSQ_OPT_PROTOCOL_VERSION:\n\t\t\tif(value == MQTT_PROTOCOL_V31){\n\t\t\t\tmosq->protocol = mosq_p_mqtt31;\n\t\t\t}else if(value == MQTT_PROTOCOL_V311){\n\t\t\t\tmosq->protocol = mosq_p_mqtt311;\n\t\t\t}else if(value == MQTT_PROTOCOL_V5){\n\t\t\t\tmosq->protocol = mosq_p_mqtt5;\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MOSQ_OPT_RECEIVE_MAXIMUM:\n\t\t\tif(value < 0 || value > UINT16_MAX){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tif(value == 0){\n\t\t\t\tmosq->msgs_in.inflight_maximum = UINT16_MAX;\n\t\t\t}else{\n\t\t\t\tmosq->msgs_in.inflight_maximum = (uint16_t)value;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MOSQ_OPT_SEND_MAXIMUM:\n\t\t\tif(value < 0 || value > UINT16_MAX){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tif(value == 0){\n\t\t\t\tmosq->msgs_out.inflight_maximum = UINT16_MAX;\n\t\t\t}else{\n\t\t\t\tmosq->msgs_out.inflight_maximum = (uint16_t)value;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MOSQ_OPT_SSL_CTX_WITH_DEFAULTS:\n#if defined(WITH_TLS)\n\t\t\tif(value){\n\t\t\t\tmosq->ssl_ctx_defaults = true;\n\t\t\t}else{\n\t\t\t\tmosq->ssl_ctx_defaults = false;\n\t\t\t}\n\t\t\tbreak;\n#else\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\n\t\tcase MOSQ_OPT_TLS_USE_OS_CERTS:\n#ifdef WITH_TLS\n\t\t\tif(value){\n\t\t\t\tmosq->tls_use_os_certs = true;\n\t\t\t}else{\n\t\t\t\tmosq->tls_use_os_certs = false;\n\t\t\t}\n\t\t\tbreak;\n#else\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\n\t\tcase MOSQ_OPT_TLS_OCSP_REQUIRED:\n#ifdef WITH_TLS\n\t\t\tmosq->tls_ocsp_required = (bool)value;\n#else\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\t\tbreak;\n\n\t\tcase MOSQ_OPT_TCP_NODELAY:\n\t\t\tmosq->tcp_nodelay = (bool)value;\n\t\t\tbreak;\n\n\t\tcase MOSQ_OPT_TRANSPORT:\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\tif(value == mosq_t_tcp || value == mosq_t_ws){\n\t\t\t\tmosq->transport = (uint8_t)value;\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n#else\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\t\tbreak;\n\n\t\tcase MOSQ_OPT_HTTP_HEADER_SIZE:\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\tif(value < 100){ /* arbitrary limit */\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}else if(mosq->http_request){\n\t\t\t\t/* Don't want to resize if part way through the handshake */\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tmosq->wsd.http_header_size = value;\n#else\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value)\n{\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tswitch(option){\n\t\tcase MOSQ_OPT_SSL_CTX:\n#ifdef WITH_TLS\n\t\t\tmosq->user_ssl_ctx = (SSL_CTX *)value;\n\t\t\tif(mosq->user_ssl_ctx){\n\t\t\t\tSSL_CTX_up_ref(mosq->user_ssl_ctx);\n\t\t\t}\n\t\t\tbreak;\n#else\n\t\t\tUNUSED(value);\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\tdefault:\n\t\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid mosquitto_user_data_set(struct mosquitto *mosq, void *userdata)\n{\n\tif(mosq){\n\t\tmosq->userdata = userdata;\n\t}\n}\n\n\nvoid *mosquitto_userdata(struct mosquitto *mosq)\n{\n\treturn mosq->userdata;\n}\n"
  },
  {
    "path": "lib/packet_datatypes.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <string.h>\n#ifndef WIN32\n#  include <arpa/inet.h>\n#endif\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#  if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n#    include <libwebsockets.h>\n#  endif\n#else\n#  include \"read_handle.h\"\n#endif\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"read_handle.h\"\n\n\nint packet__read_byte(struct mosquitto__packet_in *packet, uint8_t *byte)\n{\n\tassert(packet);\n\tif(packet->pos+1 > packet->remaining_length){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\t*byte = packet->payload[packet->pos];\n\tpacket->pos++;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid packet__write_byte(struct mosquitto__packet *packet, uint8_t byte)\n{\n\tassert(packet);\n\tassert(packet->pos+1 <= packet->packet_length);\n\n\tpacket->payload[packet->pos] = byte;\n\tpacket->pos++;\n}\n\n\nint packet__read_bytes(struct mosquitto__packet_in *packet, void *bytes, uint32_t count)\n{\n\tassert(packet);\n\tif(packet->pos+count > packet->remaining_length){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tmemcpy(bytes, &(packet->payload[packet->pos]), count);\n\tpacket->pos += count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count)\n{\n\tassert(packet);\n\tassert(packet->pos+count <= packet->packet_length);\n\n\tif(count > 0){\n\t\tmemcpy(&(packet->payload[packet->pos]), bytes, count);\n\t\tpacket->pos += count;\n\t}\n}\n\n\nint packet__read_binary(struct mosquitto__packet_in *packet, uint8_t **data, uint16_t *length)\n{\n\tuint16_t slen;\n\tint rc;\n\n\tassert(packet);\n\trc = packet__read_uint16(packet, &slen);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(slen == 0){\n\t\t*data = NULL;\n\t\t*length = 0;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(packet->pos+slen > packet->remaining_length){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\t*data = mosquitto_malloc(slen+1U);\n\tif(*data){\n\t\tmemcpy(*data, &(packet->payload[packet->pos]), slen);\n\t\t((uint8_t *)(*data))[slen] = '\\0';\n\t\tpacket->pos += slen;\n\t}else{\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t*length = slen;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint packet__read_string(struct mosquitto__packet_in *packet, char **str, uint16_t *length)\n{\n\tint rc;\n\n\trc = packet__read_binary(packet, (uint8_t **)str, length);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tif(*length == 0){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(mosquitto_validate_utf8(*str, *length)){\n\t\tmosquitto_FREE(*str);\n\t\t*length = 0;\n\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length)\n{\n\tassert(packet);\n\tpacket__write_uint16(packet, length);\n\tpacket__write_bytes(packet, str, length);\n}\n\n\nint packet__read_uint16(struct mosquitto__packet_in *packet, uint16_t *word)\n{\n\tuint16_t val;\n\n\tassert(packet);\n\tif(packet->pos+2 > packet->remaining_length){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tmemcpy(&val, &packet->payload[packet->pos], sizeof(uint16_t));\n\tpacket->pos = packet->pos + (uint32_t)sizeof(uint16_t);\n\n\t*word = ntohs(val);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid packet__write_uint16(struct mosquitto__packet *packet, uint16_t word)\n{\n\tuint16_t bigendian = htons(word);\n\n\tassert(packet);\n\tassert(packet->pos+2 <= packet->packet_length);\n\n\tmemcpy(&packet->payload[packet->pos], &bigendian, 2);\n\tpacket->pos += 2;\n}\n\n\nint packet__read_uint32(struct mosquitto__packet_in *packet, uint32_t *word)\n{\n\tuint32_t val = 0;\n\n\tassert(packet);\n\tif(packet->pos+4 > packet->remaining_length){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tmemcpy(&val, &packet->payload[packet->pos], sizeof(uint32_t));\n\tpacket->pos = packet->pos + (uint32_t)sizeof(uint32_t);\n\n\t*word = ntohl(val);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid packet__write_uint32(struct mosquitto__packet *packet, uint32_t word)\n{\n\tuint32_t bigendian = htonl(word);\n\n\tassert(packet);\n\tassert(packet->pos+4 <= packet->packet_length);\n\n\tmemcpy(&packet->payload[packet->pos], &bigendian, 4);\n\tpacket->pos += 4;\n}\n\n\nint packet__read_varint(struct mosquitto__packet_in *packet, uint32_t *word, uint8_t *bytes)\n{\n\tint i;\n\tuint8_t byte;\n\tunsigned int remaining_mult = 1;\n\tuint32_t lword = 0;\n\tuint8_t lbytes = 0;\n\n\tfor(i=0; i<4; i++){\n\t\tif(packet->pos < packet->remaining_length){\n\t\t\tlbytes++;\n\t\t\tbyte = packet->payload[packet->pos];\n\t\t\tlword += (byte & 127) * remaining_mult;\n\t\t\tremaining_mult *= 128;\n\t\t\tpacket->pos++;\n\t\t\tif((byte & 128) == 0){\n\t\t\t\tif(lbytes > 1 && byte == 0){\n\t\t\t\t\t/* Catch overlong encodings */\n\t\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t\t}else{\n\t\t\t\t\t*word = lword;\n\t\t\t\t\tif(bytes){\n\t\t\t\t\t\t(*bytes) = lbytes;\n\t\t\t\t\t}\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}\n\t\t}else{\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t}\n\treturn MOSQ_ERR_MALFORMED_PACKET;\n}\n\n\nint packet__write_varint(struct mosquitto__packet *packet, uint32_t word)\n{\n\tuint8_t byte;\n\tint count = 0;\n\n\tdo{\n\t\tbyte = (uint8_t)(word % 128);\n\t\tword = word / 128;\n\t\t/* If there are more digits to encode, set the top bit of this digit */\n\t\tif(word > 0){\n\t\t\tbyte = byte | 0x80;\n\t\t}\n\t\tpacket__write_byte(packet, byte);\n\t\tcount++;\n\t}while(word > 0 && count < 5);\n\n\tif(count == 5){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "lib/packet_mosq.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#  if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n#    include <libwebsockets.h>\n#  endif\n#else\n#  include \"read_handle.h\"\n#endif\n\n#include \"callbacks.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"read_handle.h\"\n#include \"util_mosq.h\"\n#ifdef WITH_BROKER\n#  include \"sys_tree.h\"\n#  include \"send_mosq.h\"\n#else\n#  define metrics__int_inc(stat, val)\n#  define metrics__int_dec(stat, val)\n#endif\n\n\nint packet__alloc(struct mosquitto__packet **packet, uint8_t command, uint32_t remaining_length)\n{\n\tuint8_t remaining_bytes[5] = {0}, byte;\n\tint8_t remaining_count;\n\tuint32_t packet_length;\n\tuint32_t remaining_length_stored;\n\tint i;\n\n\tassert(packet);\n\n\tremaining_length_stored = remaining_length;\n\tremaining_count = 0;\n\tdo{\n\t\tbyte = remaining_length % 128;\n\t\tremaining_length = remaining_length / 128;\n\t\t/* If there are more digits to encode, set the top bit of this digit */\n\t\tif(remaining_length > 0){\n\t\t\tbyte = byte | 0x80;\n\t\t}\n\t\tremaining_bytes[remaining_count] = byte;\n\t\tremaining_count++;\n\t}while(remaining_length > 0 && remaining_count < 5);\n\tif(remaining_count == 5){\n\t\treturn MOSQ_ERR_PAYLOAD_SIZE;\n\t}\n\n\tpacket_length = remaining_length_stored + 1 + (uint8_t)remaining_count;\n\t(*packet) = mosquitto_malloc(sizeof(struct mosquitto__packet) + packet_length + WS_PACKET_OFFSET);\n\tif((*packet) == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t/* Clear memory for everything but the payload - that will be set to valid\n\t * values when the actual payload is copied in. */\n\tmemset((*packet), 0, sizeof(struct mosquitto__packet));\n\t(*packet)->command = command;\n\t(*packet)->remaining_length = remaining_length_stored;\n\t(*packet)->remaining_count = remaining_count;\n\t(*packet)->packet_length = packet_length + WS_PACKET_OFFSET;\n\n\t(*packet)->payload[WS_PACKET_OFFSET] = (*packet)->command;\n\tfor(i=0; i<(*packet)->remaining_count; i++){\n\t\t(*packet)->payload[WS_PACKET_OFFSET+i+1] = remaining_bytes[i];\n\t}\n\t(*packet)->pos = WS_PACKET_OFFSET + 1U + (uint8_t)(*packet)->remaining_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid packet__cleanup(struct mosquitto__packet_in *packet)\n{\n\tif(!packet){\n\t\treturn;\n\t}\n\n\t/* Free data and reset values */\n\tpacket->command = 0;\n\tpacket->remaining_count = 0;\n\tpacket->remaining_mult = 1;\n\tpacket->remaining_length = 0;\n\tmosquitto_FREE(packet->payload);\n\tpacket->to_process = 0;\n\tpacket->pos = 0;\n}\n\n\nvoid packet__cleanup_all_no_locks(struct mosquitto *mosq)\n{\n\tstruct mosquitto__packet *packet;\n\n\t/* Out packet cleanup */\n\twhile(mosq->out_packet){\n\t\tpacket = mosq->out_packet;\n\t\t/* Free data and reset values */\n\t\tmosq->out_packet = mosq->out_packet->next;\n\n\t\tmosquitto_FREE(packet);\n\t}\n\tmetrics__int_dec(mosq_gauge_out_packets, mosq->out_packet_count);\n\tmetrics__int_dec(mosq_gauge_out_packet_bytes, mosq->out_packet_bytes);\n\tmosq->out_packet_count = 0;\n\tmosq->out_packet_bytes = 0;\n\tmosq->out_packet_last = NULL;\n\n\tpacket__cleanup(&mosq->in_packet);\n}\n\n\nvoid packet__cleanup_all(struct mosquitto *mosq)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->out_packet_mutex);\n\tpacket__cleanup_all_no_locks(mosq);\n\tCOMPAT_pthread_mutex_unlock(&mosq->out_packet_mutex);\n}\n\n\nstatic void packet__queue_append(struct mosquitto *mosq, struct mosquitto__packet *packet)\n{\n#ifdef WITH_BROKER\n\tif(db.config->max_queued_messages > 0 && mosq->out_packet_count >= db.config->max_queued_messages){\n\t\tmosquitto_free(packet);\n\t\tif(mosq->is_dropping == false){\n\t\t\tmosq->is_dropping = true;\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE,\n\t\t\t\t\t\"Outgoing messages are being dropped for client %s.\",\n\t\t\t\t\tmosq->id);\n\t\t}\n\t\tmetrics__int_inc(mosq_counter_mqtt_publish_dropped, 1);\n\t\treturn;\n\t}\n#endif\n\n\tCOMPAT_pthread_mutex_lock(&mosq->out_packet_mutex);\n\tif(mosq->out_packet){\n\t\tmosq->out_packet_last->next = packet;\n\t}else{\n\t\tmosq->out_packet = packet;\n\t}\n\tmosq->out_packet_last = packet;\n\tmosq->out_packet_count++;\n\tmosq->out_packet_bytes += packet->packet_length;\n\tmetrics__int_inc(mosq_gauge_out_packets, 1);\n\tmetrics__int_inc(mosq_gauge_out_packet_bytes, packet->packet_length);\n\tCOMPAT_pthread_mutex_unlock(&mosq->out_packet_mutex);\n}\n\n\nint packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet)\n{\n#ifndef WITH_BROKER\n\tchar sockpair_data = 0;\n#endif\n\tassert(mosq);\n\tassert(packet);\n\n#if defined(WITH_BROKER) && defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tif(mosq->wsi){\n\t\tpacket->next = NULL;\n\t\tpacket->pos = WS_PACKET_OFFSET;\n\t\tpacket->to_process = packet->packet_length - WS_PACKET_OFFSET;\n\n\t\tpacket__queue_append(mosq, packet);\n\n\t\tlws_callback_on_writable(mosq->wsi);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else\n#elif defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\tif(mosq->transport == mosq_t_ws){\n\t\tws__prepare_packet(mosq, packet);\n\t}else\n#endif\n\t{\n\t\t/* Normal TCP */\n\t\tpacket->next = NULL;\n\t\tpacket->pos = WS_PACKET_OFFSET;\n\t\tpacket->to_process = packet->packet_length - WS_PACKET_OFFSET;\n\t}\n\n\tpacket__queue_append(mosq, packet);\n\n#ifdef WITH_BROKER\n\treturn packet__write(mosq);\n#else\n\t/* Write a single byte to sockpairW (connected to sockpairR) to break out\n\t * of select() if in threaded mode. */\n\tif(mosq->sockpairW != INVALID_SOCKET){\n#  ifndef WIN32\n\t\tif(write(mosq->sockpairW, &sockpair_data, 1)){\n\t\t}\n#  else\n\t\tsend(mosq->sockpairW, &sockpair_data, 1, 0);\n#  endif\n\t}\n\n\tif(mosq->callback_depth == 0 && mosq->threaded == mosq_ts_none){\n\t\treturn packet__write(mosq);\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n#endif\n}\n\n\nint packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length)\n{\n\tuint32_t len;\n\n\tif(mosq->maximum_packet_size == 0){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tlen = 1 + remaining_length + mosquitto_varint_bytes(remaining_length);\n\tif(len > mosq->maximum_packet_size){\n\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\nstruct mosquitto__packet *packet__get_next_out(struct mosquitto *mosq)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->out_packet_mutex);\n\tif(mosq->out_packet){\n\t\tmosq->out_packet_count--;\n\t\tmosq->out_packet_bytes -= mosq->out_packet->packet_length;\n\t\tmetrics__int_dec(mosq_gauge_out_packets, 1);\n\t\tmetrics__int_dec(mosq_gauge_out_packet_bytes, mosq->out_packet->packet_length);\n\n\t\tmosq->out_packet = mosq->out_packet->next;\n\t\tif(!mosq->out_packet){\n\t\t\tmosq->out_packet_last = NULL;\n\t\t}\n\t\tpacket = mosq->out_packet;\n\t}\n\tCOMPAT_pthread_mutex_unlock(&mosq->out_packet_mutex);\n\n\treturn packet;\n}\n\n\nint packet__write(struct mosquitto *mosq)\n{\n\tssize_t write_length;\n\tstruct mosquitto__packet *packet, *next_packet;\n\tenum mosquitto_client_state state;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!net__is_connected(mosq)){\n\t\treturn MOSQ_ERR_NO_CONN;\n\t}\n\n\tCOMPAT_pthread_mutex_lock(&mosq->out_packet_mutex);\n\tpacket = mosq->out_packet;\n\tCOMPAT_pthread_mutex_unlock(&mosq->out_packet_mutex);\n\n\tif(packet == NULL){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n#ifdef WITH_BROKER\n\tmux__add_out(mosq);\n#endif\n\n\tstate = mosquitto__get_state(mosq);\n\tif(state == mosq_cs_connect_pending){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\twhile(packet){\n\t\twhile(packet->to_process > 0){\n\t\t\twrite_length = net__write(mosq, &(packet->payload[packet->pos]), packet->to_process);\n\t\t\tif(write_length > 0){\n\t\t\t\tmetrics__int_inc(mosq_counter_bytes_sent, write_length);\n\t\t\t\tpacket->to_process -= (uint32_t)write_length;\n\t\t\t\tpacket->pos += (uint32_t)write_length;\n\t\t\t}else{\n\t\t\t\tWINDOWS_SET_ERRNO_RW();\n\t\t\t\tif(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK\n#ifdef WIN32\n\t\t\t\t\t\t|| errno == WSAENOTCONN\n#endif\n\t\t\t\t\t\t){\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}else{\n\t\t\t\t\tswitch(errno){\n\t\t\t\t\t\tcase COMPAT_ECONNRESET:\n\t\t\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\t\t\tcase COMPAT_EINTR:\n\t\t\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t\t\tcase EPROTO:\n\t\t\t\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tmetrics__int_inc(mosq_counter_messages_sent, 1);\n\t\tif(((packet->command)&0xF6) == CMD_PUBLISH){\n#ifndef WITH_BROKER\n\t\t\tcallback__on_publish(mosq, packet->mid, 0, NULL);\n\t\t}else if(((packet->command)&0xF0) == CMD_DISCONNECT){\n\t\t\tnet__socket_shutdown(mosq);\n\t\t\treturn MOSQ_ERR_SUCCESS;\n#endif\n\t\t}\n\n\t\tnext_packet = packet__get_next_out(mosq);\n\t\tmosquitto_FREE(packet);\n\t\tpacket = next_packet;\n\n#ifdef WITH_BROKER\n\t\tmosq->next_msg_out = db.now_s + mosq->keepalive;\n#else\n\t\tCOMPAT_pthread_mutex_lock(&mosq->msgtime_mutex);\n\t\tmosq->next_msg_out = mosquitto_time() + mosq->keepalive;\n\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgtime_mutex);\n#endif\n\t}\n#ifdef WITH_BROKER\n\tif(mosq->out_packet == NULL){\n\t\tmux__remove_out(mosq);\n\t}\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int read_header(struct mosquitto *mosq, ssize_t (*func_read)(struct mosquitto *, void *, size_t))\n{\n\tssize_t read_length;\n\n\tmosq->in_packet.packet_buffer_pos = 0;\n\tmosq->in_packet.packet_buffer_to_process = 0;\n\tread_length = func_read(mosq, mosq->in_packet.packet_buffer, mosq->in_packet.packet_buffer_size);\n\tif(read_length > 0){\n\t\tmosq->in_packet.packet_buffer_to_process = (uint16_t)read_length;\n#ifdef WITH_BROKER\n\t\tmetrics__int_inc(mosq_counter_bytes_received, read_length);\n#endif\n\t}else{\n\t\tif(read_length == 0){\n\t\t\treturn MOSQ_ERR_CONN_LOST; /* EOF */\n\t\t}\n\t\tWINDOWS_SET_ERRNO_RW();\n\t\tif(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\tswitch(errno){\n\t\t\t\tcase COMPAT_ECONNRESET:\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\tcase COMPAT_EINTR:\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\tdefault:\n\t\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n#ifdef WITH_BROKER\n\n\nstatic int packet__check_in_packet_oversize(struct mosquitto *mosq)\n{\n\tuint32_t packet_length = 1 + mosquitto_varint_bytes(mosq->in_packet.remaining_length) + mosq->in_packet.remaining_length;\n\n\tswitch(mosq->in_packet.command & 0xF0){\n\t\tcase CMD_CONNECT:\n\t\t\tif(packet_length > db.config->packet_max_connect){\n\t\t\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase CMD_PUBACK:\n\t\tcase CMD_PUBREC:\n\t\tcase CMD_PUBREL:\n\t\tcase CMD_PUBCOMP:\n\t\tcase CMD_UNSUBACK:\n\t\t\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t\t\tif(packet_length > db.config->packet_max_simple){\n\t\t\t\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tif(mosq->in_packet.remaining_length != 2){\n\t\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase CMD_PINGREQ:\n\t\tcase CMD_PINGRESP:\n\t\t\tif(mosq->in_packet.remaining_length != 0){\n\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase CMD_DISCONNECT:\n\t\t\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t\t\tif(packet_length > db.config->packet_max_simple){\n\t\t\t\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tif(mosq->in_packet.remaining_length != 0){\n\t\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase CMD_SUBSCRIBE:\n\t\tcase CMD_UNSUBSCRIBE:\n\t\t\tif(mosq->protocol == mosq_p_mqtt5 && packet_length > db.config->packet_max_sub){\n\t\t\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase CMD_AUTH:\n\t\t\tif(packet_length > db.config->packet_max_auth){\n\t\t\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t\t\t}\n\t\t\tbreak;\n\n\t}\n\n\tif(db.config->max_packet_size > 0 && packet_length > db.config->max_packet_size){\n\t\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t\tsend__disconnect(mosq, MQTT_RC_PACKET_TOO_LARGE, NULL);\n\t\t}\n\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\nstatic int packet__read_single(struct mosquitto *mosq, enum mosquitto_client_state state, ssize_t (*local__read)(struct mosquitto *, void *, size_t))\n{\n\tssize_t read_length;\n\tint rc = 0;\n\n\tif(!mosq->in_packet.command){\n\t\tif(mosq->in_packet.packet_buffer_to_process == 0){\n\t\t\trc = read_header(mosq, local__read);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\n\t\tif(mosq->in_packet.packet_buffer_to_process > 0){\n\t\t\tmosq->in_packet.command = mosq->in_packet.packet_buffer[mosq->in_packet.packet_buffer_pos];\n\t\t\tmosq->in_packet.packet_buffer_to_process--;\n\t\t\tmosq->in_packet.packet_buffer_pos++;\n#ifdef WITH_BROKER\n\t\t\t/* Clients must send CONNECT as their first command. */\n\t\t\tif(!(mosq->bridge) && state == mosq_cs_new && (mosq->in_packet.command&0xF0) != CMD_CONNECT){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: First packet not CONNECT (%02X).\",\n\t\t\t\t\t\tmosq->address, mosq->remote_port, mosq->in_packet.command);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}else if((mosq->in_packet.command&0xF0) == CMD_RESERVED){\n\t\t\t\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t\t\t\tsend__disconnect(mosq, MQTT_RC_PROTOCOL_ERROR, NULL);\n\t\t\t\t}\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: RESERVED packet.\",\n\t\t\t\t\t\tmosq->address, mosq->remote_port);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n#else\n\t\t\tUNUSED(state);\n#endif\n\t\t}else{\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\t/* remaining_count is the number of bytes that the remaining_length\n\t * parameter occupied in this incoming packet. We don't use it here as such\n\t * (it is used when allocating an outgoing packet), but we must be able to\n\t * determine whether all of the remaining_length parameter has been read.\n\t * remaining_count has three states here:\n\t *   0 means that we haven't read any remaining_length bytes\n\t *   <0 means we have read some remaining_length bytes but haven't finished\n\t *   >0 means we have finished reading the remaining_length bytes.\n\t */\n\tif(mosq->in_packet.remaining_count <= 0){\n\t\tuint8_t byte;\n\t\tdo{\n\t\t\tif(mosq->in_packet.packet_buffer_to_process == 0){\n\t\t\t\trc = read_header(mosq, local__read);\n\t\t\t\tif(rc){\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(mosq->in_packet.packet_buffer_to_process > 0){\n\t\t\t\tmosq->in_packet.remaining_count--;\n\t\t\t\t/* Max 4 bytes length for remaining length as defined by protocol.\n\t\t\t\t * Anything more likely means a broken/malicious client.\n\t\t\t\t */\n\t\t\t\tif(mosq->in_packet.remaining_count < -4){\n\t\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t\t}\n\n\t\t\t\tbyte = mosq->in_packet.packet_buffer[mosq->in_packet.packet_buffer_pos];\n\t\t\t\tmosq->in_packet.packet_buffer_pos++;\n\t\t\t\tmosq->in_packet.packet_buffer_to_process--;\n\t\t\t\tmosq->in_packet.remaining_length += (byte & 127) * mosq->in_packet.remaining_mult;\n\t\t\t\tmosq->in_packet.remaining_mult *= 128;\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}while((byte & 128) != 0);\n\t\t/* We have finished reading remaining_length, so make remaining_count\n\t\t * positive. */\n\t\tmosq->in_packet.remaining_count = (int8_t)(mosq->in_packet.remaining_count * -1);\n\n#ifdef WITH_BROKER\n\t\trc = packet__check_in_packet_oversize(mosq);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n#else\n\t\t/* FIXME - client case for incoming message received from broker too large */\n#endif\n\t\tif(mosq->in_packet.remaining_length > 0){\n\t\t\tmosq->in_packet.payload = mosquitto_malloc(mosq->in_packet.remaining_length*sizeof(uint8_t));\n\t\t\tif(!mosq->in_packet.payload){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\n\t\t\tmosq->in_packet.pos = 0;\n\t\t\tmosq->in_packet.to_process = mosq->in_packet.remaining_length;\n\n\t\t\tif(mosq->in_packet.packet_buffer_to_process > 0){\n\t\t\t\tuint32_t len;\n\t\t\t\tif(mosq->in_packet.packet_buffer_to_process > mosq->in_packet.remaining_length){\n\t\t\t\t\tlen = mosq->in_packet.remaining_length;\n\t\t\t\t}else{\n\t\t\t\t\tlen = mosq->in_packet.packet_buffer_to_process;\n\t\t\t\t}\n\t\t\t\tmemcpy(mosq->in_packet.payload, &mosq->in_packet.packet_buffer[mosq->in_packet.packet_buffer_pos], len);\n\t\t\t\tif(len < mosq->in_packet.packet_buffer_to_process){\n\t\t\t\t\tmosq->in_packet.packet_buffer_pos = (uint16_t)(mosq->in_packet.packet_buffer_pos + len);\n\t\t\t\t\tmosq->in_packet.packet_buffer_to_process = (uint16_t)(mosq->in_packet.packet_buffer_to_process - len);\n\t\t\t\t}else{\n\t\t\t\t\tmosq->in_packet.packet_buffer_pos = 0;\n\t\t\t\t\tmosq->in_packet.packet_buffer_to_process = 0;\n\t\t\t\t}\n\t\t\t\tmosq->in_packet.pos += len;\n\t\t\t\tmosq->in_packet.to_process -= len;\n\t\t\t}\n\t\t}\n\t}\n\twhile(mosq->in_packet.to_process>0){\n\t\tread_length = local__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process);\n\t\tif(read_length > 0){\n\t\t\tmetrics__int_inc(mosq_counter_bytes_received, read_length);\n\t\t\tmosq->in_packet.to_process -= (uint32_t)read_length;\n\t\t\tmosq->in_packet.pos += (uint32_t)read_length;\n\t\t}else{\n\t\t\tWINDOWS_SET_ERRNO_RW();\n\t\t\tif(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){\n\t\t\t\tif(mosq->in_packet.to_process > 1000){\n\t\t\t\t\t/* Update last_msg_in time if more than 1000 bytes left to\n\t\t\t\t\t * receive. Helps when receiving large messages.\n\t\t\t\t\t * This is an arbitrary limit, but with some consideration.\n\t\t\t\t\t * If a client can't send 1000 bytes in a second it\n\t\t\t\t\t * probably shouldn't be using a 1 second keep alive. */\n#ifdef WITH_BROKER\n\t\t\t\t\tkeepalive__update(mosq);\n#else\n\t\t\t\t\tCOMPAT_pthread_mutex_lock(&mosq->msgtime_mutex);\n\t\t\t\t\tmosq->last_msg_in = mosquitto_time();\n\t\t\t\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgtime_mutex);\n#endif\n\t\t\t\t}\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}else{\n\t\t\t\tswitch(errno){\n\t\t\t\t\tcase COMPAT_ECONNRESET:\n\t\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\t\tcase COMPAT_EINTR:\n\t\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/* All data for this packet is read. */\n\tmosq->in_packet.pos = 0;\n#ifdef WITH_BROKER\n\tmetrics__int_inc(mosq_counter_messages_received, 1);\n#endif\n\trc = handle__packet(mosq);\n\n\t/* Free data and reset values */\n\tpacket__cleanup(&mosq->in_packet);\n\n#ifdef WITH_BROKER\n\tkeepalive__update(mosq);\n#else\n\tCOMPAT_pthread_mutex_lock(&mosq->msgtime_mutex);\n\tmosq->last_msg_in = mosquitto_time();\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgtime_mutex);\n#endif\n\treturn rc;\n}\n\n\nint packet__read(struct mosquitto *mosq)\n{\n\tint rc = 0;\n\tenum mosquitto_client_state state;\n\tssize_t (*local__read)(struct mosquitto *, void *, size_t);\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!net__is_connected(mosq)){\n\t\treturn MOSQ_ERR_NO_CONN;\n\t}\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\tif(mosq->transport == mosq_t_ws){\n\t\tlocal__read = net__read_ws;\n\t}else\n#endif\n\t{\n\t\tlocal__read = net__read;\n\t}\n\n\t/* This gets called if pselect() indicates that there is network data\n\t * available - ie. at least one byte.  What we do depends on what data we\n\t * already have.\n\t * If we've not got a command, attempt to read one and save it. This should\n\t * always work because it's only a single byte.\n\t * Then try to read the remaining length. This may fail because it is may\n\t * be more than one byte - will need to save data pending next read if it\n\t * does fail.\n\t * Then try to read the remaining payload, where 'payload' here means the\n\t * combined variable packet_buffer and actual payload. This is the most likely to\n\t * fail due to longer length, so save current data and current position.\n\t * After all data is read, send to mosquitto__handle_packet() to deal with.\n\t * Finally, free the memory and reset everything to starting conditions.\n\t */\n\tdo{\n\t\tstate = mosquitto__get_state(mosq);\n\t\tif(state == mosq_cs_connect_pending){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t\trc = packet__read_single(mosq, state, local__read);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}while(mosq->in_packet.packet_buffer_to_process > 0);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "lib/packet_mosq.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef PACKET_MOSQ_H\n#define PACKET_MOSQ_H\n\n#include \"mosquitto_internal.h\"\n#include \"mosquitto.h\"\n\nint packet__alloc(struct mosquitto__packet **packet, uint8_t command, uint32_t remaining_length);\nvoid packet__cleanup(struct mosquitto__packet_in *packet);\nvoid packet__cleanup_all(struct mosquitto *mosq);\nvoid packet__cleanup_all_no_locks(struct mosquitto *mosq);\nint packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet);\nstruct mosquitto__packet *packet__get_next_out(struct mosquitto *mosq);\n\nint packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length);\n\nint packet__read_byte(struct mosquitto__packet_in *packet, uint8_t *byte);\nint packet__read_bytes(struct mosquitto__packet_in *packet, void *bytes, uint32_t count);\nint packet__read_binary(struct mosquitto__packet_in *packet, uint8_t **data, uint16_t *length);\nint packet__read_string(struct mosquitto__packet_in *packet, char **str, uint16_t *length);\nint packet__read_uint16(struct mosquitto__packet_in *packet, uint16_t *word);\nint packet__read_uint32(struct mosquitto__packet_in *packet, uint32_t *word);\nint packet__read_varint(struct mosquitto__packet_in *packet, uint32_t *word, uint8_t *bytes);\n\nvoid packet__write_byte(struct mosquitto__packet *packet, uint8_t byte);\nvoid packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count);\nvoid packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length);\nvoid packet__write_uint16(struct mosquitto__packet *packet, uint16_t word);\nvoid packet__write_uint32(struct mosquitto__packet *packet, uint32_t word);\nint packet__write_varint(struct mosquitto__packet *packet, uint32_t word);\n\nint packet__write(struct mosquitto *mosq);\nint packet__read(struct mosquitto *mosq);\n\n#endif\n"
  },
  {
    "path": "lib/property_mosq.c",
    "content": "/*\nCopyright (c) 2018-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <string.h>\n\n#ifndef WIN32\n#  include <strings.h>\n#endif\n\n#include \"logging_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_common.h\"\n#include \"property_mosq.h\"\n\n\nstatic int property__read(struct mosquitto__packet_in *packet, uint32_t *len, mosquitto_property *property)\n{\n\tint rc;\n\tuint32_t property_identifier;\n\tuint8_t byte;\n\tuint8_t byte_count;\n\tuint16_t uint16;\n\tuint32_t uint32;\n\tuint32_t varint;\n\tchar *str1, *str2;\n\tuint16_t slen1, slen2;\n\n\tif(!property){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trc = packet__read_varint(packet, &property_identifier, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\t*len -= mosquitto_varint_bytes(property_identifier);\n\n\tmemset(property, 0, sizeof(mosquitto_property));\n\n\tproperty->identifier = (int32_t)property_identifier;\n\n\tswitch(property_identifier){\n\t\tcase MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:\n\t\tcase MQTT_PROP_REQUEST_PROBLEM_INFORMATION:\n\t\tcase MQTT_PROP_REQUEST_RESPONSE_INFORMATION:\n\t\tcase MQTT_PROP_MAXIMUM_QOS:\n\t\tcase MQTT_PROP_RETAIN_AVAILABLE:\n\t\tcase MQTT_PROP_WILDCARD_SUB_AVAILABLE:\n\t\tcase MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE:\n\t\tcase MQTT_PROP_SHARED_SUB_AVAILABLE:\n\t\t\trc = packet__read_byte(packet, &byte);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\t*len -= 1; /* byte */\n\t\t\tproperty->value.i8 = byte;\n\t\t\tproperty->property_type = MQTT_PROP_TYPE_BYTE;\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_SERVER_KEEP_ALIVE:\n\t\tcase MQTT_PROP_RECEIVE_MAXIMUM:\n\t\tcase MQTT_PROP_TOPIC_ALIAS_MAXIMUM:\n\t\tcase MQTT_PROP_TOPIC_ALIAS:\n\t\t\trc = packet__read_uint16(packet, &uint16);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\t*len -= 2; /* uint16 */\n\t\t\tproperty->value.i16 = uint16;\n\t\t\tproperty->property_type = MQTT_PROP_TYPE_INT16;\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:\n\t\tcase MQTT_PROP_SESSION_EXPIRY_INTERVAL:\n\t\tcase MQTT_PROP_WILL_DELAY_INTERVAL:\n\t\tcase MQTT_PROP_MAXIMUM_PACKET_SIZE:\n\t\t\trc = packet__read_uint32(packet, &uint32);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\t*len -= 4; /* uint32 */\n\t\t\tproperty->value.i32 = uint32;\n\t\t\tproperty->property_type = MQTT_PROP_TYPE_INT32;\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_SUBSCRIPTION_IDENTIFIER:\n\t\t\trc = packet__read_varint(packet, &varint, &byte_count);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\t*len -= byte_count;\n\t\t\tproperty->value.varint = varint;\n\t\t\tproperty->property_type = MQTT_PROP_TYPE_VARINT;\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_CONTENT_TYPE:\n\t\tcase MQTT_PROP_RESPONSE_TOPIC:\n\t\tcase MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER:\n\t\tcase MQTT_PROP_AUTHENTICATION_METHOD:\n\t\tcase MQTT_PROP_RESPONSE_INFORMATION:\n\t\tcase MQTT_PROP_SERVER_REFERENCE:\n\t\tcase MQTT_PROP_REASON_STRING:\n\t\t\trc = packet__read_string(packet, &str1, &slen1);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\t*len = (*len) - 2 - slen1; /* uint16, string len */\n\t\t\tproperty->value.s.v = str1;\n\t\t\tproperty->value.s.len = slen1;\n\t\t\tproperty->property_type = MQTT_PROP_TYPE_STRING;\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_AUTHENTICATION_DATA:\n\t\tcase MQTT_PROP_CORRELATION_DATA:\n\t\t\trc = packet__read_binary(packet, (uint8_t **)&str1, &slen1);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\t*len = (*len) - 2 - slen1; /* uint16, binary len */\n\t\t\tproperty->value.bin.v = str1;\n\t\t\tproperty->value.bin.len = slen1;\n\t\t\tproperty->property_type = MQTT_PROP_TYPE_BINARY;\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_USER_PROPERTY:\n\t\t\trc = packet__read_string(packet, &str1, &slen1);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\t*len = (*len) - 2 - slen1; /* uint16, string len */\n\n\t\t\trc = packet__read_string(packet, &str2, &slen2);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_FREE(str1);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\t*len = (*len) - 2 - slen2; /* uint16, string len */\n\n\t\t\tproperty->name.v = str1;\n\t\t\tproperty->name.len = slen1;\n\t\t\tproperty->value.s.v = str2;\n\t\t\tproperty->value.s.len = slen2;\n\t\t\tproperty->property_type = MQTT_PROP_TYPE_STRING_PAIR;\n\t\t\tbreak;\n\n\t\tdefault:\n#ifdef WITH_BROKER\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Unsupported property type: %d\", property_identifier);\n#endif\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint property__read_all(int command, struct mosquitto__packet_in *packet, mosquitto_property **properties)\n{\n\tint rc;\n\tuint32_t proplen;\n\tmosquitto_property *p, *tail = NULL;\n\n\trc = packet__read_varint(packet, &proplen, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\t*properties = NULL;\n\n\t/* The order of properties must be preserved for some types, so keep the\n\t * same order for all */\n\twhile(proplen > 0){\n\t\tp = mosquitto_calloc(1, sizeof(mosquitto_property));\n\t\tif(!p){\n\t\t\tmosquitto_property_free_all(properties);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\trc = property__read(packet, &proplen, p);\n\t\tif(rc){\n\t\t\tmosquitto_FREE(p);\n\t\t\tmosquitto_property_free_all(properties);\n\t\t\treturn rc;\n\t\t}\n\n\t\tif(!(*properties)){\n\t\t\t*properties = p;\n\t\t}else{\n\t\t\ttail->next = p;\n\t\t}\n\t\ttail = p;\n\n\t}\n\n\trc = mosquitto_property_check_all(command, *properties);\n\tif(rc){\n\t\tmosquitto_property_free_all(properties);\n\t\treturn rc;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int property__write(struct mosquitto__packet *packet, const mosquitto_property *property)\n{\n\tint rc;\n\n\trc = packet__write_varint(packet, (uint32_t)mosquitto_property_identifier(property));\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tswitch(property->property_type){\n\t\tcase MQTT_PROP_TYPE_BYTE:\n\t\t\tpacket__write_byte(packet, property->value.i8);\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_TYPE_INT16:\n\t\t\tpacket__write_uint16(packet, property->value.i16);\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_TYPE_INT32:\n\t\t\tpacket__write_uint32(packet, property->value.i32);\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_TYPE_VARINT:\n\t\t\treturn packet__write_varint(packet, property->value.varint);\n\n\t\tcase MQTT_PROP_TYPE_STRING:\n\t\t\tpacket__write_string(packet, property->value.s.v, property->value.s.len);\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_TYPE_BINARY:\n\t\t\tpacket__write_uint16(packet, property->value.bin.len);\n\t\t\tpacket__write_bytes(packet, property->value.bin.v, property->value.bin.len);\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_TYPE_STRING_PAIR:\n\t\t\tpacket__write_string(packet, property->name.v, property->name.len);\n\t\t\tpacket__write_string(packet, property->value.s.v, property->value.s.len);\n\t\t\tbreak;\n\n\t\tdefault:\n#ifdef WITH_BROKER\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Unsupported property type: %d\", property->identifier);\n#endif\n\t\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint property__write_all(struct mosquitto__packet *packet, const mosquitto_property *properties, bool write_len)\n{\n\tint rc;\n\tconst mosquitto_property *p;\n\n\tif(write_len){\n\t\trc = packet__write_varint(packet, mosquitto_property_get_length_all(properties));\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tp = properties;\n\twhile(p){\n\t\trc = property__write(packet, p);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\tp = p->next;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "lib/property_mosq.h",
    "content": "/*\nCopyright (c) 2018-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef PROPERTY_MOSQ_H\n#define PROPERTY_MOSQ_H\n\n#include \"mosquitto_internal.h\"\n\nint property__read_all(int command, struct mosquitto__packet_in *packet, mosquitto_property **property);\nint property__write_all(struct mosquitto__packet *packet, const mosquitto_property *property, bool write_len);\n\n#endif\n"
  },
  {
    "path": "lib/pthread_compat.h",
    "content": "#ifndef PTHREAD_COMPAT_\n#define PTHREAD_COMPAT_\n\n#if defined(WITH_THREADING) && !defined(WITH_BROKER)\n#  include <pthread.h>\n\n#  define COMPAT_pthread_create(A, B, C, D) pthread_create((A), (B), (C), (D))\n#  define COMPAT_pthread_join(A, B) pthread_join((A), (B))\n#  define COMPAT_pthread_cancel(A) pthread_cancel((A))\n#  define COMPAT_pthread_testcancel() pthread_testcancel()\n\n#  define COMPAT_pthread_mutex_init(A, B) pthread_mutex_init((A), (B))\n#  define COMPAT_pthread_mutex_destroy(A) pthread_mutex_destroy((A))\n#  define COMPAT_pthread_mutex_lock(A) pthread_mutex_lock((A))\n#  define COMPAT_pthread_mutex_unlock(A) pthread_mutex_unlock((A))\n#else\n#  define COMPAT_pthread_create(A, B, C, D)\n#  define COMPAT_pthread_join(A, B)\n#  define COMPAT_pthread_cancel(A)\n#  define COMPAT_pthread_testcancel()\n\n#  define COMPAT_pthread_mutex_init(A, B)\n#  define COMPAT_pthread_mutex_destroy(A)\n#  define COMPAT_pthread_mutex_lock(A)\n#  define COMPAT_pthread_mutex_unlock(A)\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/read_handle.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n#include \"logging_mosq.h\"\n#include \"messages_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint handle__packet(struct mosquitto *mosq)\n{\n\tint rc = MOSQ_ERR_INVAL;\n\tassert(mosq);\n\n\tswitch((mosq->in_packet.command)&0xF0){\n\t\tcase CMD_PINGREQ:\n\t\t\trc = handle__pingreq(mosq);\n\t\t\tbreak;\n\t\tcase CMD_PINGRESP:\n\t\t\trc = handle__pingresp(mosq);\n\t\t\tbreak;\n\t\tcase CMD_PUBACK:\n\t\t\trc = handle__pubackcomp(mosq, \"PUBACK\");\n\t\t\tbreak;\n\t\tcase CMD_PUBCOMP:\n\t\t\trc = handle__pubackcomp(mosq, \"PUBCOMP\");\n\t\t\tbreak;\n\t\tcase CMD_PUBLISH:\n\t\t\trc = handle__publish(mosq);\n\t\t\tbreak;\n\t\tcase CMD_PUBREC:\n\t\t\trc = handle__pubrec(mosq);\n\t\t\tbreak;\n\t\tcase CMD_PUBREL:\n\t\t\trc = handle__pubrel(mosq);\n\t\t\tbreak;\n\t\tcase CMD_CONNACK:\n\t\t\trc = handle__connack(mosq);\n\t\t\tbreak;\n\t\tcase CMD_SUBACK:\n\t\t\trc = handle__suback(mosq);\n\t\t\tbreak;\n\t\tcase CMD_UNSUBACK:\n\t\t\trc = handle__unsuback(mosq);\n\t\t\tbreak;\n\t\tcase CMD_DISCONNECT:\n\t\t\trc = handle__disconnect(mosq);\n\t\t\tbreak;\n\t\tcase CMD_AUTH:\n\t\t\trc = handle__auth(mosq);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t/* If we don't recognise the command, return an error straight away. */\n\t\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: Unrecognised command %d\\n\", (mosq->in_packet.command)&0xF0);\n\t\t\trc = MOSQ_ERR_PROTOCOL;\n\t\t\tbreak;\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tif(rc == MOSQ_ERR_PROTOCOL || rc == MOSQ_ERR_DUPLICATE_PROPERTY){\n\t\t\tsend__disconnect(mosq, MQTT_RC_PROTOCOL_ERROR, NULL);\n\t\t}else if(rc == MOSQ_ERR_MALFORMED_PACKET || rc == MOSQ_ERR_MALFORMED_UTF8){\n\t\t\tsend__disconnect(mosq, MQTT_RC_MALFORMED_PACKET, NULL);\n\t\t}else if(rc == MOSQ_ERR_QOS_NOT_SUPPORTED){\n\t\t\tsend__disconnect(mosq, MQTT_RC_QOS_NOT_SUPPORTED, NULL);\n\t\t}else if(rc == MOSQ_ERR_RETAIN_NOT_SUPPORTED){\n\t\t\tsend__disconnect(mosq, MQTT_RC_RETAIN_NOT_SUPPORTED, NULL);\n\t\t}else if(rc == MOSQ_ERR_TOPIC_ALIAS_INVALID){\n\t\t\tsend__disconnect(mosq, MQTT_RC_TOPIC_ALIAS_INVALID, NULL);\n\t\t}else if(rc == MOSQ_ERR_RECEIVE_MAXIMUM_EXCEEDED){\n\t\t\tsend__disconnect(mosq, MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED, NULL);\n\t\t}else if(rc == MOSQ_ERR_UNKNOWN || rc == MOSQ_ERR_NOMEM){\n\t\t\tsend__disconnect(mosq, MQTT_RC_UNSPECIFIED, NULL);\n\t\t}\n\t}\n\treturn rc;\n\n}\n"
  },
  {
    "path": "lib/read_handle.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef READ_HANDLE_H\n#define READ_HANDLE_H\n\n#include \"mosquitto.h\"\nstruct mosquitto_db;\n\nint handle__pingreq(struct mosquitto *mosq);\nint handle__pingresp(struct mosquitto *mosq);\n#ifdef WITH_BROKER\nint handle__pubackcomp(struct mosquitto *mosq, const char *type);\n#else\nint handle__packet(struct mosquitto *mosq);\nint handle__connack(struct mosquitto *mosq);\nint handle__disconnect(struct mosquitto *mosq);\nint handle__pubackcomp(struct mosquitto *mosq, const char *type);\nint handle__publish(struct mosquitto *mosq);\nint handle__auth(struct mosquitto *mosq);\n#endif\nint handle__pubrec(struct mosquitto *mosq);\nint handle__pubrel(struct mosquitto *mosq);\nint handle__suback(struct mosquitto *mosq);\nint handle__unsuback(struct mosquitto *mosq);\n\n\n#endif\n"
  },
  {
    "path": "lib/send_connect.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#  include \"sys_tree.h\"\n#endif\n\n#include \"logging_mosq.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"send_mosq.h\"\n\n\nint send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tuint32_t payloadlen;\n\tuint8_t will = 0;\n\tuint8_t byte;\n\tint rc;\n\tuint8_t version;\n\tchar *clientid, *username, *password;\n\tuint32_t headerlen;\n\tuint32_t proplen = 0, varbytes;\n\tmosquitto_property *local_props = NULL;\n\tuint16_t receive_maximum;\n\n\tassert(mosq);\n\n\tif(mosq->protocol == mosq_p_mqtt31 && !mosq->id){\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n#if defined(WITH_BROKER) && defined(WITH_BRIDGE)\n\tif(mosq->bridge){\n\t\tclientid = mosq->bridge->remote_clientid;\n\t\tusername = mosq->bridge->remote_username;\n\t\tpassword = mosq->bridge->remote_password;\n\t}else{\n\t\tclientid = mosq->id;\n\t\tusername = mosq->username;\n\t\tpassword = mosq->password;\n\t}\n#else\n\tclientid = mosq->id;\n\tusername = mosq->username;\n\tpassword = mosq->password;\n#endif\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t/* Generate properties from options */\n\t\tif(!mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &receive_maximum, false)){\n\t\t\trc = mosquitto_property_add_int16(&local_props, MQTT_PROP_RECEIVE_MAXIMUM, mosq->msgs_in.inflight_maximum);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}else{\n\t\t\tmosq->msgs_in.inflight_maximum = receive_maximum;\n\t\t\tmosq->msgs_in.inflight_quota = receive_maximum;\n\t\t}\n\n\t\tversion = MQTT_PROTOCOL_V5;\n\t\theaderlen = 10;\n\t\tproplen = 0;\n\t\tproplen += mosquitto_property_get_length_all(properties);\n\t\tproplen += mosquitto_property_get_length_all(local_props);\n\t\tvarbytes = mosquitto_varint_bytes(proplen);\n\t\theaderlen += proplen + varbytes;\n\t}else if(mosq->protocol == mosq_p_mqtt311){\n\t\tversion = MQTT_PROTOCOL_V311;\n\t\theaderlen = 10;\n\t}else if(mosq->protocol == mosq_p_mqtt31){\n\t\tversion = MQTT_PROTOCOL_V31;\n\t\theaderlen = 12;\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(clientid){\n\t\tpayloadlen = (uint32_t)(2U+strlen(clientid));\n\t}else{\n\t\tpayloadlen = 2U;\n\t}\n#ifdef WITH_BROKER\n\tif(mosq->will && (mosq->bridge == NULL || mosq->bridge->notifications_local_only == false)){\n#else\n\tif(mosq->will){\n#endif\n\t\twill = 1;\n\t\tassert(mosq->will->msg.topic);\n\n\t\tpayloadlen += (uint32_t)(2+strlen(mosq->will->msg.topic) + 2+(uint32_t)mosq->will->msg.payloadlen);\n\t\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t\tpayloadlen += mosquitto_property_get_remaining_length(mosq->will->properties);\n\t\t}\n\t}\n\n\t/* After this check we can be sure that the username and password are\n\t * always valid for the current protocol, so there is no need to check\n\t * username before checking password. */\n\tif(mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311){\n\t\tif(password != NULL && username == NULL){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\n\tif(username){\n\t\tpayloadlen += (uint32_t)(2+strlen(username));\n\t}\n\tif(password){\n\t\tpayloadlen += (uint32_t)(2+strlen(password));\n\t}\n\n\trc = packet__alloc(&packet, CMD_CONNECT, headerlen + payloadlen);\n\tif(rc){\n\t\tmosquitto_FREE(packet);\n\t\treturn rc;\n\t}\n\n\t/* Variable header */\n\tif(version == MQTT_PROTOCOL_V31){\n\t\tpacket__write_string(packet, PROTOCOL_NAME_v31, (uint16_t)strlen(PROTOCOL_NAME_v31));\n\t}else{\n\t\tpacket__write_string(packet, PROTOCOL_NAME, (uint16_t)strlen(PROTOCOL_NAME));\n\t}\n#if defined(WITH_BROKER) && defined(WITH_BRIDGE)\n\tif(mosq->bridge && mosq->bridge->protocol_version != mosq_p_mqtt5 && mosq->bridge->try_private && mosq->bridge->try_private_accepted){\n\t\tversion |= 0x80;\n\t}else{\n\t}\n#endif\n\tpacket__write_byte(packet, version);\n\tbyte = (uint8_t)((clean_session&0x1)<<1);\n\tif(will){\n\t\tbyte = byte | (uint8_t)(((mosq->will->msg.qos&0x3)<<3) | ((will&0x1)<<2));\n\t\tif(mosq->retain_available){\n\t\t\tbyte |= (uint8_t)((mosq->will->msg.retain&0x1)<<5);\n\t\t}\n\t}\n\tif(username){\n\t\tbyte = byte | 0x1<<7;\n\t}\n\tif(mosq->password){\n\t\tbyte = byte | 0x1<<6;\n\t}\n\tpacket__write_byte(packet, byte);\n\tpacket__write_uint16(packet, keepalive);\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t/* Write properties */\n\t\tpacket__write_varint(packet, proplen);\n\t\tproperty__write_all(packet, properties, false);\n\t\tproperty__write_all(packet, local_props, false);\n\t}\n\tmosquitto_property_free_all(&local_props);\n\n\t/* Payload */\n\tif(clientid){\n\t\tpacket__write_string(packet, clientid, (uint16_t)strlen(clientid));\n\t}else{\n\t\tpacket__write_uint16(packet, 0);\n\t}\n\tif(will){\n\t\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t\t/* Write will properties */\n\t\t\tproperty__write_all(packet, mosq->will->properties, true);\n\t\t}\n\t\tpacket__write_string(packet, mosq->will->msg.topic, (uint16_t)strlen(mosq->will->msg.topic));\n\t\tpacket__write_string(packet, (const char *)mosq->will->msg.payload, (uint16_t)mosq->will->msg.payloadlen);\n\t}\n\n\tif(username){\n\t\tpacket__write_string(packet, username, (uint16_t)strlen(username));\n\t}\n\tif(password){\n\t\tpacket__write_string(packet, password, (uint16_t)strlen(password));\n\t}\n\n\tmosq->keepalive = keepalive;\n#ifdef WITH_BROKER\n# ifdef WITH_BRIDGE\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Bridge %s sending CONNECT\", SAFE_PRINT(clientid));\n# endif\n\tmetrics__int_inc(mosq_counter_mqtt_connect_sent, 1);\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending CONNECT\", SAFE_PRINT(clientid));\n#endif\n\treturn packet__queue(mosq, packet);\n}\n\n"
  },
  {
    "path": "lib/send_disconnect.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#  include \"sys_tree.h\"\n#endif\n\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"logging_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"send_mosq.h\"\n\n\nint send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tint rc;\n\tuint32_t remaining_length = 0;\n\n\tassert(mosq);\n#ifdef WITH_BROKER\n#  ifdef WITH_BRIDGE\n\tif(mosq->bridge){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Bridge %s sending DISCONNECT\", SAFE_PRINT(mosq->id));\n\t}else\n#  else\n\t{\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Sending DISCONNECT to %s (rc%d)\", SAFE_PRINT(mosq->id), reason_code);\n\t}\n#  endif\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending DISCONNECT\", SAFE_PRINT(mosq->id));\n#endif\n\n\tif(mosq->protocol == mosq_p_mqtt5 && (reason_code != 0 || properties)){\n\t\tremaining_length = 1;\n\t\tif(properties){\n\t\t\tremaining_length += mosquitto_property_get_remaining_length(properties);\n\t\t}\n\t}else{\n\t\tremaining_length = 0;\n\t}\n\n\trc = packet__alloc(&packet, CMD_DISCONNECT, remaining_length);\n\tif(rc){\n\t\tmosquitto_FREE(packet);\n\t\treturn rc;\n\t}\n\tif(remaining_length > 0){\n\t\tpacket__write_byte(packet, reason_code);\n\t\tif(properties){\n\t\t\tproperty__write_all(packet, properties, true);\n\t\t}\n\t}\n\n#ifdef WITH_BROKER\n\tmetrics__int_inc(mosq_counter_mqtt_disconnect_sent, 1);\n#endif\n\treturn packet__queue(mosq, packet);\n}\n\n"
  },
  {
    "path": "lib/send_mosq.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#  include \"sys_tree.h\"\n#endif\n\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"logging_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint send__pingreq(struct mosquitto *mosq)\n{\n\tint rc;\n\tassert(mosq);\n#ifdef WITH_BROKER\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending PINGREQ to %s\", SAFE_PRINT(mosq->id));\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending PINGREQ\", SAFE_PRINT(mosq->id));\n#endif\n\trc = send__simple_command(mosq, CMD_PINGREQ);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tmosq->ping_t = mosquitto_time();\n#ifdef WITH_BROKER\n\t\tmetrics__int_inc(mosq_counter_mqtt_pingreq_sent, 1);\n#endif\n\t}\n\treturn rc;\n}\n\n\nint send__pingresp(struct mosquitto *mosq)\n{\n#ifdef WITH_BROKER\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending PINGRESP to %s\", SAFE_PRINT(mosq->id));\n\tmetrics__int_inc(mosq_counter_mqtt_pingresp_sent, 1);\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending PINGRESP\", SAFE_PRINT(mosq->id));\n#endif\n\treturn send__simple_command(mosq, CMD_PINGRESP);\n}\n\n\nint send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties)\n{\n#ifdef WITH_BROKER\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending PUBACK to %s (m%d, rc%d)\", SAFE_PRINT(mosq->id), mid, reason_code);\n\tmetrics__int_inc(mosq_counter_mqtt_puback_sent, 1);\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending PUBACK (m%d, rc%d)\", SAFE_PRINT(mosq->id), mid, reason_code);\n#endif\n\tutil__increment_receive_quota(mosq);\n\t/* We don't use Reason String or User Property yet. */\n\treturn send__command_with_mid(mosq, CMD_PUBACK, mid, false, reason_code, properties);\n}\n\n\nint send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties)\n{\n#ifdef WITH_BROKER\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending PUBCOMP to %s (m%d)\", SAFE_PRINT(mosq->id), mid);\n\tmetrics__int_inc(mosq_counter_mqtt_pubcomp_sent, 1);\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending PUBCOMP (m%d)\", SAFE_PRINT(mosq->id), mid);\n#endif\n\tutil__increment_receive_quota(mosq);\n\t/* We don't use Reason String or User Property yet. */\n\treturn send__command_with_mid(mosq, CMD_PUBCOMP, mid, false, 0, properties);\n}\n\n\nint send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties)\n{\n#ifdef WITH_BROKER\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending PUBREC to %s (m%d, rc%d)\", SAFE_PRINT(mosq->id), mid, reason_code);\n\tmetrics__int_inc(mosq_counter_mqtt_pubrec_sent, 1);\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending PUBREC (m%d, rc%d)\", SAFE_PRINT(mosq->id), mid, reason_code);\n#endif\n\tif(reason_code >= 0x80 && mosq->protocol == mosq_p_mqtt5){\n\t\tutil__increment_receive_quota(mosq);\n\t}\n\t/* We don't use Reason String or User Property yet. */\n\treturn send__command_with_mid(mosq, CMD_PUBREC, mid, false, reason_code, properties);\n}\n\n\nint send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties)\n{\n#ifdef WITH_BROKER\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending PUBREL to %s (m%d)\", SAFE_PRINT(mosq->id), mid);\n\tmetrics__int_inc(mosq_counter_mqtt_pubrel_sent, 1);\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending PUBREL (m%d)\", SAFE_PRINT(mosq->id), mid);\n#endif\n\t/* We don't use Reason String or User Property yet. */\n\treturn send__command_with_mid(mosq, CMD_PUBREL|2, mid, false, 0, properties);\n}\n\n\n/* For PUBACK, PUBCOMP, PUBREC, and PUBREL */\nint send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tint rc;\n\tuint32_t remaining_length;\n\n\tassert(mosq);\n\n\tif(dup){\n\t\tcommand |= 8;\n\t}\n\tremaining_length = 2;\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tif(reason_code != 0 || properties){\n\t\t\tremaining_length += 1;\n\t\t}\n\n\t\tif(properties){\n\t\t\tremaining_length += mosquitto_property_get_remaining_length(properties);\n\t\t}\n\t}\n\n\trc = packet__alloc(&packet, command, remaining_length);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tpacket__write_uint16(packet, mid);\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tif(reason_code != 0 || properties){\n\t\t\tpacket__write_byte(packet, reason_code);\n\t\t}\n\t\tif(properties){\n\t\t\tproperty__write_all(packet, properties, true);\n\t\t}\n\t}\n\n\treturn packet__queue(mosq, packet);\n}\n\n\n/* For DISCONNECT, PINGREQ and PINGRESP */\nint send__simple_command(struct mosquitto *mosq, uint8_t command)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tint rc;\n\n\tassert(mosq);\n\n\trc = packet__alloc(&packet, command, 0);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\treturn packet__queue(mosq, packet);\n}\n"
  },
  {
    "path": "lib/send_mosq.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef SEND_MOSQ_H\n#define SEND_MOSQ_H\n\n#include \"mosquitto.h\"\n#include \"property_mosq.h\"\n\nint send__simple_command(struct mosquitto *mosq, uint8_t command);\nint send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties);\nint send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, uint32_t subscription_identifier, const mosquitto_property *store_props, uint32_t expiry_interval);\n\nint send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties);\nint send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties);\nint send__pingreq(struct mosquitto *mosq);\nint send__pingresp(struct mosquitto *mosq);\nint send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties);\nint send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties);\nint send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, uint32_t subscription_identifier, const mosquitto_property *store_props, uint32_t expiry_interval);\nint send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties);\nint send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties);\nint send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos, const mosquitto_property *properties);\nint send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties);\n\n#endif\n"
  },
  {
    "path": "lib/send_publish.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#  include \"sys_tree.h\"\n#else\n#  define metrics__int_inc(stat, val)\n#endif\n\n#include \"alias_mosq.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"logging_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"property_common.h\"\n#include \"send_mosq.h\"\n#include \"utlist.h\"\n\n\nint send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, uint32_t subscription_identifier, const mosquitto_property *store_props, uint32_t expiry_interval)\n{\n#ifdef WITH_BROKER\n\tsize_t len;\n\tint rc;\n#ifdef WITH_BRIDGE\n\tstruct mosquitto__bridge_topic *cur_topic;\n\tbool match;\n\tchar *mapped_topic = NULL;\n\tchar *topic_temp = NULL;\n#endif\n#endif\n\tassert(mosq);\n\n\tif(!net__is_connected(mosq)){\n\t\treturn MOSQ_ERR_NO_CONN;\n\t}\n\n#ifdef WITH_BROKER\n\tbool payload_changed = false;\n\tbool topic_changed = false;\n\tbool properties_changed = false;\n\n\t{\n\t\tstruct mosquitto_base_msg tmp_msg;\n\t\ttmp_msg.topic = (char *)topic;\n\t\ttmp_msg.payloadlen = payloadlen;\n\t\ttmp_msg.payload = (void *)payload;\n\t\ttmp_msg.qos = qos;\n\t\ttmp_msg.retain = retain;\n\t\ttmp_msg.properties = (mosquitto_property *)store_props;\n\n\t\trc = plugin__handle_message_out(mosq, &tmp_msg);\n\n\t\tif(tmp_msg.payload != payload){\n\t\t\tpayload_changed = true;\n\t\t}\n\t\tif(tmp_msg.topic != topic){\n\t\t\ttopic_changed = true;\n\t\t}\n\t\tif(tmp_msg.properties != store_props){\n\t\t\tproperties_changed = true;\n\t\t}\n\n\t\ttopic = tmp_msg.topic;\n\t\tpayloadlen = tmp_msg.payloadlen;\n\t\tpayload = tmp_msg.payload;\n\t\tqos = tmp_msg.qos;\n\t\tretain = tmp_msg.retain;\n\t\tstore_props = tmp_msg.properties;\n\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tif(rc == MOSQ_ERR_ACL_DENIED){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG,\n\t\t\t\t\t\t\"Denied PUBLISH to %s (q%d, r%d, '%s', ... (%ld bytes))\",\n\t\t\t\t\t\tmosq->id, qos, retain, topic, (long)payloadlen);\n\n\t\t\t}else if(rc == MOSQ_ERR_QUOTA_EXCEEDED){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG,\n\t\t\t\t\t\t\"Rejected PUBLISH to %s, quota exceeded.\", mosq->id);\n\t\t\t}\n\n\t\t\tif(payload_changed){\n\t\t\t\tmosquitto_free((void *)payload);\n\t\t\t}\n\t\t\tif(topic_changed){\n\t\t\t\tmosquitto_free((void *)topic);\n\t\t\t}\n\t\t\tif(properties_changed){\n\t\t\t\tmosquitto_property_free_all((mosquitto_property **)&store_props);\n\t\t\t}\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n#endif\n\n\tif(!mosq->retain_available){\n\t\tretain = false;\n\t}\n\n#ifdef WITH_BROKER\n\tif(mosq->listener && mosq->listener->mount_point){\n\t\tlen = strlen(mosq->listener->mount_point);\n\t\tif(len < strlen(topic)){\n\t\t\ttopic += len;\n\t\t}else{\n\t\t\t/* Invalid topic string. Should never happen, but silently swallow the message anyway. */\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n#ifdef WITH_BRIDGE\n\tif(mosq->bridge && mosq->bridge->topics && mosq->bridge->topic_remapping){\n\t\tLL_FOREACH(mosq->bridge->topics, cur_topic){\n\t\t\tif((cur_topic->direction == bd_both || cur_topic->direction == bd_out)\n\t\t\t\t\t&& (cur_topic->remote_prefix || cur_topic->local_prefix)){\n\t\t\t\t/* Topic mapping required on this topic if the message matches */\n\n\t\t\t\trc = mosquitto_topic_matches_sub(cur_topic->local_topic, topic, &match);\n\t\t\t\tif(rc){\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t\tif(match){\n\t\t\t\t\tmapped_topic = mosquitto_strdup(topic);\n\t\t\t\t\tif(!mapped_topic){\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tif(cur_topic->local_prefix){\n\t\t\t\t\t\t/* This prefix needs removing. */\n\t\t\t\t\t\tif(!strncmp(cur_topic->local_prefix, mapped_topic, strlen(cur_topic->local_prefix))){\n\t\t\t\t\t\t\ttopic_temp = mosquitto_strdup(mapped_topic+strlen(cur_topic->local_prefix));\n\t\t\t\t\t\t\tmosquitto_FREE(mapped_topic);\n\t\t\t\t\t\t\tif(!topic_temp){\n\t\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmapped_topic = topic_temp;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif(cur_topic->remote_prefix){\n\t\t\t\t\t\t/* This prefix needs adding. */\n\t\t\t\t\t\tlen = strlen(mapped_topic) + strlen(cur_topic->remote_prefix)+1;\n\t\t\t\t\t\ttopic_temp = mosquitto_malloc(len+1);\n\t\t\t\t\t\tif(!topic_temp){\n\t\t\t\t\t\t\tmosquitto_FREE(mapped_topic);\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsnprintf(topic_temp, len, \"%s%s\", cur_topic->remote_prefix, mapped_topic);\n\t\t\t\t\t\ttopic_temp[len] = '\\0';\n\t\t\t\t\t\tmosquitto_FREE(mapped_topic);\n\t\t\t\t\t\tmapped_topic = topic_temp;\n\t\t\t\t\t}\n\t\t\t\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))\", SAFE_PRINT(mosq->id), dup, qos, retain, mid, mapped_topic, (long)payloadlen);\n\t\t\t\t\tmetrics__int_inc(mosq_counter_pub_bytes_sent, payloadlen);\n\t\t\t\t\trc =  send__real_publish(mosq, mid, mapped_topic, payloadlen, payload, qos, retain, dup, subscription_identifier, store_props, expiry_interval);\n\t\t\t\t\tmosquitto_FREE(mapped_topic);\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n#endif\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))\", SAFE_PRINT(mosq->id), dup, qos, retain, mid, topic, (long)payloadlen);\n\tmetrics__int_inc(mosq_counter_pub_bytes_sent, payloadlen);\n#else\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))\", SAFE_PRINT(mosq->id), dup, qos, retain, mid, topic, (long)payloadlen);\n#endif\n\n#ifdef WITH_BROKER\n\trc = send__real_publish(mosq, mid, topic, payloadlen, payload, qos, retain, dup, subscription_identifier, store_props, expiry_interval);\n\tif(payload_changed){\n\t\tmosquitto_free((void *)payload);\n\t}\n\tif(topic_changed){\n\t\tmosquitto_free((void *)topic);\n\t}\n\tif(properties_changed){\n\t\tmosquitto_property_free_all((mosquitto_property **)&store_props);\n\t}\n\treturn rc;\n#else\n\treturn send__real_publish(mosq, mid, topic, payloadlen, payload, qos, retain, dup, subscription_identifier, store_props, expiry_interval);\n#endif\n}\n\n\nint send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, uint32_t subscription_identifier, const mosquitto_property *store_props, uint32_t expiry_interval)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tunsigned int packetlen;\n\tunsigned int proplen = 0, varbytes;\n\tint rc;\n\tmosquitto_property expiry_prop;\n#ifdef WITH_BROKER\n\tmosquitto_property topic_alias_prop;\n\tuint16_t topic_alias = 0;\n\tmosquitto_property subscription_id_prop;\n#endif\n\n#ifndef WITH_BROKER\n\tUNUSED(subscription_identifier);\n#endif\n\n\tassert(mosq);\n\n#ifdef WITH_BROKER\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tif(alias__find_by_topic(mosq, ALIAS_DIR_L2R, topic, &topic_alias) == MOSQ_ERR_SUCCESS){\n\t\t\t/* If we have an existing alias, no need to send the topic */\n\t\t\ttopic = NULL;\n\t\t}else{\n\t\t\t/* Try to add a new alias - if this succeeds, topic_alias will be\n\t\t\t * set to the new alias but we still need to send the topic. If it\n\t\t\t * fails, topic_alias will be set to 0. */\n\t\t\talias__add_l2r(mosq, topic, &topic_alias);\n\t\t}\n\t}\n#endif\n\n\tif(topic){\n\t\tpacketlen = 2+(unsigned int)strlen(topic) + payloadlen;\n\t}else{\n\t\tpacketlen = 2 + payloadlen;\n\t}\n\tif(qos > 0){\n\t\tpacketlen += 2;         /* For message id */\n\t}\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tproplen = 0;\n\t\tproplen += mosquitto_property_get_length_all(store_props);\n\t\tif(expiry_interval > 0 && expiry_interval != MSG_EXPIRY_INFINITE){\n\t\t\texpiry_prop.next = NULL;\n\t\t\texpiry_prop.value.i32 = expiry_interval;\n\t\t\texpiry_prop.identifier = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL;\n\t\t\texpiry_prop.property_type = MQTT_PROP_TYPE_INT32;\n\t\t\texpiry_prop.client_generated = false;\n\n\t\t\tproplen += mosquitto_property_get_length_all(&expiry_prop);\n\t\t}\n#ifdef WITH_BROKER\n\t\tif(topic_alias != 0){\n\t\t\ttopic_alias_prop.next = NULL;\n\t\t\ttopic_alias_prop.value.i16 = topic_alias;\n\t\t\ttopic_alias_prop.identifier = MQTT_PROP_TOPIC_ALIAS;\n\t\t\ttopic_alias_prop.property_type = MQTT_PROP_TYPE_INT16;\n\t\t\ttopic_alias_prop.client_generated = false;\n\n\t\t\tproplen += mosquitto_property_get_length_all(&topic_alias_prop);\n\t\t}\n\t\tif(subscription_identifier){\n\t\t\tsubscription_id_prop.next = NULL;\n\t\t\tsubscription_id_prop.value.varint = subscription_identifier;\n\t\t\tsubscription_id_prop.identifier = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\t\t\tsubscription_id_prop.property_type = MQTT_PROP_TYPE_VARINT;\n\t\t\tsubscription_id_prop.client_generated = false;\n\n\t\t\tproplen += mosquitto_property_get_length_all(&subscription_id_prop);\n\t\t}\n#endif\n\n\t\tvarbytes = mosquitto_varint_bytes(proplen);\n\t\tif(varbytes > 4){\n\t\t\t/* FIXME - Properties too big, don't publish any - should remove some first really */\n\t\t\tstore_props = NULL;\n\t\t\texpiry_interval = 0;\n\t\t}else{\n\t\t\tpacketlen += proplen + varbytes;\n\t\t}\n\t}\n\tif(packet__check_oversize(mosq, packetlen)){\n#ifdef WITH_BROKER\n\t\tlog__printf(mosq, MOSQ_LOG_NOTICE, \"Dropping too large outgoing PUBLISH for %s (%d bytes)\", SAFE_PRINT(mosq->id), packetlen);\n#else\n\t\tlog__printf(mosq, MOSQ_LOG_NOTICE, \"Dropping too large outgoing PUBLISH (%d bytes)\", packetlen);\n#endif\n\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t}\n\n\trc = packet__alloc(&packet, (uint8_t)(CMD_PUBLISH | (uint8_t)((dup&0x1)<<3) | (uint8_t)(qos<<1) | retain), packetlen);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tpacket->mid = mid;\n\t/* Variable header (topic string) */\n\tif(topic){\n\t\tpacket__write_string(packet, topic, (uint16_t)strlen(topic));\n\t}else{\n\t\tpacket__write_uint16(packet, 0);\n\t}\n\tif(qos > 0){\n\t\tpacket__write_uint16(packet, mid);\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tpacket__write_varint(packet, proplen);\n\t\tproperty__write_all(packet, store_props, false);\n\t\tif(expiry_interval > 0 && expiry_interval != MSG_EXPIRY_INFINITE){\n\t\t\tproperty__write_all(packet, &expiry_prop, false);\n\t\t}\n#ifdef WITH_BROKER\n\t\tif(topic_alias != 0){\n\t\t\tproperty__write_all(packet, &topic_alias_prop, false);\n\t\t}\n\t\tif(subscription_identifier != 0){\n\t\t\tproperty__write_all(packet, &subscription_id_prop, false);\n\t\t}\n#endif\n\t}\n\n#if defined(WITH_BROKER) && defined(WITH_SYS_TREE)\n\tmetrics__int_inc(mosq_counter_mqtt_publish_sent, 1);\n#endif\n\n\t/* Payload */\n\tif(payloadlen && payload){\n\t\tpacket__write_bytes(packet, payload, payloadlen);\n\t}\n\n\treturn packet__queue(mosq, packet);\n}\n"
  },
  {
    "path": "lib/send_subscribe.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#  include \"sys_tree.h\"\n#endif\n\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"logging_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos, const mosquitto_property *properties)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tuint32_t packetlen;\n\tuint16_t local_mid;\n\tint rc;\n\tint i;\n\tsize_t tlen;\n\n\tassert(mosq);\n\tassert(topic);\n\n\tpacketlen = 2;\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tpacketlen += mosquitto_property_get_remaining_length(properties);\n\t}\n\tfor(i=0; i<topic_count; i++){\n\t\ttlen = strlen(topic[i]);\n\t\tif(tlen > UINT16_MAX){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tpacketlen += 2U+(uint16_t)tlen + 1U;\n\t}\n\n\trc = packet__alloc(&packet, CMD_SUBSCRIBE | 2, packetlen);\n\tif(rc){\n\t\tmosquitto_FREE(packet);\n\t\treturn rc;\n\t}\n\n\t/* Variable header */\n\tlocal_mid = mosquitto__mid_generate(mosq);\n\tif(mid){\n\t\t*mid = (int)local_mid;\n\t}\n\tpacket__write_uint16(packet, local_mid);\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tproperty__write_all(packet, properties, true);\n\t}\n\n\t/* Payload */\n\tfor(i=0; i<topic_count; i++){\n\t\tpacket__write_string(packet, topic[i], (uint16_t)strlen(topic[i]));\n\t\tpacket__write_byte(packet, (uint8_t)topic_qos);\n\t}\n\n#ifdef WITH_BROKER\n# ifdef WITH_BRIDGE\n\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Bridge %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)\", SAFE_PRINT(mosq->id), local_mid, topic[0], topic_qos&0x03, topic_qos&0xFC);\n# endif\n#else\n\tfor(i=0; i<topic_count; i++){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)\", SAFE_PRINT(mosq->id), local_mid, topic[i], topic_qos&0x03, topic_qos&0xFC);\n\t}\n#endif\n\n#ifdef WITH_BROKER\n\tmetrics__int_inc(mosq_counter_mqtt_subscribe_sent, 1);\n#endif\n\treturn packet__queue(mosq, packet);\n}\n"
  },
  {
    "path": "lib/send_unsubscribe.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#  include \"sys_tree.h\"\n#endif\n\n#include \"mosquitto.h\"\n#include \"logging_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tuint32_t packetlen;\n\tuint16_t local_mid;\n\tint rc;\n\tint i;\n\tsize_t tlen;\n\n\tassert(mosq);\n\tassert(topic);\n\n\tpacketlen = 2;\n\tfor(i=0; i<topic_count; i++){\n\t\ttlen = strlen(topic[i]);\n\t\tif(tlen > UINT16_MAX){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tpacketlen += 2U+(uint16_t)tlen;\n\t}\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tpacketlen += mosquitto_property_get_remaining_length(properties);\n\t}\n\n\trc = packet__alloc(&packet, CMD_UNSUBSCRIBE | 2, packetlen);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\t/* Variable header */\n\tlocal_mid = mosquitto__mid_generate(mosq);\n\tif(mid){\n\t\t*mid = (int)local_mid;\n\t}\n\tpacket__write_uint16(packet, local_mid);\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\t/* We don't use User Property yet. */\n\t\tproperty__write_all(packet, properties, true);\n\t}\n\n\t/* Payload */\n\tfor(i=0; i<topic_count; i++){\n\t\tpacket__write_string(packet, topic[i], (uint16_t)strlen(topic[i]));\n\t}\n\n#ifdef WITH_BROKER\n# ifdef WITH_BRIDGE\n\tfor(i=0; i<topic_count; i++){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Bridge %s sending UNSUBSCRIBE (Mid: %d, Topic: %s)\", SAFE_PRINT(mosq->id), local_mid, topic[i]);\n\t}\n# endif\n\tmetrics__int_inc(mosq_counter_mqtt_unsubscribe_sent, 1);\n#else\n\tfor(i=0; i<topic_count; i++){\n\t\tlog__printf(mosq, MOSQ_LOG_DEBUG, \"Client %s sending UNSUBSCRIBE (Mid: %d, Topic: %s)\", SAFE_PRINT(mosq->id), local_mid, topic[i]);\n\t}\n#endif\n\treturn packet__queue(mosq, packet);\n}\n\n"
  },
  {
    "path": "lib/socks_mosq.c",
    "content": "/*\nCopyright (c) 2014-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <string.h>\n#include <limits.h>\n#ifdef WIN32\n#  include <ws2tcpip.h>\n#elif defined(__QNX__)\n#  include <sys/socket.h>\n#  include <arpa/inet.h>\n#  include <netinet/in.h>\n#else\n#  include <arpa/inet.h>\n#endif\n#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(_AIX)\n#  include <sys/socket.h>\n#  include <netinet/in.h>\n#endif\n\n#include \"mosquitto_internal.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"send_mosq.h\"\n#include \"socks_mosq.h\"\n#include \"util_mosq.h\"\n\n#define SOCKS_AUTH_NONE 0x00U\n#define SOCKS_AUTH_GSS 0x01U\n#define SOCKS_AUTH_USERPASS 0x02U\n#define SOCKS_AUTH_NO_ACCEPTABLE 0xFFU\n\n#define SOCKS_ATYPE_IP_V4 1U /* four bytes */\n#define SOCKS_ATYPE_DOMAINNAME 3U /* one byte length, followed by fqdn no null, 256 max chars */\n#define SOCKS_ATYPE_IP_V6 4U /* 16 bytes */\n\n#define SOCKS_REPLY_SUCCEEDED 0x00U\n#define SOCKS_REPLY_GENERAL_FAILURE 0x01U\n#define SOCKS_REPLY_CONNECTION_NOT_ALLOWED 0x02U\n#define SOCKS_REPLY_NETWORK_UNREACHABLE 0x03U\n#define SOCKS_REPLY_HOST_UNREACHABLE 0x04U\n#define SOCKS_REPLY_CONNECTION_REFUSED 0x05U\n#define SOCKS_REPLY_TTL_EXPIRED 0x06U\n#define SOCKS_REPLY_COMMAND_NOT_SUPPORTED 0x07U\n#define SOCKS_REPLY_ADDRESS_TYPE_NOT_SUPPORTED 0x08U\n\n\nstatic inline int socks5__network_error(struct mosquitto *mosq)\n{\n\tWINDOWS_SET_ERRNO_RW();\n\tif(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tpacket__cleanup(&mosq->in_packet);\n\t\tswitch(errno){\n\t\t\tcase 0:\n\t\t\t\treturn MOSQ_ERR_PROXY;\n\t\t\tcase COMPAT_ECONNRESET:\n\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\tdefault:\n\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t}\n\t}\n}\n\n\nstatic inline int socks5__connection_error(struct mosquitto *mosq)\n{\n\tuint8_t v = mosq->in_packet.payload[1];\n\tpacket__cleanup(&mosq->in_packet);\n\tswitch(v){\n\t\tcase SOCKS_REPLY_CONNECTION_NOT_ALLOWED:\n\t\t\treturn MOSQ_ERR_AUTH;\n\n\t\tcase SOCKS_REPLY_NETWORK_UNREACHABLE:\n\t\tcase SOCKS_REPLY_HOST_UNREACHABLE:\n\t\tcase SOCKS_REPLY_CONNECTION_REFUSED:\n\t\t\treturn MOSQ_ERR_NO_CONN;\n\n\t\tcase SOCKS_REPLY_GENERAL_FAILURE:\n\t\tcase SOCKS_REPLY_TTL_EXPIRED:\n\t\tcase SOCKS_REPLY_COMMAND_NOT_SUPPORTED:\n\t\tcase SOCKS_REPLY_ADDRESS_TYPE_NOT_SUPPORTED:\n\t\t\treturn MOSQ_ERR_PROXY;\n\n\t\tdefault:\n\t\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn MOSQ_ERR_PROXY;\n}\n\n\nint mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, const char *username, const char *password)\n{\n#ifdef WITH_SOCKS\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!host || strlen(host) > 256){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(port < 1 || port > UINT16_MAX){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmosquitto_FREE(mosq->socks5_host);\n\tmosq->socks5_host = mosquitto_strdup(host);\n\tif(!mosq->socks5_host){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tmosq->socks5_port = (uint16_t)port;\n\n\tmosquitto_FREE(mosq->socks5_username);\n\tmosquitto_FREE(mosq->socks5_password);\n\n\tif(username){\n\t\tif(strlen(username) > UINT8_MAX){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tmosq->socks5_username = mosquitto_strdup(username);\n\t\tif(!mosq->socks5_username){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tif(password){\n\t\t\tif(strlen(password) > UINT8_MAX){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tmosq->socks5_password = mosquitto_strdup(password);\n\t\t\tif(!mosq->socks5_password){\n\t\t\t\tmosquitto_FREE(mosq->socks5_username);\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\tUNUSED(mosq);\n\tUNUSED(host);\n\tUNUSED(port);\n\tUNUSED(username);\n\tUNUSED(password);\n\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n#ifdef WITH_SOCKS\n\n\nstatic void socks5__packet_alloc(struct mosquitto__packet **packet, uint32_t packet_length)\n{\n\t*packet = mosquitto_calloc(1, sizeof(struct mosquitto__packet) + packet_length + WS_PACKET_OFFSET);\n\tif(!(*packet)){\n\t\treturn;\n\t}\n\t(*packet)->pos = WS_PACKET_OFFSET;\n\t(*packet)->packet_length = packet_length + WS_PACKET_OFFSET;\n\t(*packet)->to_process = packet_length;\n}\n\n\nint socks5__send(struct mosquitto *mosq)\n{\n\tstruct mosquitto__packet *packet;\n\tsize_t slen;\n\tuint8_t ulen, plen;\n\tuint32_t packet_length;\n\n\tstruct in_addr addr_ipv4;\n\tstruct in6_addr addr_ipv6;\n\tint ipv4_pton_result;\n\tint ipv6_pton_result;\n\tenum mosquitto_client_state state;\n\n\tstate = mosquitto__get_state(mosq);\n\n\tif(state == mosq_cs_socks5_new){\n\t\tif(mosq->socks5_username){\n\t\t\tpacket_length = 4;\n\t\t}else{\n\t\t\tpacket_length = 3;\n\t\t}\n\n\t\tsocks5__packet_alloc(&packet, packet_length);\n\t\tif(!packet){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tpacket->payload[0 + WS_PACKET_OFFSET] = 0x05;\n\t\tif(mosq->socks5_username){\n\t\t\tpacket->payload[1 + WS_PACKET_OFFSET] = 2;\n\t\t\tpacket->payload[2 + WS_PACKET_OFFSET] = SOCKS_AUTH_NONE;\n\t\t\tpacket->payload[3 + WS_PACKET_OFFSET] = SOCKS_AUTH_USERPASS;\n\t\t}else{\n\t\t\tpacket->payload[1 + WS_PACKET_OFFSET] = 1;\n\t\t\tpacket->payload[2 + WS_PACKET_OFFSET] = SOCKS_AUTH_NONE;\n\t\t}\n\n\t\tmosquitto__set_state(mosq, mosq_cs_socks5_start);\n\n\t\tmosq->in_packet.pos = 0;\n\t\tmosq->in_packet.packet_length = 2;\n\t\tmosq->in_packet.to_process = 2;\n\t\tmosq->in_packet.payload = mosquitto_malloc(sizeof(uint8_t)*2);\n\t\tif(!mosq->in_packet.payload){\n\t\t\tmosquitto_FREE(packet);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\treturn packet__queue(mosq, packet);\n\t}else if(state == mosq_cs_socks5_auth_ok){\n\t\tipv4_pton_result = inet_pton(AF_INET, mosq->host, &addr_ipv4);\n\t\tipv6_pton_result = inet_pton(AF_INET6, mosq->host, &addr_ipv6);\n\n\t\tif(ipv4_pton_result == 1){\n\t\t\tpacket_length = 10;\n\n\t\t\tsocks5__packet_alloc(&packet, packet_length);\n\t\t\tif(!packet){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\n\t\t\tpacket->payload[3 + WS_PACKET_OFFSET] = SOCKS_ATYPE_IP_V4;\n\t\t\tmemcpy(&(packet->payload[4 + WS_PACKET_OFFSET]), (const void *)&addr_ipv4, 4);\n\t\t\tpacket->payload[4+4 + WS_PACKET_OFFSET] = MOSQ_MSB(mosq->port);\n\t\t\tpacket->payload[4+4+1 + WS_PACKET_OFFSET] = MOSQ_LSB(mosq->port);\n\t\t}else if(ipv6_pton_result == 1){\n\t\t\tpacket_length = 22;\n\n\t\t\tsocks5__packet_alloc(&packet, packet_length);\n\t\t\tif(!packet){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\n\t\t\tpacket->payload[3 + WS_PACKET_OFFSET] = SOCKS_ATYPE_IP_V6;\n\t\t\tmemcpy(&(packet->payload[4 + WS_PACKET_OFFSET]), (const void *)&addr_ipv6, 16);\n\t\t\tpacket->payload[4+16 + WS_PACKET_OFFSET] = MOSQ_MSB(mosq->port);\n\t\t\tpacket->payload[4+16+1 + WS_PACKET_OFFSET] = MOSQ_LSB(mosq->port);\n\t\t}else{\n\t\t\tslen = strlen(mosq->host);\n\t\t\tif(slen > UCHAR_MAX){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tpacket_length = 7U + (uint32_t)slen;\n\n\t\t\tsocks5__packet_alloc(&packet, packet_length);\n\t\t\tif(!packet){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\n\t\t\tpacket->payload[3 + WS_PACKET_OFFSET] = SOCKS_ATYPE_DOMAINNAME;\n\t\t\tpacket->payload[4 + WS_PACKET_OFFSET] = (uint8_t)slen;\n\t\t\tmemcpy(&(packet->payload[5 + WS_PACKET_OFFSET]), mosq->host, slen);\n\t\t\tpacket->payload[5+slen + WS_PACKET_OFFSET] = MOSQ_MSB(mosq->port);\n\t\t\tpacket->payload[6+slen + WS_PACKET_OFFSET] = MOSQ_LSB(mosq->port);\n\t\t}\n\t\tpacket->payload[0 + WS_PACKET_OFFSET] = 0x05;\n\t\tpacket->payload[1 + WS_PACKET_OFFSET] = 0x01;\n\t\tpacket->payload[2 + WS_PACKET_OFFSET] = 0x00;\n\n\t\tmosquitto__set_state(mosq, mosq_cs_socks5_request);\n\n\t\tmosq->in_packet.pos = 0;\n\t\tmosq->in_packet.packet_length = 5;\n\t\tmosq->in_packet.to_process = 5;\n\t\tmosq->in_packet.payload = mosquitto_malloc(sizeof(uint8_t)*5);\n\t\tif(!mosq->in_packet.payload){\n\t\t\tmosquitto_FREE(packet);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\treturn packet__queue(mosq, packet);\n\t}else if(state == mosq_cs_socks5_send_userpass){\n\t\tulen = (uint8_t)strlen(mosq->socks5_username);\n\t\tplen = (uint8_t)strlen(mosq->socks5_password);\n\t\tpacket_length = 3U + ulen + plen;\n\n\t\tsocks5__packet_alloc(&packet, packet_length);\n\t\tif(!packet){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tpacket->payload[0 + WS_PACKET_OFFSET] = 0x01;\n\t\tpacket->payload[1 + WS_PACKET_OFFSET] = ulen;\n\t\tmemcpy(&(packet->payload[2 + WS_PACKET_OFFSET]), mosq->socks5_username, ulen);\n\t\tpacket->payload[2+ulen + WS_PACKET_OFFSET] = plen;\n\t\tmemcpy(&(packet->payload[3+ulen + WS_PACKET_OFFSET]), mosq->socks5_password, plen);\n\n\t\tmosquitto__set_state(mosq, mosq_cs_socks5_userpass_reply);\n\n\t\tmosq->in_packet.pos = 0;\n\t\tmosq->in_packet.packet_length = 2;\n\t\tmosq->in_packet.to_process = 2;\n\t\tmosq->in_packet.payload = mosquitto_malloc(sizeof(uint8_t)*2);\n\t\tif(!mosq->in_packet.payload){\n\t\t\tmosquitto_FREE(packet);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\treturn packet__queue(mosq, packet);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint socks5__read(struct mosquitto *mosq)\n{\n\tssize_t len;\n\tuint8_t *payload;\n\tenum mosquitto_client_state state;\n\n\tstate = mosquitto__get_state(mosq);\n\tif(state == mosq_cs_socks5_start){\n\t\twhile(mosq->in_packet.to_process > 0){\n\t\t\tlen = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process);\n\t\t\tif(len > 0){\n\t\t\t\tmosq->in_packet.pos += (uint32_t)len;\n\t\t\t\tmosq->in_packet.to_process -= (uint32_t)len;\n\t\t\t}else{\n\t\t\t\treturn socks5__network_error(mosq);\n\t\t\t}\n\t\t}\n\t\tif(mosq->in_packet.payload[0] != 5){\n\t\t\tpacket__cleanup(&mosq->in_packet);\n\t\t\treturn MOSQ_ERR_PROXY;\n\t\t}\n\t\tswitch(mosq->in_packet.payload[1]){\n\t\t\tcase SOCKS_AUTH_NONE:\n\t\t\t\tpacket__cleanup(&mosq->in_packet);\n\t\t\t\tmosquitto__set_state(mosq, mosq_cs_socks5_auth_ok);\n\t\t\t\treturn socks5__send(mosq);\n\t\t\tcase SOCKS_AUTH_USERPASS:\n\t\t\t\tpacket__cleanup(&mosq->in_packet);\n\t\t\t\tmosquitto__set_state(mosq, mosq_cs_socks5_send_userpass);\n\t\t\t\treturn socks5__send(mosq);\n\t\t\tdefault:\n\t\t\t\tpacket__cleanup(&mosq->in_packet);\n\t\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}else if(state == mosq_cs_socks5_userpass_reply){\n\t\twhile(mosq->in_packet.to_process > 0){\n\t\t\tlen = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process);\n\t\t\tif(len > 0){\n\t\t\t\tmosq->in_packet.pos += (uint32_t)len;\n\t\t\t\tmosq->in_packet.to_process -= (uint32_t)len;\n\t\t\t}else{\n\t\t\t\treturn socks5__network_error(mosq);\n\t\t\t}\n\t\t}\n\t\tif(mosq->in_packet.payload[0] != 1){\n\t\t\tpacket__cleanup(&mosq->in_packet);\n\t\t\treturn MOSQ_ERR_PROXY;\n\t\t}\n\t\tif(mosq->in_packet.payload[1] == 0){\n\t\t\tpacket__cleanup(&mosq->in_packet);\n\t\t\tmosquitto__set_state(mosq, mosq_cs_socks5_auth_ok);\n\t\t\treturn socks5__send(mosq);\n\t\t}else{\n\t\t\treturn socks5__connection_error(mosq);\n\t\t}\n\t}else if(state == mosq_cs_socks5_request){\n\t\twhile(mosq->in_packet.to_process > 0){\n\t\t\tlen = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process);\n\t\t\tif(len > 0){\n\t\t\t\tmosq->in_packet.pos += (uint32_t)len;\n\t\t\t\tmosq->in_packet.to_process -= (uint32_t)len;\n\t\t\t}else{\n\t\t\t\treturn socks5__network_error(mosq);\n\t\t\t}\n\t\t}\n\n\t\tif(mosq->in_packet.packet_length == 5){\n\t\t\t/* First part of the packet has been received, we now know what else to expect. */\n\t\t\tif(mosq->in_packet.payload[3] == SOCKS_ATYPE_IP_V4){\n\t\t\t\tmosq->in_packet.to_process += 4+2-1; /* 4 bytes IPv4, 2 bytes port, -1 byte because we've already read the first byte */\n\t\t\t\tmosq->in_packet.packet_length += 4+2-1;\n\t\t\t}else if(mosq->in_packet.payload[3] == SOCKS_ATYPE_IP_V6){\n\t\t\t\tmosq->in_packet.to_process += 16+2-1; /* 16 bytes IPv6, 2 bytes port, -1 byte because we've already read the first byte */\n\t\t\t\tmosq->in_packet.packet_length += 16+2-1;\n\t\t\t}else if(mosq->in_packet.payload[3] == SOCKS_ATYPE_DOMAINNAME){\n\t\t\t\tif(mosq->in_packet.payload[4] > 0){\n\t\t\t\t\tmosq->in_packet.to_process += mosq->in_packet.payload[4];\n\t\t\t\t\tmosq->in_packet.packet_length += mosq->in_packet.payload[4];\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tpacket__cleanup(&mosq->in_packet);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\t/* We know the value of mosq->in_packet.packet_lenth is within a\n\t\t\t * bound. At the start of this if statement, it was 5. The next set\n\t\t\t * of if statements add either (4+2-1)=5 to its value, or\n\t\t\t * (16+2-1)=17 to its value, or the contents of a uint8_t, which\n\t\t\t * can be a maximum of 255. So the range is 10 to 260 bytes.\n\t\t\t * Coverity most likely doesn't realise this because the +=\n\t\t\t * promotes to the size of packet_length. */\n\t\t\t/* coverity[tainted_data] */\n\t\t\tpayload = mosquitto_realloc(mosq->in_packet.payload, mosq->in_packet.packet_length);\n\t\t\tif(payload){\n\t\t\t\tmosq->in_packet.payload = payload;\n\t\t\t}else{\n\t\t\t\tpacket__cleanup(&mosq->in_packet);\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\n\t\t/* Entire packet is now read. */\n\t\tif(mosq->in_packet.payload[0] != 5){\n\t\t\tpacket__cleanup(&mosq->in_packet);\n\t\t\treturn MOSQ_ERR_PROXY;\n\t\t}\n\t\tif(mosq->in_packet.payload[1] == 0){\n\t\t\t/* Auth passed */\n\t\t\tpacket__cleanup(&mosq->in_packet);\n\t\t\tmosquitto__set_state(mosq, mosq_cs_new);\n\t\t\tif(mosq->socks5_host){\n\t\t\t\tint rc = net__socket_connect_step3(mosq, mosq->host);\n\t\t\t\tif(rc){\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn send__connect(mosq, mosq->keepalive, mosq->clean_start, NULL);\n\t\t}else{\n\t\t\tmosquitto__set_state(mosq, mosq_cs_socks5_new);\n\t\t\treturn socks5__connection_error(mosq);\n\t\t}\n\t}else{\n\t\treturn packet__read(mosq);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n"
  },
  {
    "path": "lib/socks_mosq.h",
    "content": "/*\nCopyright (c) 2014-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef SOCKS_MOSQ_H\n#define SOCKS_MOSQ_H\n\nint socks5__send(struct mosquitto *mosq);\nint socks5__read(struct mosquitto *mosq);\n\n#endif\n"
  },
  {
    "path": "lib/srv_mosq.c",
    "content": "/*\nCopyright (c) 2013-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_SRV\n#  include <ares.h>\n\n#  include <arpa/nameser.h>\n#  include <stdio.h>\n#  include <string.h>\n#endif\n\n#include \"callbacks.h\"\n#include \"logging_mosq.h\"\n#include \"mosquitto_internal.h\"\n#include \"mosquitto.h\"\n#include \"util_mosq.h\"\n\n#ifdef WITH_SRV\n\n\nstatic void srv_callback(void *arg, int status, int timeouts, unsigned char *abuf, int alen)\n{\n\tstruct mosquitto *mosq = arg;\n\tstruct ares_srv_reply *reply = NULL;\n\n\tUNUSED(timeouts);\n\n\tif(status == ARES_SUCCESS){\n\t\tstatus = ares_parse_srv_reply(abuf, alen, &reply);\n\t\tif(status == ARES_SUCCESS){\n\t\t\t// FIXME - choose which answer to use based on rfc2782 page 3. */\n\t\t\tmosquitto_connect(mosq, reply->host, reply->port, mosq->keepalive);\n\t\t}\n\t}else{\n\t\tlog__printf(mosq, MOSQ_LOG_ERR, \"Error: SRV lookup failed (%d).\", status);\n\t\t/* FIXME - calling on_disconnect here isn't correct. */\n\t\tcallback__on_disconnect(mosq, MOSQ_ERR_LOOKUP, NULL);\n\t}\n}\n#endif\n\n\nint mosquitto_connect_srv(struct mosquitto *mosq, const char *host, int keepalive, const char *bind_address)\n{\n#ifdef WITH_SRV\n\tchar *h;\n\tint rc;\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tUNUSED(bind_address);\n\n\tif(keepalive < 0 || keepalive > UINT16_MAX){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trc = ares_init(&mosq->achan);\n\tif(rc != ARES_SUCCESS){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!host){\n\t\t// get local domain\n\t}else{\n#ifdef WITH_TLS\n\t\tif(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk){\n\t\t\th = mosquitto_malloc(strlen(host) + strlen(\"_secure-mqtt._tcp.\") + 1);\n\t\t\tif(!h){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tsprintf(h, \"_secure-mqtt._tcp.%s\", host);\n\t\t}else\n#endif\n\t\t{\n\t\t\th = mosquitto_malloc(strlen(host) + strlen(\"_mqtt._tcp.\") + 1);\n\t\t\tif(!h){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tsprintf(h, \"_mqtt._tcp.%s\", host);\n\t\t}\n\t\tares_search(mosq->achan, h, ns_c_in, ns_t_srv, srv_callback, mosq);\n\t\tmosquitto_FREE(h);\n\t}\n\n\tmosquitto__set_state(mosq, mosq_cs_connect_srv);\n\n\tmosq->keepalive = (uint16_t)keepalive;\n\n\treturn MOSQ_ERR_SUCCESS;\n\n#else\n\tUNUSED(mosq);\n\tUNUSED(host);\n\tUNUSED(keepalive);\n\tUNUSED(bind_address);\n\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\n"
  },
  {
    "path": "lib/thread_mosq.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#if defined(__linux__)\n#  define _GNU_SOURCE  /* Exposes pthread_setname_np on Linux */\n#endif\n\n#include \"config.h\"\n\n#if defined(WITH_THREADING)\n#if defined(__linux__)\n#  include <pthread.h>\n#elif defined(__NetBSD__)\n#  include <pthread.h>\n#elif defined(__FreeBSD__) || defined(__OpenBSD__)\n#  include <pthread_np.h>\n#endif\n#endif\n\n#ifndef WIN32\n#include <time.h>\n#endif\n\n#include \"mosquitto_internal.h\"\n#include \"net_mosq.h\"\n#include \"util_mosq.h\"\nvoid *mosquitto__thread_main(void *obj);\n\n\nint mosquitto_loop_start(struct mosquitto *mosq)\n{\n#if defined(WITH_THREADING)\n\tif(!mosq || mosq->threaded != mosq_ts_none){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(!COMPAT_pthread_create(&mosq->thread_id, NULL, mosquitto__thread_main, mosq)){\n#if defined(__linux__)\n\t\tpthread_setname_np(mosq->thread_id, \"mosquitto loop\");\n#elif defined(__NetBSD__)\n\t\tpthread_setname_np(mosq->thread_id, \"%s\", \"mosquitto loop\");\n#elif defined(__FreeBSD__) || defined(__OpenBSD__)\n\t\tpthread_set_name_np(mosq->thread_id, \"mosquitto loop\");\n#endif\n\t\tmosq->threaded = mosq_ts_self;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n#else\n\tUNUSED(mosq);\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nint mosquitto_loop_stop(struct mosquitto *mosq, bool force)\n{\n#if defined(WITH_THREADING)\n#  ifndef WITH_BROKER\n\tchar sockpair_data = 0;\n#  endif\n\n\tif(!mosq || mosq->threaded != mosq_ts_self){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmosq->run = false;\n\n\t/* Write a single byte to sockpairW (connected to sockpairR) to break out\n\t * of select() if in threaded mode. */\n\tif(mosq->sockpairW != INVALID_SOCKET){\n#ifndef WIN32\n\t\tif(write(mosq->sockpairW, &sockpair_data, 1)){\n\t\t}\n#else\n\t\tsend(mosq->sockpairW, &sockpair_data, 1, 0);\n#endif\n\t}\n\n#ifdef HAVE_PTHREAD_CANCEL\n\tif(force){\n\t\tCOMPAT_pthread_cancel(mosq->thread_id);\n\t}\n#endif\n\tCOMPAT_pthread_join(mosq->thread_id, NULL);\n\tmosq->thread_id = pthread_self();\n\tmosq->threaded = mosq_ts_none;\n\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\tUNUSED(mosq);\n\tUNUSED(force);\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n#ifdef WITH_THREADING\n\n\nvoid *mosquitto__thread_main(void *obj)\n{\n\tstruct mosquitto *mosq = obj;\n#ifndef WIN32\n\tstruct timespec ts;\n\tts.tv_sec = 0;\n\tts.tv_nsec = 10000000;\n#endif\n\n\tif(!mosq){\n\t\treturn NULL;\n\t}\n\n\tdo{\n\t\tif(mosquitto__get_state(mosq) == mosq_cs_new){\n#ifdef WIN32\n\t\t\tSleep(10);\n#else\n\t\t\tnanosleep(&ts, NULL);\n#endif\n\t\t}else{\n\t\t\tbreak;\n\t\t}\n\t}while(1);\n\n\tif(!mosq->keepalive){\n\t\t/* Sleep for a day if keepalive disabled. */\n\t\tmosquitto_loop_forever(mosq, 1000*86400, 1);\n\t}else{\n\t\t/* Sleep for our keepalive value. publish() etc. will wake us up. */\n\t\tmosquitto_loop_forever(mosq, mosq->keepalive*1000, 1);\n\t}\n\tif(mosq->threaded == mosq_ts_self){\n\t\tmosq->threaded = mosq_ts_none;\n\t}\n\n\treturn obj;\n}\n#endif\n\n\nint mosquitto_threaded_set(struct mosquitto *mosq, bool threaded)\n{\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(threaded){\n\t\tmosq->threaded = mosq_ts_external;\n\t}else{\n\t\tmosq->threaded = mosq_ts_none;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "lib/tls_mosq.c",
    "content": "/*\nCopyright (c) 2013-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_TLS\n\n#ifdef WIN32\n#  include <winsock2.h>\n#  include <ws2tcpip.h>\n#else\n#  include <arpa/inet.h>\n#  include <sys/socket.h>\n#  include <strings.h>\n#endif\n\n#include <string.h>\n#include <openssl/conf.h>\n#include <openssl/x509v3.h>\n#include <openssl/ssl.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#endif\n#include \"mosquitto_internal.h\"\n#include \"logging_mosq.h\"\n#include \"tls_mosq.h\"\n\n\nint mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx)\n{\n\tUNUSED(ctx);\n\n\treturn preverify_ok;\n}\n\n\nint tls__set_verify_hostname(struct mosquitto *mosq, const char *hostname)\n{\n\tunsigned char ipv6_addr[16];\n\tunsigned char ipv4_addr[4];\n\tint ipv6_ok;\n\tint ipv4_ok;\n\tint rc;\n\n\tif(mosq->tls_insecure == true\n\t\t\t|| (mosq->tls_cafile == NULL && mosq->tls_capath == NULL && mosq->tls_use_os_certs == false)){\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n#ifndef WITH_BROKER\n\tif(mosq->port == 0){\n\t\t/* No hostname verification for unix sockets */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n#endif\n#ifdef WIN32\n\tipv6_ok = InetPton(AF_INET6, hostname, &ipv6_addr);\n\tipv4_ok = InetPton(AF_INET, hostname, &ipv4_addr);\n#else\n\tipv6_ok = inet_pton(AF_INET6, hostname, &ipv6_addr);\n\tipv4_ok = inet_pton(AF_INET, hostname, &ipv4_addr);\n#endif\n\n\tX509_VERIFY_PARAM *param = SSL_get0_param(mosq->ssl);\n\tif(ipv4_ok || ipv6_ok){\n\t\trc = X509_VERIFY_PARAM_set1_ip_asc(param, hostname);\n\t}else{\n\t\trc = X509_VERIFY_PARAM_set1_host(param, hostname, 0);\n\t}\n\tif(rc == 1){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_TLS;\n\t}\n}\n#endif\n"
  },
  {
    "path": "lib/tls_mosq.h",
    "content": "/*\nCopyright (c) 2013-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef TLS_MOSQ_H\n#define TLS_MOSQ_H\n\n#ifdef WITH_TLS\n#  define SSL_DATA_PENDING(A) ((A)->ssl && SSL_pending((A)->ssl))\n#else\n#  define SSL_DATA_PENDING(A) 0\n#endif\n\n#ifdef WITH_TLS\n\n#include <openssl/ssl.h>\n#include <openssl/engine.h>\n#include \"mosquitto.h\"\n\nint mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx);\nint tls__set_verify_hostname(struct mosquitto *mosq, const char *hostname);\n\n#endif /* WITH_TLS */\n\n#endif\n"
  },
  {
    "path": "lib/util_mosq.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <ctype.h>\n#include <string.h>\n\n#ifdef WIN32\n#  include <winsock2.h>\n#  include <aclapi.h>\n#  include <io.h>\n#  include <lmcons.h>\n#else\n#  include <sys/stat.h>\n#endif\n\n#ifdef WITH_TLS\n#  include <openssl/bn.h>\n#endif\n\n#ifdef WITH_BROKER\n#include \"mosquitto_broker_internal.h\"\n#else\n#  include \"callbacks.h\"\n#endif\n\n#include \"mosquitto.h\"\n#include \"net_mosq.h\"\n#include \"send_mosq.h\"\n#include \"tls_mosq.h\"\n#include \"util_mosq.h\"\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n#include <libwebsockets.h>\n#endif\n\n\nint mosquitto__check_keepalive(struct mosquitto *mosq)\n{\n\ttime_t next_msg_out;\n\ttime_t last_msg_in;\n\ttime_t now;\n#ifndef WITH_BROKER\n\tint rc;\n#endif\n\tenum mosquitto_client_state state;\n\n\tassert(mosq);\n#ifdef WITH_BROKER\n\tnow = db.now_s;\n#else\n\tnow = mosquitto_time();\n#endif\n\n#if defined(WITH_BROKER) && defined(WITH_BRIDGE)\n\t/* Check if a lazy bridge should be timed out due to idle. */\n\tif(mosq->bridge && mosq->bridge->start_type == bst_lazy\n\t\t\t&& net__is_connected(mosq)\n\t\t\t&& now - mosq->next_msg_out - mosq->keepalive >= mosq->bridge->idle_timeout){\n\n\t\tlog__printf(mosq, MOSQ_LOG_NOTICE, \"Bridge connection %s has exceeded idle timeout, disconnecting.\", mosq->id);\n\t\tnet__socket_close(mosq);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n#endif\n\tCOMPAT_pthread_mutex_lock(&mosq->msgtime_mutex);\n\tnext_msg_out = mosq->next_msg_out;\n\tlast_msg_in = mosq->last_msg_in;\n\tCOMPAT_pthread_mutex_unlock(&mosq->msgtime_mutex);\n\tif(mosq->keepalive && net__is_connected(mosq) &&\n\t\t\t(now >= next_msg_out || now - last_msg_in >= mosq->keepalive)){\n\n\t\tstate = mosquitto__get_state(mosq);\n\t\tif(state == mosq_cs_active && mosq->ping_t == 0){\n\t\t\tsend__pingreq(mosq);\n\t\t\t/* Reset last msg times to give the server time to send a pingresp */\n\t\t\tCOMPAT_pthread_mutex_lock(&mosq->msgtime_mutex);\n\t\t\tmosq->last_msg_in = now;\n\t\t\tmosq->next_msg_out = now + mosq->keepalive;\n\t\t\tCOMPAT_pthread_mutex_unlock(&mosq->msgtime_mutex);\n\t\t}else{\n#ifdef WITH_BROKER\n#  ifdef WITH_BRIDGE\n\t\t\tif(mosq->bridge){\n\t\t\t\tcontext__send_will(mosq);\n\t\t\t}\n#  endif\n\t\t\tnet__socket_close(mosq);\n#else\n\t\t\tnet__socket_close(mosq);\n\t\t\tstate = mosquitto__get_state(mosq);\n\t\t\tif(state == mosq_cs_disconnecting){\n\t\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t\t}else{\n\t\t\t\trc = MOSQ_ERR_KEEPALIVE;\n\t\t\t}\n\t\t\tcallback__on_disconnect(mosq, rc, NULL);\n\n\t\t\treturn rc;\n#endif\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nuint16_t mosquitto__mid_generate(struct mosquitto *mosq)\n{\n\t/* FIXME - this would be better with atomic increment, but this is safer\n\t * for now for a bug fix release.\n\t *\n\t * If this is changed to use atomic increment, callers of this function\n\t * will have to be aware that they may receive a 0 result, which may not be\n\t * used as a mid.\n\t */\n\tuint16_t mid;\n\tassert(mosq);\n\n\tCOMPAT_pthread_mutex_lock(&mosq->mid_mutex);\n\tmosq->last_mid++;\n\tif(mosq->last_mid == 0){\n\t\tmosq->last_mid++;\n\t}\n\tmid = mosq->last_mid;\n\tCOMPAT_pthread_mutex_unlock(&mosq->mid_mutex);\n\n\treturn mid;\n}\n\n\n#ifdef WITH_TLS\n\n\nint mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin)\n{\n\tunsigned char *sha, tmp[SHA_DIGEST_LENGTH];\n\n\tif(mosquitto__hex2bin(hex, tmp, SHA_DIGEST_LENGTH) != SHA_DIGEST_LENGTH){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tsha = mosquitto_malloc(SHA_DIGEST_LENGTH);\n\tif(!sha){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tmemcpy(sha, tmp, SHA_DIGEST_LENGTH);\n\t*bin = sha;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len)\n{\n\tBIGNUM *bn = NULL;\n\tint len;\n\tint leading_zero = 0;\n\tsize_t i = 0;\n\n\t/* Count the number of leading zero */\n\tfor(i=0; i<strlen(hex); i=i+2){\n\t\tif(strncmp(hex + i, \"00\", 2) == 0){\n\t\t\tif(leading_zero >= bin_max_len){\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t/* output leading zero to bin */\n\t\t\tbin[leading_zero] = 0;\n\t\t\tleading_zero++;\n\t\t}else{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(BN_hex2bn(&bn, hex) == 0){\n\t\tif(bn){\n\t\t\tBN_free(bn);\n\t\t}\n\t\treturn 0;\n\t}\n\tif(BN_num_bytes(bn) + leading_zero > bin_max_len){\n\t\tBN_free(bn);\n\t\treturn 0;\n\t}\n\n\tlen = BN_bn2bin(bn, bin + leading_zero);\n\tBN_free(bn);\n\treturn len + leading_zero;\n}\n#endif\n\n\nvoid util__increment_receive_quota(struct mosquitto *mosq)\n{\n\tif(mosq->msgs_in.inflight_quota < mosq->msgs_in.inflight_maximum){\n\t\tmosq->msgs_in.inflight_quota++;\n\t}\n}\n\n\nvoid util__increment_send_quota(struct mosquitto *mosq)\n{\n\tif(mosq->msgs_out.inflight_quota < mosq->msgs_out.inflight_maximum){\n\t\tmosq->msgs_out.inflight_quota++;\n\t}\n}\n\n\nvoid util__decrement_receive_quota(struct mosquitto *mosq)\n{\n\tif(mosq->msgs_in.inflight_quota > 0){\n\t\tmosq->msgs_in.inflight_quota--;\n\t}\n}\n\n\nvoid util__decrement_send_quota(struct mosquitto *mosq)\n{\n\tif(mosq->msgs_out.inflight_quota > 0){\n\t\tmosq->msgs_out.inflight_quota--;\n\t}\n}\n\n\nint mosquitto__set_state(struct mosquitto *mosq, enum mosquitto_client_state state)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->state_mutex);\n#ifdef WITH_BROKER\n\tif(mosq->state != mosq_cs_disused)\n#endif\n\t{\n\t\tmosq->state = state;\n\t}\n\tCOMPAT_pthread_mutex_unlock(&mosq->state_mutex);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\nenum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq)\n{\n\tenum mosquitto_client_state state;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->state_mutex);\n\tstate = mosq->state;\n\tCOMPAT_pthread_mutex_unlock(&mosq->state_mutex);\n\n\treturn state;\n}\n\n#ifndef WITH_BROKER\n\n\nvoid mosquitto__set_request_disconnect(struct mosquitto *mosq, bool request_disconnect)\n{\n\tCOMPAT_pthread_mutex_lock(&mosq->state_mutex);\n\tmosq->request_disconnect = request_disconnect;\n\tCOMPAT_pthread_mutex_unlock(&mosq->state_mutex);\n}\n\n\nbool mosquitto__get_request_disconnect(struct mosquitto *mosq)\n{\n\tbool request_disconnect;\n\n\tCOMPAT_pthread_mutex_lock(&mosq->state_mutex);\n\trequest_disconnect = mosq->request_disconnect;\n\tCOMPAT_pthread_mutex_unlock(&mosq->state_mutex);\n\n\treturn request_disconnect;\n}\n#endif\n"
  },
  {
    "path": "lib/util_mosq.h",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef UTIL_MOSQ_H\n#define UTIL_MOSQ_H\n\n#include <stdio.h>\n\n#include \"tls_mosq.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#endif\n\nint mosquitto__check_keepalive(struct mosquitto *mosq);\nuint16_t mosquitto__mid_generate(struct mosquitto *mosq);\n\nint mosquitto__set_state(struct mosquitto *mosq, enum mosquitto_client_state state);\nenum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq);\n#ifndef WITH_BROKER\nvoid mosquitto__set_request_disconnect(struct mosquitto *mosq, bool request_disconnect);\nbool mosquitto__get_request_disconnect(struct mosquitto *mosq);\n#endif\n\n#ifdef WITH_TLS\nint mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin);\nint mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len);\n#endif\n\nvoid util__increment_receive_quota(struct mosquitto *mosq);\nvoid util__increment_send_quota(struct mosquitto *mosq);\nvoid util__decrement_receive_quota(struct mosquitto *mosq);\nvoid util__decrement_send_quota(struct mosquitto *mosq);\n\n\n#endif\n"
  },
  {
    "path": "lib/will_mosq.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#ifdef WITH_BROKER\n#  include \"mosquitto_broker_internal.h\"\n#endif\n\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n#include \"logging_mosq.h\"\n#include \"messages_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n#include \"will_mosq.h\"\n\n\nint will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\tmosquitto_property *p;\n\n\tif(!mosq || !topic){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(payloadlen < 0 || payloadlen > (int)MQTT_MAX_PAYLOAD){\n\t\treturn MOSQ_ERR_PAYLOAD_SIZE;\n\t}\n\tif(payloadlen > 0 && !payload){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(mosquitto_pub_topic_check(topic)){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(topic, (uint16_t)strlen(topic))){\n\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t}\n\n\tif(properties){\n\t\tif(mosq->protocol != mosq_p_mqtt5){\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t\t}\n\t\tp = properties;\n\t\twhile(p){\n\t\t\trc = mosquitto_property_check_command(CMD_WILL, mosquitto_property_identifier(p));\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tp = mosquitto_property_next(p);\n\t\t}\n\t}\n\n\tif(mosq->will){\n\t\tmosquitto_FREE(mosq->will->msg.topic);\n\t\tmosquitto_FREE(mosq->will->msg.payload);\n\t\tmosquitto_property_free_all(&mosq->will->properties);\n\t\tmosquitto_FREE(mosq->will);\n\t}\n\n\tmosq->will = mosquitto_calloc(1, sizeof(struct mosquitto_message_all));\n\tif(!mosq->will){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tmosq->will->msg.topic = mosquitto_strdup(topic);\n\tif(!mosq->will->msg.topic){\n\t\trc = MOSQ_ERR_NOMEM;\n\t\tgoto cleanup;\n\t}\n\tmosq->will->msg.payloadlen = payloadlen;\n\tif(mosq->will->msg.payloadlen > 0){\n\t\tif(!payload){\n\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\tgoto cleanup;\n\t\t}\n\t\tmosq->will->msg.payload = mosquitto_malloc(sizeof(char)*(unsigned int)mosq->will->msg.payloadlen);\n\t\tif(!mosq->will->msg.payload){\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\tmemcpy(mosq->will->msg.payload, payload, (unsigned int)payloadlen);\n\t}\n\tmosq->will->msg.qos = qos;\n\tmosq->will->msg.retain = retain;\n\n\tmosq->will->properties = properties;\n\n\treturn MOSQ_ERR_SUCCESS;\n\ncleanup:\n\tif(mosq->will){\n\t\tmosquitto_FREE(mosq->will->msg.topic);\n\t\tmosquitto_FREE(mosq->will->msg.payload);\n\t\tmosquitto_FREE(mosq->will);\n\t}\n\n\treturn rc;\n}\n\n\nint will__clear(struct mosquitto *mosq)\n{\n\tif(!mosq->will){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tmosquitto_FREE(mosq->will->msg.topic);\n\tmosquitto_FREE(mosq->will->msg.payload);\n\n\tmosquitto_property_free_all(&mosq->will->properties);\n\n\tmosquitto_FREE(mosq->will);\n\tmosq->will_delay_interval = 0;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n"
  },
  {
    "path": "lib/will_mosq.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef WILL_MOSQ_H\n#define WILL_MOSQ_H\n\n#include \"mosquitto.h\"\n#include \"mosquitto_internal.h\"\n\nint will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties);\nint will__clear(struct mosquitto *mosq);\n\n#endif\n"
  },
  {
    "path": "libcommon/CMakeLists.txt",
    "content": "set(C_SRC\n\tbase64_common.c\n\tcjson_common.c\n\tfile_common.c\n\tmemory_common.c\n\tmqtt_common.c\n\tpassword_common.c\n\tproperty_common.c\n\trandom_common.c\n\tstrings_common.c\n\ttime_common.c\n\ttopic_common.c\n\tutf8_common.c\n\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon.h\"\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_base64.h\"\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_cjson.h\"\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_file.h\"\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_memory.h\"\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_properties.h\"\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_random.h\"\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_string.h\"\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_time.h\"\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_topic.h\"\n\t\"${mosquitto_SOURCE_DIR}/include/mosquitto/libcommon_utf8.h\"\n)\n\nif(WIN32)\n\tadd_library(libmosquitto_common SHARED\n\t\t${C_SRC}\n\t)\nelse()\n\tadd_library(libmosquitto_common OBJECT\n\t\t${C_SRC}\n\t)\nendif()\n\ntarget_include_directories(libmosquitto_common\n\tPUBLIC\n\t\t\"${CJSON_INCLUDE_DIRS}\"\n)\n\ntarget_link_libraries(libmosquitto_common\n\tPUBLIC\n\t\tcommon-options\n\t\tcJSON\n)\n\nif(ARGON2_FOUND)\n\ttarget_link_libraries(libmosquitto_common PRIVATE argon2)\nendif()\n\nif (WITH_TLS)\n\ttarget_link_libraries(libmosquitto_common\n\t\tPUBLIC\n\t\t\tOpenSSL::SSL\n\t)\nelseif(NOT WIN32)\n\tinclude(CheckSymbolExists)\n\tcheck_symbol_exists(getrandom \"sys/random.h\" GETRANDOM_FOUND)\n\tif(GETRANDOM_FOUND)\n\t\tadd_definitions(\"-DHAVE_GETRANDOM\")\n\telse()\n\t\tmessage(FATAL_ERROR \"C library does not provide getrandom(); enable WITH_TLS instead\")\n\tendif()\nendif()\n\nif(INC_MEMTRACK)\n\ttarget_compile_definitions(libmosquitto_common PUBLIC \"WITH_MEMORY_TRACKING\")\nendif()\n\noption(ALLOC_MISMATCH_INVALID_READ \"Memory function mismatch detection.\" OFF)\nif(ALLOC_MISMATCH_INVALID_READ)\n\ttarget_compile_definitions(libmosquitto_common PRIVATE \"ALLOC_MISMATCH_INVALID_READ\")\nendif()\n\noption(ALLOC_MISMATCH_ABORT \"Memory function mismatch abort.\" OFF)\nif(ALLOC_MISMATCH_ABORT)\n\ttarget_compile_definitions(libmosquitto_common PRIVATE \"ALLOC_MISMATCH_ABORT\")\nendif()\n\nset_target_properties(libmosquitto_common PROPERTIES\n\tOUTPUT_NAME mosquitto_common\n\tVERSION ${VERSION}\n\tSOVERSION 1\n\tPOSITION_INDEPENDENT_CODE 1\n)\n\ninstall(TARGETS libmosquitto_common\n\tRUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n\tARCHIVE DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n\tLIBRARY DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n)\n"
  },
  {
    "path": "libcommon/Makefile",
    "content": "R=..\ninclude ${R}/config.mk\n\nLOCAL_CFLAGS+=-fPIC\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=-Wl,--version-script=linker.version -Wl,-soname,libmosquitto_common.so.$(SOVERSION) -fPIC\nLOCAL_LIBADD+=-lcjson\n\nifeq ($(WITH_MEMORY_TRACKING),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_MEMORY_TRACKING\nendif\n\nifeq ($(ALLOC_MISMATCH_INVALID_READ),yes)\n\tLOCAL_CPPFLAGS+=-DALLOC_MISMATCH_INVALID_READ\nendif\n\nifeq ($(ALLOC_MISMATCH_ABORT),yes)\n\tLOCAL_CPPFLAGS+=-DALLOC_MISMATCH_ABORT\nendif\n\nifeq ($(WITH_TLS),yes)\n\tLOCAL_LIBADD+=-lcrypto\nendif\n\n# ------------------------------------------\n#  Targets\n# ------------------------------------------\n.PHONY : really clean install\n\nOBJS= \\\n\tbase64_common.o \\\n\tcjson_common.o \\\n\tfile_common.o \\\n\tmemory_common.o \\\n\tmqtt_common.o \\\n\tpassword_common.o \\\n\tproperty_common.o \\\n\trandom_common.o \\\n\tstrings_common.o \\\n\ttime_common.o \\\n\ttopic_common.o \\\n\tutf8_common.o\n\nall : libmosquitto_common.a libmosquitto_common.so.${SOVERSION}\n\ninstall :\n\t$(INSTALL) -d \"${DESTDIR}${libdir}/\"\n\t$(INSTALL) ${STRIP_OPTS} libmosquitto_common.so.${SOVERSION} \"${DESTDIR}${libdir}/libmosquitto_common.so.${SOVERSION}\"\n\tln -sf libmosquitto_common.so.${SOVERSION} \"${DESTDIR}${libdir}/libmosquitto_common.so\"\n\nuninstall :\n\nreallyclean : clean\n\nclean :\n\t-rm -f ${OBJS} libmosquitto_common.so.${SOVERSION} libmosquitto_common.so libmosquitto_common.a *.gcno *.gcda\n\nlibmosquitto_common.so.${SOVERSION} : ${OBJS}\n\t${CROSS_COMPILE}$(CC) $(LOCAL_LDFLAGS) $^ -o $@ ${LOCAL_LIBADD} -shared\n\nlibmosquitto_common.a : ${OBJS}\n\t${CROSS_COMPILE}$(AR) cr $@ $^\n\n${OBJS} : %.o: %.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n"
  },
  {
    "path": "libcommon/base64_common.c",
    "content": "/*\nCopyright (c) 2012-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_TLS\n#  include <openssl/opensslv.h>\n#  include <openssl/evp.h>\n#  include <openssl/buffer.h>\n#endif\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#ifdef WITH_TLS\n\n\nint mosquitto_base64_encode(const unsigned char *in, size_t in_len, char **encoded)\n{\n\tBIO *bmem, *b64;\n\tBUF_MEM *bptr = NULL;\n\tint rc = 1;\n\n\tb64 = BIO_new(BIO_f_base64());\n\tif(b64 == NULL){\n\t\treturn 1;\n\t}\n\n\tBIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);\n\tbmem = BIO_new(BIO_s_mem());\n\tif(bmem){\n\t\tb64 = BIO_push(b64, bmem);\n\t\tBIO_write(b64, in, (int)in_len);\n\n\t\tif(BIO_flush(b64) == 1){\n\t\t\tBIO_get_mem_ptr(b64, &bptr);\n\t\t\t*encoded = mosquitto_malloc(bptr->length+1);\n\t\t\tif(*encoded){\n\t\t\t\tmemcpy(*encoded, bptr->data, bptr->length);\n\t\t\t\t(*encoded)[bptr->length] = '\\0';\n\t\t\t\trc = 0;\n\t\t\t}\n\t\t}\n\t}\n\tBIO_free_all(b64);\n\n\treturn rc;\n}\n\n\nint mosquitto_base64_decode(const char *in, unsigned char **decoded, unsigned int *decoded_len)\n{\n\tBIO *bmem, *b64;\n\tsize_t slen;\n\tint len;\n\tint rc = 1;\n\n\tslen = strlen(in);\n\t*decoded = NULL;\n\t*decoded_len = 0;\n\n\tb64 = BIO_new(BIO_f_base64());\n\tif(!b64){\n\t\treturn 1;\n\t}\n\n\tBIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);\n\tbmem = BIO_new(BIO_s_mem());\n\tif(bmem){\n\t\tb64 = BIO_push(b64, bmem);\n\t\tBIO_write(bmem, in, (int)slen);\n\n\t\tif(BIO_flush(bmem) == 1){\n\t\t\t*decoded = mosquitto_calloc(slen, 1);\n\n\t\t\tif(*decoded){\n\t\t\t\tlen = BIO_read(b64, *decoded, (int)slen);\n\t\t\t\tif(len > 0){\n\t\t\t\t\t*decoded_len = (unsigned int)len;\n\t\t\t\t\trc = 0;\n\t\t\t\t}else{\n\t\t\t\t\tmosquitto_free(*decoded);\n\t\t\t\t\t*decoded = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tBIO_free_all(b64);\n\n\treturn rc;\n}\n#endif\n"
  },
  {
    "path": "libcommon/cjson_common.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <ctype.h>\n#include <errno.h>\n#include <limits.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n\ncJSON *mosquitto_properties_to_json(const mosquitto_property *properties)\n{\n\tcJSON *array, *obj;\n\tchar *name, *value;\n\tuint8_t i8;\n\tuint16_t len;\n\tint propid;\n\n\tif(!properties){\n\t\treturn NULL;\n\t}\n\n\tarray = cJSON_CreateArray();\n\tif(!array){\n\t\treturn NULL;\n\t}\n\n\tdo{\n\t\tpropid = mosquitto_property_identifier(properties);\n\t\tobj = cJSON_CreateObject();\n\t\tif(!obj){\n\t\t\tcJSON_Delete(array);\n\t\t\treturn NULL;\n\t\t}\n\t\tcJSON_AddItemToArray(array, obj);\n\t\t/* identifier, (key), value */\n\t\tif(cJSON_AddStringToObject(obj,\n\t\t\t\t\"identifier\",\n\t\t\t\tmosquitto_property_identifier_to_string(propid)) == NULL\n\t\t\t\t){\n\t\t\tcJSON_Delete(array);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tswitch(propid){\n\t\t\tcase MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:\n\t\t\t\t/* byte */\n\t\t\t\tmosquitto_property_read_byte(properties, propid, &i8, false);\n\t\t\t\tif(cJSON_AddNumberToObject(obj, \"value\", i8) == NULL){\n\t\t\t\t\tcJSON_Delete(array);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_CONTENT_TYPE:\n\t\t\tcase MQTT_PROP_RESPONSE_TOPIC:\n\t\t\tcase MQTT_PROP_REASON_STRING:\n\t\t\t\t/* str */\n\t\t\t\tif(mosquitto_property_read_string(properties, propid, &value, false) == NULL){\n\t\t\t\t\tcJSON_Delete(array);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tif(cJSON_AddStringToObject(obj, \"value\", value) == NULL){\n\t\t\t\t\tfree(value);\n\t\t\t\t\tcJSON_Delete(array);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tfree(value);\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_CORRELATION_DATA:\n\t\t\t\t{\n\t\t\t\t\t/* bin */\n\t\t\t\t\tvoid *binval = NULL;\n\t\t\t\t\tmosquitto_property_read_binary(properties, propid, &binval, &len, false);\n\t\t\t\t\tchar *hexval = malloc(2*(size_t)len + 1);\n\t\t\t\t\tif(!hexval){\n\t\t\t\t\t\tfree(binval);\n\t\t\t\t\t\tcJSON_Delete(array);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t}\n\t\t\t\t\tfor(int i=0; i<len; i++){\n\t\t\t\t\t\tsprintf(&hexval[i*2], \"%02X\", ((uint8_t *)binval)[i]);\n\t\t\t\t\t}\n\t\t\t\t\thexval[2*len] = '\\0';\n\t\t\t\t\tfree(binval);\n\n\t\t\t\t\tif(cJSON_AddStringToObject(obj, \"value\", hexval) == NULL){\n\t\t\t\t\t\tfree(hexval);\n\t\t\t\t\t\tcJSON_Delete(array);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t}\n\t\t\t\t\tfree(hexval);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_USER_PROPERTY:\n\t\t\t\t/* pair */\n\t\t\t\tmosquitto_property_read_string_pair(properties, propid, &name, &value, false);\n\t\t\t\tif(cJSON_AddStringToObject(obj, \"name\", name) == NULL\n\t\t\t\t\t\t|| cJSON_AddStringToObject(obj, \"value\", value) == NULL){\n\n\t\t\t\t\tfree(name);\n\t\t\t\t\tfree(value);\n\t\t\t\t\tcJSON_Delete(array);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tfree(name);\n\t\t\t\tfree(value);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\tproperties = mosquitto_property_next(properties);\n\t}while(properties);\n\n\treturn array;\n}\n"
  },
  {
    "path": "libcommon/file_common.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/* This contains general purpose utility functions that are not specific to\n * Mosquitto/MQTT features. */\n\n#include \"config.h\"\n\n#include <ctype.h>\n#include <errno.h>\n#include <limits.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef WIN32\n#  include <winsock2.h>\n#  include <aclapi.h>\n#  include <io.h>\n#  include <lmcons.h>\n#  include <fcntl.h>\n#  define PATH_MAX MAX_PATH\n#else\n#  include <sys/stat.h>\n#  include <pwd.h>\n#  include <grp.h>\n#  include <unistd.h>\n#  include <fcntl.h>\n#endif\n\n#include \"mosquitto.h\"\n\nvoid (*libcommon_vprintf)(const char *fmt, va_list va) = NULL;\n\n\nvoid libcommon_printf(const char *fmt, ...)\n{\n\tva_list va;\n\n\tva_start(va, fmt);\n\n\tif(libcommon_vprintf){\n\t\tlibcommon_vprintf(fmt, va);\n\t}else{\n\t\tvfprintf(stderr, fmt, va);\n\t}\n\n\tva_end(va);\n}\n\n\nFILE *mosquitto_fopen(const char *path, const char *mode, bool restrict_read)\n{\n#ifdef WIN32\n\tchar buf[4096];\n\tint rc;\n\tint flags = 0;\n\n\trc = ExpandEnvironmentStringsA(path, buf, 4096);\n\tif(rc == 0 || rc > 4096){\n\t\treturn NULL;\n\t}else{\n\t\tif(restrict_read){\n\t\t\tHANDLE hfile;\n\t\t\tSECURITY_ATTRIBUTES sec;\n\t\t\tEXPLICIT_ACCESS_A ea;\n\t\t\tPACL pacl = NULL;\n\t\t\tchar username[UNLEN + 1];\n\t\t\tDWORD ulen = UNLEN;\n\t\t\tSECURITY_DESCRIPTOR sd;\n\t\t\tDWORD dwCreationDisposition;\n\t\t\tDWORD dwShareMode;\n\t\t\tint fd;\n\t\t\tFILE *fptr;\n\n\t\t\tswitch(mode[0]){\n\t\t\t\tcase 'a':\n\t\t\t\t\tdwCreationDisposition = OPEN_ALWAYS;\n\t\t\t\t\tdwShareMode = GENERIC_WRITE;\n\t\t\t\t\tflags = _O_APPEND;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'r':\n\t\t\t\t\tdwCreationDisposition = OPEN_EXISTING;\n\t\t\t\t\tdwShareMode = GENERIC_READ;\n\t\t\t\t\tflags = _O_RDONLY;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'w':\n\t\t\t\t\tdwCreationDisposition = CREATE_ALWAYS;\n\t\t\t\t\tdwShareMode = GENERIC_WRITE;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tif(mode[1] == '+'){\n\t\t\t\tdwShareMode = GENERIC_READ | GENERIC_WRITE;\n\t\t\t}\n\n\t\t\tGetUserNameA(username, &ulen);\n\t\t\tif(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)){\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tBuildExplicitAccessWithNameA(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE);\n\t\t\tif(SetEntriesInAclA(1, &ea, NULL, &pacl) != ERROR_SUCCESS){\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tif(!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)){\n\t\t\t\tLocalFree(pacl);\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tmemset(&sec, 0, sizeof(sec));\n\t\t\tsec.nLength = sizeof(SECURITY_ATTRIBUTES);\n\t\t\tsec.bInheritHandle = FALSE;\n\t\t\tsec.lpSecurityDescriptor = &sd;\n\n\t\t\thfile = CreateFileA(buf, dwShareMode, FILE_SHARE_READ,\n\t\t\t\t\t&sec,\n\t\t\t\t\tdwCreationDisposition,\n\t\t\t\t\tFILE_ATTRIBUTE_NORMAL,\n\t\t\t\t\tNULL);\n\n\t\t\tLocalFree(pacl);\n\n\t\t\tfd = _open_osfhandle((intptr_t)hfile, flags);\n\t\t\tif(fd < 0){\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tfptr = _fdopen(fd, mode);\n\t\t\tif(!fptr){\n\t\t\t\t_close(fd);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tif(mode[0] == 'a'){\n\t\t\t\tfseek(fptr, 0, SEEK_END);\n\t\t\t}\n\t\t\treturn fptr;\n\n\t\t}else{\n\t\t\treturn fopen(buf, mode);\n\t\t}\n\t}\n#else\n\tFILE *fptr;\n\tstruct stat statbuf;\n\n\tif(restrict_read){\n\t\tmode_t old_mask;\n\n\t\told_mask = umask(0077);\n\n\t\tint open_flags = 0;\n\t\tif(!getenv(\"MOSQUITTO_UNSAFE_ALLOW_SYMLINKS\")){\n\t\t\topen_flags |= O_NOFOLLOW;\n\t\t}\n\t\tfor(size_t i = 0; i<strlen(mode); i++){\n\t\t\tif(mode[i] == 'r'){\n\t\t\t\topen_flags |= O_RDONLY;\n\t\t\t}else if(mode[i] == 'w'){\n\t\t\t\topen_flags |= O_WRONLY;\n\t\t\t\topen_flags |= (O_TRUNC | O_CREAT | O_EXCL);\n\n\t\t\t}else if(mode[i] == 'a'){\n\t\t\t\topen_flags |= O_WRONLY;\n\t\t\t\topen_flags |= (O_APPEND | O_CREAT);\n\t\t\t}else if(mode[i] == 't'){\n\t\t\t}else if(mode[i] == 'b'){\n\t\t\t}else if(mode[i] == '+'){\n\t\t\t\topen_flags |= O_RDWR;\n\t\t\t}\n\t\t}\n\t\tint fd = open(path, open_flags, 0600);\n\t\tif(fd < 0){\n\t\t\treturn NULL;\n\t\t}\n\t\tfptr = fdopen(fd, mode);\n\n\t\tumask(old_mask);\n\t}else{\n\t\tfptr = fopen(path, mode);\n\t}\n\tif(!fptr){\n\t\treturn NULL;\n\t}\n\n\tif(fstat(fileno(fptr), &statbuf) < 0){\n\t\tfclose(fptr);\n\t\treturn NULL;\n\t}\n\n\tif(restrict_read){\n\t\tif(statbuf.st_mode & S_IRWXO){\n\t\t\tlibcommon_printf(\n\t\t\t\t\t\"Warning: File %s has world readable permissions. Future versions will refuse to load this file.\\n\"\n\t\t\t\t\t\"To fix this, use `chmod 0700 %s`.\\n\",\n\t\t\t\t\tpath, path);\n#if 0\n\t\t\treturn NULL;\n#endif\n\t\t}\n\t\tif(statbuf.st_uid != getuid()){\n\t\t\tchar buf[4096];\n\t\t\tstruct passwd pw, *result;\n\n\t\t\tgetpwuid_r(getuid(), &pw, buf, sizeof(buf), &result);\n\t\t\tif(result){\n\t\t\t\tlibcommon_printf(\n\t\t\t\t\t\t\"Warning: File %s owner is not %s. Future versions will refuse to load this file.\"\n\t\t\t\t\t\t\"To fix this, use `chown %s %s`.\\n\",\n\t\t\t\t\t\tpath, result->pw_name, result->pw_name, path);\n\t\t\t}\n#if 0\n\t\t\t// Future version\n\t\t\treturn NULL;\n#endif\n\t\t}\n\t\tif(statbuf.st_gid != getgid()){\n\t\t\tchar buf[4096];\n\t\t\tstruct group grp, *result;\n\n\t\t\tif(getgrgid_r(getgid(), &grp, buf, sizeof(buf), &result) == 0 && result){\n\t\t\t\tlibcommon_printf(\n\t\t\t\t\t\t\"Warning: File %s group is not %s. Future versions will refuse to load this file.\\n\",\n\t\t\t\t\t\tpath, result->gr_name);\n\t\t\t}\n#if 0\n\t\t\t// Future version\n\t\t\treturn NULL\n#endif\n\t\t}\n\t}\n\n\tif(!S_ISREG(statbuf.st_mode)){\n\t\tlibcommon_printf(\"Error: %s is not a file.\", path);\n\t\tfclose(fptr);\n\t\treturn NULL;\n\t}\n\treturn fptr;\n#endif\n}\n\n\nchar *mosquitto_trimblanks(char *str)\n{\n\tchar *endptr;\n\n\tif(str == NULL){\n\t\treturn NULL;\n\t}\n\n\twhile(isspace((unsigned char)str[0])){\n\t\tstr++;\n\t}\n\tendptr = &str[strlen(str)-1];\n\twhile(endptr > str && isspace((unsigned char)endptr[0])){\n\t\tendptr[0] = '\\0';\n\t\tendptr--;\n\t}\n\treturn str;\n}\n\n\nchar *mosquitto_fgets(char **buf, int *buflen, FILE *stream)\n{\n\tchar *rc;\n\tchar endchar;\n\tint offset = 0;\n\tchar *newbuf;\n\tsize_t len;\n\n\tif(stream == NULL || buf == NULL || buflen == NULL || *buflen < 1){\n\t\treturn NULL;\n\t}\n\n\tdo{\n\t\trc = fgets(&((*buf)[offset]), (*buflen)-offset, stream);\n\t\tif(feof(stream) || rc == NULL){\n\t\t\treturn rc;\n\t\t}\n\n\t\tlen = strlen(*buf);\n\t\tif(len == 0){\n\t\t\treturn rc;\n\t\t}\n\t\tendchar = (*buf)[len-1];\n\t\tif(endchar == '\\n'){\n\t\t\treturn rc;\n\t\t}\n\t\tif((int)(len+1) < *buflen){\n\t\t\t/* Embedded nulls, invalid string */\n\t\t\treturn NULL;\n\t\t}\n\n\t\t/* No EOL char found, so extend buffer */\n\t\toffset = (*buflen)-1;\n\t\t*buflen += 1000;\n\t\tnewbuf = realloc(*buf, (size_t)*buflen);\n\t\tif(!newbuf){\n\t\t\treturn NULL;\n\t\t}\n\t\t*buf = newbuf;\n\t}while(1);\n}\n\n\n#define INVOKE_LOG_FN(format, ...) \\\n\t\tdo{ \\\n\t\t\tif(log_fn){ \\\n\t\t\t\tint tmp_err_no = errno; \\\n\t\t\t\tchar msg[2*PATH_MAX]; \\\n\t\t\t\tsnprintf(msg, sizeof(msg), (format), __VA_ARGS__); \\\n\t\t\t\tmsg[sizeof(msg)-1] = '\\0'; \\\n\t\t\t\t(*log_fn)(msg); \\\n\t\t\t\terrno = tmp_err_no; \\\n\t\t\t} \\\n\t\t}while(0)\n\n\nint mosquitto_write_file(const char *target_path, bool restrict_read, int (*write_fn)(FILE *fptr, void *user_data), void *user_data, void (*log_fn)(const char *msg))\n{\n\tint rc = 0;\n\tFILE *fptr = NULL;\n\tchar tmp_file_path[PATH_MAX];\n\n\tif(!target_path || !write_fn){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trc = snprintf(tmp_file_path, PATH_MAX, \"%s.new\", target_path);\n\tif(rc < 0 || rc >= PATH_MAX){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n#ifndef WIN32\n\t/**\n\t*\n\t* If a system lost power during the rename operation at the\n\t* end of this file the filesystem could potentially be left\n\t* with a directory that looks like this after powerup:\n\t*\n\t* 24094 -rw-r--r--    2 root     root          4099 May 30 16:27 mosquitto.db\n\t* 24094 -rw-r--r--    2 root     root          4099 May 30 16:27 mosquitto.db.new\n\t*\n\t* The 24094 shows that mosquitto.db.new is hard-linked to the\n\t* same file as mosquitto.db.  If fopen(outfile, \"wb\") is naively\n\t* called then mosquitto.db will be truncated and the database\n\t* potentially corrupted.\n\t*\n\t* Any existing mosquitto.db.new file must be removed prior to\n\t* opening to guarantee that it is not hard-linked to\n\t* mosquitto.db.\n\t*\n\t*/\n\tif(unlink(tmp_file_path) && errno != ENOENT){\n\t\tINVOKE_LOG_FN(\"unable to remove stale tmp file %s, error %s\", tmp_file_path, strerror(errno));\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n#endif\n\n\tfptr = mosquitto_fopen(tmp_file_path, \"wb\", restrict_read);\n\tif(fptr == NULL){\n\t\tINVOKE_LOG_FN(\"unable to open %s for writing, error %s\", tmp_file_path, strerror(errno));\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif((rc = (*write_fn)(fptr, user_data)) != MOSQ_ERR_SUCCESS){\n\t\tgoto error;\n\t}\n\n\trc = MOSQ_ERR_ERRNO;\n#ifndef WIN32\n\t/**\n\t*\n\t* Closing a file does not guarantee that the contents are\n\t* written to disk.  Need to flush to send data from app to OS\n\t* buffers, then fsync to deliver data from OS buffers to disk\n\t* (as well as disk hardware permits).\n\t*\n\t* man close (http://linux.die.net/man/2/close, 2016-06-20):\n\t*\n\t*   \"successful close does not guarantee that the data has\n\t*   been successfully saved to disk, as the kernel defers\n\t*   writes.  It is not common for a filesystem to flush\n\t*   the  buffers  when  the stream is closed.  If you need\n\t*   to be sure that the data is physically stored, use\n\t*   fsync(2).  (It will depend on the disk hardware at this\n\t*   point.\"\n\t*\n\t* This guarantees that the new state file will not overwrite\n\t* the old state file before its contents are valid.\n\t*\n\t*/\n\tif(fflush(fptr) != 0 && errno != EINTR){\n\t\tINVOKE_LOG_FN(\"unable to flush %s, error %s\", tmp_file_path, strerror(errno));\n\t\tgoto error;\n\t}\n\n\tif(fsync(fileno(fptr)) != 0 && errno != EINTR){\n\t\tINVOKE_LOG_FN(\"unable to sync %s to disk, error %s\", tmp_file_path, strerror(errno));\n\t\tgoto error;\n\t}\n#endif\n\n\tif(fclose(fptr) != 0){\n\t\tINVOKE_LOG_FN(\"unable to close %s on disk, error %s\", tmp_file_path, strerror(errno));\n\t\tfptr = NULL;\n\t\tgoto error;\n\t}\n\tfptr = NULL;\n\n#ifdef WIN32\n\tif(remove(target_path) != 0 && errno != ENOENT){\n\t\tINVOKE_LOG_FN(\"unable to remove %s on disk, error %s\", target_path, strerror(errno));\n\t\tgoto error;\n\t}\n#endif\n\n\tif(rename(tmp_file_path, target_path) != 0){\n\t\tINVOKE_LOG_FN(\"unable to replace %s by tmp file  %s, error %s\", target_path, tmp_file_path, strerror(errno));\n\t\tgoto error;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n\nerror:\n\tif(fptr){\n\t\tfclose(fptr);\n\t\tunlink(tmp_file_path);\n\t}\n\treturn MOSQ_ERR_ERRNO;\n}\n\n\nint mosquitto_read_file(const char *file, bool restrict_read, char **buf, size_t *buflen)\n{\n\tFILE *fptr;\n\tlong l;\n\tsize_t buflen_i;\n\n\t*buf = NULL;\n\tif(buflen){\n\t\t*buflen = 0;\n\t}\n\tfptr = mosquitto_fopen(file, \"rt\", restrict_read);\n\tif(fptr == NULL){\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n\n\tfseek(fptr, 0, SEEK_END);\n\tl = ftell(fptr);\n\tfseek(fptr, 0, SEEK_SET);\n\tif(l < 0){\n\t\tfclose(fptr);\n\t\treturn MOSQ_ERR_ERRNO;\n\t}else if(l == 0){\n\t\tfclose(fptr);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tbuflen_i = (size_t)l;\n\n\t*buf = mosquitto_calloc(buflen_i+1, sizeof(char));\n\tif((*buf) == NULL){\n\t\tfclose(fptr);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tif(fread(*buf, 1, buflen_i, fptr) != buflen_i){\n\t\tmosquitto_FREE(*buf);\n\t\tfclose(fptr);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tfclose(fptr);\n\tif(buflen){\n\t\t*buflen = buflen_i;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "libcommon/linker.version",
    "content": "/* Linker version script - currently used here primarily to control which\n * symbols are exported.\n */\n\nMOSQ_2.1 {\n\tglobal:\n\t\tlibcommon_printf;\n\t\tmosquitto_base64_decode;\n\t\tmosquitto_base64_encode;\n\t\tmosquitto_calloc;\n\t\tmosquitto_connack_string;\n\t\tmosquitto_fgets;\n\t\tmosquitto_fopen;\n\t\tmosquitto_free;\n\t\tmosquitto_free;\n\t\tmosquitto_getrandom;\n\t\tmosquitto_malloc;\n\t\tmosquitto_malloc;\n\t\tmosquitto_max_memory_used;\n\t\tmosquitto_memory_set_limit;\n\t\tmosquitto_memory_used;\n\t\tmosquitto_properties_to_json;\n\t\tmosquitto_property_next;\n\t\tmosquitto_property_read_binary;\n\t\tmosquitto_property_read_byte;\n\t\tmosquitto_property_read_int16;\n\t\tmosquitto_property_read_int32;\n\t\tmosquitto_property_read_string;\n\t\tmosquitto_property_read_string_pair;\n\t\tmosquitto_property_read_varint;\n\t\tmosquitto_property_add_binary;\n\t\tmosquitto_property_add_byte;\n\t\tmosquitto_property_add_int16;\n\t\tmosquitto_property_add_int32;\n\t\tmosquitto_property_add_string;\n\t\tmosquitto_property_add_string_pair;\n\t\tmosquitto_property_add_varint;\n\t\tmosquitto_property_binary_value;\n\t\tmosquitto_property_binary_value_length;\n\t\tmosquitto_property_byte_value;\n\t\tmosquitto_property_check_all;\n\t\tmosquitto_property_check_command;\n\t\tmosquitto_property_copy_all;\n\t\tmosquitto_property_free;\n\t\tmosquitto_property_free_all;\n\t\tmosquitto_property_get_length;\n\t\tmosquitto_property_get_length_all;\n\t\tmosquitto_property_get_remaining_length;\n\t\tmosquitto_property_identifier;\n\t\tmosquitto_property_identifier_to_string;\n\t\tmosquitto_property_int16_value;\n\t\tmosquitto_property_int32_value;\n\t\tmosquitto_property_remove;\n\t\tmosquitto_property_string_name;\n\t\tmosquitto_property_string_name_length;\n\t\tmosquitto_property_string_value;\n\t\tmosquitto_property_string_value_length;\n\t\tmosquitto_property_type;\n\t\tmosquitto_property_varint_value;\n\t\tmosquitto_pub_topic_check;\n\t\tmosquitto_pub_topic_check2;\n\t\tmosquitto_pw_cleanup;\n\t\tmosquitto_pw_decode;\n\t\tmosquitto_pw_get_encoded;\n\t\tmosquitto_pw_hash_encoded;\n\t\tmosquitto_pw_is_valid;\n\t\tmosquitto_pw_new;\n\t\tmosquitto_pw_set_param;\n\t\tmosquitto_pw_set_valid;\n\t\tmosquitto_pw_verify;\n\t\tmosquitto_read_file;\n\t\tmosquitto_realloc;\n\t\tmosquitto_realloc;\n\t\tmosquitto_reason_string;\n\t\tmosquitto_strdup;\n\t\tmosquitto_strerror;\n\t\tmosquitto_string_to_command;\n\t\tmosquitto_string_to_property_info;\n\t\tmosquitto_strndup;\n\t\tmosquitto_sub_matches_acl;\n\t\tmosquitto_sub_matches_acl_with_pattern;\n\t\tmosquitto_sub_topic_check;\n\t\tmosquitto_sub_topic_check2;\n\t\tmosquitto_sub_topic_tokenise;\n\t\tmosquitto_sub_topic_tokens_free;\n\t\tmosquitto_time;\n\t\tmosquitto_time_cmp;\n\t\tmosquitto_time_init;\n\t\tmosquitto_time_ns;\n\t\tmosquitto_topic_matches_sub;\n\t\tmosquitto_topic_matches_sub2;\n\t\tmosquitto_topic_matches_sub_with_pattern;\n\t\tmosquitto_trimblanks;\n\t\tmosquitto_validate_utf8;\n\t\tmosquitto_varint_bytes;\n\t\tmosquitto_write_file;\n\tlocal: *;\n};\n"
  },
  {
    "path": "libcommon/memory_common.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n\n#include \"mosquitto.h\"\n\n#if defined(WITH_MEMORY_TRACKING)\n#  if defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)\n#    define REAL_WITH_MEMORY_TRACKING\n#  endif\n#endif\n\n#ifdef REAL_WITH_MEMORY_TRACKING\n#  if defined(__APPLE__)\n#    include <malloc/malloc.h>\n#    define malloc_usable_size malloc_size\n#  elif defined(__FreeBSD__)\n#    include <malloc_np.h>\n#  else\n#    include <malloc.h>\n#  endif\n#endif\n\nstatic unsigned long memcount = 0;\nstatic unsigned long max_memcount = 0;\n\nstatic size_t mem_limit = 0;\n\n\nvoid mosquitto_memory_set_limit(size_t lim)\n{\n\tmem_limit = lim;\n}\n\n\nunsigned long mosquitto_memory_used(void)\n{\n\treturn memcount;\n}\n\n\nunsigned long mosquitto_max_memory_used(void)\n{\n\treturn max_memcount;\n}\n\n#ifdef REAL_WITH_MEMORY_TRACKING\n\n/* ==================================================\n * Alloc mismatch tracking\n * ================================================== */\n#if defined(ALLOC_MISMATCH_INVALID_READ) || defined(ALLOC_MISMATCH_ABORT)\n#define ALLOC_MARKER_SIZE 8\nstatic const char *alloc_marker = \"MOSQ_MEM\";\n\nstatic unsigned long dummycounter = 0;\n\n\nunsigned long mosq__get_dummy_counter(void)\n{\n\treturn dummycounter;\n}\n\n\nstatic void set_alloc_marker(char *mem, size_t size)\n{\n\tmemcpy(mem + size - ALLOC_MARKER_SIZE, alloc_marker, ALLOC_MARKER_SIZE);\n}\n\n\nstatic bool check_alloc_marker(char *mem, size_t size)\n{\n\treturn strncmp(mem + size - ALLOC_MARKER_SIZE, alloc_marker, ALLOC_MARKER_SIZE) == 0;\n}\n\n\nstatic void trigger_alloc_mismatch(char *mem, size_t size)\n{\n\t(void)mem;\n\t(void)size;\n#ifdef ALLOC_MISMATCH_INVALID_READ\n\t/* Trigger an invalid read on the freed memory and increment dummy counter */\n\tif(strncmp(mem + size - ALLOC_MARKER_SIZE, alloc_marker, ALLOC_MARKER_SIZE) == 0){\n\t\t++dummycounter;\n\t}\n#endif\n#ifdef ALLOC_MISMATCH_ABORT\n\tabort();\n#endif\n}\n\n#if defined(__linux__)\n\n#if !defined(__libc_free)\nvoid __libc_free(void *ptr);\n#endif\n\n\nvoid free(void *ptr)\n{\n\tif(!ptr){\n\t\treturn;\n\t}\n\tsize_t free_size = malloc_usable_size(ptr);\n\n\t/* If we find the marker the memory was allocated using mosquitto_* allocation function */\n\tbool alloc_mismatch = check_alloc_marker(ptr, free_size);\n\n\t__libc_free(ptr);\n\n\tif(alloc_mismatch){\n\t\ttrigger_alloc_mismatch(ptr, free_size);\n\t}\n}\n#endif /* defined(__linux__) */\n\n#else /* defined(ALLOC_MISMATCH_INVALID_READ) || defined(ALLOC_MISMATCH_ABORT) */\n\n#define ALLOC_MARKER_SIZE 0\n\n\nstatic void set_alloc_marker(char *mem, size_t size)\n{\n\tUNUSED(mem); UNUSED(size);\n}\n\n#endif /* defined(ALLOC_MISMATCH_INVALID_READ) */\n\n\n/* ==================================================\n * Alloc functions with tracking\n * ================================================== */\n\n\nBROKER_EXPORT void *mosquitto_malloc(size_t size)\n{\n\tvoid *mem;\n\n\tif(mem_limit && memcount + size > mem_limit){\n\t\treturn NULL;\n\t}\n\tmem = malloc(size + ALLOC_MARKER_SIZE);\n\tif(mem){\n\t\tsize = malloc_usable_size(mem);\n\t\tmemcount += size;\n\t\tif(memcount > max_memcount){\n\t\t\tmax_memcount = memcount;\n\t\t}\n\t\tset_alloc_marker(mem, size);\n\t}\n\n\treturn mem;\n}\n\n\nBROKER_EXPORT void *mosquitto_realloc(void *ptr, size_t size)\n{\n\tvoid *mem;\n\tsize_t free_size = ptr != NULL ? malloc_usable_size(ptr) : 0UL;\n\n#if ALLOC_MARKER_SIZE\n\tbool alloc_mismatch = free_size > 0 && !check_alloc_marker(ptr, free_size);\n#endif\n\n\t/* Avoid counter underflow due to mismatched memory allocation function usage */\n\tif(free_size > memcount){\n\t\tfree_size = memcount;\n\t}\n\tif(mem_limit && memcount - free_size + size > mem_limit){\n\t\treturn NULL;\n\t}\n\tmem = realloc(ptr, size + ALLOC_MARKER_SIZE);\n#if ALLOC_MARKER_SIZE\n\tif(alloc_mismatch){\n\t\t/* This will not trigger if realloc was able to extend the memory in place. */\n\t\ttrigger_alloc_mismatch(ptr, free_size);\n\t}\n#endif\n\n\tif(mem){\n\t\tsize = malloc_usable_size(mem);\n\t\tmemcount -= free_size;\n\t\tmemcount += size;\n\t\tif(memcount > max_memcount){\n\t\t\tmax_memcount = memcount;\n\t\t}\n\t\tset_alloc_marker(mem, size);\n\t}else if(size == 0){\n\t\tmemcount -= free_size;\n\t}\n\n\treturn mem;\n}\n\n\nBROKER_EXPORT void mosquitto_free(void *mem)\n{\n\tif(!mem){\n\t\treturn;\n\t}\n\tsize_t free_size = malloc_usable_size(mem);\n#if ALLOC_MARKER_SIZE\n\tbool alloc_mismatch = !check_alloc_marker(mem, free_size);\n#ifdef __linux__\n\t__libc_free(mem);\n#else\n\tfree(mem);\n#endif\n\n\tif(alloc_mismatch){\n\t\ttrigger_alloc_mismatch(mem, free_size);\n\t}\n#else /* ALLOC_MARKER_SIZE */\n\tfree(mem);\n#endif /* ALLOC_MARKER_SIZE */\n\n\t/* Avoid counter underflow due to mismatched memory function allocation usage */\n\tif(free_size > memcount){\n\t\tfree_size = memcount;\n\t}\n\tmemcount -= free_size;\n}\n\n#else /* #ifdef WITH_REAL_MEMORY_TRACKING */\n\n\n/* ==================================================\n * Alloc functions without tracking\n * ================================================== */\n\n\nBROKER_EXPORT void *mosquitto_malloc(size_t size)\n{\n\treturn malloc(size);\n}\n\n\nBROKER_EXPORT void *mosquitto_realloc(void *ptr, size_t size)\n{\n\treturn realloc(ptr, size);\n}\n\n\nBROKER_EXPORT void mosquitto_free(void *mem)\n{\n\tfree(mem);\n}\n\n#endif /* #ifdef WITH_REAL_MEMORY_TRACKING */\n\n\n/* ==================================================\n * Alloc functions that use the tracked/untracked versions\n * ================================================== */\n\n\nBROKER_EXPORT void *mosquitto_calloc(size_t nmemb, size_t size)\n{\n\tvoid *mem;\n\tconst size_t alloc_size = nmemb * size;\n\tmem = mosquitto_malloc(alloc_size);\n\tif(mem){\n\t\tmemset(mem, 0, alloc_size);\n\t}\n\treturn mem;\n}\n\n\nBROKER_EXPORT char *mosquitto_strdup(const char *s)\n{\n\tchar *str;\n\tsize_t size = strlen(s) + 1;\n\n\tstr = mosquitto_malloc(size);\n\tif(str){\n\t\tmemcpy(str, s, size);\n\t}\n\treturn str;\n}\n\n\nBROKER_EXPORT char *mosquitto_strndup(const char *s, size_t n)\n{\n\tchar *str;\n\tsize_t size = strnlen(s, n);\n\n\tif(size > n){\n\t\tsize = n;\n\t}\n\tstr = mosquitto_malloc(size + 1);\n\tif(str){\n\t\tmemcpy(str, s, size);\n\t\tstr[size] = 0;\n\t}\n\treturn str;\n}\n"
  },
  {
    "path": "libcommon/mqtt_common.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdint.h>\n\n#include \"mosquitto.h\"\n\n\nunsigned int mosquitto_varint_bytes(uint32_t word)\n{\n\tif(word < 128){\n\t\treturn 1;\n\t}else if(word < 16384){\n\t\treturn 2;\n\t}else if(word < 2097152){\n\t\treturn 3;\n\t}else if(word < 268435456){\n\t\treturn 4;\n\t}else{\n\t\treturn 5;\n\t}\n}\n"
  },
  {
    "path": "libcommon/password_common.c",
    "content": "/*\nCopyright (c) 2012-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdbool.h>\n#include <string.h>\n\n#ifdef WITH_TLS\n#  include <openssl/opensslv.h>\n#  include <openssl/evp.h>\n#  include <openssl/rand.h>\n#  define HASH_LEN EVP_MAX_MD_SIZE\n#endif\n\n#include \"mosquitto.h\"\n\n#ifdef WITH_TLS\n#  define HASH_LEN EVP_MAX_MD_SIZE\n#else\n/* 64 bytes big enough for SHA512 */\n#  define HASH_LEN 64\n#endif\n\n#ifdef WITH_ARGON2\n#  include <argon2.h>\n#  define MOSQ_ARGON2_T 1\n#  define MOSQ_ARGON2_M 47104\n#  define MOSQ_ARGON2_P 1\n#endif\n\n#define PW_DEFAULT_ITERATIONS 1000\nstatic int pw__encode(struct mosquitto_pw *pw);\n\nstruct mosquitto_pw {\n\tunion {\n\t\tstruct {\n\t\t\tunsigned char password_hash[HASH_LEN]; /* For SHA512 */\n\t\t\tunsigned char salt[HASH_LEN];\n\t\t\tsize_t salt_len;\n\t\t} sha512;\n\t\tstruct {\n\t\t\tunsigned char password_hash[HASH_LEN]; /* For SHA512 */\n\t\t\tunsigned char salt[HASH_LEN];\n\t\t\tsize_t salt_len;\n\t\t\tint iterations;\n\t\t} sha512_pbkdf2;\n\t\tstruct {\n\t\t\tunsigned char password_hash[HASH_LEN];\n\t\t\tunsigned char salt[HASH_LEN];\n\t\t\tsize_t salt_len;\n\t\t\tint iterations;\n\t\t} argon2id;\n\t} params;\n\tchar *encoded_password;\n\tenum mosquitto_pwhash_type hashtype;\n\tbool valid;\n};\n\n\nstatic int pw__memcmp_const(const void *a, const void *b, size_t len)\n{\n#ifdef WITH_TLS\n\treturn CRYPTO_memcmp(a, b, len);\n#else\n\tint rc = 0;\n\tconst volatile char *ac = a;\n\tconst volatile char *bc = b;\n\n\tif(!a || !b){\n\t\treturn 1;\n\t}\n\n\tfor(size_t i=0; i<len; i++){\n\t\trc |= ((char *)ac)[i] ^ ((char *)bc)[i];\n\t}\n\treturn rc;\n#endif\n}\n\n\n/* ==================================================\n * ARGON2\n * ================================================== */\n\n\nstatic int pw__create_argon2id(struct mosquitto_pw *pw, const char *password)\n{\n#ifdef WITH_ARGON2\n\tpw->hashtype = MOSQ_PW_ARGON2ID;\n\tpw->params.argon2id.salt_len = HASH_LEN;\n\n\tint rc = mosquitto_getrandom(pw->params.argon2id.salt, (int)pw->params.argon2id.salt_len);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tsize_t encoded_len = argon2_encodedlen(MOSQ_ARGON2_T, MOSQ_ARGON2_M, MOSQ_ARGON2_P,\n\t\t\t(uint32_t)pw->params.argon2id.salt_len, sizeof(pw->params.argon2id.password_hash), Argon2_id);\n\n\tmosquitto_free(pw->encoded_password);\n\tpw->encoded_password = mosquitto_calloc(1, encoded_len+1);\n\n\trc = argon2id_hash_encoded(MOSQ_ARGON2_T, MOSQ_ARGON2_M, MOSQ_ARGON2_P,\n\t\t\tpassword, strlen(password),\n\t\t\tpw->params.argon2id.salt, pw->params.argon2id.salt_len,\n\t\t\tHASH_LEN,\n\t\t\tpw->encoded_password, encoded_len+1);\n\n\tif(rc == ARGON2_OK){\n\t\tpw->valid = true;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n#else\n\tUNUSED(pw);\n\tUNUSED(password);\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nstatic int pw__verify_argon2id(struct mosquitto_pw *pw, const char *password)\n{\n#ifdef WITH_ARGON2\n\tint rc = argon2id_verify(pw->encoded_password,\n\t\t\tpassword, strlen(password));\n\n\tif(rc == ARGON2_OK){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n#else\n\tUNUSED(pw);\n\tUNUSED(password);\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nstatic int pw__decode_argon2id(struct mosquitto_pw *pw, const char *password)\n{\n#ifdef WITH_ARGON2\n\tchar *new_password = mosquitto_strdup(password);\n\n\tif(new_password){\n\t\tmosquitto_free(pw->encoded_password);\n\t\tpw->encoded_password = new_password;\n\t\tpw->valid = true;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n#else\n\tUNUSED(pw);\n\tUNUSED(password);\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\n/* ==================================================\n * SHA512 PBKDF2\n * ================================================== */\n#ifdef WITH_TLS\n\n\nstatic int pw__hash_sha512_pbkdf2(const char *password, struct mosquitto_pw *pw, unsigned char *password_hash, unsigned int hash_len, int iterations)\n{\n\tconst EVP_MD *digest;\n\n\tdigest = EVP_get_digestbyname(\"sha512\");\n\tif(!digest){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tPKCS5_PBKDF2_HMAC(password, (int)strlen(password),\n\t\t\tpw->params.sha512.salt, (int)pw->params.sha512.salt_len, iterations,\n\t\t\tdigest, (int)hash_len, password_hash);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\nstatic int pw__create_sha512_pbkdf2(struct mosquitto_pw *pw, const char *password)\n{\n#ifdef WITH_TLS\n\tpw->hashtype = MOSQ_PW_SHA512_PBKDF2;\n\tpw->params.sha512_pbkdf2.salt_len = HASH_LEN;\n\tint rc = RAND_bytes(pw->params.sha512_pbkdf2.salt, (int)pw->params.sha512_pbkdf2.salt_len);\n\tif(!rc){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(pw->params.sha512_pbkdf2.iterations == 0){\n\t\tpw->params.sha512_pbkdf2.iterations = PW_DEFAULT_ITERATIONS;\n\t}\n\trc = pw__hash_sha512_pbkdf2(password, pw,\n\t\t\tpw->params.sha512_pbkdf2.password_hash,\n\t\t\tsizeof(pw->params.sha512_pbkdf2.password_hash),\n\t\t\tpw->params.sha512_pbkdf2.iterations);\n\n\tpw->valid = (rc == MOSQ_ERR_SUCCESS);\n\treturn rc;\n#else\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nstatic int pw__verify_sha512_pbkdf2(struct mosquitto_pw *pw, const char *password)\n{\n#ifdef WITH_TLS\n\tint rc;\n\tunsigned char password_hash[HASH_LEN];\n\n\trc = pw__hash_sha512_pbkdf2(password, pw,\n\t\t\tpassword_hash, sizeof(password_hash),\n\t\t\tpw->params.sha512_pbkdf2.iterations);\n\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\tif(!pw__memcmp_const(pw->params.sha512_pbkdf2.password_hash, password_hash, HASH_LEN)){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n#else\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nstatic int pw__encode_sha512_pbkdf2(struct mosquitto_pw *pw)\n{\n#ifdef WITH_TLS\n\tint rc;\n\tchar *salt64 = NULL, *hash64 = NULL;\n\n\trc = mosquitto_base64_encode(pw->params.sha512_pbkdf2.salt, pw->params.sha512_pbkdf2.salt_len, &salt64);\n\tif(rc){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\trc = mosquitto_base64_encode(pw->params.sha512_pbkdf2.password_hash, sizeof(pw->params.sha512_pbkdf2.password_hash), &hash64);\n\tif(rc){\n\t\tmosquitto_free(salt64);\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tmosquitto_free(pw->encoded_password);\n\tsize_t len = strlen(\"$6$$\") + strlen(\"1,000,000,000,000\") + strlen(salt64) + strlen(hash64) + 1;\n\tpw->encoded_password = mosquitto_calloc(1, len);\n\tif(!pw->encoded_password){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tsnprintf(pw->encoded_password, len, \"$%d$%d$%s$%s\", pw->hashtype, pw->params.sha512_pbkdf2.iterations, salt64, hash64);\n\n\tmosquitto_free(salt64);\n\tmosquitto_free(hash64);\n\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nstatic int pw__decode_sha512_pbkdf2(struct mosquitto_pw *pw, const char *salt_password)\n{\n#ifdef WITH_TLS\n\tchar *sp_heap, *saveptr = NULL;\n\tchar *iterations_s;\n\tchar *salt_b64, *password_b64;\n\tunsigned char *salt, *password;\n\tunsigned int salt_len, password_len;\n\tint rc;\n\n\tsp_heap = mosquitto_strdup(salt_password);\n\tif(!sp_heap){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\titerations_s = strtok_r(sp_heap, \"$\", &saveptr);\n\tif(iterations_s == NULL){\n\t\tmosquitto_free(sp_heap);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tpw->params.sha512_pbkdf2.iterations = atoi(iterations_s);\n\tif(pw->params.sha512_pbkdf2.iterations < 1){\n\t\tmosquitto_free(sp_heap);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tsalt_b64 = strtok_r(NULL, \"$\", &saveptr);\n\tif(salt_b64 == NULL){\n\t\tmosquitto_free(sp_heap);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trc = mosquitto_base64_decode(salt_b64, &salt, &salt_len);\n\tif(rc != MOSQ_ERR_SUCCESS || (salt_len != 12 && salt_len != HASH_LEN)){\n\t\tmosquitto_free(sp_heap);\n\t\tmosquitto_free(salt);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tmemcpy(pw->params.sha512_pbkdf2.salt, salt, salt_len);\n\tmosquitto_free(salt);\n\tpw->params.sha512_pbkdf2.salt_len = salt_len;\n\n\tpassword_b64 = strtok_r(NULL, \"$\", &saveptr);\n\tif(password_b64 == NULL){\n\t\tmosquitto_free(sp_heap);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trc = mosquitto_base64_decode(password_b64, &password, &password_len);\n\tmosquitto_free(sp_heap);\n\n\tif(rc != MOSQ_ERR_SUCCESS || password_len != HASH_LEN){\n\t\tmosquitto_free(password);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tmemcpy(pw->params.sha512_pbkdf2.password_hash, password, password_len);\n\tmosquitto_free(password);\n\n\tpw->valid = true;\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\n/* ==================================================\n * SHA512\n * ================================================== */\n#ifdef WITH_TLS\n\n\nstatic int pw__hash_sha512(const char *password, struct mosquitto_pw *pw, unsigned char *password_hash, unsigned int hash_len)\n{\n\tconst EVP_MD *digest;\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n\tEVP_MD_CTX context;\n#else\n\tEVP_MD_CTX *context;\n#endif\n\n\tdigest = EVP_get_digestbyname(\"sha512\");\n\tif(!digest){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n\tEVP_MD_CTX_init(&context);\n\tEVP_DigestInit_ex(&context, digest, NULL);\n\tEVP_DigestUpdate(&context, password, strlen(password));\n\tEVP_DigestUpdate(&context, pw->params.sha512.salt, pw->params.sha512.salt_len);\n\tEVP_DigestFinal_ex(&context, password_hash, &hash_len);\n\tEVP_MD_CTX_cleanup(&context);\n#else\n\tcontext = EVP_MD_CTX_new();\n\tEVP_DigestInit_ex(context, digest, NULL);\n\tEVP_DigestUpdate(context, password, strlen(password));\n\tEVP_DigestUpdate(context, pw->params.sha512.salt, pw->params.sha512.salt_len);\n\tEVP_DigestFinal_ex(context, password_hash, &hash_len);\n\tEVP_MD_CTX_free(context);\n#endif\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\nstatic int pw__create_sha512(struct mosquitto_pw *pw, const char *password)\n{\n#ifdef WITH_TLS\n\tpw->hashtype = MOSQ_PW_SHA512;\n\tpw->params.sha512.salt_len = HASH_LEN;\n\tint rc = RAND_bytes(pw->params.sha512.salt, (int)pw->params.sha512.salt_len);\n\tif(!rc){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\trc = pw__hash_sha512(password, pw, pw->params.sha512.password_hash, sizeof(pw->params.sha512.password_hash));\n\tpw->valid = (rc == MOSQ_ERR_SUCCESS);\n\treturn rc;\n#else\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nstatic int pw__verify_sha512(struct mosquitto_pw *pw, const char *password)\n{\n#ifdef WITH_TLS\n\tint rc;\n\tunsigned char password_hash[HASH_LEN];\n\n\trc = pw__hash_sha512(password, pw, password_hash, sizeof(password_hash));\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\tif(!pw__memcmp_const(pw->params.sha512.password_hash, password_hash, HASH_LEN)){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n#else\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nstatic int pw__encode_sha512(struct mosquitto_pw *pw)\n{\n#ifdef WITH_TLS\n\tint rc;\n\tchar *salt64 = NULL, *hash64 = NULL;\n\n\trc = mosquitto_base64_encode(pw->params.sha512.salt, pw->params.sha512.salt_len, &salt64);\n\tif(rc){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\trc = mosquitto_base64_encode(pw->params.sha512.password_hash, sizeof(pw->params.sha512.password_hash), &hash64);\n\tif(rc){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tmosquitto_free(pw->encoded_password);\n\tsize_t len = strlen(\"$6$$\") + strlen(salt64) + strlen(hash64) + 1;\n\tpw->encoded_password = mosquitto_calloc(1, len);\n\tif(!pw->encoded_password){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tsnprintf(pw->encoded_password, len, \"$%d$%s$%s\", pw->hashtype, salt64, hash64);\n\n\tmosquitto_free(salt64);\n\tmosquitto_free(hash64);\n\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nstatic int pw__decode_sha512(struct mosquitto_pw *pw, const char *salt_password)\n{\n#ifdef WITH_TLS\n\tchar *sp_heap, *saveptr = NULL;\n\tchar *salt_b64, *password_b64;\n\tunsigned char *salt, *password;\n\tunsigned int salt_len, password_len;\n\tint rc;\n\n\tsp_heap = mosquitto_strdup(salt_password);\n\tif(!sp_heap){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tsalt_b64 = strtok_r(sp_heap, \"$\", &saveptr);\n\tif(salt_b64 == NULL){\n\t\tmosquitto_free(sp_heap);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trc = mosquitto_base64_decode(salt_b64, &salt, &salt_len);\n\tif(rc != MOSQ_ERR_SUCCESS || (salt_len != 12 && salt_len != HASH_LEN)){\n\t\tmosquitto_free(sp_heap);\n\t\tmosquitto_free(salt);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tmemcpy(pw->params.sha512.salt, salt, salt_len);\n\tmosquitto_free(salt);\n\tpw->params.sha512.salt_len = salt_len;\n\n\tpassword_b64 = strtok_r(NULL, \"$\", &saveptr);\n\tif(password_b64 == NULL){\n\t\tmosquitto_free(sp_heap);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trc = mosquitto_base64_decode(password_b64, &password, &password_len);\n\tmosquitto_free(sp_heap);\n\n\tif(rc != MOSQ_ERR_SUCCESS || password_len != HASH_LEN){\n\t\tmosquitto_free(password);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tmemcpy(pw->params.sha512.password_hash, password, password_len);\n\tmosquitto_free(password);\n\n\tpw->valid = true;\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nstatic int pw__encode(struct mosquitto_pw *pw)\n{\n\tswitch(pw->hashtype){\n\t\tcase MOSQ_PW_ARGON2ID:\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\tcase MOSQ_PW_SHA512_PBKDF2:\n\t\t\treturn pw__encode_sha512_pbkdf2(pw);\n\t\tcase MOSQ_PW_SHA512:\n\t\t\treturn pw__encode_sha512(pw);\n\t\tcase MOSQ_PW_DEFAULT:\n\t\t\tbreak;\n\t}\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n\n/* ==================================================\n * Public\n * ================================================== */\n\n\nint mosquitto_pw_new(struct mosquitto_pw **pw, enum mosquitto_pwhash_type hashtype)\n{\n\t*pw = mosquitto_calloc(1, sizeof(struct mosquitto_pw));\n\tif(*pw){\n\t\t(*pw)->hashtype = hashtype;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n}\n\n\nint mosquitto_pw_hash_encoded(struct mosquitto_pw *pw, const char *password)\n{\n\tint rc = MOSQ_ERR_INVAL;\n\n\tswitch(pw->hashtype){\n\t\tcase MOSQ_PW_ARGON2ID:\n\t\t\trc = pw__create_argon2id(pw, password);\n\t\t\tbreak;\n\t\tcase MOSQ_PW_DEFAULT:\n\t\tcase MOSQ_PW_SHA512_PBKDF2:\n\t\t\trc = pw__create_sha512_pbkdf2(pw, password);\n\t\t\tbreak;\n\t\tcase MOSQ_PW_SHA512:\n\t\t\trc = pw__create_sha512(pw, password);\n\t\t\tbreak;\n\t\tdefault:\n#ifdef WITH_ARGON2\n\t\t\trc = pw__create_argon2id(pw, password);\n#else\n\t\t\trc = pw__create_sha512_pbkdf2(pw, password);\n#endif\n\t\t\tbreak;\n\t}\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\treturn pw__encode(pw);\n\t}else{\n\t\treturn rc;\n\t}\n}\n\n\nint mosquitto_pw_verify(struct mosquitto_pw *pw, const char *password)\n{\n\tif(pw && pw->valid){\n\t\tswitch(pw->hashtype){\n\t\t\tcase MOSQ_PW_ARGON2ID:\n\t\t\t\treturn pw__verify_argon2id(pw, password);\n\t\t\tcase MOSQ_PW_SHA512_PBKDF2:\n\t\t\t\treturn pw__verify_sha512_pbkdf2(pw, password);\n\t\t\tcase MOSQ_PW_SHA512:\n\t\t\t\treturn pw__verify_sha512(pw, password);\n\t\t\tcase MOSQ_PW_DEFAULT:\n\t\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n\nvoid mosquitto_pw_set_valid(struct mosquitto_pw *pw, bool valid)\n{\n\tif(pw){\n\t\tpw->valid = valid;\n\t}\n}\n\n\nbool mosquitto_pw_is_valid(struct mosquitto_pw *pw)\n{\n\treturn pw && pw->valid;\n}\n\n\nint mosquitto_pw_decode(struct mosquitto_pw *pw, const char *password)\n{\n\tif(!pw){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tpw->valid = false;\n\tif(password[0] != '$'){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tpw->encoded_password = mosquitto_strdup(password);\n\tif(!pw->encoded_password){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(password[1] == '6' && password[2] == '$'){\n\t\tpw->hashtype = MOSQ_PW_SHA512;\n\t\treturn pw__decode_sha512(pw, &password[3]);\n\t}else if(password[1] == '7' && password[2] == '$'){\n\t\tpw->hashtype = MOSQ_PW_SHA512_PBKDF2;\n\t\treturn pw__decode_sha512_pbkdf2(pw, &password[3]);\n\t}else if(!strncmp(password, \"$argon2id$\", strlen(\"$argon2id$\"))){\n\t\tpw->hashtype = MOSQ_PW_ARGON2ID;\n\t\treturn pw__decode_argon2id(pw, password);\n\t}else{\n\t\tmosquitto_FREE(pw->encoded_password);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n}\n\n\nconst char *mosquitto_pw_get_encoded(struct mosquitto_pw *pw)\n{\n\treturn pw?pw->encoded_password:NULL;\n}\n\n\nint mosquitto_pw_set_param(struct mosquitto_pw *pw, int param, int value)\n{\n\tif(!pw){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tswitch(param){\n\t\tcase MOSQ_PW_PARAM_ITERATIONS:\n\t\t\tif(pw->hashtype != MOSQ_PW_SHA512_PBKDF2){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\tpw->params.sha512_pbkdf2.iterations = value;\n\t\t\tbreak;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid mosquitto_pw_cleanup(struct mosquitto_pw *pw)\n{\n\tif(pw){\n\t\tmosquitto_free(pw->encoded_password);\n\t\tpw->encoded_password = NULL;\n\t\tmosquitto_free(pw);\n\t}\n}\n"
  },
  {
    "path": "libcommon/property_common.c",
    "content": "/*\nCopyright (c) 2018-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifndef WIN32\n#  include <strings.h>\n#endif\n\n#include \"mosquitto.h\"\n#include \"property_common.h\"\n\n\nvoid mosquitto_property_free(mosquitto_property **property)\n{\n\tif(!property || !(*property)){\n\t\treturn;\n\t}\n\n\tswitch((*property)->property_type){\n\t\tcase MQTT_PROP_TYPE_STRING:\n\t\t\tmosquitto_FREE((*property)->value.s.v);\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_TYPE_BINARY:\n\t\t\tmosquitto_FREE((*property)->value.bin.v);\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_TYPE_STRING_PAIR:\n\t\t\tmosquitto_FREE((*property)->name.v);\n\t\t\tmosquitto_FREE((*property)->value.s.v);\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_TYPE_BYTE:\n\t\tcase MQTT_PROP_TYPE_INT16:\n\t\tcase MQTT_PROP_TYPE_INT32:\n\t\tcase MQTT_PROP_TYPE_VARINT:\n\t\t\t/* Nothing to free */\n\t\t\tbreak;\n\t}\n\n\tmosquitto_FREE(*property);\n}\n\n\nBROKER_EXPORT void mosquitto_property_free_all(mosquitto_property **property)\n{\n\tmosquitto_property *p, *next;\n\n\tif(!property){\n\t\treturn;\n\t}\n\n\tp = *property;\n\twhile(p){\n\t\tnext = p->next;\n\t\tmosquitto_property_free(&p);\n\t\tp = next;\n\t}\n\t*property = NULL;\n}\n\n\nunsigned int mosquitto_property_get_length(const mosquitto_property *property)\n{\n\tif(!property){\n\t\treturn 0;\n\t}\n\n\tswitch(property->property_type){\n\t\tcase MQTT_PROP_TYPE_BYTE:\n\t\t\treturn 2; /* 1 (identifier) + 1 byte */\n\n\t\tcase MQTT_PROP_TYPE_INT16:\n\t\t\treturn 3; /* 1 (identifier) + 2 bytes */\n\n\t\tcase MQTT_PROP_TYPE_INT32:\n\t\t\treturn 5; /* 1 (identifier) + 4 bytes */\n\n\t\tcase MQTT_PROP_TYPE_VARINT:\n\t\t\tif(property->value.varint < 128){\n\t\t\t\treturn 2;\n\t\t\t}else if(property->value.varint < 16384){\n\t\t\t\treturn 3;\n\t\t\t}else if(property->value.varint < 2097152){\n\t\t\t\treturn 4;\n\t\t\t}else if(property->value.varint < 268435456){\n\t\t\t\treturn 5;\n\t\t\t}else{\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\tcase MQTT_PROP_TYPE_BINARY:\n\t\t\treturn 3U + property->value.bin.len; /* 1 + 2 bytes (len) + X bytes (payload) */\n\n\t\tcase MQTT_PROP_TYPE_STRING:\n\t\t\treturn 3U + property->value.s.len; /* 1 + 2 bytes (len) + X bytes (string) */\n\n\t\tcase MQTT_PROP_TYPE_STRING_PAIR:\n\t\t\treturn 5U + property->value.s.len + property->name.len; /* 1 + 2*(2 bytes (len) + X bytes (string))*/\n\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n\treturn 0;\n}\n\n\nunsigned int mosquitto_property_get_length_all(const mosquitto_property *property)\n{\n\tconst mosquitto_property *p;\n\tunsigned int len = 0;\n\n\tp = property;\n\twhile(p){\n\t\tlen += mosquitto_property_get_length(p);\n\t\tp = p->next;\n\t}\n\treturn len;\n}\n\n\nBROKER_EXPORT int mosquitto_property_check_command(int command, int identifier)\n{\n\tswitch(identifier){\n\t\tcase MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:\n\t\tcase MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:\n\t\tcase MQTT_PROP_CONTENT_TYPE:\n\t\tcase MQTT_PROP_RESPONSE_TOPIC:\n\t\tcase MQTT_PROP_CORRELATION_DATA:\n\t\t\tif(command != CMD_PUBLISH && command != CMD_WILL){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_SUBSCRIPTION_IDENTIFIER:\n\t\t\tif(command != CMD_PUBLISH && command != CMD_SUBSCRIBE){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_SESSION_EXPIRY_INTERVAL:\n\t\t\tif(command != CMD_CONNECT && command != CMD_CONNACK && command != CMD_DISCONNECT){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_AUTHENTICATION_METHOD:\n\t\tcase MQTT_PROP_AUTHENTICATION_DATA:\n\t\t\tif(command != CMD_CONNECT && command != CMD_CONNACK && command != CMD_AUTH){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER:\n\t\tcase MQTT_PROP_SERVER_KEEP_ALIVE:\n\t\tcase MQTT_PROP_RESPONSE_INFORMATION:\n\t\tcase MQTT_PROP_MAXIMUM_QOS:\n\t\tcase MQTT_PROP_RETAIN_AVAILABLE:\n\t\tcase MQTT_PROP_WILDCARD_SUB_AVAILABLE:\n\t\tcase MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE:\n\t\tcase MQTT_PROP_SHARED_SUB_AVAILABLE:\n\t\t\tif(command != CMD_CONNACK){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_WILL_DELAY_INTERVAL:\n\t\t\tif(command != CMD_WILL){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_REQUEST_PROBLEM_INFORMATION:\n\t\tcase MQTT_PROP_REQUEST_RESPONSE_INFORMATION:\n\t\t\tif(command != CMD_CONNECT){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_SERVER_REFERENCE:\n\t\t\tif(command != CMD_CONNACK && command != CMD_DISCONNECT){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_REASON_STRING:\n\t\t\tif(command == CMD_CONNECT || command == CMD_PUBLISH || command == CMD_SUBSCRIBE || command == CMD_UNSUBSCRIBE){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_RECEIVE_MAXIMUM:\n\t\tcase MQTT_PROP_TOPIC_ALIAS_MAXIMUM:\n\t\tcase MQTT_PROP_MAXIMUM_PACKET_SIZE:\n\t\t\tif(command != CMD_CONNECT && command != CMD_CONNACK){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_TOPIC_ALIAS:\n\t\t\tif(command != CMD_PUBLISH){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MQTT_PROP_USER_PROPERTY:\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT const char *mosquitto_property_identifier_to_string(int identifier)\n{\n\tswitch(identifier){\n\t\tcase MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:\n\t\t\treturn \"payload-format-indicator\";\n\t\tcase MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:\n\t\t\treturn \"message-expiry-interval\";\n\t\tcase MQTT_PROP_CONTENT_TYPE:\n\t\t\treturn \"content-type\";\n\t\tcase MQTT_PROP_RESPONSE_TOPIC:\n\t\t\treturn \"response-topic\";\n\t\tcase MQTT_PROP_CORRELATION_DATA:\n\t\t\treturn \"correlation-data\";\n\t\tcase MQTT_PROP_SUBSCRIPTION_IDENTIFIER:\n\t\t\treturn \"subscription-identifier\";\n\t\tcase MQTT_PROP_SESSION_EXPIRY_INTERVAL:\n\t\t\treturn \"session-expiry-interval\";\n\t\tcase MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER:\n\t\t\treturn \"assigned-client-identifier\";\n\t\tcase MQTT_PROP_SERVER_KEEP_ALIVE:\n\t\t\treturn \"server-keep-alive\";\n\t\tcase MQTT_PROP_AUTHENTICATION_METHOD:\n\t\t\treturn \"authentication-method\";\n\t\tcase MQTT_PROP_AUTHENTICATION_DATA:\n\t\t\treturn \"authentication-data\";\n\t\tcase MQTT_PROP_REQUEST_PROBLEM_INFORMATION:\n\t\t\treturn \"request-problem-information\";\n\t\tcase MQTT_PROP_WILL_DELAY_INTERVAL:\n\t\t\treturn \"will-delay-interval\";\n\t\tcase MQTT_PROP_REQUEST_RESPONSE_INFORMATION:\n\t\t\treturn \"request-response-information\";\n\t\tcase MQTT_PROP_RESPONSE_INFORMATION:\n\t\t\treturn \"response-information\";\n\t\tcase MQTT_PROP_SERVER_REFERENCE:\n\t\t\treturn \"server-reference\";\n\t\tcase MQTT_PROP_REASON_STRING:\n\t\t\treturn \"reason-string\";\n\t\tcase MQTT_PROP_RECEIVE_MAXIMUM:\n\t\t\treturn \"receive-maximum\";\n\t\tcase MQTT_PROP_TOPIC_ALIAS_MAXIMUM:\n\t\t\treturn \"topic-alias-maximum\";\n\t\tcase MQTT_PROP_TOPIC_ALIAS:\n\t\t\treturn \"topic-alias\";\n\t\tcase MQTT_PROP_MAXIMUM_QOS:\n\t\t\treturn \"maximum-qos\";\n\t\tcase MQTT_PROP_RETAIN_AVAILABLE:\n\t\t\treturn \"retain-available\";\n\t\tcase MQTT_PROP_USER_PROPERTY:\n\t\t\treturn \"user-property\";\n\t\tcase MQTT_PROP_MAXIMUM_PACKET_SIZE:\n\t\t\treturn \"maximum-packet-size\";\n\t\tcase MQTT_PROP_WILDCARD_SUB_AVAILABLE:\n\t\t\treturn \"wildcard-subscription-available\";\n\t\tcase MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE:\n\t\t\treturn \"subscription-identifier-available\";\n\t\tcase MQTT_PROP_SHARED_SUB_AVAILABLE:\n\t\t\treturn \"shared-subscription-available\";\n\t\tdefault:\n\t\t\treturn NULL;\n\t}\n}\n\n\nBROKER_EXPORT int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type)\n{\n\tif(!propname){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(!strcasecmp(propname, \"payload-format-indicator\")){\n\t\t*identifier = MQTT_PROP_PAYLOAD_FORMAT_INDICATOR;\n\t\t*type = MQTT_PROP_TYPE_BYTE;\n\t}else if(!strcasecmp(propname, \"message-expiry-interval\")){\n\t\t*identifier = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL;\n\t\t*type = MQTT_PROP_TYPE_INT32;\n\t}else if(!strcasecmp(propname, \"content-type\")){\n\t\t*identifier = MQTT_PROP_CONTENT_TYPE;\n\t\t*type = MQTT_PROP_TYPE_STRING;\n\t}else if(!strcasecmp(propname, \"response-topic\")){\n\t\t*identifier = MQTT_PROP_RESPONSE_TOPIC;\n\t\t*type = MQTT_PROP_TYPE_STRING;\n\t}else if(!strcasecmp(propname, \"correlation-data\")){\n\t\t*identifier = MQTT_PROP_CORRELATION_DATA;\n\t\t*type = MQTT_PROP_TYPE_BINARY;\n\t}else if(!strcasecmp(propname, \"subscription-identifier\")){\n\t\t*identifier = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\t\t*type = MQTT_PROP_TYPE_VARINT;\n\t}else if(!strcasecmp(propname, \"session-expiry-interval\")){\n\t\t*identifier = MQTT_PROP_SESSION_EXPIRY_INTERVAL;\n\t\t*type = MQTT_PROP_TYPE_INT32;\n\t}else if(!strcasecmp(propname, \"assigned-client-identifier\")){\n\t\t*identifier = MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER;\n\t\t*type = MQTT_PROP_TYPE_STRING;\n\t}else if(!strcasecmp(propname, \"server-keep-alive\")){\n\t\t*identifier = MQTT_PROP_SERVER_KEEP_ALIVE;\n\t\t*type = MQTT_PROP_TYPE_INT16;\n\t}else if(!strcasecmp(propname, \"authentication-method\")){\n\t\t*identifier = MQTT_PROP_AUTHENTICATION_METHOD;\n\t\t*type = MQTT_PROP_TYPE_STRING;\n\t}else if(!strcasecmp(propname, \"authentication-data\")){\n\t\t*identifier = MQTT_PROP_AUTHENTICATION_DATA;\n\t\t*type = MQTT_PROP_TYPE_BINARY;\n\t}else if(!strcasecmp(propname, \"request-problem-information\")){\n\t\t*identifier = MQTT_PROP_REQUEST_PROBLEM_INFORMATION;\n\t\t*type = MQTT_PROP_TYPE_BYTE;\n\t}else if(!strcasecmp(propname, \"will-delay-interval\")){\n\t\t*identifier = MQTT_PROP_WILL_DELAY_INTERVAL;\n\t\t*type = MQTT_PROP_TYPE_INT32;\n\t}else if(!strcasecmp(propname, \"request-response-information\")){\n\t\t*identifier = MQTT_PROP_REQUEST_RESPONSE_INFORMATION;\n\t\t*type = MQTT_PROP_TYPE_BYTE;\n\t}else if(!strcasecmp(propname, \"response-information\")){\n\t\t*identifier = MQTT_PROP_RESPONSE_INFORMATION;\n\t\t*type = MQTT_PROP_TYPE_STRING;\n\t}else if(!strcasecmp(propname, \"server-reference\")){\n\t\t*identifier = MQTT_PROP_SERVER_REFERENCE;\n\t\t*type = MQTT_PROP_TYPE_STRING;\n\t}else if(!strcasecmp(propname, \"reason-string\")){\n\t\t*identifier = MQTT_PROP_REASON_STRING;\n\t\t*type = MQTT_PROP_TYPE_STRING;\n\t}else if(!strcasecmp(propname, \"receive-maximum\")){\n\t\t*identifier = MQTT_PROP_RECEIVE_MAXIMUM;\n\t\t*type = MQTT_PROP_TYPE_INT16;\n\t}else if(!strcasecmp(propname, \"topic-alias-maximum\")){\n\t\t*identifier = MQTT_PROP_TOPIC_ALIAS_MAXIMUM;\n\t\t*type = MQTT_PROP_TYPE_INT16;\n\t}else if(!strcasecmp(propname, \"topic-alias\")){\n\t\t*identifier = MQTT_PROP_TOPIC_ALIAS;\n\t\t*type = MQTT_PROP_TYPE_INT16;\n\t}else if(!strcasecmp(propname, \"maximum-qos\")){\n\t\t*identifier = MQTT_PROP_MAXIMUM_QOS;\n\t\t*type = MQTT_PROP_TYPE_BYTE;\n\t}else if(!strcasecmp(propname, \"retain-available\")){\n\t\t*identifier = MQTT_PROP_RETAIN_AVAILABLE;\n\t\t*type = MQTT_PROP_TYPE_BYTE;\n\t}else if(!strcasecmp(propname, \"user-property\")){\n\t\t*identifier = MQTT_PROP_USER_PROPERTY;\n\t\t*type = MQTT_PROP_TYPE_STRING_PAIR;\n\t}else if(!strcasecmp(propname, \"maximum-packet-size\")){\n\t\t*identifier = MQTT_PROP_MAXIMUM_PACKET_SIZE;\n\t\t*type = MQTT_PROP_TYPE_INT32;\n\t}else if(!strcasecmp(propname, \"wildcard-subscription-available\")){\n\t\t*identifier = MQTT_PROP_WILDCARD_SUB_AVAILABLE;\n\t\t*type = MQTT_PROP_TYPE_BYTE;\n\t}else if(!strcasecmp(propname, \"subscription-identifier-available\")){\n\t\t*identifier = MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE;\n\t\t*type = MQTT_PROP_TYPE_BYTE;\n\t}else if(!strcasecmp(propname, \"shared-subscription-available\")){\n\t\t*identifier = MQTT_PROP_SHARED_SUB_AVAILABLE;\n\t\t*type = MQTT_PROP_TYPE_BYTE;\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void property__add(mosquitto_property **proplist, struct mqtt5__property *prop)\n{\n\tmosquitto_property *p;\n\n\tif(!(*proplist)){\n\t\t*proplist = prop;\n\t}\n\n\tp = *proplist;\n\twhile(p->next){\n\t\tp = p->next;\n\t}\n\tp->next = prop;\n\tprop->next = NULL;\n}\n\n\nBROKER_EXPORT int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value)\n{\n\tmosquitto_property *prop;\n\n\tif(!proplist){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(identifier != MQTT_PROP_PAYLOAD_FORMAT_INDICATOR\n\t\t\t&& identifier != MQTT_PROP_REQUEST_PROBLEM_INFORMATION\n\t\t\t&& identifier != MQTT_PROP_REQUEST_RESPONSE_INFORMATION\n\t\t\t&& identifier != MQTT_PROP_MAXIMUM_QOS\n\t\t\t&& identifier != MQTT_PROP_RETAIN_AVAILABLE\n\t\t\t&& identifier != MQTT_PROP_WILDCARD_SUB_AVAILABLE\n\t\t\t&& identifier != MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE\n\t\t\t&& identifier != MQTT_PROP_SHARED_SUB_AVAILABLE){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tprop = mosquitto_calloc(1, sizeof(mosquitto_property));\n\tif(!prop){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tprop->client_generated = true;\n\tprop->identifier = identifier;\n\tprop->value.i8 = value;\n\tprop->property_type = MQTT_PROP_TYPE_BYTE;\n\n\tproperty__add(proplist, prop);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value)\n{\n\tmosquitto_property *prop;\n\n\tif(!proplist){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(identifier != MQTT_PROP_SERVER_KEEP_ALIVE\n\t\t\t&& identifier != MQTT_PROP_RECEIVE_MAXIMUM\n\t\t\t&& identifier != MQTT_PROP_TOPIC_ALIAS_MAXIMUM\n\t\t\t&& identifier != MQTT_PROP_TOPIC_ALIAS){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tprop = mosquitto_calloc(1, sizeof(mosquitto_property));\n\tif(!prop){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tprop->client_generated = true;\n\tprop->identifier = identifier;\n\tprop->value.i16 = value;\n\tprop->property_type = MQTT_PROP_TYPE_INT16;\n\n\tproperty__add(proplist, prop);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value)\n{\n\tmosquitto_property *prop;\n\n\tif(!proplist){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(identifier != MQTT_PROP_MESSAGE_EXPIRY_INTERVAL\n\t\t\t&& identifier != MQTT_PROP_SESSION_EXPIRY_INTERVAL\n\t\t\t&& identifier != MQTT_PROP_WILL_DELAY_INTERVAL\n\t\t\t&& identifier != MQTT_PROP_MAXIMUM_PACKET_SIZE){\n\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tprop = mosquitto_calloc(1, sizeof(mosquitto_property));\n\tif(!prop){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tprop->client_generated = true;\n\tprop->identifier = identifier;\n\tprop->value.i32 = value;\n\tprop->property_type = MQTT_PROP_TYPE_INT32;\n\n\tproperty__add(proplist, prop);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value)\n{\n\tmosquitto_property *prop;\n\n\tif(!proplist || value > MQTT_MAX_PAYLOAD){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tprop = mosquitto_calloc(1, sizeof(mosquitto_property));\n\tif(!prop){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tprop->client_generated = true;\n\tprop->identifier = identifier;\n\tprop->value.varint = value;\n\tprop->property_type = MQTT_PROP_TYPE_VARINT;\n\n\tproperty__add(proplist, prop);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len)\n{\n\tmosquitto_property *prop;\n\n\tif(!proplist){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(identifier != MQTT_PROP_CORRELATION_DATA\n\t\t\t&& identifier != MQTT_PROP_AUTHENTICATION_DATA){\n\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tprop = mosquitto_calloc(1, sizeof(mosquitto_property));\n\tif(!prop){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tprop->client_generated = true;\n\tprop->identifier = identifier;\n\tprop->property_type = MQTT_PROP_TYPE_BINARY;\n\n\tif(len){\n\t\tprop->value.bin.v = mosquitto_malloc(len);\n\t\tif(!prop->value.bin.v){\n\t\t\tmosquitto_FREE(prop);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tmemcpy(prop->value.bin.v, value, len);\n\t\tprop->value.bin.len = len;\n\t}\n\n\tproperty__add(proplist, prop);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value)\n{\n\tmosquitto_property *prop;\n\tsize_t slen = 0;\n\n\tif(!proplist){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(value){\n\t\tslen = strlen(value);\n\t\tif(mosquitto_validate_utf8(value, (int)slen)){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t}\n\n\tif(identifier != MQTT_PROP_CONTENT_TYPE\n\t\t\t&& identifier != MQTT_PROP_RESPONSE_TOPIC\n\t\t\t&& identifier != MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER\n\t\t\t&& identifier != MQTT_PROP_AUTHENTICATION_METHOD\n\t\t\t&& identifier != MQTT_PROP_RESPONSE_INFORMATION\n\t\t\t&& identifier != MQTT_PROP_SERVER_REFERENCE\n\t\t\t&& identifier != MQTT_PROP_REASON_STRING){\n\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tprop = mosquitto_calloc(1, sizeof(mosquitto_property));\n\tif(!prop){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tprop->client_generated = true;\n\tprop->identifier = identifier;\n\tprop->property_type = MQTT_PROP_TYPE_STRING;\n\tif(value && slen > 0){\n\t\tprop->value.s.v = mosquitto_strdup(value);\n\t\tif(!prop->value.s.v){\n\t\t\tmosquitto_FREE(prop);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tprop->value.s.len = (uint16_t)slen;\n\t}\n\n\tproperty__add(proplist, prop);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value)\n{\n\tmosquitto_property *prop;\n\tsize_t slen_name = 0, slen_value = 0;\n\n\tif(!proplist){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(identifier != MQTT_PROP_USER_PROPERTY){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(name){\n\t\tslen_name = strlen(name);\n\t\tif(mosquitto_validate_utf8(name, (int)slen_name)){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t}\n\tif(value){\n\t\tif(mosquitto_validate_utf8(value, (int)slen_value)){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t}\n\n\tprop = mosquitto_calloc(1, sizeof(mosquitto_property));\n\tif(!prop){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tprop->client_generated = true;\n\tprop->identifier = identifier;\n\tprop->property_type = MQTT_PROP_TYPE_STRING_PAIR;\n\n\tif(name){\n\t\tprop->name.v = mosquitto_strdup(name);\n\t\tif(!prop->name.v){\n\t\t\tmosquitto_FREE(prop);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tprop->name.len = (uint16_t)strlen(name);\n\t}\n\n\tif(value){\n\t\tprop->value.s.v = mosquitto_strdup(value);\n\t\tif(!prop->value.s.v){\n\t\t\tmosquitto_FREE(prop->name.v);\n\t\t\tmosquitto_FREE(prop);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tprop->value.s.len = (uint16_t)strlen(value);\n\t}\n\n\tproperty__add(proplist, prop);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_property_check_all(int command, const mosquitto_property *properties)\n{\n\tconst mosquitto_property *p, *tail;\n\tint rc;\n\n\tp = properties;\n\n\twhile(p){\n\t\t/* Validity checks */\n\t\tif(p->identifier == MQTT_PROP_REQUEST_PROBLEM_INFORMATION\n\t\t\t\t|| p->identifier == MQTT_PROP_PAYLOAD_FORMAT_INDICATOR\n\t\t\t\t|| p->identifier == MQTT_PROP_REQUEST_RESPONSE_INFORMATION\n\t\t\t\t|| p->identifier == MQTT_PROP_MAXIMUM_QOS\n\t\t\t\t|| p->identifier == MQTT_PROP_RETAIN_AVAILABLE\n\t\t\t\t|| p->identifier == MQTT_PROP_WILDCARD_SUB_AVAILABLE\n\t\t\t\t|| p->identifier == MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE\n\t\t\t\t|| p->identifier == MQTT_PROP_SHARED_SUB_AVAILABLE){\n\n\t\t\tif(p->value.i8 > 1){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t}else if(p->identifier == MQTT_PROP_MAXIMUM_PACKET_SIZE){\n\t\t\tif(p->value.i32 == 0){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t}else if(p->identifier == MQTT_PROP_RECEIVE_MAXIMUM\n\t\t\t\t|| p->identifier == MQTT_PROP_TOPIC_ALIAS){\n\n\t\t\tif(p->value.i16 == 0){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t}else if(p->identifier == MQTT_PROP_RESPONSE_TOPIC){\n\t\t\tif(mosquitto_pub_topic_check(p->value.s.v) != MOSQ_ERR_SUCCESS){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t}\n\n\t\t/* Check for properties on incorrect commands */\n\t\trc = mosquitto_property_check_command(command, p->identifier);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n\t\t/* Check for duplicates */\n\t\tif(p->identifier != MQTT_PROP_USER_PROPERTY){\n\t\t\ttail = p->next;\n\t\t\twhile(tail){\n\t\t\t\tif(p->identifier == tail->identifier){\n\t\t\t\t\treturn MOSQ_ERR_DUPLICATE_PROPERTY;\n\t\t\t\t}\n\t\t\t\ttail = tail->next;\n\t\t\t}\n\t\t}\n\n\t\tp = p->next;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic const mosquitto_property *property__get_property(const mosquitto_property *proplist, int identifier, bool skip_first)\n{\n\tconst mosquitto_property *p;\n\tbool is_first = true;\n\n\tp = proplist;\n\n\twhile(p){\n\t\tif(p->identifier == identifier){\n\t\t\tif(!is_first || !skip_first){\n\t\t\t\treturn p;\n\t\t\t}\n\t\t\tis_first = false;\n\t\t}\n\t\tp = p->next;\n\t}\n\treturn NULL;\n}\n\n\nBROKER_EXPORT int mosquitto_property_identifier(const mosquitto_property *property)\n{\n\tif(property == NULL){\n\t\treturn 0;\n\t}\n\n\treturn property->identifier;\n}\n\n\nBROKER_EXPORT int mosquitto_property_type(const mosquitto_property *property)\n{\n\tif(property == NULL){\n\t\treturn 0;\n\t}\n\n\treturn property->property_type;\n}\n\n\nBROKER_EXPORT mosquitto_property *mosquitto_property_next(const mosquitto_property *proplist)\n{\n\tif(proplist == NULL){\n\t\treturn NULL;\n\t}\n\n\treturn proplist->next;\n}\n\n\nBROKER_EXPORT const mosquitto_property *mosquitto_property_read_byte(const mosquitto_property *proplist, int identifier, uint8_t *value, bool skip_first)\n{\n\tconst mosquitto_property *p;\n\tif(!proplist){\n\t\treturn NULL;\n\t}\n\n\tp = property__get_property(proplist, identifier, skip_first);\n\tif(!p){\n\t\treturn NULL;\n\t}\n\tif(p->identifier != MQTT_PROP_PAYLOAD_FORMAT_INDICATOR\n\t\t\t&& p->identifier != MQTT_PROP_REQUEST_PROBLEM_INFORMATION\n\t\t\t&& p->identifier != MQTT_PROP_REQUEST_RESPONSE_INFORMATION\n\t\t\t&& p->identifier != MQTT_PROP_MAXIMUM_QOS\n\t\t\t&& p->identifier != MQTT_PROP_RETAIN_AVAILABLE\n\t\t\t&& p->identifier != MQTT_PROP_WILDCARD_SUB_AVAILABLE\n\t\t\t&& p->identifier != MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE\n\t\t\t&& p->identifier != MQTT_PROP_SHARED_SUB_AVAILABLE){\n\t\treturn NULL;\n\t}\n\n\tif(value){\n\t\t*value = p->value.i8;\n\t}\n\n\treturn p;\n}\n\n\nBROKER_EXPORT const mosquitto_property *mosquitto_property_read_int16(const mosquitto_property *proplist, int identifier, uint16_t *value, bool skip_first)\n{\n\tconst mosquitto_property *p;\n\tif(!proplist){\n\t\treturn NULL;\n\t}\n\n\tp = property__get_property(proplist, identifier, skip_first);\n\tif(!p){\n\t\treturn NULL;\n\t}\n\tif(p->identifier != MQTT_PROP_SERVER_KEEP_ALIVE\n\t\t\t&& p->identifier != MQTT_PROP_RECEIVE_MAXIMUM\n\t\t\t&& p->identifier != MQTT_PROP_TOPIC_ALIAS_MAXIMUM\n\t\t\t&& p->identifier != MQTT_PROP_TOPIC_ALIAS){\n\t\treturn NULL;\n\t}\n\n\tif(value){\n\t\t*value = p->value.i16;\n\t}\n\n\treturn p;\n}\n\n\nBROKER_EXPORT const mosquitto_property *mosquitto_property_read_int32(const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first)\n{\n\tconst mosquitto_property *p;\n\tif(!proplist){\n\t\treturn NULL;\n\t}\n\n\tp = property__get_property(proplist, identifier, skip_first);\n\tif(!p){\n\t\treturn NULL;\n\t}\n\tif(p->identifier != MQTT_PROP_MESSAGE_EXPIRY_INTERVAL\n\t\t\t&& p->identifier != MQTT_PROP_SESSION_EXPIRY_INTERVAL\n\t\t\t&& p->identifier != MQTT_PROP_WILL_DELAY_INTERVAL\n\t\t\t&& p->identifier != MQTT_PROP_MAXIMUM_PACKET_SIZE){\n\n\t\treturn NULL;\n\t}\n\n\tif(value){\n\t\t*value = p->value.i32;\n\t}\n\n\treturn p;\n}\n\n\nBROKER_EXPORT const mosquitto_property *mosquitto_property_read_varint(const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first)\n{\n\tconst mosquitto_property *p;\n\tif(!proplist){\n\t\treturn NULL;\n\t}\n\n\tp = property__get_property(proplist, identifier, skip_first);\n\tif(!p){\n\t\treturn NULL;\n\t}\n\tif(p->identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER){\n\t\treturn NULL;\n\t}\n\n\tif(value){\n\t\t*value = p->value.varint;\n\t}\n\n\treturn p;\n}\n\n\nBROKER_EXPORT const mosquitto_property *mosquitto_property_read_binary(const mosquitto_property *proplist, int identifier, void **value, uint16_t *len, bool skip_first)\n{\n\tconst mosquitto_property *p;\n\tif(!proplist || (value && !len) || (!value && len)){\n\t\treturn NULL;\n\t}\n\n\tif(value){\n\t\t*value = NULL;\n\t}\n\n\tp = property__get_property(proplist, identifier, skip_first);\n\tif(!p){\n\t\treturn NULL;\n\t}\n\tif(p->identifier != MQTT_PROP_CORRELATION_DATA\n\t\t\t&& p->identifier != MQTT_PROP_AUTHENTICATION_DATA){\n\n\t\treturn NULL;\n\t}\n\n\tif(value){\n\t\t*len = p->value.bin.len;\n\t\tif(p->value.bin.len){\n\t\t\t*value = mosquitto_calloc(1, *len + 1U);\n\t\t\tif(!(*value)){\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tmemcpy(*value, p->value.bin.v, *len);\n\t\t}else{\n\t\t\t*value = NULL;\n\t\t}\n\t}\n\n\treturn p;\n}\n\n\nBROKER_EXPORT const mosquitto_property *mosquitto_property_read_string(const mosquitto_property *proplist, int identifier, char **value, bool skip_first)\n{\n\tconst mosquitto_property *p;\n\tif(!proplist){\n\t\treturn NULL;\n\t}\n\n\tp = property__get_property(proplist, identifier, skip_first);\n\tif(!p){\n\t\treturn NULL;\n\t}\n\tif(p->identifier != MQTT_PROP_CONTENT_TYPE\n\t\t\t&& p->identifier != MQTT_PROP_RESPONSE_TOPIC\n\t\t\t&& p->identifier != MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER\n\t\t\t&& p->identifier != MQTT_PROP_AUTHENTICATION_METHOD\n\t\t\t&& p->identifier != MQTT_PROP_RESPONSE_INFORMATION\n\t\t\t&& p->identifier != MQTT_PROP_SERVER_REFERENCE\n\t\t\t&& p->identifier != MQTT_PROP_REASON_STRING){\n\n\t\treturn NULL;\n\t}\n\n\tif(value){\n\t\tif(p->value.s.len){\n\t\t\t*value = mosquitto_calloc(1, (size_t)p->value.s.len+1);\n\t\t\tif(!(*value)){\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tmemcpy(*value, p->value.s.v, p->value.s.len);\n\t\t}else{\n\t\t\t*value = NULL;\n\t\t}\n\t}\n\n\treturn p;\n}\n\n\nBROKER_EXPORT const mosquitto_property *mosquitto_property_read_string_pair(const mosquitto_property *proplist, int identifier, char **name, char **value, bool skip_first)\n{\n\tconst mosquitto_property *p;\n\tif(!proplist){\n\t\treturn NULL;\n\t}\n\n\tif(name){\n\t\t*name = NULL;\n\t}\n\tif(value){\n\t\t*value = NULL;\n\t}\n\n\tp = property__get_property(proplist, identifier, skip_first);\n\tif(!p){\n\t\treturn NULL;\n\t}\n\tif(p->identifier != MQTT_PROP_USER_PROPERTY){\n\t\treturn NULL;\n\t}\n\n\tif(name){\n\t\tif(p->name.len){\n\t\t\t*name = mosquitto_calloc(1, (size_t)p->name.len+1);\n\t\t\tif(!(*name)){\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tmemcpy(*name, p->name.v, p->name.len);\n\t\t}else{\n\t\t\t*name = NULL;\n\t\t}\n\t}\n\n\tif(value){\n\t\tif(p->value.s.len){\n\t\t\t*value = mosquitto_calloc(1, (size_t)p->value.s.len+1);\n\t\t\tif(!(*value)){\n\t\t\t\tif(name){\n\t\t\t\t\tmosquitto_FREE(*name);\n\t\t\t\t}\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tmemcpy(*value, p->value.s.v, p->value.s.len);\n\t\t}else{\n\t\t\t*value = NULL;\n\t\t}\n\t}\n\n\treturn p;\n}\n\n\nBROKER_EXPORT int mosquitto_property_remove(mosquitto_property **proplist, const mosquitto_property *property)\n{\n\tmosquitto_property *item, *item_prev = NULL;\n\n\tif(proplist == NULL || property == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\titem = *proplist;\n\twhile(item){\n\t\tif(item == property){\n\t\t\tif(item_prev == NULL){\n\t\t\t\t*proplist = (*proplist)->next;\n\t\t\t}else{\n\t\t\t\titem_prev->next = item->next;\n\t\t\t}\n\t\t\titem->next = NULL;\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t\titem_prev = item;\n\t\titem = item->next;\n\t}\n\n\treturn MOSQ_ERR_NOT_FOUND;\n}\n\n\nBROKER_EXPORT int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src)\n{\n\tmosquitto_property *pnew, *plast = NULL;\n\n\tif(!src){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tif(!dest){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t*dest = NULL;\n\n\twhile(src){\n\t\tpnew = mosquitto_calloc(1, sizeof(mosquitto_property));\n\t\tif(!pnew){\n\t\t\tmosquitto_property_free_all(dest);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tif(plast){\n\t\t\tplast->next = pnew;\n\t\t}else{\n\t\t\t*dest = pnew;\n\t\t}\n\t\tplast = pnew;\n\n\t\tpnew->client_generated = src->client_generated;\n\t\tpnew->identifier = src->identifier;\n\t\tpnew->property_type = src->property_type;\n\t\tswitch(pnew->property_type){\n\t\t\tcase MQTT_PROP_TYPE_BYTE:\n\t\t\t\tpnew->value.i8 = src->value.i8;\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_TYPE_INT16:\n\t\t\t\tpnew->value.i16 = src->value.i16;\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_TYPE_INT32:\n\t\t\t\tpnew->value.i32 = src->value.i32;\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_TYPE_VARINT:\n\t\t\t\tpnew->value.varint = src->value.varint;\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_TYPE_STRING:\n\t\t\t\tpnew->value.s.len = src->value.s.len;\n\t\t\t\tpnew->value.s.v = src->value.s.v ? mosquitto_strdup(src->value.s.v) : (char *)mosquitto_calloc(1, 1);\n\t\t\t\tif(!pnew->value.s.v){\n\t\t\t\t\tmosquitto_property_free_all(dest);\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_TYPE_BINARY:\n\t\t\t\tpnew->value.bin.len = src->value.bin.len;\n\t\t\t\tif(src->value.bin.len){\n\t\t\t\t\tpnew->value.bin.v = mosquitto_malloc(pnew->value.bin.len);\n\t\t\t\t\tif(!pnew->value.bin.v){\n\t\t\t\t\t\tmosquitto_property_free_all(dest);\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tmemcpy(pnew->value.bin.v, src->value.bin.v, pnew->value.bin.len);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_TYPE_STRING_PAIR:\n\t\t\t\tpnew->value.s.len = src->value.s.len;\n\t\t\t\tpnew->value.s.v = src->value.s.v ? mosquitto_strdup(src->value.s.v) : (char *)mosquitto_calloc(1, 1);\n\t\t\t\tif(!pnew->value.s.v){\n\t\t\t\t\tmosquitto_property_free_all(dest);\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\n\t\t\t\tpnew->name.len = src->name.len;\n\t\t\t\tpnew->name.v = src->name.v ? mosquitto_strdup(src->name.v) : (char *)mosquitto_calloc(1, 1);\n\t\t\t\tif(!pnew->name.v){\n\t\t\t\t\tmosquitto_property_free_all(dest);\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tmosquitto_property_free_all(dest);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\tsrc = mosquitto_property_next(src);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nuint8_t mosquitto_property_byte_value(const mosquitto_property *property)\n{\n\tif(property && property->property_type == MQTT_PROP_TYPE_BYTE){\n\t\treturn property->value.i8;\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\nuint16_t mosquitto_property_int16_value(const mosquitto_property *property)\n{\n\tif(property && property->property_type == MQTT_PROP_TYPE_INT16){\n\t\treturn property->value.i16;\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\nuint32_t mosquitto_property_int32_value(const mosquitto_property *property)\n{\n\tif(property && property->property_type == MQTT_PROP_TYPE_INT32){\n\t\treturn property->value.i32;\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\nuint32_t mosquitto_property_varint_value(const mosquitto_property *property)\n{\n\tif(property && property->property_type == MQTT_PROP_TYPE_VARINT){\n\t\treturn property->value.varint;\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\nconst void *mosquitto_property_binary_value(const mosquitto_property *property)\n{\n\tif(property && property->property_type == MQTT_PROP_TYPE_BINARY){\n\t\treturn property->value.bin.v;\n\t}else{\n\t\treturn NULL;\n\t}\n}\n\n\nuint16_t mosquitto_property_binary_value_length(const mosquitto_property *property)\n{\n\tif(property && property->property_type == MQTT_PROP_TYPE_BINARY){\n\t\treturn property->value.bin.len;\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\nconst char *mosquitto_property_string_value(const mosquitto_property *property)\n{\n\tif(property && (property->property_type == MQTT_PROP_TYPE_STRING || property->property_type == MQTT_PROP_TYPE_STRING_PAIR)){\n\t\treturn property->value.s.v;\n\t}else{\n\t\treturn NULL;\n\t}\n}\n\n\nuint16_t mosquitto_property_string_value_length(const mosquitto_property *property)\n{\n\tif(property && (property->property_type == MQTT_PROP_TYPE_STRING || property->property_type == MQTT_PROP_TYPE_STRING_PAIR)){\n\t\treturn property->value.s.len;\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\nconst char *mosquitto_property_string_name(const mosquitto_property *property)\n{\n\tif(property && property->property_type == MQTT_PROP_TYPE_STRING_PAIR){\n\t\treturn property->name.v;\n\t}else{\n\t\treturn NULL;\n\t}\n}\n\n\nuint16_t mosquitto_property_string_name_length(const mosquitto_property *property)\n{\n\tif(property && property->property_type == MQTT_PROP_TYPE_STRING_PAIR){\n\t\treturn property->name.len;\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\n/* Return the number of bytes we need to add on to the remaining length when\n * encoding these properties. */\nunsigned int mosquitto_property_get_remaining_length(const mosquitto_property *props)\n{\n\tunsigned int proplen, varbytes;\n\n\tproplen = mosquitto_property_get_length_all(props);\n\tvarbytes = mosquitto_varint_bytes(proplen);\n\treturn proplen + varbytes;\n}\n\n\n"
  },
  {
    "path": "libcommon/property_common.h",
    "content": "/*\nCopyright (c) 2018-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef PROPERTY_COMMON_H\n#define PROPERTY_COMMON_H\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#include \"mosquitto.h\"\n\nstruct mqtt__string {\n\tchar *v;\n\tuint16_t len;\n};\n\nstruct mqtt5__property {\n\tstruct mqtt5__property *next;\n\tunion {\n\t\tuint8_t i8;\n\t\tuint16_t i16;\n\t\tuint32_t i32;\n\t\tuint32_t varint;\n\t\tstruct mqtt__string bin;\n\t\tstruct mqtt__string s;\n\t} value;\n\tstruct mqtt__string name;\n\tint32_t identifier;\n\tuint8_t property_type;\n\tbool client_generated;\n};\n\n#endif\n"
  },
  {
    "path": "libcommon/random_common.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n#include <stdlib.h> /* Keep this here to allow glibc detection */\n\n#ifdef WIN32\n#  include <winsock2.h>\n#  include <aclapi.h>\n#  include <io.h>\n#  include <lmcons.h>\n#endif\n\n#ifdef WITH_TLS\n#  include <openssl/bn.h>\n#  include <openssl/rand.h>\n#elif defined(HAVE_GETRANDOM)  /* From CMakeLists.txt */\n# include <sys/random.h>\n#elif defined(__linux__) && defined(__GLIBC__) /* For legacy Makefiles */\n#  if __GLIBC_PREREQ(2, 25)\n#    include <sys/random.h>\n#    define HAVE_GETRANDOM 1\n#  endif\n#endif\n\n#include \"mosquitto.h\"\n\n\nint mosquitto_getrandom(void *bytes, int count)\n{\n\tint rc = MOSQ_ERR_UNKNOWN;\n\n#ifdef WITH_TLS\n\tif(RAND_bytes(bytes, count) == 1){\n\t\trc = MOSQ_ERR_SUCCESS;\n\t}\n#elif defined(HAVE_GETRANDOM)\n\tif(getrandom(bytes, (size_t)count, 0) == count){\n\t\trc = MOSQ_ERR_SUCCESS;\n\t}\n#elif defined(WIN32)\n\tHCRYPTPROV provider;\n\n\tif(!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(CryptGenRandom(provider, count, bytes)){\n\t\trc = MOSQ_ERR_SUCCESS;\n\t}\n\n\tCryptReleaseContext(provider, 0);\n#else /* For legacy Makefiles */\n#  error \"No suitable random function found.\"\n#endif\n\treturn rc;\n}\n"
  },
  {
    "path": "libcommon/strings_common.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <string.h>\n\n#ifndef WIN32\n#  include <strings.h>\n#endif\n\n#include \"mosquitto.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n\n\nconst char *mosquitto_strerror(int mosq_errno)\n{\n\tswitch(mosq_errno){\n\t\tcase MOSQ_ERR_QUOTA_EXCEEDED:\n\t\t\treturn \"Quota exceeded\";\n\t\tcase MOSQ_ERR_AUTH_DELAYED:\n\t\t\treturn \"Authentication delayed\";\n\t\tcase MOSQ_ERR_AUTH_CONTINUE:\n\t\t\treturn \"Continue with authentication\";\n\t\tcase MOSQ_ERR_NO_SUBSCRIBERS:\n\t\t\treturn \"No subscribers\";\n\t\tcase MOSQ_ERR_SUB_EXISTS:\n\t\t\treturn \"Subscription already exists\";\n\t\tcase MOSQ_ERR_CONN_PENDING:\n\t\t\treturn \"Connection pending\";\n\t\tcase MOSQ_ERR_SUCCESS:\n\t\t\treturn \"No error\";\n\t\tcase MOSQ_ERR_NOMEM:\n\t\t\treturn \"Out of memory\";\n\t\tcase MOSQ_ERR_PROTOCOL:\n\t\t\treturn \"A network protocol error occurred when communicating with the broker\";\n\t\tcase MOSQ_ERR_INVAL:\n\t\t\treturn \"Invalid input\";\n\t\tcase MOSQ_ERR_NO_CONN:\n\t\t\treturn \"The client is not currently connected\";\n\t\tcase MOSQ_ERR_CONN_REFUSED:\n\t\t\treturn \"The connection was refused\";\n\t\tcase MOSQ_ERR_NOT_FOUND:\n\t\t\treturn \"Message not found (internal error)\";\n\t\tcase MOSQ_ERR_CONN_LOST:\n\t\t\treturn \"The connection was lost\";\n\t\tcase MOSQ_ERR_TLS:\n\t\t\treturn \"A TLS error occurred\";\n\t\tcase MOSQ_ERR_PAYLOAD_SIZE:\n\t\t\treturn \"Payload too large\";\n\t\tcase MOSQ_ERR_NOT_SUPPORTED:\n\t\t\treturn \"This feature is not supported\";\n\t\tcase MOSQ_ERR_AUTH:\n\t\t\treturn \"Authorisation failed\";\n\t\tcase MOSQ_ERR_ACL_DENIED:\n\t\t\treturn \"Access denied by ACL\";\n\t\tcase MOSQ_ERR_UNKNOWN:\n\t\t\treturn \"Unknown error\";\n\t\tcase MOSQ_ERR_ERRNO:\n\t\t\treturn strerror(errno);\n\t\tcase MOSQ_ERR_EAI:\n\t\t\treturn \"Lookup error\";\n\t\tcase MOSQ_ERR_PROXY:\n\t\t\treturn \"Proxy error\";\n\t\tcase MOSQ_ERR_PLUGIN_DEFER:\n\t\t\treturn \"Plugin deferring result\";\n\t\tcase MOSQ_ERR_MALFORMED_UTF8:\n\t\t\treturn \"Malformed UTF-8\";\n\t\tcase MOSQ_ERR_KEEPALIVE:\n\t\t\treturn \"Keepalive exceeded\";\n\t\tcase MOSQ_ERR_LOOKUP:\n\t\t\treturn \"DNS Lookup failed\";\n\t\tcase MOSQ_ERR_MALFORMED_PACKET:\n\t\t\treturn \"Malformed packet\";\n\t\tcase MOSQ_ERR_DUPLICATE_PROPERTY:\n\t\t\treturn \"Duplicate property in property list\";\n\t\tcase MOSQ_ERR_TLS_HANDSHAKE:\n\t\t\treturn \"TLS handshake failed\";\n\t\tcase MOSQ_ERR_QOS_NOT_SUPPORTED:\n\t\t\treturn \"Requested QoS not supported on server\";\n\t\tcase MOSQ_ERR_OVERSIZE_PACKET:\n\t\t\treturn \"Packet larger than supported by the server\";\n\t\tcase MOSQ_ERR_OCSP:\n\t\t\treturn \"OCSP error\";\n\t\tcase MOSQ_ERR_TIMEOUT:\n\t\t\treturn \"Timeout\";\n\t\tcase MOSQ_ERR_ALREADY_EXISTS:\n\t\t\treturn \"Entry already exists\";\n\t\tcase MOSQ_ERR_PLUGIN_IGNORE:\n\t\t\treturn \"Ignore plugin\";\n\t\tcase MOSQ_ERR_HTTP_BAD_ORIGIN:\n\t\t\treturn \"Bad http origin\";\n\n\t\tcase MOSQ_ERR_UNSPECIFIED:\n\t\t\treturn \"Unspecified error\";\n\t\tcase MOSQ_ERR_IMPLEMENTATION_SPECIFIC:\n\t\t\treturn \"Implementaion specific error\";\n\t\tcase MOSQ_ERR_CLIENT_IDENTIFIER_NOT_VALID:\n\t\t\treturn \"Client identifier not valid\";\n\t\tcase MOSQ_ERR_BAD_USERNAME_OR_PASSWORD:\n\t\t\treturn \"Bad username or password\";\n\t\tcase MOSQ_ERR_SERVER_UNAVAILABLE:\n\t\t\treturn \"Server unavailable\";\n\t\tcase MOSQ_ERR_SERVER_BUSY:\n\t\t\treturn \"Server busy\";\n\t\tcase MOSQ_ERR_BANNED:\n\t\t\treturn \"Banned\";\n\t\tcase MOSQ_ERR_BAD_AUTHENTICATION_METHOD:\n\t\t\treturn \"Bad authentication method\";\n\t\tcase MOSQ_ERR_SESSION_TAKEN_OVER:\n\t\t\treturn \"Session taken over\";\n\t\tcase MOSQ_ERR_RECEIVE_MAXIMUM_EXCEEDED:\n\t\t\treturn \"Receive maximum exceeded\";\n\t\tcase MOSQ_ERR_TOPIC_ALIAS_INVALID:\n\t\t\treturn \"Topic alias invalid\";\n\t\tcase MOSQ_ERR_ADMINISTRATIVE_ACTION:\n\t\t\treturn \"Administrative action\";\n\t\tcase MOSQ_ERR_RETAIN_NOT_SUPPORTED:\n\t\t\treturn \"Retain not supported\";\n\t\tcase MOSQ_ERR_CONNECTION_RATE_EXCEEDED:\n\t\t\treturn \"Connection rate exceeded\";\n\t\tdefault:\n\t\t\tif(mosq_errno >= 128){\n\t\t\t\t// If mosq_errno is greater than 127,\n\t\t\t\t// a mqtt5_return_code error was used\n\t\t\t\treturn mosquitto_reason_string(mosq_errno);\n\t\t\t}else{\n\t\t\t\treturn \"Unknown error\";\n\t\t\t}\n\t}\n}\n\n\nconst char *mosquitto_connack_string(int connack_code)\n{\n\tswitch(connack_code){\n\t\tcase 0:\n\t\t\treturn \"Connection Accepted\";\n\t\tcase 1:\n\t\t\treturn \"Connection Refused: unacceptable protocol version\";\n\t\tcase 2:\n\t\t\treturn \"Connection Refused: identifier rejected\";\n\t\tcase 3:\n\t\t\treturn \"Connection Refused: broker unavailable\";\n\t\tcase 4:\n\t\t\treturn \"Connection Refused: bad user name or password\";\n\t\tcase 5:\n\t\t\treturn \"Connection Refused: not authorised\";\n\t\tdefault:\n\t\t\treturn \"Connection Refused: unknown reason\";\n\t}\n}\n\n\nconst char *mosquitto_reason_string(int reason_code)\n{\n\tswitch(reason_code){\n\t\tcase MQTT_RC_SUCCESS:\n\t\t\treturn \"Success\";\n\t\tcase MQTT_RC_GRANTED_QOS1:\n\t\t\treturn \"Granted QoS 1\";\n\t\tcase MQTT_RC_GRANTED_QOS2:\n\t\t\treturn \"Granted QoS 2\";\n\t\tcase MQTT_RC_DISCONNECT_WITH_WILL_MSG:\n\t\t\treturn \"Disconnect with Will Message\";\n\t\tcase MQTT_RC_NO_MATCHING_SUBSCRIBERS:\n\t\t\treturn \"No matching subscribers\";\n\t\tcase MQTT_RC_NO_SUBSCRIPTION_EXISTED:\n\t\t\treturn \"No subscription existed\";\n\t\tcase MQTT_RC_CONTINUE_AUTHENTICATION:\n\t\t\treturn \"Continue authentication\";\n\t\tcase MQTT_RC_REAUTHENTICATE:\n\t\t\treturn \"Re-authenticate\";\n\n\t\tcase MQTT_RC_UNSPECIFIED:\n\t\t\treturn \"Unspecified error\";\n\t\tcase MQTT_RC_MALFORMED_PACKET:\n\t\t\treturn \"Malformed Packet\";\n\t\tcase MQTT_RC_PROTOCOL_ERROR:\n\t\t\treturn \"Protocol Error\";\n\t\tcase MQTT_RC_IMPLEMENTATION_SPECIFIC:\n\t\t\treturn \"Implementation specific error\";\n\t\tcase MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION:\n\t\t\treturn \"Unsupported Protocol Version\";\n\t\tcase MQTT_RC_CLIENTID_NOT_VALID:\n\t\t\treturn \"Client Identifier not valid\";\n\t\tcase MQTT_RC_BAD_USERNAME_OR_PASSWORD:\n\t\t\treturn \"Bad User Name or Password\";\n\t\tcase MQTT_RC_NOT_AUTHORIZED:\n\t\t\treturn \"Not authorized\";\n\t\tcase MQTT_RC_SERVER_UNAVAILABLE:\n\t\t\treturn \"Server unavailable\";\n\t\tcase MQTT_RC_SERVER_BUSY:\n\t\t\treturn \"Server busy\";\n\t\tcase MQTT_RC_BANNED:\n\t\t\treturn \"Banned\";\n\t\tcase MQTT_RC_SERVER_SHUTTING_DOWN:\n\t\t\treturn \"Server shutting down\";\n\t\tcase MQTT_RC_BAD_AUTHENTICATION_METHOD:\n\t\t\treturn \"Bad authentication method\";\n\t\tcase MQTT_RC_KEEP_ALIVE_TIMEOUT:\n\t\t\treturn \"Keep Alive timeout\";\n\t\tcase MQTT_RC_SESSION_TAKEN_OVER:\n\t\t\treturn \"Session taken over\";\n\t\tcase MQTT_RC_TOPIC_FILTER_INVALID:\n\t\t\treturn \"Topic Filter invalid\";\n\t\tcase MQTT_RC_TOPIC_NAME_INVALID:\n\t\t\treturn \"Topic Name invalid\";\n\t\tcase MQTT_RC_PACKET_ID_IN_USE:\n\t\t\treturn \"Packet Identifier in use\";\n\t\tcase MQTT_RC_PACKET_ID_NOT_FOUND:\n\t\t\treturn \"Packet Identifier not found\";\n\t\tcase MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED:\n\t\t\treturn \"Receive Maximum exceeded\";\n\t\tcase MQTT_RC_TOPIC_ALIAS_INVALID:\n\t\t\treturn \"Topic Alias invalid\";\n\t\tcase MQTT_RC_PACKET_TOO_LARGE:\n\t\t\treturn \"Packet too large\";\n\t\tcase MQTT_RC_MESSAGE_RATE_TOO_HIGH:\n\t\t\treturn \"Message rate too high\";\n\t\tcase MQTT_RC_QUOTA_EXCEEDED:\n\t\t\treturn \"Quota exceeded\";\n\t\tcase MQTT_RC_ADMINISTRATIVE_ACTION:\n\t\t\treturn \"Administrative action\";\n\t\tcase MQTT_RC_PAYLOAD_FORMAT_INVALID:\n\t\t\treturn \"Payload format invalid\";\n\t\tcase MQTT_RC_RETAIN_NOT_SUPPORTED:\n\t\t\treturn \"Retain not supported\";\n\t\tcase MQTT_RC_QOS_NOT_SUPPORTED:\n\t\t\treturn \"QoS not supported\";\n\t\tcase MQTT_RC_USE_ANOTHER_SERVER:\n\t\t\treturn \"Use another server\";\n\t\tcase MQTT_RC_SERVER_MOVED:\n\t\t\treturn \"Server moved\";\n\t\tcase MQTT_RC_SHARED_SUBS_NOT_SUPPORTED:\n\t\t\treturn \"Shared Subscriptions not supported\";\n\t\tcase MQTT_RC_CONNECTION_RATE_EXCEEDED:\n\t\t\treturn \"Connection rate exceeded\";\n\t\tcase MQTT_RC_MAXIMUM_CONNECT_TIME:\n\t\t\treturn \"Maximum connect time\";\n\t\tcase MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED:\n\t\t\treturn \"Subscription identifiers not supported\";\n\t\tcase MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED:\n\t\t\treturn \"Wildcard Subscriptions not supported\";\n\t\tdefault:\n\t\t\treturn \"Unknown reason\";\n\t}\n}\n\n\nint mosquitto_string_to_command(const char *str, int *cmd)\n{\n\tif(!strcasecmp(str, \"connect\")){\n\t\t*cmd = CMD_CONNECT;\n\t}else if(!strcasecmp(str, \"connack\")){\n\t\t*cmd = CMD_CONNACK;\n\t}else if(!strcasecmp(str, \"publish\")){\n\t\t*cmd = CMD_PUBLISH;\n\t}else if(!strcasecmp(str, \"puback\")){\n\t\t*cmd = CMD_PUBACK;\n\t}else if(!strcasecmp(str, \"pubrec\")){\n\t\t*cmd = CMD_PUBREC;\n\t}else if(!strcasecmp(str, \"pubrel\")){\n\t\t*cmd = CMD_PUBREL;\n\t}else if(!strcasecmp(str, \"pubcomp\")){\n\t\t*cmd = CMD_PUBCOMP;\n\t}else if(!strcasecmp(str, \"subscribe\")){\n\t\t*cmd = CMD_SUBSCRIBE;\n\t}else if(!strcasecmp(str, \"suback\")){\n\t\t*cmd = CMD_SUBACK;\n\t}else if(!strcasecmp(str, \"unsubscribe\")){\n\t\t*cmd = CMD_UNSUBSCRIBE;\n\t}else if(!strcasecmp(str, \"unsuback\")){\n\t\t*cmd = CMD_UNSUBACK;\n\t}else if(!strcasecmp(str, \"disconnect\")){\n\t\t*cmd = CMD_DISCONNECT;\n\t}else if(!strcasecmp(str, \"auth\")){\n\t\t*cmd = CMD_AUTH;\n\t}else if(!strcasecmp(str, \"will\")){\n\t\t*cmd = CMD_WILL;\n\t}else{\n\t\t*cmd = 0;\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "libcommon/time_common.c",
    "content": "/*\nCopyright (c) 2013-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef __APPLE__\n#include <mach/mach.h>\n#include <mach/mach_time.h>\n#endif\n\n#if defined(__APPLE__) || defined(__OpenBSD__)\n#  include <sys/time.h>\n#endif\n\n#ifdef WIN32\n#if !(defined(_MSC_VER) && _MSC_VER <= 1500)\n#  define _WIN32_WINNT _WIN32_WINNT_VISTA\n#endif\n#  include <windows.h>\n#else\n#  include <unistd.h>\n#endif\n#include <time.h>\n\n#include \"mosquitto.h\"\n\n#if _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK)\nstatic clockid_t time_clock = CLOCK_MONOTONIC;\n#endif\n\n\nvoid mosquitto_time_init(void)\n{\n#if _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK)\n\tstruct timespec tp;\n\n#ifdef CLOCK_BOOTTIME\n\tif(clock_gettime(CLOCK_BOOTTIME, &tp) == 0){\n\t\ttime_clock = CLOCK_BOOTTIME;\n\t}else{\n\t\ttime_clock = CLOCK_MONOTONIC;\n\t}\n#else\n\ttime_clock = CLOCK_MONOTONIC;\n#endif\n#endif\n}\n\n\ntime_t mosquitto_time(void)\n{\n#ifdef WIN32\n\treturn GetTickCount64()/1000;\n#elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK)\n\tstruct timespec tp;\n\n\tif(clock_gettime(time_clock, &tp) == 0){\n\t\treturn tp.tv_sec;\n\t}\n\n\treturn (time_t)-1;\n#elif defined(__APPLE__)\n\tstatic mach_timebase_info_data_t tb;\n\tuint64_t ticks;\n\tuint64_t sec;\n\n\tticks = mach_absolute_time();\n\n\tif(tb.denom == 0){\n\t\tmach_timebase_info(&tb);\n\t}\n\tsec = ticks*tb.numer/tb.denom/1000000000;\n\n\treturn (time_t)sec;\n#else\n\treturn time(NULL);\n#endif\n}\n\n\nvoid mosquitto_time_ns(time_t *s, long *ns)\n{\n#ifdef WIN32\n\tSYSTEMTIME st;\n\tGetLocalTime(&st);\n\t*s = time(NULL);\n\t*ns = st.wMilliseconds*1000000L;\n#elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK)\n\tstruct timespec tp;\n\n\tclock_gettime(CLOCK_REALTIME, &tp);\n\t*s = tp.tv_sec;\n\t*ns = tp.tv_nsec;\n#else\n\tstruct timeval tv;\n\n\tgettimeofday(&tv, NULL);\n\t*s = tv.tv_sec;\n\t*ns = tv.tv_usec * 1000;\n#endif\n}\n\n\nlong mosquitto_time_cmp(time_t t1_s, long t1_ns, time_t t2_s, long t2_ns)\n{\n\tif(t1_s == t2_s){\n\t\treturn (long)(t1_ns - t2_ns);\n\t}else{\n\t\treturn (long)(t1_s - t2_s);\n\t}\n}\n"
  },
  {
    "path": "libcommon/topic_common.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <string.h>\n\n#ifdef WIN32\n#  include <winsock2.h>\n#  include <aclapi.h>\n#  include <io.h>\n#  include <lmcons.h>\n#else\n#  include <sys/stat.h>\n#endif\n\n#include \"mosquitto.h\"\n\n\n/* Check that a topic used for publishing is valid.\n * Search for + or # in a topic. Return MOSQ_ERR_INVAL if found.\n * Also returns MOSQ_ERR_INVAL if the topic string is too long.\n * Returns MOSQ_ERR_SUCCESS if everything is fine.\n */\nBROKER_EXPORT int mosquitto_pub_topic_check(const char *str)\n{\n\tint len = 0;\n\tint hier_count = 0;\n\n\tif(str == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\twhile(str && str[0]){\n\t\tif(str[0] == '+' || str[0] == '#'){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}else if(str[0] == '/'){\n\t\t\thier_count++;\n\t\t}\n\t\tlen++;\n\t\tstr = &str[1];\n\t}\n\tif(len == 0 || len > 65535){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(hier_count > TOPIC_HIERARCHY_LIMIT){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_pub_topic_check2(const char *str, size_t len)\n{\n\tsize_t i;\n\tint hier_count = 0;\n\n\tif(str == NULL || len == 0 || len > 65535){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tfor(i=0; i<len; i++){\n\t\tif(str[i] == '+' || str[i] == '#'){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}else if(str[i] == '/'){\n\t\t\thier_count++;\n\t\t}\n\t}\n\tif(hier_count > TOPIC_HIERARCHY_LIMIT){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* Check that a topic used for subscriptions is valid.\n * Search for + or # in a topic, check they aren't in invalid positions such as\n * foo/#/bar, foo/+bar or foo/bar#.\n * Return MOSQ_ERR_INVAL if invalid position found.\n * Also returns MOSQ_ERR_INVAL if the topic string is too long.\n * Returns MOSQ_ERR_SUCCESS if everything is fine.\n */\nBROKER_EXPORT int mosquitto_sub_topic_check(const char *str)\n{\n\tchar c = '\\0';\n\tint len = 0;\n\tint hier_count = 0;\n\n\tif(str == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\twhile(str[0]){\n\t\tif(str[0] == '+'){\n\t\t\tif((c != '\\0' && c != '/') || (str[1] != '\\0' && str[1] != '/')){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}else if(str[0] == '#'){\n\t\t\tif((c != '\\0' && c != '/')  || str[1] != '\\0'){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}else if(str[0] == '/'){\n\t\t\thier_count++;\n\t\t}\n\t\tlen++;\n\t\tc = str[0];\n\t\tstr = &str[1];\n\t}\n\tif(len == 0 || len > 65535){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(hier_count > TOPIC_HIERARCHY_LIMIT){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_sub_topic_check2(const char *str, size_t len)\n{\n\tchar c = '\\0';\n\tsize_t i;\n\tint hier_count = 0;\n\n\tif(str == NULL || len == 0 || len > 65535){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tfor(i=0; i<len; i++){\n\t\tif(str[i] == '+'){\n\t\t\tif((c != '\\0' && c != '/') || (i<len-1 && str[i+1] != '/')){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}else if(str[i] == '#'){\n\t\t\tif((c != '\\0' && c != '/')  || i<len-1){\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}else if(str[i] == '/'){\n\t\t\thier_count++;\n\t\t}\n\t\tc = str[i];\n\t}\n\tif(hier_count > TOPIC_HIERARCHY_LIMIT){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int topic_matches_sub(const char *sub, const char *topic, const char *clientid, const char *username, bool match_patterns, bool *result)\n{\n\tsize_t spos;\n\tconst char *pattern_check;\n\tconst char *lastchar = NULL;\n\n\tif(!result){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\t*result = false;\n\n\tif(!sub || !topic || sub[0] == 0 || topic[0] == 0){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif((sub[0] == '$' && topic[0] != '$')\n\t\t\t|| (topic[0] == '$' && sub[0] != '$')){\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tspos = 0;\n\n\twhile(sub[0] != 0){\n\t\tif(topic[0] == '+' || topic[0] == '#'){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(match_patterns &&\n\t\t\t\t(lastchar == NULL || lastchar[0] == '/') &&\n\t\t\t\tsub[0] == '%' &&\n\t\t\t\t(sub[1] == 'c' || sub[1] == 'u') &&\n\t\t\t\t(sub[2] == '/' || sub[2] == '\\0')\n\t\t\t\t){\n\n\t\t\tif(sub[1] == 'c'){\n\t\t\t\tpattern_check = clientid;\n\t\t\t}else{\n\t\t\t\tpattern_check = username;\n\t\t\t}\n\t\t\tif(pattern_check == NULL || pattern_check[0] == '\\0'){\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t\tspos += 2;\n\t\t\tsub += 2;\n\n\t\t\twhile(pattern_check[0] != 0 && topic[0] != 0 && topic[0] != '/'){\n\t\t\t\tif(pattern_check[0] != topic[0]){\n\t\t\t\t\t/* Valid input, but no match */\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t\tpattern_check++;\n\t\t\t\ttopic++;\n\t\t\t}\n\t\t\tif(pattern_check[0] != '\\0'){\n\t\t\t\t/* substituted pattern part smaller than publish topic part, so fail */\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t\tif((sub[0] == '\\0' && topic[0] == '\\0') ||\n\t\t\t\t\t(sub[0] == '/' && sub[1] == '#' && sub[2] == '\\0' && topic[0] == '\\0')\n\t\t\t\t\t){\n\n\t\t\t\t*result = true;\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}\n\t\tif(sub[0] != topic[0] || topic[0] == 0){ /* Check for wildcard matches */\n\t\t\tif(sub[0] == '+'){\n\t\t\t\t/* Check for bad \"+foo\" or \"a/+foo\" subscription */\n\t\t\t\tif(spos > 0 && sub[-1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\t/* Check for bad \"foo+\" or \"foo+/a\" subscription */\n\t\t\t\tif(sub[1] != 0 && sub[1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\tspos++;\n\t\t\t\tsub++;\n\t\t\t\twhile(topic[0] != 0 && topic[0] != '/'){\n\t\t\t\t\tif(topic[0] == '+' || topic[0] == '#'){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\ttopic++;\n\t\t\t\t}\n\t\t\t\tif(topic[0] == 0 && sub[0] == 0){\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}else if(sub[0] == '#'){\n\t\t\t\t/* Check for bad \"foo#\" subscription */\n\t\t\t\tif(spos > 0 && sub[-1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\t/* Check for # not the final character of the sub, e.g. \"#foo\" */\n\t\t\t\tif(sub[1] != 0){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}else{\n\t\t\t\t\twhile(topic[0] != 0){\n\t\t\t\t\t\tif(topic[0] == '+' || topic[0] == '#'){\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttopic++;\n\t\t\t\t\t}\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\t/* Check for e.g. foo/bar matching foo/+/# */\n\t\t\t\tif(topic[0] == 0\n\t\t\t\t\t\t&& spos > 0\n\t\t\t\t\t\t&& sub[-1] == '+'\n\t\t\t\t\t\t&& sub[0] == '/'\n\t\t\t\t\t\t&& sub[1] == '#'){\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\n\t\t\t\t/* There is no match at this point, but is the sub invalid? */\n\t\t\t\twhile(sub[0] != 0){\n\t\t\t\t\tif(sub[0] == '#' && sub[1] != 0){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tspos++;\n\t\t\t\t\tsub++;\n\t\t\t\t}\n\n\t\t\t\t/* Valid input, but no match */\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}else{\n\t\t\t/* sub[spos] == topic[tpos] */\n\t\t\tif(topic[1] == 0){\n\t\t\t\t/* Check for e.g. foo matching foo/# */\n\t\t\t\tif(sub[1] == '/'\n\t\t\t\t\t\t&& sub[2] == '#'\n\t\t\t\t\t\t&& sub[3] == 0){\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}\n\t\t\tspos++;\n\t\t\tsub++;\n\t\t\ttopic++;\n\t\t\tif(sub[0] == 0 && topic[0] == 0){\n\t\t\t\t*result = true;\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}else if(topic[0] == 0 && sub[0] == '+' && sub[1] == 0){\n\t\t\t\tif(spos > 0 && sub[-1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\tspos++;\n\t\t\t\tsub++;\n\t\t\t\t*result = true;\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}\n\t\tlastchar = sub-1;\n\t}\n\tif((topic[0] != 0 || sub[0] != 0)){\n\t\t*result = false;\n\t}\n\twhile(topic[0] != 0){\n\t\tif(topic[0] == '+' || topic[0] == '#'){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\ttopic++;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int sub_matches_acl(const char *acl, const char *sub, const char *clientid, const char *username, bool match_patterns, bool *result)\n{\n\tsize_t apos;\n\tconst char *pattern_check;\n\tconst char *lastchar = NULL;\n\n\t*result = false;\n\n\tif(!acl || !sub || acl[0] == 0 || sub[0] == 0){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif((acl[0] == '$' && sub[0] != '$')\n\t\t\t|| (sub[0] == '$' && acl[0] != '$')){\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tapos = 0;\n\n\twhile(acl[0] != 0){\n\t\tif(match_patterns &&\n\t\t\t\t(lastchar == NULL || lastchar[0] == '/') &&\n\t\t\t\tacl[0] == '%' &&\n\t\t\t\t(acl[1] == 'c' || acl[1] == 'u') &&\n\t\t\t\t(acl[2] == '/' || acl[2] == '\\0')\n\t\t\t\t){\n\n\t\t\tif(acl[1] == 'c'){\n\t\t\t\tpattern_check = clientid;\n\t\t\t}else{\n\t\t\t\tpattern_check = username;\n\t\t\t}\n\t\t\tif(pattern_check == NULL || pattern_check[0] == '\\0'){\n\t\t\t\t/* no match */\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t\tif(pattern_check[1] == '\\0' &&\n\t\t\t\t\t(\n\t\t\t\t\t\tpattern_check[0] == '+' ||\n\t\t\t\t\t\tpattern_check[0] == '#' ||\n\t\t\t\t\t\tpattern_check[0] == '/')\n\t\t\t\t\t){\n\n\t\t\t\t/* username/client id of just + / # not allowed */\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\n\t\t\tapos +=2;\n\t\t\tacl += 2;\n\n\t\t\twhile(pattern_check[0] != 0 && sub[0] != 0 && sub[0] != '/'){\n\t\t\t\tif(pattern_check[0] != sub[0]){\n\t\t\t\t\t/* Valid input, but no match */\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t\tpattern_check++;\n\t\t\t\tsub++;\n\t\t\t}\n\t\t\tif(pattern_check[0] != '\\0'){\n\t\t\t\t/* substituted pattern part smaller than publish topic part, so fail */\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t\tif(sub[0] == '\\0' && (acl[0] == '\\0' ||\n\t\t\t\t\t(acl[0] == '/' && acl[1] == '#' && acl[2] == '\\0'))\n\t\t\t\t\t){\n\n\t\t\t\t*result = true;\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}\n\t\tif(acl[0] != sub[0] || sub[0] == 0){ /* Check for wildcard matches */\n\t\t\tif(acl[0] == '+'){\n\t\t\t\tif(sub[0] == '#'){\n\t\t\t\t\t/* + doesn't match # */\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t\t/* Check for bad \"+foo\" or \"a/+foo\" subscription */\n\t\t\t\tif(apos > 0 && acl[-1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\t/* Check for bad \"foo+\" or \"foo+/a\" subscription */\n\t\t\t\tif(acl[1] != 0 && acl[1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\tapos++;\n\t\t\t\tacl++;\n\t\t\t\twhile(sub[0] != 0 && sub[0] != '/'){\n\t\t\t\t\tsub++;\n\t\t\t\t}\n\t\t\t\tif(sub[0] == 0 && acl[0] == 0){\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}else if(acl[0] == '#'){\n\t\t\t\t/* Check for bad \"foo#\" subscription */\n\t\t\t\tif(apos > 0 && acl[-1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\t/* Check for # not the final character of the sub, e.g. \"#foo\" */\n\t\t\t\tif(acl[1] != 0){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}else{\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\t/* Check for e.g. foo/bar matching foo/+/# */\n\t\t\t\tif(sub[0] == 0\n\t\t\t\t\t\t&& apos > 0\n\t\t\t\t\t\t&& acl[-1] == '+'\n\t\t\t\t\t\t&& acl[0] == '/'\n\t\t\t\t\t\t&& acl[1] == '#'){\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\n\t\t\t\t/* There is no match at this point, but is the sub invalid? */\n\t\t\t\twhile(acl[0] != 0){\n\t\t\t\t\tif(acl[0] == '#' && acl[1] != 0){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tapos++;\n\t\t\t\t\tacl++;\n\t\t\t\t}\n\n\t\t\t\t/* Valid input, but no match */\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}else{\n\t\t\t/* acl[apos] == sub[spos] */\n\t\t\tif(sub[1] == 0){\n\t\t\t\t/* Check for e.g. foo matching foo/# */\n\t\t\t\tif(acl[1] == '/'\n\t\t\t\t\t\t&& acl[2] == '#'\n\t\t\t\t\t\t&& acl[3] == 0){\n\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}\n\t\t\tapos++;\n\t\t\tacl++;\n\t\t\tsub++;\n\t\t\tif(acl[0] == 0 && sub[0] == 0){\n\t\t\t\t*result = true;\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}else if(sub[0] == 0 && acl[0] == '+' && acl[1] == 0){\n\t\t\t\tif(apos > 0 && acl[-1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\tapos++;\n\t\t\t\tacl++;\n\t\t\t\t*result = true;\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}\n\t\tlastchar = acl-1;\n\t}\n\tif((sub[0] != 0 || acl[0] != 0)){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\twhile(sub[0] != 0){\n\t\tif(sub[0] == '+' || sub[0] == '#'){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t\tsub++;\n\t}\n\n\t*result = true;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_sub_matches_acl(const char *acl, const char *sub, bool *result)\n{\n\treturn sub_matches_acl(acl, sub, NULL, NULL, false, result);\n}\n\n\nBROKER_EXPORT int mosquitto_sub_matches_acl_with_pattern(const char *acl, const char *sub, const char *clientid, const char *username, bool *result)\n{\n\treturn sub_matches_acl(acl, sub, clientid, username, true, result);\n}\n\n\nBROKER_EXPORT int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result)\n{\n\treturn topic_matches_sub(sub, topic, NULL, NULL, false, result);\n}\n\n\nBROKER_EXPORT int mosquitto_topic_matches_sub_with_pattern(const char *sub, const char *topic, const char *clientid, const char *username, bool *result)\n{\n\treturn topic_matches_sub(sub, topic, clientid, username, true, result);\n}\n\n\n/* Does a topic match a subscription? */\nBROKER_EXPORT int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result)\n{\n\tsize_t spos, tpos;\n\n\tif(!result){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\t*result = false;\n\n\tif(!sub || !topic || !sublen || !topiclen){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif((sub[0] == '$' && topic[0] != '$')\n\t\t\t|| (topic[0] == '$' && sub[0] != '$')){\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tspos = 0;\n\ttpos = 0;\n\n\twhile(spos < sublen){\n\t\tif(tpos < topiclen && (topic[tpos] == '+' || topic[tpos] == '#')){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(tpos == topiclen || sub[spos] != topic[tpos]){\n\t\t\tif(sub[spos] == '+'){\n\t\t\t\t/* Check for bad \"+foo\" or \"a/+foo\" subscription */\n\t\t\t\tif(spos > 0 && sub[spos-1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\t/* Check for bad \"foo+\" or \"foo+/a\" subscription */\n\t\t\t\tif(spos+1 < sublen && sub[spos+1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\tspos++;\n\t\t\t\twhile(tpos < topiclen && topic[tpos] != '/'){\n\t\t\t\t\tif(topic[tpos] == '+' || topic[tpos] == '#'){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\ttpos++;\n\t\t\t\t}\n\t\t\t\tif(tpos == topiclen && spos == sublen){\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}else if(sub[spos] == '#'){\n\t\t\t\t/* Check for bad \"foo#\" subscription */\n\t\t\t\tif(spos > 0 && sub[spos-1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\t/* Check for # not the final character of the sub, e.g. \"#foo\" */\n\t\t\t\tif(spos+1 < sublen){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}else{\n\t\t\t\t\twhile(tpos < topiclen){\n\t\t\t\t\t\tif(topic[tpos] == '+' || topic[tpos] == '#'){\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttpos++;\n\t\t\t\t\t}\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\t/* Check for e.g. foo/bar matching foo/+/# */\n\t\t\t\tif(tpos == topiclen\n\t\t\t\t\t\t&& spos > 0\n\t\t\t\t\t\t&& sub[spos-1] == '+'\n\t\t\t\t\t\t&& sub[spos] == '/'\n\t\t\t\t\t\t&& spos+1 < sublen\n\t\t\t\t\t\t&& sub[spos+1] == '#'){\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\n\t\t\t\t/* There is no match at this point, but is the sub invalid? */\n\t\t\t\twhile(spos < sublen){\n\t\t\t\t\tif(sub[spos] == '#' && spos+1 < sublen){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tspos++;\n\t\t\t\t}\n\n\t\t\t\t/* Valid input, but no match */\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}else{\n\t\t\t/* sub[spos] == topic[tpos] */\n\t\t\tif(tpos+1 == topiclen){\n\t\t\t\t/* Check for e.g. foo matching foo/# */\n\t\t\t\tif(spos+3 == sublen\n\t\t\t\t\t\t&& sub[spos+1] == '/'\n\t\t\t\t\t\t&& sub[spos+2] == '#'){\n\t\t\t\t\t*result = true;\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t}\n\t\t\tspos++;\n\t\t\ttpos++;\n\t\t\tif(spos == sublen && tpos == topiclen){\n\t\t\t\t*result = true;\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}else if(tpos == topiclen && sub[spos] == '+' && spos+1 == sublen){\n\t\t\t\tif(spos > 0 && sub[spos-1] != '/'){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t\tspos++;\n\t\t\t\t*result = true;\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}\n\t}\n\tif(tpos < topiclen || spos < sublen){\n\t\t*result = false;\n\t}\n\twhile(tpos < topiclen){\n\t\tif(topic[tpos] == '+' || topic[tpos] == '#'){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\ttpos++;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count)\n{\n\tsize_t len;\n\tsize_t hier_count = 1;\n\tsize_t start, stop;\n\tsize_t hier;\n\tsize_t tlen;\n\tsize_t i, j;\n\n\tif(!subtopic || !topics || !count){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tlen = strlen(subtopic);\n\n\tfor(i=0; i<len; i++){\n\t\tif(subtopic[i] == '/'){\n\t\t\tif(i > len-1){\n\t\t\t\t/* Separator at end of line */\n\t\t\t}else{\n\t\t\t\thier_count++;\n\t\t\t}\n\t\t}\n\t}\n\n\t(*topics) = mosquitto_calloc(hier_count, sizeof(char *));\n\tif(!(*topics)){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tstart = 0;\n\thier = 0;\n\n\tfor(i=0; i<len+1; i++){\n\t\tif(subtopic[i] == '/' || subtopic[i] == '\\0'){\n\t\t\tstop = i;\n\t\t\tif(start != stop){\n\t\t\t\ttlen = stop-start + 1;\n\t\t\t\t(*topics)[hier] = mosquitto_calloc(tlen, sizeof(char));\n\t\t\t\tif(!(*topics)[hier]){\n\t\t\t\t\tfor(j=0; j<hier; j++){\n\t\t\t\t\t\tmosquitto_FREE((*topics)[j]);\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_FREE((*topics));\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\tfor(j=start; j<stop; j++){\n\t\t\t\t\t(*topics)[hier][j-start] = subtopic[j];\n\t\t\t\t}\n\t\t\t}\n\t\t\tstart = i+1;\n\t\t\thier++;\n\t\t}\n\t}\n\n\t*count = (int)hier_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_sub_topic_tokens_free(char ***topics, int count)\n{\n\tint i;\n\n\tif(!topics || !(*topics) || count<1){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tfor(i=0; i<count; i++){\n\t\tmosquitto_FREE((*topics)[i]);\n\t}\n\tmosquitto_FREE(*topics);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "libcommon/utf8_common.c",
    "content": "/*\nCopyright (c) 2016-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include \"mosquitto.h\"\n\n\nBROKER_EXPORT int mosquitto_validate_utf8(const char *str, int len)\n{\n\tint i;\n\tint j;\n\tint codelen;\n\tint codepoint;\n\tconst unsigned char *ustr = (const unsigned char *)str;\n\n\tif(!str){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(len < 0 || len > 65536){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tfor(i=0; i<len; i++){\n\t\tif(ustr[i] == 0){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}else if(ustr[i] <= 0x7f){\n\t\t\tcodelen = 1;\n\t\t\tcodepoint = ustr[i];\n\t\t}else if((ustr[i] & 0xE0) == 0xC0){\n\t\t\t/* 110xxxxx - 2 byte sequence */\n\t\t\tif(ustr[i] == 0xC0 || ustr[i] == 0xC1){\n\t\t\t\t/* Invalid bytes */\n\t\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t\t}\n\t\t\tcodelen = 2;\n\t\t\tcodepoint = (ustr[i] & 0x1F);\n\t\t}else if((ustr[i] & 0xF0) == 0xE0){\n\t\t\t/* 1110xxxx - 3 byte sequence */\n\t\t\tcodelen = 3;\n\t\t\tcodepoint = (ustr[i] & 0x0F);\n\t\t}else if((ustr[i] & 0xF8) == 0xF0){\n\t\t\t/* 11110xxx - 4 byte sequence */\n\t\t\tif(ustr[i] > 0xF4){\n\t\t\t\t/* Invalid, this would produce values > 0x10FFFF. */\n\t\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t\t}\n\t\t\tcodelen = 4;\n\t\t\tcodepoint = (ustr[i] & 0x07);\n\t\t}else{\n\t\t\t/* Unexpected continuation byte. */\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\n\t\t/* Reconstruct full code point */\n\t\tif(i >= len-codelen+1){\n\t\t\t/* Not enough data */\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t\tfor(j=0; j<codelen-1; j++){\n\t\t\tif((ustr[++i] & 0xC0) != 0x80){\n\t\t\t\t/* Not a continuation byte */\n\t\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t\t}\n\t\t\tcodepoint = (codepoint<<6) | (ustr[i] & 0x3F);\n\t\t}\n\n\t\t/* Check for UTF-16 high/low surrogates */\n\t\tif(codepoint >= 0xD800 && codepoint <= 0xDFFF){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\n\t\t/* Check for overlong or out of range encodings */\n\t\t/* Checking codelen == 2 isn't necessary here, because it is already\n\t\t * covered above in the C0 and C1 checks.\n\t\t * if(codelen == 2 && codepoint < 0x0080){\n\t\t *\t return MOSQ_ERR_MALFORMED_UTF8;\n\t\t * }else\n\t\t*/\n\t\tif(codelen == 3 && codepoint < 0x0800){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}else if(codelen == 4 && (codepoint < 0x10000 || codepoint > 0x10FFFF)){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\n\t\t/* Check for non-characters */\n\t\tif(codepoint >= 0xFDD0 && codepoint <= 0xFDEF){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t\tif((codepoint & 0xFFFF) == 0xFFFE || (codepoint & 0xFFFF) == 0xFFFF){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t\t/* Check for control characters */\n\t\tif(codepoint <= 0x001F || (codepoint >= 0x007F && codepoint <= 0x009F)){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "libmosquitto.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=${prefix}\nincludedir=${prefix}/include\nlibdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@\n\nName: mosquitto\nDescription: mosquitto MQTT library (C bindings)\nVersion: @VERSION@\nCflags: -I${includedir}\nLibs: -L${libdir} -lmosquitto\n"
  },
  {
    "path": "libmosquittopp.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=${prefix}\nincludedir=${prefix}/include\nlibdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@\n\nName: mosquittopp\nDescription: mosquitto MQTT library (C++ bindings)\nVersion: @VERSION@\nCflags: -I${includedir}\nLibs: -L${libdir} -lmosquittopp\n"
  },
  {
    "path": "make/broker.mk",
    "content": "ifeq ($(WITH_ADNS),yes)\n\tNEED_LIBANL:=$(shell printf 'int main(){return 0;}' | gcc -D_GNU_SOURCE -lanl -o /dev/null -x c - 2>/dev/null || echo yes)\n\tifeq ($(NEED_LIBANL),yes)\n\t\tLOCAL_LDADD+=-lanl\n\tendif\n\tLOCAL_CPPFLAGS+=-DWITH_ADNS\nendif\n\nifeq ($(WITH_BRIDGE),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_BRIDGE\nendif\n\nifeq ($(WITH_CONTROL),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_CONTROL\nendif\n\nifeq ($(WITH_EPOLL),yes)\n\tifeq ($(UNAME),Linux)\n\t\tLOCAL_CPPFLAGS+=-DWITH_EPOLL\n\tendif\nendif\n\nifeq ($(WITH_HTTP_API),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_HTTP_API\n\tLOCAL_LDADD+=-lmicrohttpd\nendif\nifeq ($(WITH_MEMORY_TRACKING),yes)\n\tifneq ($(UNAME),SunOS)\n\t\tLOCAL_CPPFLAGS+=-DWITH_MEMORY_TRACKING\n\tendif\nendif\n\nifeq ($(WITH_OLD_KEEPALIVE),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_OLD_KEEPALIVE\nendif\n\nifeq ($(WITH_PERSISTENCE),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_PERSISTENCE\nendif\n\nifeq ($(WITH_SYS_TREE),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_SYS_TREE\nendif\n\nifeq ($(WITH_SYSTEMD),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_SYSTEMD\n\tLOCAL_LDADD+=-lsystemd\nendif\n\nifeq ($(WITH_THREADING),yes)\n\tLOCAL_CFLAGS+=-pthread\n\tLOCAL_LDFLAGS+=-pthread\nendif\n\nifeq ($(WITH_TLS),yes)\n\tLOCAL_LDADD+=-lssl -lcrypto\nendif\n\nifeq ($(WITH_WEBSOCKETS),lws)\n\tLOCAL_CPPFLAGS+=-DWITH_WEBSOCKETS=WS_IS_LWS\n\tLOCAL_LDADD+=-lwebsockets\nendif\n\nifeq ($(WITH_WRAP),yes)\n\tLOCAL_LDADD+=-lwrap\n\tLOCAL_CPPFLAGS+=-DWITH_WRAP\nendif\n\nifeq ($(WITH_XTREPORT),yes)\n\tLOCAL_CPPFLAGS+=-DWITH_XTREPORT\nendif\n"
  },
  {
    "path": "make/unit-test.mk",
    "content": "ifdef MOSQ_USE_VALGRIND\n\tifeq ($(MOSQ_USE_VALGRIND),callgrind)\n\t\tSANITIZER_COMMAND=valgrind -q --tool=callgrind --log-file=$${t}.vglog\n\tendif\n\tifeq ($(MOSQ_USE_VALGRIND),massif)\n\t\tSANITIZER_COMMAND=valgrind -q --tool=massif --log-file=$${t}.vglog\n\tendif\n\tifeq ($(MOSQ_USE_VALGRIND),failgrind)\n\t\tSANITIZER_COMMAND=fg-helper\n\tendif\n\tifndef SANITIZER_COMMAND\n\t\tSANITIZER_COMMAND=valgrind -q --trace-children=yes --leak-check=full --show-leak-kinds=all --log-file=$${t}.vglog\n\tendif\nelse\n\tSANITIZER_COMMAND=\nendif\n"
  },
  {
    "path": "man/CMakeLists.txt",
    "content": "# If we are building from a release tarball, the man pages should already be built, so them.\n# If we are building from git, then the man pages will not be built. In this\n# case, attempt to find xsltproc, and if found build the man pages. If xsltproc\n# could not be found, then the man pages will not be built or installed -\n# because the install is optional.\n\nfind_program(XSLTPROC xsltproc OPTIONAL)\nif(XSLTPROC AND NOT WIN32)\n\tfunction(compile_manpage page)\n\t\tadd_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/man/${page}\n\t\t\tCOMMAND xsltproc --xinclude --nonet ${PROJECT_SOURCE_DIR}/man/${page}.xml -o ${PROJECT_SOURCE_DIR}/man/\n\t\t\tMAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/man/${page}.xml)\n\t\tadd_custom_target(${page} ALL DEPENDS ${PROJECT_SOURCE_DIR}/man/${page})\n\tendfunction()\n\n\tcompile_manpage(\"libmosquitto.3\")\n\tcompile_manpage(\"mosquitto-tls.7\")\n\tcompile_manpage(\"mosquitto.7\")\n\tcompile_manpage(\"mosquitto.8\")\n\tcompile_manpage(\"mosquitto.conf.5\")\n\tcompile_manpage(\"mosquitto_ctrl.1\")\n\tcompile_manpage(\"mosquitto_ctrl_dynsec.1\")\n\tcompile_manpage(\"mosquitto_ctrl_shell.1\")\n\tcompile_manpage(\"mosquitto_passwd.1\")\n\tcompile_manpage(\"mosquitto_pub.1\")\n\tcompile_manpage(\"mosquitto_rr.1\")\n\tcompile_manpage(\"mosquitto_signal.1\")\n\tcompile_manpage(\"mosquitto_sub.1\")\n\tcompile_manpage(\"mqtt.7\")\n\n\tinstall(FILES\n\t\tmosquitto_ctrl.1\n\t\tmosquitto_ctrl_dynsec.1\n\t\tmosquitto_ctrl_shell.1\n\t\tmosquitto_passwd.1\n\t\tmosquitto_pub.1\n\t\tmosquitto_sub.1\n\t\tmosquitto_rr.1\n\t\tmosquitto_signal.1\n\t\tDESTINATION ${CMAKE_INSTALL_MANDIR}/man1\n\t\tOPTIONAL)\n\n\tinstall(FILES libmosquitto.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3 OPTIONAL)\n\tinstall(FILES mosquitto.conf.5 DESTINATION ${CMAKE_INSTALL_MANDIR}/man5 OPTIONAL)\n\tinstall(FILES\n\t\tmosquitto-tls.7\n\t\tmosquitto.7\n\t\tmqtt.7\n\t\tDESTINATION ${CMAKE_INSTALL_MANDIR}/man7 OPTIONAL\n\t)\n\tinstall(FILES mosquitto.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8 OPTIONAL)\n\nelseif(WIN32)\n\tmessage(WARNING \"xsltproc not found: manpages cannot be built\")\nelse()\n\tmessage(FATAL_ERROR \"xsltproc not found: manpages cannot be built\")\nendif()\n"
  },
  {
    "path": "man/Makefile",
    "content": "R=..\ninclude ${R}/config.mk\n\n.PHONY : all clean install uninstall dist\n\nMANPAGES = \\\n\tlibmosquitto.3 \\\n\tmosquitto-tls.7 \\\n\tmosquitto.7 \\\n\tmosquitto.8 \\\n\tmosquitto.conf.5 \\\n\tmosquitto_ctrl.1 \\\n\tmosquitto_ctrl_dynsec.1 \\\n\tmosquitto_ctrl_shell.1 \\\n\tmosquitto_passwd.1 \\\n\tmosquitto_pub.1 \\\n\tmosquitto_rr.1 \\\n\tmosquitto_signal.1 \\\n\tmosquitto_sub.1 \\\n\tmqtt.7\n\nall :  ${MANPAGES}\n\nclean :\n\nreallyclean : clean\n\t-rm -f *.orig\n\t-rm -f ${MANPAGES}\n\ndist : ${MANPAGES}\n\ninstall :\n\t$(INSTALL) -d \"${DESTDIR}$(mandir)/man1\"\n\t$(INSTALL) -m 644 mosquitto_ctrl.1 \"${DESTDIR}${mandir}/man1/mosquitto_ctrl.1\"\n\t$(INSTALL) -m 644 mosquitto_ctrl_dynsec.1 \"${DESTDIR}${mandir}/man1/mosquitto_ctrl_dynsec.1\"\n\t$(INSTALL) -m 644 mosquitto_ctrl_shell.1 \"${DESTDIR}${mandir}/man1/mosquitto_ctrl_shell.1\"\n\t$(INSTALL) -m 644 mosquitto_passwd.1 \"${DESTDIR}${mandir}/man1/mosquitto_passwd.1\"\n\t$(INSTALL) -m 644 mosquitto_pub.1 \"${DESTDIR}${mandir}/man1/mosquitto_pub.1\"\n\t$(INSTALL) -m 644 mosquitto_rr.1 \"${DESTDIR}${mandir}/man1/mosquitto_rr.1\"\n\t$(INSTALL) -m 644 mosquitto_signal.1 \"${DESTDIR}${mandir}/man1/mosquitto_signal.1\"\n\t$(INSTALL) -m 644 mosquitto_sub.1 \"${DESTDIR}${mandir}/man1/mosquitto_sub.1\"\n\t$(INSTALL) -d \"${DESTDIR}$(mandir)/man3\"\n\t$(INSTALL) -m 644 libmosquitto.3 \"${DESTDIR}${mandir}/man3/libmosquitto.3\"\n\t$(INSTALL) -d \"${DESTDIR}$(mandir)/man5\"\n\t$(INSTALL) -m 644 mosquitto.conf.5 \"${DESTDIR}${mandir}/man5/mosquitto.conf.5\"\n\t$(INSTALL) -d \"${DESTDIR}$(mandir)/man7\"\n\t$(INSTALL) -m 644 mosquitto.7 \"${DESTDIR}${mandir}/man7/mosquitto.7\"\n\t$(INSTALL) -m 644 mosquitto-tls.7 \"${DESTDIR}${mandir}/man7/mosquitto-tls.7\"\n\t$(INSTALL) -m 644 mqtt.7 \"${DESTDIR}${mandir}/man7/mqtt.7\"\n\t$(INSTALL) -d \"${DESTDIR}$(mandir)/man8\"\n\t$(INSTALL) -m 644 mosquitto.8 \"${DESTDIR}${mandir}/man8/mosquitto.8\"\n\nuninstall :\n\t-rm -f \"${DESTDIR}${mandir}/man1/mosquitto_ctrl.1\"\n\t-rm -f \"${DESTDIR}${mandir}/man1/mosquitto_ctrl_dynsec.1\"\n\t-rm -f \"${DESTDIR}${mandir}/man1/mosquitto_ctrl_shell.1\"\n\t-rm -f \"${DESTDIR}${mandir}/man1/mosquitto_passwd.1\"\n\t-rm -f \"${DESTDIR}${mandir}/man1/mosquitto_pub.1\"\n\t-rm -f \"${DESTDIR}${mandir}/man1/mosquitto_rr.1\"\n\t-rm -f \"${DESTDIR}${mandir}/man1/mosquitto_signal.1\"\n\t-rm -f \"${DESTDIR}${mandir}/man1/mosquitto_sub.1\"\n\t-rm -f \"${DESTDIR}${mandir}/man3/libmosquitto.3\"\n\t-rm -f \"${DESTDIR}${mandir}/man5/mosquitto.conf.5\"\n\t-rm -f \"${DESTDIR}${mandir}/man7/mosquitto-tls.7\"\n\t-rm -f \"${DESTDIR}${mandir}/man7/mosquitto.7\"\n\t-rm -f \"${DESTDIR}${mandir}/man7/mqtt.7\"\n\t-rm -f \"${DESTDIR}${mandir}/man8/mosquitto.8\"\n\n% : %.xml %.meta manpage.xsl\n\t$(XSLTPROC) --xinclude $<\n\nhtml : *.xml\n\tset -e; for m in *.xml; \\\n\t\tdo \\\n\t\thfile=$$(echo $${m} | sed -e 's#\\(.*\\)\\.xml#\\1#' | sed -e 's/\\./-/g'); \\\n\t\t$(XSLTPROC) html.xsl $${m} > $${hfile}.html; \\\n\tdone\n\npotgen :\n\t xml2po -o po/mosquitto/mosquitto.7.pot mosquitto.7.xml\n\t xml2po -o po/mosquitto/mosquitto.8.pot mosquitto.8.xml\n\t xml2po -o po/mosquitto.conf/mosquitto.conf.5.pot mosquitto.conf.5.xml\n\t xml2po -o po/mosquitto_ctrl/mosquitto_ctrl.1.pot mosquitto_ctrl.1.xml\n\t xml2po -o po/mosquitto_ctrl/mosquitto_ctrl_dynsec.1.pot mosquitto_ctrl_dynsec.1.xml\n\t xml2po -o po/mosquitto_ctrl/mosquitto_ctrl_shell.1.pot mosquitto_ctrl_shell.1.xml\n\t xml2po -o po/mosquitto_passwd/mosquitto_passwd.1.pot mosquitto_passwd.1.xml\n\t xml2po -o po/mosquitto_pub/mosquitto_pub.1.pot mosquitto_pub.1.xml\n\t xml2po -o po/mosquitto_sub/mosquitto_sub.1.pot mosquitto_sub.1.xml\n\t xml2po -o po/mosquitto_sub/mosquitto_rr.1.pot mosquitto_rr.1.xml\n\t xml2po -o po/mosquitto_sub/mosquitto_signal.1.pot mosquitto_signal.1.xml\n\t xml2po -o po/mqtt/mqtt.7.pot mqtt.7.xml\n\t xml2po -o po/mosquitto-tls/mosquitto-tls.7.pot mosquitto-tls.7.xml\n\t xml2po -o po/libmosquitto/libmosquitto.3.pot libmosquitto.3.xml\n\n# To merge new translations do:\n# /usr/bin/xml2po -p de.po chapter1.xml > chapter1.de.xml\n"
  },
  {
    "path": "man/common/env-var-mosquitto-unsafe-allow-symlinks.xml",
    "content": "<varlistentry>\n\t<term><envar>MOSQUITTO_UNSAFE_ALLOW_SYMLINKS</envar></term>\n\t<listitem>\n\t\t<para>\n\t\t\tBy default, sensitive file with a path including a\n\t\t\tsymbolic link will be refused to be loaded. Set this\n\t\t\tenvironment variable to any value to allow load files\n\t\t\tthrough symbolic links. Note that making use of this\n\t\t\tvariable could expose you to symlink attacks and so it\n\t\t\tshould only be used in cases where you are absolutely\n\t\t\tsure this is not a risk.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-bind.xml",
    "content": "<varlistentry>\n\t<term><option>-A</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tBind the outgoing connection to a local ip address/hostname. Use\n\t\t\tthis argument if you need to restrict network communication to a\n\t\t\tparticular interface.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-clean-session.xml",
    "content": "<varlistentry>\n\t<term><option>-c</option></term>\n\t<term><option>--disable-clean-session</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tDisable 'clean session' / enable persistent client mode. When this\n\t\t\targument is used, the broker will be instructed not to clean\n\t\t\texisting sessions for the same client id when the client connects,\n\t\t\tand sessions will never expire when the client disconnects. MQTT v5\n\t\t\tclients can change their session expiry interval with the\n\t\t\t<option>-x</option> argument.\n\t\t</para>\n\t\t<para>\n\t\t\tWhen a session is persisted on the broker, the subscriptions for the\n\t\t\tclient will be maintained after it disconnects, along with\n\t\t\tsubsequent QoS 1 and QoS 2 messages that arrive. When the client\n\t\t\treconnects and does not clean the session, it will receive all of\n\t\t\tthe queued messages.\n\t\t</para>\n\t\t<para>\n\t\t\tIf using this option, the client id must be set manually with\n\t\t\t<option>--id</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-clientid-prefix.xml",
    "content": "<varlistentry>\n\t<term><option>-I</option></term>\n\t<term><option>--id-prefix</option></term>\n\t<listitem>\n\t\t<para>\n            Provide a prefix that the client id will be built from by appending\n            the process id of the client. This is useful where the broker is\n            using the clientid_prefixes option. Cannot be used at the same time\n            as the <option>--id</option> argument.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-clientid.xml",
    "content": "<varlistentry>\n\t<term><option>-i</option></term>\n\t<term><option>--id</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tThe id to use for this client. If not given, a client id will be\n\t\t\tgenerated depending on the MQTT version being used. For v3.1.1/v3.1,\n\t\t\tthe client generates a client id in the format\n\t\t\t<option>mosq-XXXXXXXXXXXXXXXXXX</option>, where the <option>X</option>\n\t\t\tare replaced with random alphanumeric characters. For v5.0, the client\n\t\t\tsends a zero length client id, and the server will generate a client\n\t\t\tid for the client.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-config-file.xml",
    "content": "<!DOCTYPE varlistentry [\n\t<!ENTITY from-version21 SYSTEM \"version-2.1.xml\">\n]>\n<varlistentry>\n\t<term><option>-o</option> <replaceable>config-file</replaceable></term>\n\t<listitem>\n\t\t&from-version21;\n\t\t<para>\n\t\t\tLoad options from a config file. See the Default Config File and\n\t\t\tConfig File sections at the start of the Options section.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-debug.xml",
    "content": "<varlistentry>\n\t<term><option>-d</option></term>\n\t<term><option>--debug</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tEnable debug messages.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-format-no-eol.xml",
    "content": "<varlistentry>\n\t<term><option>-N</option></term>\n\t<listitem>\n\t\t<para>\n            Do not append an end of line character to the payload when printing.\n            This allows streaming of payload data from multiple messages\n            directly to another application unmodified. Only really makes sense\n            when not using <option>-v</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-format-pretty.xml",
    "content": "<varlistentry>\n\t<term><option>--pretty</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tWhen using the JSON output format %j or %J, the default is to print\n\t\t\tin an unformatted fashion. Specifying <option>--pretty</option>\n\t\t\tprints messages in a prettier, more human readable format.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-format-verbose.xml",
    "content": "<varlistentry>\n\t<term><option>-v</option></term>\n\t<term><option>--verbose</option></term>\n\t<listitem>\n\t\t<para>\n            Print received messages verbosely. With this argument, messages will\n            be printed as \"topic payload\". When this argument is not given, the\n            messages are printed as \"payload\".\n\t\t</para>\n\t\t<para>\n\t\t\tSee also <option>-F</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-format.xml",
    "content": "<varlistentry>\n\t<term><option>-F</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSpecify output printing format. This option allows you to choose\n\t\t\twhat information from each message is printed to the screen. See the\n\t\t\t<link linkend='outputformat'>Output Format</link> section below for\n\t\t\tfull details.\n\t\t</para>\n\t\t<para>\n\t\t\tThis option overrides the <option>-v</option> option, but does not\n\t\t\toverride the <option>-N</option> option.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-help.xml",
    "content": "<varlistentry>\n\t<term><option>--help</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tDisplay usage information.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-hide-retain.xml",
    "content": "<varlistentry>\n\t<term><option>-R</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tIf this argument is given, messages that are received that have the\n\t\t\tretain bit set will not be printed. Messages with retain set are\n\t\t\t\"stale\", in that it is not known when they were originally\n\t\t\tpublished. When subscribing to a wildcard topic there may be a large\n\t\t\tnumber of retained messages. This argument suppresses their display.\n\t\t</para>\n\t\t<para>\n\t\t\tSee also <option>--retain-handling</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-host.xml",
    "content": "<varlistentry>\n\t<term><option>-h</option></term>\n\t<term><option>--host</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSpecify the host to connect to. Defaults to localhost.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-keepalive.xml",
    "content": "<varlistentry>\n\t<term><option>-k</option></term>\n\t<term><option>--keepalive</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tThe number of seconds between sending PING commands to the broker\n\t\t\tfor the purposes of informing it we are still connected and\n\t\t\tfunctioning. Defaults to 60 seconds.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-no-tls.xml",
    "content": "<!DOCTYPE varlistentry [\n\t<!ENTITY from-version21 SYSTEM \"version-2.1.xml\">\n]>\n<varlistentry>\n\t<term><option>--no-tls</option></term>\n\t<listitem>\n\t\t&from-version21;\n\t\t<para>\n\t\t\tDisable all use of TLS encryption. This is useful if you specify TLS\n\t\t\toptions in a configuration file but want to disable those options.\n\t\t\tIt also stops the automatic use of TLS when connecting to port 8883.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-nodelay.xml",
    "content": "<varlistentry>\n\t<term><option>--nodelay</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tDisable Nagle's algorithm for the socket. This means that latency of\n\t\t\tsent messages is reduced, which is particularly noticeable for\n\t\t\tsmall, reasonably infrequent messages. Using this option may result\n\t\t\tin more packets being sent than would normally be necessary.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-password.xml",
    "content": "<varlistentry>\n\t<term><option>-P</option></term>\n\t<term><option>--pw</option></term>\n\t<listitem>\n\t\t<para>\n            Provide a password to be used for authenticating with\n            the broker. Using this argument without also specifying\n            a username is invalid when using MQTT v3.1 or v3.1.1.\n\t\t</para>\n\t\t<para>\n\t\t\tSee also the <option>--username</option> option.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-payload-file.xml",
    "content": "<varlistentry>\n\t<term><option>-f</option></term>\n\t<term><option>--file</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSend the contents of a file as the message.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-payload-message.xml",
    "content": "<varlistentry>\n\t<term><option>-m</option></term>\n\t<term><option>--message</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSend a single request message from the command line.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-payload-null.xml",
    "content": "<varlistentry>\n\t<term><option>-n</option></term>\n\t<term><option>--null-message</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSend a null (zero length) message.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-payload-stdin-file.xml",
    "content": "<varlistentry>\n\t<term><option>-s</option></term>\n\t<term><option>--stdin-file</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSend a request message read from stdin, sending the entire content\n\t\t\tas a single message.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-port.xml",
    "content": "<varlistentry>\n\t<term><option>-p</option></term>\n\t<term><option>--port</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tConnect to the port specified. If not given, the default of 1883 for\n\t\t\tplain MQTT or 8883 for MQTT over TLS will be used.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-property.xml",
    "content": "<varlistentry>\n\t<term><option>-D</option></term>\n\t<term><option>--property</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSet MQTT v5 properties for with this client. If you use this option,\n\t\t\tthe client will be set to be an MQTT v5 client. This option has two\n\t\t\tforms:\n\t\t</para>\n\t\t<para><option>-D command identifier value</option></para>\n\t\t<para><option>-D command identifier name value</option></para>\n\t\t<para>\n\t\t\t<option>command</option> is the MQTT command/packet identifier and\n\t\t\tcan be one of CONNECT, PUBLISH, PUBACK, PUBREC, PUBCOMP, SUBSCRIBE,\n\t\t\tUNSUBSCRIBE, DISCONNECT, AUTH, or WILL. The properties available\n\t\t\tfor each command are listed in the Properties section.\n\t\t</para>\n\n\t\t<para>\n\t\t\t<option>identifier</option> is the name of the property to add.\n\t\t\tThis is as described in the specification, but with '-' as a\n\t\t\tword separator. For example: <option>payload-format-indicator</option>.\n\t\t\tMore details are in the <link linkend='properties'>Properties</link>\n\t\t\tsection.\n\t\t</para>\n\n\t\t<para>\n\t\t\t<option>value</option> is the value of the property to add, with a\n\t\t\tdata type that is property specific.\n\t\t</para>\n\n\t\t<para>\n\t\t\t<option>name</option> is only used for the <option>user-property</option>\n\t\t\tproperty as the first of the two strings in the string pair. In that\n\t\t\tcase, <option>value</option> is the second of the strings in the pair.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-protocol-version.xml",
    "content": "<varlistentry>\n\t<term><option>-V</option></term>\n\t<term><option>--protocol-version</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSpecify which version of the MQTT protocol should be\n\t\t\tused when connecting to the remote broker. Can be\n\t\t\t<option>5</option>, <option>311</option>,\n\t\t\t<option>31</option>, or the more verbose\n\t\t\t<option>mqttv5</option>, <option>mqttv311</option>, or\n\t\t\t<option>mqttv31</option>.\n\t\t\tDefaults to <option>311</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-proxy.xml",
    "content": "<varlistentry xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t<term><option>--proxy</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSpecify a SOCKS5 proxy to connect through. \"None\" and \"username\"\n\t\t\tauthentication types are supported. The <option>socks-url</option>\n\t\t\tmust be of the form <option>socks5h://[username[:password]@]host[:port]</option>.\n\t\t\tThe protocol prefix <option>socks5h</option> means that hostnames\n\t\t\tare resolved by the proxy. The symbols %25, %3A and %40 are URL\n\t\t\tdecoded into %, : and @ respectively, if present in the username or\n\t\t\tpassword.\n\t\t</para>\n\n\t\t<para>\n\t\t\tIf username is not given, then no authentication is attempted.\n\t\t\tIf the port is not given, then the default of 1080 is used.\n\t\t</para>\n\n\t\t<para>\n\t\t\tIf the host is given as an IPv6 address, it must be enclosed in\n\t\t\tsquare brackets, e.g. <option>socks5h://[::1]:1080</option>.\n\t\t\tNote that square brackets have special meaning in some shells, so\n\t\t\tthe proxy url may need quoting in double or single quotes.\n\t\t</para>\n\n\t\t<para>\n\t\t\tMore SOCKS versions may be available in the future, depending on\n\t\t\tdemand, and will use different protocol prefixes as described in\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"https://linux.die.net/man/1/curl\">curl</link></refentrytitle><manvolnum>1</manvolnum> </citerefentry>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-qos-incoming.xml",
    "content": "<varlistentry xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t<term><option>-q</option></term>\n\t<term><option>--qos</option></term>\n\t<listitem>\n\t\t<para>\n            Specify the quality of service desired for the incoming messages,\n            from 0, 1 and 2. Defaults to 0. See\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle><manvolnum>7</manvolnum></citerefentry>\n\t\t\tfor more information on QoS.\n\t\t</para>\n\t\t<para>\n            The QoS applies to all topics subscribed to in a single instance of\n            this client.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-quiet.xml",
    "content": "<varlistentry>\n\t<term><option>--quiet</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tIf this argument is given, no runtime errors will be printed. This\n\t\t\texcludes any error messages given in case of invalid user input\n\t\t\t(e.g. using <option>--port</option> without a port).\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-retain-handling.xml",
    "content": "<!DOCTYPE varlistentry [\n\t<!ENTITY from-version21 SYSTEM \"version-2.1.xml\">\n]>\n<varlistentry>\n\t<term><option>--retain-handling</option> always | new | never</term>\n\t<listitem>\n\t\t&from-version21;\n\t\t<para>\n\t\t\tUse this option to control the retain handling option when making a\n\t\t\tsubscription. This controls under what circumstances an existing\n\t\t\tretained message is sent to the client when the subscription is\n\t\t\tmade.\n\t\t</para>\n\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para><option>always</option> - always deliver retained messages</para></listitem>\n\t\t\t<listitem><para>\n\t\t\t\t<option>new</option> - deliver retained messages the first time\n\t\t\t\ta subscription is made, but not on subsequent subscriptions. This\n\t\t\t\tis useful for the case where you have a long running client using\n\t\t\t\ta non-clean session. If the connection is dropped briefly, when the\n\t\t\t\tclient reconnects you will not receive the retained messages again.\n\t\t\t</para></listitem>\n\t\t\t<listitem><para><option>never</option> - never deliver retained messages</para></listitem>\n\t\t</itemizedlist>\n\t</listitem>\n</varlistentry>\n\n"
  },
  {
    "path": "man/common/option-session-expiry-interval.xml",
    "content": "<varlistentry>\n\t<term><option>-x</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSet the session-expiry-interval property on the CONNECT packet. If\n\t\t\tyou use this option, the client will be set to be an MQTT v5 client.\n\t\t\tSet to 0-4294967294 to specify the session will expire in that many\n\t\t\tseconds after the client disconnects, or use -1, 4294967295, or ∞\n\t\t\tfor a session that does not expire. Defaults to -1 if -c is also\n\t\t\tgiven, or 0 if -c not given.\n\t\t</para>\n\t\t<para>\n\t\t\tIf the session is set to never expire, either with -x or -c, then a\n\t\t\tclient id must be provided.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-srv.xml",
    "content": "<varlistentry>\n\t<term><option>-S</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tUse SRV lookups to determine which host to connect to.\n\t\t\tPerforms lookups to <option>_mqtt._tcp.&lt;host&gt;</option>\n\t\t\twhen used in conjunction with <option>-h</option>,\n\t\t\totherwise uses <option>_mqtt._tcp.&lt;local dns domain&gt;</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-timeout.xml",
    "content": "<varlistentry>\n\t<term><option>-W</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tProvide a timeout as an integer number of seconds. The client will\n\t\t\tstop processing messages and disconnect after this number of seconds\n\t\t\thas passed. The timeout starts just after the client has connected\n\t\t\tto the broker.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-alpn.xml",
    "content": "<varlistentry>\n\t<term><option>--tls-alpn</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tProvide a protocol to use when connecting to a broker that has\n\t\t\tmultiple protocols available on a single port, e.g. MQTT and\n\t\t\tWebSockets.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-cafile.xml",
    "content": "<varlistentry>\n\t<term><option>--cafile</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tDefine the path to a file containing PEM encoded CA certificates\n\t\t\tthat are trusted. Used to enable SSL communication.\n\t\t</para>\n\t\t<para>\n\t\t\tSee also <option>--capath</option>\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-capath.xml",
    "content": "<varlistentry>\n\t<term><option>--capath</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tDefine the path to a directory containing PEM encoded CA\n\t\t\tcertificates that are trusted. Used to enable SSL communication.\n\t\t</para>\n\n\t\t<para>\n\t\t\tFor <option>--capath</option> to work correctly, the certificate\n\t\t\tfiles must have \".crt\" as the file ending and you must run\n\t\t\t\"openssl rehash &lt;path to capath&gt;\" each time you add/remove\n\t\t\t\ta certificate.\n\t\t</para>\n\n\t\t<para>\n\t\t\tSee also <option>--cafile</option>\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-cert.xml",
    "content": "<varlistentry>\n\t<term><option>--cert</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tDefine the path to a file containing a PEM encoded certificate for\n\t\t\tthis client, if required by the server.\n\t\t</para>\n\n\t\t<para>\n\t\t\tSee also <option>--key</option> and the Encrypted Connections\n\t\t\tsection.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-ciphers.xml",
    "content": "<varlistentry xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t<term><option>--ciphers</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tAn openssl compatible list of TLS ciphers to support in the\n\t\t\tclient. See\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"https://linux.die.net/man/1/ciphers\">ciphers</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>\n\t\t\tfor more information.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-engine-kpass-sha1.xml",
    "content": "<varlistentry>\n\t<term><option>--tls-engine-kpass-sha1</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSHA1 of the private key password when using an TLS engine. Some TLS\n\t\t\tengines such as the TPM engine may require the use of a password in\n\t\t\torder to be accessed. This option allows a hex encoded SHA1 hash of\n\t\t\tthe password to the engine directly, instead of the user being\n\t\t\tprompted for the password.\n\t\t</para>\n\t\t<para>\n\t\t\tSee also <option>--tls-engine</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-engine.xml",
    "content": "<varlistentry>\n\t<term><option>--tls-engine</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tA valid openssl engine id. These can be listed with the\n\t\t\t<command>openssl engine</command> command.\n\t\t</para>\n\t\t<para>\n\t\t\tSee also <option>--keyform</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-insecure.xml",
    "content": "<varlistentry>\n\t<term><option>--insecure</option></term>\n\t<listitem>\n\t\t<para>\n            When using certificate based encryption, this option disables\n            verification of the server hostname in the server certificate. This\n            can be useful when testing initial server configurations but makes\n            it possible for a malicious third party to impersonate your server\n            through DNS spoofing, for example. Use this option in testing\n            only. If you need to resort to using this option in a production\n            environment, your setup is at fault and there is no point using\n            encryption.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-key.xml",
    "content": "<varlistentry>\n\t<term><option>--key</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tDefine the path to a file containing a PEM encoded private key for\n\t\t\tthis client, carrying out mutual TLS with the server.\n\t\t</para>\n\t\t<para>\n\t\t\tSee also <option>--cert</option> and the Encrypted Connections section.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-keyform.xml",
    "content": "<varlistentry>\n\t<term><option>--keyform</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSpecifies the type of private key in use when making TLS\n\t\t\tconnections.. This can be \"pem\" or \"engine\". This parameter is\n\t\t\tuseful when a TPM module is being used and the private key has been\n\t\t\tcreated with it. Defaults to \"pem\", which means normal private key\n\t\t\tfiles are used.\n\t\t</para>\n\t\t<para>\n\t\t\tSee also <option>--tls-engine</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n\n"
  },
  {
    "path": "man/common/option-tls-keylog.xml",
    "content": "<!DOCTYPE varlistentry [\n\t<!ENTITY from-version21 SYSTEM \"version-2.1.xml\">\n]>\n<varlistentry>\n\t<term><option>--tls-keylog</option> <replaceable>file</replaceable></term>\n\t<listitem>\n\t\t&from-version21;\n\t\t<para>\n\t\t\tLog TLS connection information to <replaceable>file</replaceable>.\n\t\t\tThis option allows tools such as <command>tcpdump</command>,\n\t\t\t<command>wireshark</command> and <command>mqttshark</command>\n\t\t\tto decrypt TLS traffic and inspect the MQTT traffic. In Wireshark\n\t\t\tthis can be done by setting the\n\t\t\t<option>(Pre)-Master-Secret log filename</option> option for the\n\t\t\t<option>Transport Layer Security</option> protocol.\n\t\t</para>\n\t\t<para>\n\t\t\tThis option should be used for debugging only, it must not be used\n\t\t\tin production.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-psk-identity.xml",
    "content": "<varlistentry>\n\t<term><option>--psk-identity</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tThe client identity to use with TLS-PSK support. This may be used\n\t\t\tinstead of a username if the broker is configured to do so.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-psk.xml",
    "content": "<varlistentry>\n\t<term><option>--psk</option></term>\n\t<listitem>\n\t\t<para>\n            Provide the hexadecimal (no leading 0x) pre-shared-key matching the\n            one used on the broker to use TLS-PSK encryption support.\n            <option>--psk-identity</option> must also be provided to enable TLS-PSK.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-use-os-certs.xml",
    "content": "<varlistentry>\n\t<term><option>--tls-use-os-certs</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tIf used, this will load and trust the OS provided CA\n\t\t\tcertificates. This can be used in conjunction with\n\t\t\t<option>--cafile</option> and <option>--capath</option>\n\t\t\tand can be used on its own to enable TLS mode. This\n\t\t\twill be set by default if <option>-L mqtts://...</option>\n\t\t\tis used, or if port is 8883 and no other certificate\n\t\t\toptions are used.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-tls-version.xml",
    "content": "<varlistentry>\n\t<term><option>--tls-version</option></term>\n\t<listitem>\n\t\t<para>\n            Choose which TLS protocol version to use when communicating with the\n\t\t\tbroker. Valid options are <option>tlsv1.3</option> and\n\t\t\t<option>tlsv1.2</option>. The default value is <option>tlsv1.2</option>.\n\t\t\tMust match the protocol version used by the broker.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-unix-socket.xml",
    "content": "<varlistentry xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t<term><option>--unix</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tConnect to a broker through a local unix domain socket\n\t\t\tinstead of a TCP socket. This is a replacement for\n\t\t\t<option>-h</option> and <option>-L</option>. For example:\n\t\t\t<option>mosquitto_pub --unix /tmp/mosquitto.sock ...</option>\n\t\t</para>\n\t\t<para>\n\t\t\tSee the <option>socket_domain</option> option in\n\t\t\t<refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle><manvolnum>5</manvolnum>\n\t\t\tto configure Mosquitto to listen on a unix socket.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-url.xml",
    "content": "<varlistentry>\n\t<term><option>-L</option></term>\n\t<term><option>--url</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSpecify specify user, password, hostname, port and topic at once as\n\t\t\ta URL. The URL must be in the form:\n\t\t\tmqtt(s)://[username[:password]@]host[:port]/topic or\n\t\t\tws(s)://[username[:password]@]host[:port]/path\n\t\t</para>\n\t\t<para>\n\t\t\tDepending on the scheme, the port will default to different values.\n\t\t\tmqtt:// - 1883, mqtts:// - 8883, ws:// - 80, wss:// - 443.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-username.xml",
    "content": "<varlistentry>\n\t<term><option>-u</option></term>\n\t<term><option>--username</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tProvide a username to be used for authenticating with\n\t\t\tthe broker.\n\t\t</para>\n\t\t<para>\n\t\t\tSee also the <option>--pw</option> argument.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n\n"
  },
  {
    "path": "man/common/option-websockets.xml",
    "content": "<varlistentry>\n\t<term><option>--ws</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tConnect using WebSockets instead of plain TCP.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-will-payload.xml",
    "content": "<varlistentry>\n\t<term><option>--will-payload</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tSpecify a message that will be stored by the broker and sent out if\n\t\t\tthis client disconnects unexpectedly. This must be used in\n\t\t\tconjunction with <option>--will-topic</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-will-qos.xml",
    "content": "<varlistentry>\n\t<term><option>--will-qos</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tThe QoS to use for the Will. Defaults to 0. This must be used in\n\t\t\tconjunction with <option>--will-topic</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-will-retain.xml",
    "content": "<varlistentry>\n\t<term><option>--will-retain</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tIf given, if the client disconnects unexpectedly the message sent\n\t\t\tout will be treated as a retained message. This must be used in\n\t\t\tconjunction with <option>--will-topic</option>.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/option-will-topic.xml",
    "content": "<varlistentry>\n\t<term><option>--will-topic</option></term>\n\t<listitem>\n\t\t<para>\n\t\t\tThe topic on which to send a Will, in the event that the client\n\t\t\tdisconnects unexpectedly.\n\t\t</para>\n\t</listitem>\n</varlistentry>\n"
  },
  {
    "path": "man/common/options-intro.xml",
    "content": "<para>\n\tThere are three ways to provide options to &commandname;: the\n\tdefault config file, a specified config file, or options on\n\tthe command line.\n</para>\n\n<refsect2>\n\t<title>Default Config File</title>\n\t<para>\n\t\tThe default config file is located at\n\t\t<option>$XDG_CONFIG_HOME/&commandname;</option> or\n\t\t<option>$HOME/.config/&commandname;</option> on POSIX systems, or\n\t\t<option>%USERPROFILE%\\&commandname;</option> on Windows.\n\t</para>\n\n\t<para>\n\t\tThe contents of the config file should consist of\n\t\toptions, one per line in the format:\n\t\t<option>-option <replaceable>value</replaceable></option>. If\n\t\toptions are also specified on the command line, those\n\t\toptions will override the same options set in the config\n\t\tfile. The exceptions to this are the message type options, of which\n\t\tonly one can be specified. Note also that currently some options\n\t\tcannot be negated, e.g. <option>-S</option>. TLS encryption options\n\t\tcan be negated with the <option>--no-tls</option> option.\n\t</para>\n\t<para>\n\t\tConfig file lines that have a <option>#</option> as the first\n\t\tcharacter are treated as comments and not processed any further.\n\t</para>\n\t<para>\n\t\tIt is suggested that config files are primarily used for\n\t\tauthentication purposes. Use of a config file allows you to\n\t\tauthenticate without the need to show the username and password\n\t\ton the command line.\n\t</para>\n</refsect2>\n\n<refsect2>\n\t<title>Config File</title>\n\t&from-version21;\n\t<para>\n\t\tA config file can be specified on the command line using\n\t\t<option>-o <replaceable>config-file</replaceable></option>.\n\t\tIf the <option>-o</option> option is used, the default config\n\t\tfile will not be loaded.\n\t</para>\n\t<para>\n\t\tThe format is the same as for the default config file.\n\t</para>\n</refsect2>\n"
  },
  {
    "path": "man/common/section-bugs.xml",
    "content": "<refsect1>\n\t<title>Bugs</title>\n\t<para>\n\t\t<command>mosquitto</command> bug information can be found at\n\t\t<ulink url=\"https://github.com/eclipse-mosquitto/mosquitto/issues\"/>\n\t</para>\n</refsect1>\n"
  },
  {
    "path": "man/common/section-encrypted-connections.xml",
    "content": "<refsect1>\n\t<title>Encrypted Connections</title>\n\t<para>\n\t\tThis client supports TLS encrypted connections. It is strongly\n\t\trecommended that you use an encrypted connection for anything more than\n\t\tthe most basic setup.\n\t</para>\n\t<para>\n\t\tTo enable TLS connections when using x509 certificates, one of\n\t\teither <option>--cafile</option> or <option>--capath</option> can\n\t\tbe provided as an option.\n\t</para>\n\t<para>\n\t\tAlternatively, if the <option>-p 8883</option> option is used\n\t\tthen the OS provided certificates will be loaded and neither\n\t\t<option>--cafile</option> or <option>--capath</option> are\n\t\tneeded.\n\t</para>\n\t<para>\n\t\tTo enable TLS connections when using TLS-PSK, you must use the\n\t\t<option>--psk</option> and the <option>--psk-identity</option>\n\t\toptions.\n\t</para>\n</refsect1>\n"
  },
  {
    "path": "man/common/section-exit-status.xml",
    "content": "<refsect1>\n\t<title>Exit Status</title>\n\t\t<para>\n\t\t\tZero on success, or non-zero on error. If the connection is refused\n\t\t\tby the broker at the MQTT level, then the exit code is the CONNACK\n\t\t\treason code. If another error occurs, the exit code is a\n\t\t\tlibmosquitto return value.\n\t\t</para>\n\n\t\t<para>MQTT v3.1.1 CONNACK codes:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para><option>0</option> Success</para></listitem>\n\t\t\t<listitem><para><option>1</option> Connection refused: Bad protocol version</para></listitem>\n\t\t\t<listitem><para><option>2</option> Connection refused: Identifier rejected</para></listitem>\n\t\t\t<listitem><para><option>3</option> Connection refused: Server unavailable</para></listitem>\n\t\t\t<listitem><para><option>4</option> Connection refused: Bad username/password</para></listitem>\n\t\t\t<listitem><para><option>5</option> Connection refused: Not authorized</para></listitem>\n\t\t</itemizedlist>\n\n\t\t<para>MQTT v5 CONNACK codes:</para>\n\t\t<itemizedlist>\n\t\t\t<listitem><para><option>0</option> Success</para></listitem>\n\t\t\t<listitem><para><option>128</option> Unspecified error</para></listitem>\n\t\t\t<listitem><para><option>129</option> Malformed packet</para></listitem>\n\t\t\t<listitem><para><option>130</option> Protocol error</para></listitem>\n\t\t\t<listitem><para><option>131</option> Implementation specific error</para></listitem>\n\t\t\t<listitem><para><option>132</option> Unsupported protocol version</para></listitem>\n\t\t\t<listitem><para><option>133</option> Client ID not valid</para></listitem>\n\t\t\t<listitem><para><option>134</option> Bad username or password</para></listitem>\n\t\t\t<listitem><para><option>135</option> Not authorized</para></listitem>\n\t\t\t<listitem><para><option>136</option> Server unavailable</para></listitem>\n\t\t\t<listitem><para><option>137</option> Server busy</para></listitem>\n\t\t\t<listitem><para><option>138</option> Banned</para></listitem>\n\t\t\t<listitem><para><option>139</option> Server shutting down</para></listitem>\n\t\t\t<listitem><para><option>140</option> Bad authentication method</para></listitem>\n\t\t\t<listitem><para><option>141</option> Keep alive timeout</para></listitem>\n\t\t\t<listitem><para><option>142</option> Session taken over</para></listitem>\n\t\t\t<listitem><para><option>143</option> Topic filter invalid</para></listitem>\n\t\t\t<listitem><para><option>144</option> Topic name invalid</para></listitem>\n\t\t\t<listitem><para><option>147</option> Receive maximum exceeded</para></listitem>\n\t\t\t<listitem><para><option>148</option> Topic alias invalid</para></listitem>\n\t\t\t<listitem><para><option>149</option> Packet too large</para></listitem>\n\t\t\t<listitem><para><option>148</option> Message rate too high</para></listitem>\n\t\t\t<listitem><para><option>151</option> Quota exceeded</para></listitem>\n\t\t\t<listitem><para><option>152</option> Administrative action</para></listitem>\n\t\t\t<listitem><para><option>153</option> Payload format invalid</para></listitem>\n\t\t\t<listitem><para><option>154</option> Retain not supported</para></listitem>\n\t\t\t<listitem><para><option>155</option> QoS not supported</para></listitem>\n\t\t\t<listitem><para><option>156</option> Use another server</para></listitem>\n\t\t\t<listitem><para><option>157</option> Server moved</para></listitem>\n\t\t\t<listitem><para><option>158</option> Shared subscriptions not supported</para></listitem>\n\t\t\t<listitem><para><option>159</option> Connection rate exceeded</para></listitem>\n\t\t\t<listitem><para><option>160</option> Maximum connect time</para></listitem>\n\t\t\t<listitem><para><option>161</option> Subscription IDs not supported</para></listitem>\n\t\t\t<listitem><para><option>162</option> Wildcard subscriptions not supported</para></listitem>\n\t\t</itemizedlist>\n\n\t\t<para>Other codes:</para>\n\t\t<itemizedlist>\n\t\t\t<listitem><para><option>27</option> Timed out waiting for message</para></listitem>\n\t\t</itemizedlist>\n</refsect1>\n"
  },
  {
    "path": "man/common/section-output-format.xml",
    "content": "<refsect1 id='outputformat' xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t<title>Output Format</title>\n\t<para>\n\t\tThere are three ways of formatting the printed output. In all cases a\n\t\tnew-line character is appended for each message received unless the\n\t\t<option>-N</option> argument is given.\n\t</para>\n\t<para>\n\t\tPayload-only is the default output format and will print the payload\n\t\texactly as it is received.\n\t</para>\n\t<para>\n\t\tVerbose mode is activated with <option>-v</option> and prints the\n\t\tmessage topic and the payload, separated by a space.\n\t</para>\n\t<para>\n\t\tThe final option is formatted output, which allows the user to\n\t\tdefine a custom output format. The behaviour is controlled with\n\t\tthe <option>-F format-string</option> option. The format string is\n\t\ta free text string where interpreted sequences are replaced by\n\t\tdifferent parameters. The available interpreted sequences are\n\t\tdescribed below.\n\t</para>\n\t<para>\n\t\tThree characters are used to start an interpreted sequence:\n\t\t<option>%</option>, <option>@</option> and <option>\\</option>.\n\t\tSequences starting with <option>%</option> are either parameters\n\t\trelated to the MQTT message being printed, or are helper sequences\n\t\tto avoid the need to type long date format strings for example.\n\t\tSequences starting with <option>@</option> are passed to the\n\t\t<citerefentry><refentrytitle><link xlink:href=\"https://linux.die.net/man/3/strftime\">strftime</link></refentrytitle><manvolnum>3</manvolnum></citerefentry>\n\t\tfunction (with the @ replaced with a % - note that only the\n\t\tcharacter immediately after the @ is passed to strftime). This\n\t\tallows the construction of a wide variety of time based outputs.\n\t\tThe output options for strftime vary from platform to platform, so\n\t\tplease check what is available for your platform. One extension to\n\t\tstrftime is provided which is <option>@N</option>, which can be\n\t\tused to obtain the number of nanoseconds passed in the current\n\t\tsecond. The resolution of this option varies depending on the\n\t\tplatform. The final sequence character is <option>\\</option>,\n\t\twhich is used to input some characters that would otherwise be\n\t\tdifficult to enter.\n\t</para>\n\n\t<refsect2>\n\t\t<title>Flag characters</title>\n\t\t<para>\n\t\t\tThe parameters %A, %C, %d, %E, %F, %f, %I, %l, %m, %p, %R, %S, %t,\n\t\t\t%x, and %X can have optional flags immediately after the %\n\t\t\tcharacter.\n\t\t</para>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>0</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe value should be zero padded. This applies to the\n\t\t\t\t\t\tparameters %A, %E, %d, %F, %f, %l, %m, %S, %X, and %x.\n\t\t\t\t\t\tIt will be ignored for other parameters. If used with\n\t\t\t\t\t\tthe <option>-</option> flag, the <option>0</option>\n\t\t\t\t\t\tflag will be ignored.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe value will be left aligned to the field width,\n\t\t\t\t\t\tpadded with blanks. The default is right alignment, with\n\t\t\t\t\t\teither 0 or blank padding.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect2>\n\n\t<refsect2>\n\t\t<title>Field width</title>\n\t\t<para>\n\t\t\tSome of the MQTT related parameters can be formatted with an\n\t\t\toption to set their field width in a similar way to regular\n\t\t\tprintf style formats, i.e. this sets the minimum width when\n\t\t\tprinting this parameter. If the output length is smaller than\n\t\t\tthis width, the field will be padded to meet this width. This\n\t\t\tapplies to the options %A, %C, %d, %E, %F, %f, %I, %l, %m, %p,\n\t\t\t%R, %S, %t, %x, %X.\n\t\t</para>\n\t\t<para>\n\t\t\tFor example <option>%10t</option> would set the minimum topic\n\t\t\tfield width to 10 characters.\n\t\t</para>\n\t</refsect2>\n\n\t<refsect2>\n\t\t<title>Maximum width</title>\n\t\t<para>\n\t\t\tSome of the MQTT related parameters can be formatted with an\n\t\t\toption to set a maximum field width in a similar way to regular\n\t\t\tprintf style formats, for example <option>%.20t</option> for a\n\t\t\tmaximum width of 20. This applies to the options %C, %I, %R, %t.\n\t\t</para>\n\t\t<para>\n\t\t\tFor example <option>%10.10t</option> would set the minimum topic\n\t\t\tfield width to 10 characters, and the maximum topic width to\n\t\t\t10 characters, i.e. the field will always be exactly 10\n\t\t\tcharacters long.\n\t\t</para>\n\t</refsect2>\n\n\t<refsect2>\n\t\t<title>Hexadecimal binary field width</title>\n\t\t<para>\n\t\t\tThe %x and %X parameters output the payload as a single\n\t\t\thexadecimal string by default. It is also possible to split the\n\t\t\thexadecimal payload into fields by a chosen length of nibbles.\n\t\t\tFor example, <option>%.2x</option> would split the payload into\n\t\t\ttwo nibble or one byte values, separated by spaces and might produce\n\t\t\tan output of <computeroutput>18 83</computeroutput>.\n\t\t</para>\n\t\t<para>\n\t\t\tThe separator character is a space by default, but can be changed\n\t\t\tto one of <option>!\"#$&amp;'()*+,-./:;&lt;=&gt;?@[\\]^_`{|}~</option>\n\t\t\tby adding that character after the binary field width. For example\n\t\t\t<option>%.2:x</option> might produce an output of\n\t\t\t<computeroutput>18:83</computeroutput>.\n\t\t</para>\n\t</refsect2>\n\n\t<refsect2>\n\t\t<title>Floating point number printing consideration</title>\n\t\t<para>\n\t\t\tThe output format supports only the IEEE 754 floating point standard\n\t\t\tas described in Annex F of ISO/IEC 9899:1999. Don't try to use %f or\n\t\t\t%d if the platform of the publisher uses a different floating point\n\t\t\trepresentation standard than IEEE 754 or you will get invalid data.\n\t\t\tIf you are unsure what floating representation your platform is\n\t\t\tusing, then it is most likely IEEE 754. If you get malformed or\n\t\t\tunexpected values, check if the floating point number in the payload\n\t\t\tfrom the publisher is encoded in IEEE 754.\n\t\t</para>\n\t\t<para>\n\t\t\tIf want to print floats, make sure you only subscribe to topics that\n\t\t\tsend only IEEE 754 formatted floats. The processing is very strict\n\t\t\tabout floats and if anything that is not a float is received, an\n\t\t\terror message will be printed.\n\t\t</para>\n\t</refsect2>\n\n\t<refsect2>\n\t\t<title>MQTT related parameters</title>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para><option>%%</option> a literal %.</para></listitem>\n\t\t\t<listitem><para><option>%A</option> the MQTT v5 topic-alias property, if present.</para></listitem>\n\t\t\t<listitem><para><option>%C</option> the MQTT v5 content-type property, if present.</para></listitem>\n\t\t\t<listitem><para><option>%D</option> the MQTT v5 correlation-data property, if present. Note that this\n\t\t\t\t\tproperty is specified as binary data, so may produce non-printable characters.</para></listitem>\n\t\t\t<listitem><para><option>%d</option> the payload treated as an 8 byte IEEE 754 float (double).</para></listitem>\n\t\t\t<listitem><para><option>%E</option> the MQTT v5 message-expiry-interval property, if present.</para></listitem>\n\t\t\t<listitem><para><option>%F</option> the MQTT v5 payload-format-indicator property, if present.</para></listitem>\n\t\t\t<listitem><para><option>%f</option> the payload treated as an 4 byte IEEE 754 float.</para></listitem>\n\t\t\t<listitem><para><option>%l</option> the length of the payload in bytes.</para></listitem>\n\t\t\t<listitem><para><option>%m</option> the message id (only relevant for messages with QoS>0).</para></listitem>\n\t\t\t<listitem><para><option>%P</option> the MQTT v5 user-property property, if present. This will be printed in the\n\t\t\t\t\tform key:value. It is possible for any number of user properties to be attached to a message, and to\n\t\t\t\t\thave duplicate keys.</para></listitem>\n\t\t\t<listitem><para><option>%p</option> the payload raw bytes (may produce non-printable characters depending on the payload).</para></listitem>\n\t\t\t<listitem><para><option>%q</option> the message QoS.</para></listitem>\n\t\t\t<listitem><para><option>%R</option> the MQTT v5 response-topic property, if present.</para></listitem>\n\t\t\t<listitem><para><option>%r</option> the retained flag for the message.</para></listitem>\n\t\t\t<listitem><para><option>%S</option> the MQTT v5 subscription-identifier property, if present.</para></listitem>\n\t\t\t<listitem><para><option>%t</option> the message topic.</para></listitem>\n\t\t\t<listitem><para><option>%x</option> the payload with each byte as a hexadecimal number (lower case).</para></listitem>\n\t\t\t<listitem><para><option>%X</option> the payload with each byte as a hexadecimal number (upper case).</para></listitem>\n\t\t</itemizedlist>\n\t</refsect2>\n\n\t<refsect2>\n\t\t<title>Helpers</title>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para><option>%I</option> ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100</para></listitem>\n\t\t\t<listitem><para><option>%j</option> JSON output of message\n\t\t\t\t\tparameters and timestamp, with a quoted and escaped\n\t\t\t\t\tpayload. For example\n\t\t\t\t\t<code>{\"tst\":\"2020-05-06T22:12:00.000000+0100\",\"topic\":\"greeting\",\"qos\":0,\"retain\":0,\"payload\":\"hello world\"}</code>\n\t\t\t</para></listitem>\n\t\t\t<listitem><para><option>%J</option> JSON output of message\n\t\t\t\t\tparameters and timestamp, with a non-quoted and\n\t\t\t\t\tnon-escaped payload - this means the payload must\n\t\t\t\t\titself be valid JSON. For example:\n\t\t\t\t\t<code>{\"tst\":\"2020-05-06T22:12:00.000000+0100\",\"topic\":\"foo\",\"qos\":0,\"retain\":0,\"payload\":{\"temperature\":27.0,\"humidity\":57}}</code>.\n\t\t\t</para>\n\t\t\t<para>\n\t\t\t\tIf the payload is not valid JSON, then the error message\n\t\t\t\t\"Error: Message payload is not valid JSON on topic\n\t\t\t\t&lt;topic&gt;\" will be printed to stderr.\n\t\t\t</para></listitem>\n\n\t\t\t<listitem><para><option>%U</option> Unix timestamp with nanoseconds, e.g. 1470818943.786368637</para></listitem>\n\t\t</itemizedlist>\n\t</refsect2>\n\n\t<refsect2>\n\t\t<title>Time related parameters</title>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para><option>@@</option> a literal @.</para></listitem>\n\t\t\t<listitem><para><option>@X</option> pass the character represented\n\t\t\t\t\tby <option>X</option> to the strftime function as\n\t\t\t\t\t<option>%X</option>. The options supported are platform\n\t\t\t\t\tdependent.</para></listitem>\n\t\t\t<listitem><para><option>@N</option> the number of nanoseconds that\n\t\t\t\t\thave passed in the current second, with varying timing\n\t\t\t\t\tresolution depending on platform.</para></listitem>\n\t\t</itemizedlist>\n\t</refsect2>\n\n\t<refsect2>\n\t\t<title>Escape characters</title>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para><option>\\\\</option> a literal \\.</para></listitem>\n\t\t\t<listitem><para><option>\\0</option> a null character. Can be used\n\t\t\t\t\tto separate different parameters that may contain spaces\n\t\t\t\t\t(e.g. topic, payload) so that processing with tools such as\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"https://linux.die.net/man/1/xargs\">xargs</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>\n\t\t\t\t\tis easier.</para></listitem>\n\t\t\t<listitem><para><option>\\a</option> alert/bell.</para></listitem>\n\t\t\t<listitem><para><option>\\e</option> the escape sequence, which can\n\t\t\t\t\tbe used with ANSI colour codes to provide coloured output\n\t\t\t\t\tfor example.</para></listitem>\n\t\t\t<listitem><para><option>\\n</option> end of line.</para></listitem>\n\t\t\t<listitem><para><option>\\r</option> carriage return.</para></listitem>\n\t\t\t<listitem><para><option>\\t</option> horizontal tab.</para></listitem>\n\t\t\t<listitem><para><option>\\v</option> vertical tab.</para></listitem>\n\t\t</itemizedlist>\n\t</refsect2>\n</refsect1>\n"
  },
  {
    "path": "man/common/section-properties-connect.xml",
    "content": "<refsect2>\n\t<title>Connect</title>\n\t<itemizedlist>\n\t\t<listitem><para><option>authentication-data</option> (binary data - note treated as a string)</para></listitem>\n\t\t<listitem><para><option>authentication-method</option> (UTF-8 string pair)</para></listitem>\n\t\t<listitem><para><option>maximum-packet-size</option> (32-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>receive-maximum</option> (16-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>request-problem-information</option> (8-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>request-response-information</option> (8-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>session-expiry-interval</option> (32-bit unsigned integer, note use <option>-x</option> instead)</para></listitem>\n\t\t<listitem><para><option>topic-alias-maximum</option> (16-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>user-property</option> (UTF-8 string pair)</para></listitem>\n\t</itemizedlist>\n</refsect2>\n"
  },
  {
    "path": "man/common/section-properties-disconnect.xml",
    "content": "<refsect2>\n\t<title>Disconnect</title>\n\t<itemizedlist>\n\t\t<listitem><para><option>session-expiry-interval</option> (32-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>user-property</option> (UTF-8 string pair)</para></listitem>\n\t</itemizedlist>\n</refsect2>\n"
  },
  {
    "path": "man/common/section-properties-publish.xml",
    "content": "<refsect2>\n\t<title>Publish</title>\n\t<itemizedlist>\n\t\t<listitem><para><option>content-type</option> (UTF-8 string)</para></listitem>\n\t\t<listitem><para><option>correlation-data</option> (binary data - note treated as a string)</para></listitem>\n\t\t<listitem><para><option>message-expiry-interval</option> (32-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>payload-format-indicator</option> (8-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>response-topic</option> (UTF-8 string)</para></listitem>\n\t\t<listitem><para><option>topic-alias</option> (16-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>user-property</option> (UTF-8 string pair)</para></listitem>\n\t</itemizedlist>\n</refsect2>\n"
  },
  {
    "path": "man/common/section-properties-subscribe.xml",
    "content": "<refsect2>\n\t<title>Subscribe</title>\n\t<itemizedlist>\n\t\t<listitem><para><option>user-property</option> (UTF-8 string pair)</para></listitem>\n\t</itemizedlist>\n</refsect2>\n"
  },
  {
    "path": "man/common/section-properties-unsubscribe.xml",
    "content": "<refsect2>\n\t<title>Unsubscribe</title>\n\t<itemizedlist>\n\t\t<listitem><para><option>user-property</option> (UTF-8 string pair)</para></listitem>\n\t</itemizedlist>\n</refsect2>\n"
  },
  {
    "path": "man/common/section-properties-will.xml",
    "content": "<refsect2>\n\t<title>Will properties</title>\n\t<itemizedlist>\n\t\t<listitem><para><option>content-type</option> (UTF-8 string)</para></listitem>\n\t\t<listitem><para><option>correlation-data</option> (binary data - note treated as a string)</para></listitem>\n\t\t<listitem><para><option>message-expiry-interval</option> (32-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>payload-format-indicator</option> (8-bit unsigned integer)</para></listitem>\n\t\t<listitem><para><option>response-topic</option> (UTF-8 string)</para></listitem>\n\t\t<listitem><para><option>user-property</option> (UTF-8 string pair)</para></listitem>\n\t\t<listitem><para><option>will-delay-interval</option> (32-bit unsigned integer)</para></listitem>\n\t</itemizedlist>\n</refsect2>\n"
  },
  {
    "path": "man/common/section-wills.xml",
    "content": "<refsect1 xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t<title>Wills</title>\n\t<para>\n\t\tThe client can register a message with the broker that will be sent out\n\t\tif it disconnects unexpectedly. See\n\t\t<citerefentry><refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle><manvolnum>7</manvolnum></citerefentry>\n\t\tfor more information.\n\t</para>\n\t<para>\n\t\tThe minimum requirement for this is to use <option>--will-topic</option>\n\t\tto specify which topic the will should be sent out on. This will result\n\t\tin a non-retained, zero length message with QoS 0.\n\t</para>\n\t<para>\n\t\tUse the <option>--will-retain</option>, <option>--will-payload</option>\n\t\tand <option>--will-qos</option> arguments to modify the other will\n\t\tparameters.\n\t</para>\n</refsect1>\n"
  },
  {
    "path": "man/common/synopsis-tls-certificate-options.xml",
    "content": "<cmdsynopsis>\n\t<command>tls-certificate-options:</command>\n\t<sbr/>\n\t<arg><option>--no-tls</option></arg>\n\t<sbr/>\n\t<group choice='req'>\n\t\t<arg choice='plain'><option>--cafile</option> <replaceable>file</replaceable></arg>\n\t\t<arg choice='plain'><option>--capath</option> <replaceable>dir</replaceable></arg>\n\t</group>\n\t<sbr/>\n\t<arg><option>--tls-use-os-certs</option></arg>\n\t<sbr/>\n\t<arg><option>--cert</option> <replaceable>file</replaceable></arg>\n\t<arg><option>--key</option> <replaceable>file</replaceable></arg>\n\t<sbr/>\n\t<arg><option>--ciphers</option> <replaceable>ciphers</replaceable></arg>\n\t<arg><option>--insecure</option></arg>\n\t<arg><option>--tls-alpn</option> <replaceable>protocol</replaceable></arg>\n\t<arg><option>--tls-keylog</option> <replaceable>file</replaceable></arg>\n\t<arg><option>--tls-version</option> <replaceable>version</replaceable></arg>\n\t<sbr/>\n\t<arg><option>--tls-engine</option> <replaceable>engine</replaceable></arg>\n\t<arg><option>--keyform</option>\n\t<group choice='req'>\n\t\t<arg choice='plain'><replaceable>pem</replaceable></arg>\n\t\t<arg choice='plain'><replaceable>engine</replaceable></arg>\n\t</group></arg>\n\t<arg><option>--tls-engine-kpass-sha1</option> <replaceable>kpass-sha1</replaceable></arg>\n\t<sbr/>\n</cmdsynopsis>\n"
  },
  {
    "path": "man/common/synopsis-tls-psk-options.xml",
    "content": "<cmdsynopsis>\n\t<command>tls-psk-options:</command>\n\t<sbr/>\n\t<arg choice='plain'><option>--psk</option> <replaceable>hex-key</replaceable></arg>\n\t<arg choice='plain'><option>--psk-identity</option> <replaceable>identity</replaceable></arg>\n\t<arg><option>--ciphers</option> <replaceable>ciphers</replaceable></arg>\n\t<arg><option>--tls-version</option> <replaceable>version</replaceable></arg>\n</cmdsynopsis>\n"
  },
  {
    "path": "man/common/synopsis-will.xml",
    "content": "<arg>\n\t<arg choice='plain'><option>--will-topic</option> <replaceable>topic</replaceable></arg>\n\t<arg><option>--will-payload</option> <replaceable>payload</replaceable></arg>\n\t<arg><option>--will-qos</option> <replaceable>qos</replaceable></arg>\n\t<arg><option>--will-retain</option></arg>\n</arg>\n"
  },
  {
    "path": "man/common/version-2.1.xml",
    "content": "<para>Available from version 2.1.</para>\n"
  },
  {
    "path": "man/html.xsl",
    "content": "<!-- Set parameters for manpage xsl -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n\t<xsl:import href=\"http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl\"/>\n\t<xsl:output encoding=\"utf-8\" indent=\"yes\"/>\n\t<xsl:param name=\"html.stylesheet\">man.css</xsl:param>\n\t<!-- Generate ansi style function synopses. -->\n\t<xsl:param name=\"man.funcsynopsis.style\">ansi</xsl:param>\n\t<xsl:param name=\"make.clean.html\" select=\"1\"></xsl:param>\n\t<xsl:param name=\"make.valid.html\" select=\"1\"></xsl:param>\n\t<xsl:param name=\"html.cleanup\" select=\"1\"></xsl:param>\n\t<xsl:param name=\"docbook.css.source\"></xsl:param>\n</xsl:stylesheet>\n"
  },
  {
    "path": "man/libmosquitto.3.meta",
    "content": ".. title: libmosquitto man page\n.. slug: libmosquitto-3\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/libmosquitto.3.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"libmosquitto\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t<refmeta>\n\t\t<refentrytitle>libmosquitto</refentrytitle>\n\t\t<manvolnum>3</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Library calls</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>libmosquitto</refname>\n\t\t<refpurpose>MQTT version 5.0/3.1.1 client library</refpurpose>\n\t</refnamediv>\n\n\t<refsect1>\n\t\t<title>Documentation</title>\n\t\t<para>See <ulink url=\"https://mosquitto.org/api/\"/></para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/manpage.xsl",
    "content": "<!-- Set parameters for manpage xsl -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n\t<xsl:import href=\"http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl\"/>\n\t<xsl:strip-space elements=\"member\"/>\n\t<!-- Don't display notes list of link urls. -->\n\t<xsl:param name=\"man.endnotes.list.enabled\">0</xsl:param>\n\t<xsl:param name=\"man.endnotes.are.numbered\">0</xsl:param>\n\t<!-- But if we do, set a base url for the relative links. -->\n\t<xsl:param name=\"man.base.url.for.relative.links\">https://mosquitto.org/man/</xsl:param>\n\t<!-- Don't output filename when generating. -->\n\t<xsl:param name=\"man.output.quietly\" select=\"1\"></xsl:param>\n\t<!-- Generate ansi style function synopses. -->\n\t<xsl:param name=\"man.funcsynopsis.style\">ansi</xsl:param>\n</xsl:stylesheet>\n"
  },
  {
    "path": "man/mosquitto-tls.7.meta",
    "content": ".. title: mosquitto-tls man page\n.. slug: mosquitto-tls-7\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto-tls.7.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto-tls\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t<refmeta>\n\t\t<refentrytitle>mosquitto-tls</refentrytitle>\n\t\t<manvolnum>7</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Conventions and miscellaneous</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto-tls</refname>\n\t\t<refpurpose>Configure SSL/TLS support for Mosquitto</refpurpose>\n\t</refnamediv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>mosquitto</command> provides SSL support for encrypted\n\t\t\tnetwork connections and authentication. This manual describes how\n\t\t\tto create the files needed.\n\t\t</para>\n\t\t<note><para>\n\t\t\tIt is important to use different certificate subject\n\t\t\tparameters for your CA, server and clients. If the certificates\n\t\t\tappear identical, even though generated separately, the\n\t\t\tbroker/client will not be able to distinguish between them and\n\t\t\tyou will experience difficult to diagnose errors.\n\t\t</para></note>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Generating certificates</title>\n\t\t<para>\n\t\t\tThe sections below give the openssl commands that can be used to\n\t\t\tgenerate certificates, but without any context. The asciicast at\n\t\t\t<link xlink:href=\"https://asciinema.org/a/201826\">https://asciinema.org/a/201826</link>\n\t\t\tgives a full run through of how to use those commands.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Certificate Authority</title>\n\t\t<para>Generate a certificate authority certificate and key.</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>openssl req -new -x509 -days &lt;duration&gt; -extensions v3_ca -keyout ca.key -out ca.crt</para></listitem>\n\t\t</itemizedlist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Server</title>\n\t\t<para>Generate a server key.</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>openssl genrsa -aes256 -out server.key 2048</para></listitem>\n\t\t</itemizedlist>\n\n\t\t<para>Generate a certificate signing request to send to the CA.</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>openssl req -out server.csr -key server.key -new</para></listitem>\n\t\t</itemizedlist>\n\t\t<note><para>When prompted for the CN (Common Name), please enter either your server (or broker) hostname or domain name.</para></note>\n\n\t\t<para>Send the CSR to the CA, or sign it with your CA key:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days &lt;duration&gt;</para></listitem>\n\t\t</itemizedlist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Client</title>\n\t\t<para>Generate a client key.</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>openssl genrsa -aes256 -out client.key 2048</para></listitem>\n\t\t</itemizedlist>\n\n\t\t<para>Generate a certificate signing request to send to the CA.</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>openssl req -out client.csr -key client.key -new</para></listitem>\n\t\t</itemizedlist>\n\n\t\t<para>Send the CSR to the CA, or sign it with your CA key:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days &lt;duration&gt;</para></listitem>\n\t\t</itemizedlist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle>\n\t\t\t\t\t<manvolnum>5</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto.7.meta",
    "content": ".. title: Mosquitto man page\n.. slug: mosquitto-7\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto.7.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<!DOCTYPE refentry [\n\t<!ENTITY from-version21 SYSTEM \"common/version-2.1.xml\">\n]>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t<refmeta>\n\t\t<refentrytitle>mosquitto</refentrytitle>\n\t\t<manvolnum>7</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Conventions and miscellaneous</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto</refname>\n\t\t<refpurpose>Project overview</refpurpose>\n\t</refnamediv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\tThe mosquitto project contains MQTT related programs, and comprises\n\t\t\tthe mosquitto broker and applications, command line clients, and a\n\t\t\tclient library for implementing custom MQTT clients. A brief description\n\t\t\tof some MQTT features is available at\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle><manvolnum>7</manvolnum></citerefentry>.\n\t\t</para>\n\n\t\t<para>\n\t\t\tMosquitto is an\n\t\t\t<link xlink:href=\"https://projects.eclipse.org/projects/iot.mosquitto\">Eclipse Foundation project</link>,\n\t\t\tpart of the <link xlink:href=\"https://iot.eclipse.org/\">IoT top level project</link>.\n\t\t</para>\n\n\t\t<para>\n\t\t\tThe project home page is at\n\t\t\t<link xlink:href=\"https://mosquitto.org/\">mosquitto.org</link>,\n\t\t\tthe github project is at\n\t\t\t<link xlink:href=\"https://github.com/eclipse-mosquitto/mosquitto/\">github.com/eclipse-mosquitto/mosquitto</link>,\n\t\t\tand the project provides a public MQTT test broker available at\n\t\t\t<link xlink:href=\"https://test.mosquitto.org/\">test.mosquitto.org</link>.\n\t\t</para>\n\n\t\t<para>\n\t\t\tDevelopment is driven by <link xlink:href=\"https://cedalo.com/\">Cedalo</link>,\n\t\t\twho also provide professional support and enterprise features for mosquitto.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Mosquitto Broker</title>\n\t\t<para>\n\t\t\tThe mosquitto broker is the core of the project. Details on starting the broker, MQTT support, and feature descriptions can be found in\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle><manvolnum>8</manvolnum></citerefentry>. A full description of the configuration file format can be found in <citerefentry><refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle><manvolnum>5</manvolnum></citerefentry>.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Mosquitto Broker Applications</title>\n\t\t<para>\n\t\t\tThese command line applications directly support the running of the\n\t\t\tmosquitto broker.\n\t\t</para>\n\n\t\t<refsect2>\n\t\t\t<title>mosquitto_ctrl</title>\n\n\t\t\t<para>\n\t\t\t\tThis can be used to interact with the broker MQTT APIs when the\n\t\t\t\tbroker is running. It can run in batch mode, where all arguments\n\t\t\t\tare provided on the command line, or from version 2.1 onwards,\n\t\t\t\tin an interactive shell mode.\n\t\t\t</para>\n\n\t\t\t<para>\n\t\t\t\tThe batch mode is described in\n\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_ctrl-1.html\">mosquitto_ctrl</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>,\n\t\t\t\twith the Dynamic Security module described in\n\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_ctrl_dynsec-1.html\">mosquitto_ctrl_dynsec</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>.\n\t\t\t</para>\n\t\t\t<para>\n\t\t\t\tThe interactive shell mode is described in\n\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_ctrl_shell-1.html\">mosquitto_ctrl_shell</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>.\n\t\t\t</para>\n\t\t</refsect2>\n\n\t\t<refsect2>\n\t\t\t<title>mosquitto_passwd</title>\n\n\t\t\t<para>\n\t\t\t\tThis tool is used to generate and modify password files that can\n\t\t\t\tbe used with the <option>mosquitto_password_file</option> plugin\n\t\t\t\tor <option>password_file</option> configuration option. It is\n\t\t\t\tdescribed in\n\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_passwd-1.html\">mosquitto_passwd</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>.\n\t\t\t</para>\n\t\t</refsect2>\n\n\t\t<refsect2>\n\t\t\t<title>mosquitto_signal</title>\n\t\t\t&from-version21;\n\t\t\t<para>\n\t\t\t\tThis tool is used to send signals to the broker. On POSIX systems it\n\t\t\t\tperforms the same task as the <command>kill</command> command, but\n\t\t\t\twith named signals. On Windows it is the only method of sending\n\t\t\t\tsignals to the broker. It is described in\n\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_signal-1.html\">mosquitto_signal</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>.\n\t\t\t</para>\n\t\t</refsect2>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Mosquitto MQTT Command Line Clients</title>\n\t\t<para>\n\t\t\tThese command line tools act as MQTT clients and can be useful for\n\t\t\ttesting and automation of tasks related to any MQTT broker.\n\t\t</para>\n\n\t\t<refsect2>\n\t\t\t<title>mosquitto_pub</title>\n\n\t\t\t<para>\n\t\t\t\tThis client can publish messages to a broker. It supports sending\n\t\t\t\tsimple messages from the command line, reading from stdin, and\n\t\t\t\tsending files. It is described at\n\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_pub-1.html\">mosquitto_pub</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>.\n\t\t\t</para>\n\t\t</refsect2>\n\n\t\t<refsect2>\n\t\t\t<title>mosquitto_rr</title>\n\n\t\t\t<para>\n\t\t\t\tThis client operates in a request/response style by sending a\n\t\t\t\tsingle message to a broker and waiting for a response on another\n\t\t\t\ttopic. Request/response support is an MQTT v5.0 feature, but it\n\t\t\t\tis not required most cases for this client. It is described at\n\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_rr-1.html\">mosquitto_rr</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>.\n\t\t\t</para>\n\t\t</refsect2>\n\n\t\t<refsect2>\n\t\t\t<title>mosquitto_sub</title>\n\n\t\t\t<para>\n\t\t\t\tThis client subscribes to topics on the broker and prints\n\t\t\t\tmessages that it receives. It has a wide variety of output\n\t\t\t\toptions. It is described at\n\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_sub-1.html\">mosquitto_sub</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>.\n\t\t\t</para>\n\t\t</refsect2>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Mosquitto Client Library</title>\n\t\t<para>\n\t\t\tThe Mosquitto client library is described at\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"libmosquitto-3.html\">libmosquitto</link></refentrytitle><manvolnum>3</manvolnum></citerefentry>.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto.8.meta",
    "content": ".. title: Mosquitto man page\n.. slug: mosquitto-8\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto.8.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<!DOCTYPE refentry [\n\t<!ENTITY from-version21 SYSTEM \"common/version-2.1.xml\">\n]>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\t<refmeta>\n\t\t<refentrytitle>mosquitto</refentrytitle>\n\t\t<manvolnum>8</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Mosquitto</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto</refname>\n\t\t<refpurpose>an MQTT broker</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto</command>\n\t\t\t<arg><option>-c</option> <replaceable>config file</replaceable></arg>\n\t\t\t<group>\n\t\t\t\t<arg choice='plain'><option>-d</option></arg>\n\t\t\t\t<arg choice='plain'><option>--daemon</option></arg>\n\t\t\t</group>\n\t\t\t<arg><option>-p</option> <replaceable>port number</replaceable></arg>\n\t\t\t<arg><option>-q</option></arg>\n\t\t\t<arg><option>-v</option></arg>\n\t\t\t<arg><option>--tls-keylog</option> <replaceable>file</replaceable></arg>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>mosquitto</command> is a broker for the MQTT protocol\n\t\t\tversion 5.0/3.1.1/3.1.\n\t\t</para>\n\t\t<para>\n\t\t\tIt is part of the overall mosquitto project. See\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle><manvolnum>7</manvolnum></citerefentry>\n\t\t\tfor an overview of all man pages.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Options</title>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-c</option></term>\n\t\t\t\t<term><option>--config-file</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tLoad configuration from a file. If not given, then the broker will listen on port 1883 bound to the loopback interface,\n\t\t\t\t\t\tand the default values as described in\n\t\t\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle><manvolnum>5</manvolnum></citerefentry>\n\t\t\t\t\t\tare used.\n\t\t\t\t\t</para>\n\t\t\t\t\t<important><para>See the <option>-p</option> option for a description of changes in behaviour from 1.6.x to 2.0.</para></important>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-d</option></term>\n\t\t\t\t<term><option>--daemon</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Run <command>mosquitto</command> in the background as a daemon. All other behaviour remains the same.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-p</option></term>\n\t\t\t\t<term><option>--port</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Listen on the port specified. May be specified up to 10 times to open multiple sockets listening on different ports.</para>\n\t\t\t\t\t<important><para>In version 1.6.x and earlier, the listener defined by <option>-p</option> (or the default port of 1883) would be\n\t\t\t\t\t\tbound to all interfaces and so be accessible from any network. It could also be used in combination with <option>-c</option>.</para>\n\t\t\t\t\t\t<para>From version 2.0 onwards, the listeners defined with <option>-p</option> are bound to the loopback interface only, and so can\n\t\t\t\t\t\tonly be connected to from the local machine. If both <option>-p</option> is used and a listener is defined in a configuration\n\t\t\t\t\t\tfile, then the <option>-p</option> options are IGNORED.</para></important>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-q</option></term>\n\t\t\t\t<term><option>--quiet</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t&from-version21;\n\t\t\t\t\t<para>Disable all logging. This is equivalent to setting\n\t\t\t\t\t\t<option>log_type</option> to <option>none</option> in\n\t\t\t\t\t\tthe configuration file. This overrides any logging\n\t\t\t\t\t\toptions given in the configuration file and also\n\t\t\t\t\t\toverrides <option>--verbose</option>.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>--test-config</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t&from-version21;\n\t\t\t\t\t<para>\n\t\t\t\t\t\tLoad the config file specified with <option>-c</option>,\n\t\t\t\t\t\tand verify that it is valid but do not start the broker.\n\t\t\t\t\t\tThe broker exit code will be 0 if the config was valid,\n\t\t\t\t\t\tor non-zero if no config file was specified or the\n\t\t\t\t\t\tconfig file was invalid.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\n\t\t\t<xi:include href=\"common/option-tls-keylog.xml\" />\n\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-v</option></term>\n\t\t\t\t<term><option>--verbose</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Use verbose logging. This is equivalent to setting\n\t\t\t\t\t\t<option>log_type</option> to <option>all</option> in\n\t\t\t\t\t\tthe configuration file. This overrides any logging\n\t\t\t\t\t\toptions given in the configuration file.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Configuration</title>\n\t\t<para>The broker can be configured using a configuration file as\n\t\t\tdescribed in\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle><manvolnum>5</manvolnum></citerefentry>\n\t\t\tand this is the main point of information for mosquitto.\n\t\t\tThe files required for SSL/TLS support are described in\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto-tls-7.html\">mosquitto-tls</link></refentrytitle><manvolnum>7</manvolnum></citerefentry>.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Platform limitations</title>\n\t\t<para>\n\t\t\tSome versions of Windows have limitations on the number of\n\t\t\tconcurrent connections due to the Windows API being used. In\n\t\t\tmodern versions of Windows, e.g. Windows 10 or Windows Server\n\t\t\t2019, this is approximately 8192 connections. In earlier\n\t\t\tversions of Windows, this limit is 2048 connections.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>MQTT Support</title>\n\t\t<para>Mosquitto supports MQTT v5.0, v3.1.1, and v3.1.</para>\n\n\t\t<refsect2>\n\t\t\t<title>MQTT v5.0</title>\n\t\t\t<para>\n\t\t\t\tMosquitto provides full MQTT v5.0 support, but some features\n\t\t\t\tare not used directly. The following sections describe the new\n\t\t\t\tfeatures and explain where Mosquitto does not make use of a feature.\n\t\t\t</para>\n\n\t\t\t<refsect3>\n\t\t\t\t<title>Features</title>\n\t\t\t\t<variablelist>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>Enhanced authentication</option></term>\n\t\t\t\t\t\t<listitem><para>\n\t\t\t\t\t\t\tBasic MQTT authentication uses username/password\n\t\t\t\t\t\t\tchecks. Enhanced authentication allows different\n\t\t\t\t\t\t\tauthentication schemes to be integrated into MQTT,\n\t\t\t\t\t\t\tand even those schemes with multiple step processes.\n\t\t\t\t\t\t\tClients request a particular type of authentication\n\t\t\t\t\t\t\tand if the broker is configured for that scheme the\n\t\t\t\t\t\t\tauthentication continues. Mosquitto supports\n\t\t\t\t\t\t\tenhanced authentication through plugins.\n\t\t\t\t\t\t</para></listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>Error handling</option></term>\n\t\t\t\t\t\t<listitem><para>\n\t\t\t\t\t\t\tMost MQTT packets now have the concept of a\n\t\t\t\t\t\t\t<option>reason code</option>\n\t\t\t\t\t\t\twhich indicates success or failure, and what the failure\n\t\t\t\t\t\t\twas. Mosquitto provides full support for reason codes,\n\t\t\t\t\t\t\tbut does not make use of the\n\t\t\t\t\t\t\t<option>reason string</option>\n\t\t\t\t\t\t\tfeature which can be used to provide a human readable\n\t\t\t\t\t\t\terror string to explain the reason code.\n\t\t\t\t\t\t</para></listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>Flow control</option></term>\n\t\t\t\t\t\t<listitem><para>\n\t\t\t\t\t\t\tThe number of \"in flight\" messages for QoS 1 and QoS\n\t\t\t\t\t\t\t2 can be controlled by both the client and the\n\t\t\t\t\t\t\tbroker.\n\t\t\t\t\t\t</para></listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>Request / response</option></term>\n\t\t\t\t\t\t<listitem><para>\n\t\t\t\t\t\t\tMQTT v5.0 adds a request/response pattern that\n\t\t\t\t\t\t\tallows a client to publish a message and instruct\n\t\t\t\t\t\t\tthe subscribers of that message where to publish a\n\t\t\t\t\t\t\tresponse.\n\t\t\t\t\t\t</para></listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>Server redirection</option></term>\n\t\t\t\t\t\t<listitem><para>\n\t\t\t\t\t\t\tServer redirection is the concept of telling a\n\t\t\t\t\t\t\tclient to connect to a different MQTT broker, either\n\t\t\t\t\t\t\ton CONNECT or with a broker initiated DISCONNECT.\n\t\t\t\t\t\t\tMosquitto does not currently make use of this\n\t\t\t\t\t\t\tfeature.\n\t\t\t\t\t\t</para></listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>Shared subscriptions</option></term>\n\t\t\t\t\t\t<listitem><para>\n\t\t\t\t\t\t\tWhen multiple clients subscribe to the same shared\n\t\t\t\t\t\t\tsubscription, only one client out of the group will\n\t\t\t\t\t\t\treceive each message which allows for distributing\n\t\t\t\t\t\t\twork loads.\n\t\t\t\t\t\t</para></listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t</variablelist>\n\t\t\t</refsect3>\n\n\t\t\t<refsect3>\n\t\t\t\t<title>Packet properties</title>\n\t\t\t\t<para>\n\t\t\t\t\tMQTT v5.0 allows properties to be added to packets to\n\t\t\t\t\tcontrol certain behaviour. Unless noted, Mosquitto\n\t\t\t\t\tsupport the properties listed below.\n\t\t\t\t</para>\n\n\t\t\t\t<variablelist>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>CONNECT</option></term>\n\t\t\t\t\t\t<listitem>\n\t\t\t\t\t\t\t<itemizedlist>\n\t\t\t\t\t\t\t\t<listitem><para>Authentication data</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Authentication method</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Maximum packet size</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Receive maximum</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Request problem information - supported but not used</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Request response information - supported but not used</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Session expiry interval</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Topic alias maximum</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>User property</para></listitem>\n\t\t\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t\t</listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>Last will and testament</option></term>\n\t\t\t\t\t\t<listitem>\n\t\t\t\t\t\t\t<itemizedlist>\n\t\t\t\t\t\t\t\t<listitem><para>Content type</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Correlation data</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Message expiry interval</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Payload format indicator</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Response topic</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>User property</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Will delay interval</para></listitem>\n\t\t\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t\t</listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>CONNACK</option></term>\n\t\t\t\t\t\t<listitem>\n\t\t\t\t\t\t\t<itemizedlist>\n\t\t\t\t\t\t\t\t<listitem><para>Assigned client identifier</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Authentication data</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Authentication method</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Maximum packet size</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Maximum qos</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Reason string - supported but not used</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Receive maximum</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Response information - supported but not used</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Retain available</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Server keep alive</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Server reference - supported but not used</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Session expiry interval</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Shared subscription available</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Subscription identifiers available</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Topic alias maximum</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>User property</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Wildcard subscription available</para></listitem>\n\t\t\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t\t</listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>PUBLISH</option></term>\n\t\t\t\t\t\t<listitem>\n\t\t\t\t\t\t\t<itemizedlist>\n\t\t\t\t\t\t\t\t<listitem><para>Content type</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Correlation data</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Message expiry interval</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Payload format indicator</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Response topic</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Subscription identifier</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Topic alias</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>User property</para></listitem>\n\t\t\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t\t</listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>PUBACK / PUBREC / PUBREL / PUBCOMP / SUBACK / SUBSCRIBE / SUBACK</option></term>\n\t\t\t\t\t\t<listitem>\n\t\t\t\t\t\t\t<itemizedlist>\n\t\t\t\t\t\t\t\t<listitem><para>Reason string - supported but not used</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>User property</para></listitem>\n\t\t\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t\t</listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>SUBSCRIBE</option></term>\n\t\t\t\t\t\t<listitem>\n\t\t\t\t\t\t\t<itemizedlist>\n\t\t\t\t\t\t\t\t<listitem><para>Subscription identifier</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>User property</para></listitem>\n\t\t\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t\t</listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>DISCONNECT</option></term>\n\t\t\t\t\t\t<listitem>\n\t\t\t\t\t\t\t<itemizedlist>\n\t\t\t\t\t\t\t\t<listitem><para>Reason string - supported but not used</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Server reference - supported but not used</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Session expiry interval</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>User property</para></listitem>\n\t\t\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t\t</listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t\t<varlistentry>\n\t\t\t\t\t\t<term><option>AUTH</option></term>\n\t\t\t\t\t\t<listitem>\n\t\t\t\t\t\t\t<itemizedlist>\n\t\t\t\t\t\t\t\t<listitem><para>Authentication method</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Authentication data</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>Reason string - supported but not used</para></listitem>\n\t\t\t\t\t\t\t\t<listitem><para>User property</para></listitem>\n\t\t\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t\t</listitem>\n\t\t\t\t\t</varlistentry>\n\t\t\t\t</variablelist>\n\t\t\t</refsect3>\n\t\t</refsect2>\n\n\t\t<refsect2>\n\t\t\t<title>MQTT v3.1.1</title>\n\t\t\t<para>Mosquitto provides full MQTT v3.1.1 support.</para>\n\t\t</refsect2>\n\n\t\t<refsect2>\n\t\t\t<title>MQTT v3.1</title>\n\t\t\t<para>Mosquitto provides full MQTT v3.1 support.</para>\n\t\t</refsect2>\n\n\t\t<refsect2>\n\t\t\t<title>MQTT v3</title>\n\t\t\t<para>\n\t\t\t\tMQTT v3 is an obsolete version of the protocol that does not\n\t\t\t\tsupport username/password authentication and used the\n\t\t\t\t<option>clean start</option> flag in the CONNECT packet which\n\t\t\t\tapplied only to the start of a session. An MQTT v3 client\n\t\t\t\twill be able to successfully connect to a Mosquitto instance\n\t\t\t\tthat does not require authentication.\n\t\t\t</para>\n\t\t</refsect2>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Broker Status</title>\n\t\t<para>\n\t\t\tClients can find information about the broker by subscribing to\n\t\t\ttopics in the $SYS hierarchy as follows. Topics marked as static are\n\t\t\tonly sent once per client on subscription. All other topics are updated\n\t\t\tevery <option>sys_interval</option> seconds. If\n\t\t\t<option>sys_interval</option> is 0, then updates are not sent.\n\t\t</para>\n\t\t<para>\n\t\t\tNote that if you are using a command line client to interact with the\n\t\t\t$SYS topics and your shell interprets $ as an environment variable,\n\t\t\tyou need to place the topic in single quotes '$SYS/...' or to\n\t\t\tescape the dollar symbol: \\$SYS/... otherwise the $SYS will be\n\t\t\ttreated as an environment variable.\n\t\t</para>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/bytes/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe total number of bytes received since the broker started.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/bytes/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe total number of bytes sent since the broker started.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/clients/connected</option></term>\n\t\t\t\t<term><option>$SYS/broker/clients/active</option> (deprecated)</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe number of currently connected clients.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/clients/expired</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe number of disconnected persistent clients that have been\n\t\t\t\t\t\texpired and removed through the persistent_client_expiration\n\t\t\t\t\t\toption.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/clients/disconnected</option></term>\n\t\t\t\t<term><option>$SYS/broker/clients/inactive</option> (deprecated)</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe total number of persistent clients (with clean session\n\t\t\t\t\t\tdisabled) that are registered at the broker but are\n\t\t\t\t\t\tcurrently disconnected.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/clients/maximum</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe maximum number of clients that have been connected to\n\t\t\t\t\t\tthe broker at the same time.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/clients/total</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe total number of connected and disconnected client\n\t\t\t\t\t\tsessions currently registered on the broker.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/connection/#</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tWhen bridges are configured to/from the broker,\n\t\t\t\t\t\tcommon practice is to provide a status topic that\n\t\t\t\t\t\tindicates the state of the connection. This is provided\n\t\t\t\t\t\twithin $SYS/broker/connection/ by default. If the value\n\t\t\t\t\t\tof the topic is 1 the connection is active, if 0 then\n\t\t\t\t\t\tit is not active. See the Bridges section below for\n\t\t\t\t\t\tmore information on bridges.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/connections/socket/count</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe total number of socket connections that have been\n\t\t\t\t\t\tmade to the broker, whether or not the MQTT connections\n\t\t\t\t\t\twere ultimately successful.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/heap/current</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe current size of the heap memory in use by\n\t\t\t\t\t\tmosquitto. Note that this topic may be unavailable\n\t\t\t\t\t\tdepending on compile time options.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/heap/maximum</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe largest amount of heap memory used by\n\t\t\t\t\t\tmosquitto. Note that this topic may be unavailable\n\t\t\t\t\t\tdepending on compile time options.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/load/connections/+</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe moving average of the number of CONNECT packets\n\t\t\t\t\t\treceived by the broker over different time intervals.\n\t\t\t\t\t\tThe final \"+\" of the hierarchy can be 1min, 5min or\n\t\t\t\t\t\t15min. The value returned represents the number of\n\t\t\t\t\t\tconnections received in 1 minute, averaged over 1, 5 or\n\t\t\t\t\t\t15 minutes.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/load/bytes/received/+</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe moving average of the number of bytes received by\n\t\t\t\t\t\tthe broker over different time intervals. The final \"+\"\n\t\t\t\t\t\tof the hierarchy can be 1min, 5min or 15min. The value\n\t\t\t\t\t\treturned represents the number of bytes received in 1\n\t\t\t\t\t\tminute, averaged over 1, 5 or 15 minutes.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/load/bytes/sent/+</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe moving average of the number of bytes sent by the\n\t\t\t\t\t\tbroker over different time intervals. The final \"+\" of\n\t\t\t\t\t\tthe hierarchy can be 1min, 5min or 15min. The value\n\t\t\t\t\t\treturned represents the number of bytes sent in 1\n\t\t\t\t\t\tminute, averaged over 1, 5 or 15 minutes.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/load/messages/received/+</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe moving average of the number of all types of MQTT\n\t\t\t\t\t\tmessages received by the broker over different time\n\t\t\t\t\t\tintervals. The final \"+\" of the hierarchy can be 1min,\n\t\t\t\t\t\t5min or 15min. The value returned represents the number\n\t\t\t\t\t\tof messages received in 1 minute, averaged over 1, 5 or\n\t\t\t\t\t\t15 minutes.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/load/messages/sent/+</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe moving average of the number of all types of MQTT\n\t\t\t\t\t\tmessages sent by the broker over different time\n\t\t\t\t\t\tintervals. The final \"+\" of the hierarchy can be 1min,\n\t\t\t\t\t\t5min or 15min. The value returned represents the number\n\t\t\t\t\t\tof messages send in 1 minute, averaged over 1, 5 or 15\n\t\t\t\t\t\tminutes.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/load/publish/dropped/+</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe moving average of the number of publish messages\n\t\t\t\t\t\tdropped by the broker over different time intervals.\n\t\t\t\t\t\tThis shows the rate at which durable clients that are\n\t\t\t\t\t\tdisconnected are losing messages. The final \"+\" of the\n\t\t\t\t\t\thierarchy can be 1min, 5min or 15min. The value\n\t\t\t\t\t\treturned represents the number of messages dropped in 1\n\t\t\t\t\t\tminute, averaged over 1, 5 or 15 minutes.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/load/publish/received/+</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe moving average of the number of publish messages\n\t\t\t\t\t\treceived by the broker over different time intervals.\n\t\t\t\t\t\tThe final \"+\" of the hierarchy can be 1min, 5min or\n\t\t\t\t\t\t15min. The value returned represents the number of\n\t\t\t\t\t\tpublish messages received in 1 minute, averaged over 1,\n\t\t\t\t\t\t5 or 15 minutes.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/load/publish/sent/+</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe moving average of the number of publish messages\n\t\t\t\t\t\tsent by the broker over different time intervals. The\n\t\t\t\t\t\tfinal \"+\" of the hierarchy can be 1min, 5min or 15min.\n\t\t\t\t\t\tThe value returned represents the number of publish\n\t\t\t\t\t\tmessages sent in 1 minute, averaged over 1, 5 or 15\n\t\t\t\t\t\tminutes.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/load/sockets/+</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe moving average of the number of socket\n\t\t\t\t\t\tconnections opened to the broker over different time\n\t\t\t\t\t\tintervals. The final \"+\" of the hierarchy can be 1min,\n\t\t\t\t\t\t5min or 15min. The value returned represents the number\n\t\t\t\t\t\tof socket connections in 1 minute, averaged over 1, 5\n\t\t\t\t\t\tor 15 minutes.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/messages/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of messages of any type received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/messages/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of messages of any type sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/connect/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT CONNECT messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/connack/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT CONNACK messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/publish/dropped</option></term>\n\t\t\t\t<term><option>$SYS/broker/publish/messages/dropped</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe total number of MQTT PUBLISH messages that have been\n\t\t\t\t\t\tdropped due to inflight/queuing limits. See the\n\t\t\t\t\t\tmax_inflight_messages and max_queued_messages options\n\t\t\t\t\t\tin <citerefentry><refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle><manvolnum>5</manvolnum></citerefentry>\n\t\t\t\t\t\tfor more information.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/publish/received</option></term>\n\t\t\t\t<term><option>$SYS/broker/publish/messages/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PUBLISH messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/publish/sent</option></term>\n\t\t\t\t<term><option>$SYS/broker/publish/messages/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PUBLISH messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/puback/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PUBACK messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/puback/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PUBACK messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/pubrec/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PUBREC messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/pubrec/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PUBREC messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/pubrel/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PUBREL messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/pubrel/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PUBREL messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/pubcomp/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PUBCOMP messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/pubcomp/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PUBCOMP messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/subscribe/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT SUBSCRIBE messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/suback/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT SUBACK messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/unsubscribe/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT UNSUBSCRIBE messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/unsuback/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT UNSUBACK messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/pingreq/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PINGREQ messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/pingresp/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT PINGRESP messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/disconnect/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT DISCONNECT messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/disconnect/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT DISCONNECT messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/auth/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT AUTH messages received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/mqtt/auth/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of MQTT AUTH messages sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/packet/out/count</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe current number of packets queued for delivery across\n\t\t\t\t\t\tall clients. A large and increasing value here may\n\t\t\t\t\t\tindicate messages are being sent faster than the network\n\t\t\t\t\t\tcan handle.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/packet/out/bytes</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe current number of bytes in packets queued for\n\t\t\t\t\t\tdelivery across all clients. A large and increasing\n\t\t\t\t\t\tvalue here may indicate messages are being sent faster\n\t\t\t\t\t\tthan the network can handle.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/publish/bytes/received</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of PUBLISH payload bytes received since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/publish/bytes/sent</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of PUBLISH payload bytes sent since the broker started.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/retained messages/count</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of retained messages active on the broker.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/store/messages/count</option></term>\n\t\t\t\t<term><option>$SYS/broker/messages/stored</option> (deprecated)</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The number of messages currently held in the message\n\t\t\t\t\t\tstore. This includes retained messages and messages\n\t\t\t\t\t\tqueued for durable clients.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/store/messages/bytes</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The number of bytes currently held by message payloads\n\t\t\t\t\tin the message store. This includes retained messages\n\t\t\t\t\tand messages queued for durable clients.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/shared_subscriptions/count</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of shared subscriptions active on the broker.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/subscriptions/count</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The total number of subscriptions active on the broker.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>$SYS/broker/version</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The version of the broker. Static.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Wildcard Topic Subscriptions</title>\n\t\t<para>\n\t\t\tIn addition to allowing clients to subscribe to specific topics,\n\t\t\tmosquitto also allows the use of two wildcards in subscriptions.\n\t\t\t<option>+</option> is the wildcard used to match a single level of\n\t\t\thierarchy. For example, for a topic of \"a/b/c/d\", the following example\n\t\t\tsubscriptions will match:\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>a/b/c/d</para></listitem>\n\t\t\t<listitem><para>+/b/c/d</para></listitem>\n\t\t\t<listitem><para>a/+/c/d</para></listitem>\n\t\t\t<listitem><para>a/+/+/d</para></listitem>\n\t\t\t<listitem><para>+/+/+/+</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>\n\t\t\tThe following subscriptions will not match:\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>a/b/c</para></listitem>\n\t\t\t<listitem><para>b/+/c/d</para></listitem>\n\t\t\t<listitem><para>+/+/+</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>\n\t\t\tThe second wildcard is <option>#</option> and is used to match\n\t\t\tall subsequent levels of hierarchy. With a topic of \"a/b/c/d\", the\n\t\t\tfollowing example subscriptions will match:\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>a/b/c/d</para></listitem>\n\t\t\t<listitem><para>#</para></listitem>\n\t\t\t<listitem><para>a/#</para></listitem>\n\t\t\t<listitem><para>a/b/#</para></listitem>\n\t\t\t<listitem><para>a/b/c/#</para></listitem>\n\t\t\t<listitem><para>+/b/c/#</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>\n\t\t\tThe $SYS hierarchy does not match a subscription of \"#\". If you want\n\t\t\tto observe the entire $SYS hierarchy, subscribe to $SYS/#.\n\t\t</para>\n\t\t<para>\n\t\t\tNote that the wildcards must be only ever used on their own, so a\n\t\t\tsubscription of \"a/b+/c\" is not valid use of a wildcard. The\n\t\t\t<option>#</option> wildcard must only ever be used as the final\n\t\t\tcharacter of a subscription.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Bridges</title>\n\t\t<para>\n\t\t\tMultiple brokers can be connected together with the bridging\n\t\t\tfunctionality. This is useful where it is desirable to share\n\t\t\tinformation between locations, but where not all of the information\n\t\t\tneeds to be shared. An example could be where a number of users are\n\t\t\trunning a broker to help record power usage and for a number of\n\t\t\tother reasons. The power usage could be shared through bridging all\n\t\t\tof the user brokers to a common broker, allowing the power usage of\n\t\t\tall users to be collected and compared. The other information would\n\t\t\tremain local to each broker.\n\t\t</para>\n\t\t<para>\n\t\t\tFor information on configuring bridges, see\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle><manvolnum>5</manvolnum></citerefentry>.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Signals</title>\n\t\t<para>\n\t\t\tOn POSIX systems Mosquitto can receive signals and act on them as\n\t\t\tdescribed below. To send signals, use e.g.\n\t\t\t<command>kill -HUP &lt;process id of mosquitto&gt;</command>\n\t\t</para>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term>SIGHUP</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tUpon receiving the SIGHUP signal, mosquitto will\n\t\t\t\t\t\tattempt to reload configuration file data, assuming that\n\t\t\t\t\t\tthe <option>-c</option> argument was provided when\n\t\t\t\t\t\tmosquitto was started. Not all configuration parameters can\n\t\t\t\t\t\tbe reloaded without restarting. See\n\t\t\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle><manvolnum>5</manvolnum></citerefentry>\n\t\t\t\t\t\tfor details.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf TLS certificates are in use, then mosquitto will\n\t\t\t\t\t\talso reload certificate on receiving a SIGHUP.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe logs will also be closed and reopened.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term>SIGRTMIN</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tUpon receiving the SIGRTMIN signal, mosquitto will\n\t\t\t\t\t\tclose and reopen the logs to support log rotation.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term>SIGUSR1</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tUpon receiving the SIGUSR1 signal, mosquitto will\n\t\t\t\t\t\twrite the persistence database to disk. This signal is only\n\t\t\t\t\t\tacted upon if persistence is enabled.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term>SIGUSR2</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe SIGUSR2 signal causes mosquitto to print out the\n\t\t\t\t\t\tcurrent subscription tree, along with information about\n\t\t\t\t\t\twhere retained messages exist. This is intended as a\n\t\t\t\t\t\ttesting feature only and may be removed at any time.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Environment Variables</title>\n\t\t<variablelist>\n\t\t\t<xi:include href=\"common/env-var-mosquitto-unsafe-allow-symlinks.xml\" />\n\t\t</variablelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Files</title>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><filename>/etc/mosquitto/mosquitto.conf</filename></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Configuration file. See <citerefentry><refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><filename>/var/lib/mosquitto/mosquitto.db</filename></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Persistent message data storage location if persist enabled.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><filename>/etc/hosts.allow</filename></term>\n\t\t\t\t<term><filename>/etc/hosts.deny</filename></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Host access control via tcp-wrappers as described in <citerefentry><refentrytitle><link xlink:href=\"https://linux.die.net/man/5/hosts_access\">hosts_access</link></refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-bugs.xml\" />\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-tls-7.html\">mosquitto-tls</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle>\n\t\t\t\t\t<manvolnum>5</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_ctrl-1.html\">mosquitto_ctrl</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_passwd-1.html\">mosquitto_passwd</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_pub-1.html\">mosquitto_pub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_rr-1.html\">mosquitto_rr</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_sub-1.html\">mosquitto_sub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"libmosquitto-3.html\">libmosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>3</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Thanks</title>\n\t\t<para>Thanks to Andy Stanford-Clark for being one of the people who\n\t\t\tcame up with MQTT in the first place. Thanks to Andy and Nicholas\n\t\t\tO'Leary for providing clarifications of the protocol.</para>\n\t\t<para>Thanks also to everybody at the Ubuntu UK Podcast and Linux\n\t\t\tOutlaws for organising OggCamp, where Andy gave a talk that\n\t\t\tinspired mosquitto.</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto.conf.5.meta",
    "content": ".. title: mosquitto.conf man page\n.. slug: mosquitto-conf-5\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto.conf.5.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto.conf\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\t<refmeta>\n\t\t<refentrytitle>mosquitto.conf</refentrytitle>\n\t\t<manvolnum>5</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">File formats and conventions</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto.conf</refname>\n\t\t<refpurpose>the configuration file for mosquitto</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto.conf</command>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>mosquitto.conf</command> is the configuration file for\n\t\t\tmosquitto. This file can reside anywhere as long as mosquitto can read\n\t\t\tit. By default, mosquitto does not need a configuration file and will\n\t\t\tuse the default values listed below. See\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle><manvolnum>8</manvolnum></citerefentry>\n\t\t\tfor information on how to load a configuration file.\n\t\t</para>\n\t\t<para>\n\t\t\tMosquitto can be instructed to reload the configuration file by\n\t\t\tsending a SIGHUP signal as described in the Signals section of\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle><manvolnum>8</manvolnum></citerefentry>.\n\t\t\tNot all configuration options can be reloaded, as detailed in\n\t\t\tthe options below.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Sections</title>\n\t\t<para>\n\t\t\tThe rest of this man page is divided into the following sections:\n\t\t\t<variablelist>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>File Format</option> a description of the configuration\n\t\t\t\t\t\tfile syntax.</term>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>Authentication</option> a discussion of the authentication\n\t\t\t\t\t\toptions available.</term>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>General Options</option> the general options for configuring\n\t\t\t\t\t\tMosquitto.</term>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>Listeners</option> general options for configuring listeners,\n\t\t\t\t\t\twhich are the ways that MQTT clients can connect to Mosquitto,\n\t\t\t\t\t\tas well as certificate and pre-shared-key based SSL/TLS options.</term>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>Configuring Bridges</option> general options for configuring bridges,\n\t\t\t\t\t\twhich are a way of connecting multiple brokers together,\n\t\t\t\t\t\tas well as certificate and pre-shared-key based SSL/TLS options.</term>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>Other</option> The Files, Bugs, See Also sections.</term>\n\t\t\t\t</varlistentry>\n\t\t\t</variablelist>\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>File Format</title>\n\t\t<para>\n            All lines with a # as the very first character are treated as a\n            comment.\n\t\t</para>\n\t\t<para>\n            Configuration lines start with a variable name. The variable value\n            is separated from the name by a single space.\n        </para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Authentication</title>\n        <para>\n\t\t\tThe authentication options described below allow a wide range of\n\t\t\tpossibilities in conjunction with the listener options. This\n\t\t\tsection aims to clarify the possibilities. An overview is also\n\t\t\tavailable at\n\t\t\t<ulink url=\"https://mosquitto.org/documentation/authentication-methods/\"/>\n        </para>\n\t\t<para>\n\t\t\tThe simplest option is to have no authentication at all. This is\n\t\t\tthe default if no other options are given. Unauthenticated\n\t\t\tencrypted support is provided by using the certificate based\n\t\t\tSSL/TLS based options certfile and keyfile.\n\t\t</para>\n\t\t<para>\n\t\t\tMQTT provides username/password authentication as part of the\n\t\t\tprotocol. Use the password_file option to define the valid\n\t\t\tusernames and passwords. Be sure to use network encryption if you\n\t\t\tare using this option otherwise the username and password will be\n\t\t\tvulnerable to interception. Use the\n\t\t\t<option>per_listener_settings</option> to control whether passwords\n\t\t\tare required globally or on a per-listener basis.\n\t\t</para>\n\t\t<para>\n\t\t\tMosquitto provides the Dynamic Security plugin which handles\n\t\t\tusername/password authentication and access control in a much\n\t\t\tmore flexible way than a password file. See\n\t\t\t<ulink url=\"https://mosquitto.org/documentation/dynamic-security/\"/>\n\t\t</para>\n\t\t<para>\n\t\t\tWhen using certificate based encryption there are three options\n\t\t\tthat affect authentication. The first is require_certificate, which\n\t\t\tmay be set to true or false. If false, the SSL/TLS component of the\n\t\t\tclient will verify the server but there is no requirement for the\n\t\t\tclient to provide anything for the server: authentication is\n\t\t\tlimited to the MQTT built in username/password. If\n\t\t\trequire_certificate is true, the client must provide a valid\n\t\t\tcertificate in order to connect successfully. In this case, the\n\t\t\tsecond and third options, use_identity_as_username and\n\t\t\tuse_subject_as_username, become relevant. If set to true,\n\t\t\tuse_identity_as_username causes the Common Name (CN) from the\n\t\t\tclient certificate to be used instead of the MQTT username for\n\t\t\taccess control purposes. The password is not used because it is\n\t\t\tassumed that only authenticated clients have valid certificates.\n\t\t\tThis means that any CA certificates you include in cafile or capath\n\t\t\twill be able to issue client certificates that are valid for\n\t\t\tconnecting to your broker. If use_identity_as_username is false,\n\t\t\tthe client must authenticate as normal (if required by\n\t\t\tpassword_file) through the MQTT options. The same principle applies\n\t\t\tfor the use_subject_as_username option, but the entire certificate\n\t\t\tsubject is used as the username instead of just the CN.\n\t\t</para>\n\t\t<para>\n\t\t\tWhen using pre-shared-key based encryption through the psk_hint\n\t\t\tand psk_file options, the client must provide a valid identity and\n\t\t\tkey in order to connect to the broker before any MQTT communication\n\t\t\ttakes place. If use_identity_as_username is true, the PSK identity\n\t\t\tis used instead of the MQTT username for access control purposes.\n\t\t\tIf use_identity_as_username is false, the client may still\n\t\t\tauthenticate using the MQTT username/password if using the\n\t\t\tpassword_file option.\n\t\t</para>\n\t\t<para>\n\t\t\tBoth certificate and PSK based encryption are configured on a\n\t\t\tper-listener basis.\n\t\t</para>\n\t\t<para>\n\t\t\tAuthentication plugins can be created to augment the\n\t\t\tpassword_file, acl_file and psk_file options with e.g. SQL based\n\t\t\tlookups.\n\t\t</para>\n\t\t<para>\n\t\t\tIt is possible to support multiple authentication schemes at\n\t\t\tonce. A config could be created that had a listener for all of the\n\t\t\tdifferent encryption options described above and hence a large\n\t\t\tnumber of ways of authenticating.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>General Options</title>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>acl_file</option> <replaceable>file path</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\t<emphasis>Note:</emphasis> It is suggested that you use the\n\t\t\t\t\t\t<option>mosquitto_acl_file</option> plugin instead of this\n\t\t\t\t\t\toption. Using plugins for authentication and authorisation\n\t\t\t\t\t\tallows greater control of what listeners they are applied to,\n\t\t\t\t\t\twithout the need for the deprecated\n\t\t\t\t\t\t<option>per_listener_settings</option> option.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tSet the path to an access control list file. If\n\t\t\t\t\t\tdefined, the contents of the file are used to control\n\t\t\t\t\t\tclient access to topics on the broker.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf this parameter is defined then only the topics\n\t\t\t\t\t\tlisted will have access. Topic access is added with\n\t\t\t\t\t\tlines of the format:\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\t<code>topic [read|write|readwrite|deny] &lt;topic&gt;</code>\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe access type is controlled using \"read\", \"write\",\n\t\t\t\t\t\t\"readwrite\" or \"deny\". This parameter is optional (unless\n\t\t\t\t\t\t&lt;topic&gt; includes a space character) - if not\n\t\t\t\t\t\tgiven then the access is read/write. &lt;topic&gt; can\n\t\t\t\t\t\tcontain the + or # wildcards as in\n\t\t\t\t\t\tsubscriptions. The \"deny\" option can used to explicitly\n\t\t\t\t\t\tdeny access to a topic that would otherwise be granted\n\t\t\t\t\t\tby a broader read/write/readwrite statement. Any \"deny\"\n\t\t\t\t\t\ttopics are handled before topics that grant read/write access.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe first set of topics are applied to anonymous\n\t\t\t\t\t\tclients, assuming <option>allow_anonymous</option> is\n\t\t\t\t\t\ttrue. User specific topic ACLs are added after a user\n\t\t\t\t\t\tline as follows:\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\t<code>user &lt;username&gt;</code>\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe username referred to here is the same as in\n\t\t\t\t\t\t<option>password_file</option>. It is not the\n\t\t\t\t\t\tclientid.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIt is also possible to define ACLs based on pattern\n\t\t\t\t\t\tsubstitution within the topic. The form is the same as\n\t\t\t\t\t\tfor the topic keyword, but using pattern as the\n\t\t\t\t\t\tkeyword.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\t<code>pattern [read|write|readwrite|deny] &lt;topic&gt;</code>\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe patterns available for substitution are:\n\t\t\t\t\t</para>\n\t\t\t\t\t<itemizedlist mark=\"circle\">\n\t\t\t\t\t\t<listitem><para>%c to match the client id of the client</para></listitem>\n\t\t\t\t\t\t<listitem><para>%u to match the username of the client</para></listitem>\n\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe substitution pattern must be the only text for\n\t\t\t\t\t\tthat level of hierarchy. Pattern ACLs apply to all\n\t\t\t\t\t\tusers even if the \"user\" keyword has previously been\n\t\t\t\t\t\tgiven.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>Example:</para>\n\t\t\t\t\t<para><code>pattern write sensor/%u/data</code></para>\n\t\t\t\t\t<para>Allow access for bridge connection messages:</para>\n\t\t\t\t\t<para><code>pattern write $SYS/broker/connection/%c/state</code></para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf the first character of a line of the ACL file is a\n\t\t\t\t\t\t# it is treated as a comment.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf <option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>true</replaceable>, this option applies to\n\t\t\t\t\t\tthe current listener being configured only. If\n\t\t\t\t\t\t<option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>false</replaceable>, this option applies\n\t\t\t\t\t\tto all listeners.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tNote: In general it is not possible to grant write\n\t\t\t\t\t\taccess to topics in the $SYS topic tree. The exception\n\t\t\t\t\t\tis <replaceable>$SYS/broker/connection/+/state</replaceable>.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tReloaded on reload signal. The currently loaded ACLs\n\t\t\t\t\t\twill be freed and reloaded. Existing subscriptions will\n\t\t\t\t\t\tbe affected after the reload.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tSee also\n\t\t\t\t\t\t<ulink url=\"https://mosquitto.org/documentation/dynamic-security/\"/>\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>allow_anonymous</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Global boolean value that determines whether clients that\n\t\t\t\t\t\tconnect without providing a username are allowed to\n\t\t\t\t\t\tconnect. If set to <replaceable>false</replaceable>\n\t\t\t\t\t\tthen another means of connection should be created to\n\t\t\t\t\t\tcontrol authenticated client access.</para>\n\t\t\t\t\t<para>Defaults to <replaceable>false</replaceable>,\n\t\t\t\t\t\tunless no listeners are defined in the configuration\n\t\t\t\t\t\tfile, in which case it set to <replaceable>true</replaceable>,\n\t\t\t\t\t\tbut connections are only allowed from the local machine.</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf you want to control this setting for each listener individually,\n\t\t\t\t\t\tuse the <option>listener_allow_anonymous</option> option.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>(Note: Deprecated) If <option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>true</replaceable>, this option applies to\n\t\t\t\t\t\tthe current listener being configured only. If\n\t\t\t\t\t\t<option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>false</replaceable>, this option applies\n\t\t\t\t\t\tto all listeners.</para>\n\n\t\t\t\t\t<important><para>In version 1.6.x and earlier, this option defaulted\n\t\t\t\t\t\tto <replaceable>true</replaceable> unless there was another security\n\t\t\t\t\t\toption set.</para></important>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>allow_duplicate_messages</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>This option is deprecated and will be removed in a\n\t\t\t\t\t\tfuture version. The behaviour will default to true.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>If a client is subscribed to multiple subscriptions\n\t\t\t\t\t\tthat overlap, e.g. foo/# and foo/+/baz , then MQTT\n\t\t\t\t\t\texpects that when the broker receives a message on a\n\t\t\t\t\t\ttopic that matches both subscriptions, such as\n\t\t\t\t\t\tfoo/bar/baz, then the client should only receive the\n\t\t\t\t\t\tmessage once.</para>\n\t\t\t\t\t<para>Mosquitto keeps track of which clients a message has\n\t\t\t\t\t\tbeen sent to in order to meet this requirement. This\n\t\t\t\t\t\toption allows this behaviour to be disabled, which may\n\t\t\t\t\t\tbe useful if you have a large number of clients\n\t\t\t\t\t\tsubscribed to the same set of topics and want to\n\t\t\t\t\t\tminimise memory usage.</para>\n\t\t\t\t\t<para>It can be safely set to\n\t\t\t\t\t\t<replaceable>true</replaceable> if you know in advance\n\t\t\t\t\t\tthat your clients will never have overlapping\n\t\t\t\t\t\tsubscriptions, otherwise your clients must be able to\n\t\t\t\t\t\tcorrectly deal with duplicate messages even when then\n\t\t\t\t\t\thave QoS=2.</para>\n\t\t\t\t\t<para>Defaults to <replaceable>true</replaceable>.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>allow_zero_length_clientid</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>MQTT 3.1.1 and MQTT 5 allow clients to connect with a zero\n\t\t\t\t\t\tlength client id and have the broker generate a client\n\t\t\t\t\t\tid for them. Use this option to allow/disallow this\n\t\t\t\t\t\tbehaviour. Defaults to true.</para>\n\t\t\t\t\t<para>See also the <option>auto_id_prefix</option> option.</para>\n\n\t\t\t\t\t<para>If <option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>true</replaceable>, this option applies to\n\t\t\t\t\t\tthe current listener being configured only. If\n\t\t\t\t\t\t<option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>false</replaceable>, this option applies\n\t\t\t\t\t\tto all listeners.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>auth_plugin_deny_special_chars</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If <replaceable>true</replaceable> then before an ACL\n\t\t\t\t\t\tcheck is made, the username/client id of the client\n\t\t\t\t\t\tneeding the check is searched for the presence of\n\t\t\t\t\t\teither a '+' or '#' character. If either of these\n\t\t\t\t\t\tcharacters is found in either the username or client\n\t\t\t\t\t\tid, then the ACL check is denied before it is sent to\n\t\t\t\t\t\tthe plugin.</para>\n\t\t\t\t\t<para>This check prevents the case where a malicious user\n\t\t\t\t\t\tcould circumvent an ACL check by using one of these\n\t\t\t\t\t\tcharacters as their username or client id. This is the\n\t\t\t\t\t\tsame issue as was reported with mosquitto itself as\n\t\t\t\t\t\tCVE-2017-7650.</para>\n\t\t\t\t\t<para>If you are entirely sure that the plugin you are\n\t\t\t\t\t\tusing is not vulnerable to this attack (i.e. if you\n\t\t\t\t\t\tnever use usernames or client ids in topics) then you\n\t\t\t\t\t\tcan disable this extra check and hence have all ACL\n\t\t\t\t\t\tchecks delivered to your plugin by setting this option\n\t\t\t\t\t\tto <replaceable>false</replaceable>.</para>\n\t\t\t\t\t<para>Defaults to <replaceable>true</replaceable>.</para>\n\n\t\t\t\t\t<para>Applies to the current authentication plugin being configured.</para>\n\t\t\t\t\t<para>Not currently reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>auto_id_prefix</option> <replaceable>prefix</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThis option is deprecated, please use <option>listener_auto_id_prefix</option> instead.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>If <option>allow_zero_length_clientid</option> is\n\t\t\t\t\t\t<replaceable>true</replaceable>, this option allows you\n\t\t\t\t\t\tto set a string that will be prefixed to the\n\t\t\t\t\t\tautomatically generated client ids to aid visibility in\n\t\t\t\t\t\tlogs. Defaults to <option>auto-</option>.</para>\n\n\t\t\t\t\t<para>If <option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>true</replaceable>, this option applies to\n\t\t\t\t\t\tthe current listener being configured only. If\n\t\t\t\t\t\t<option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>false</replaceable>, this option applies\n\t\t\t\t\t\tto all listeners.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>autosave_interval</option> <replaceable>seconds</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe number of seconds that mosquitto will wait\n\t\t\t\t\t\tbetween each time it saves the in-memory database to\n\t\t\t\t\t\tdisk. If set to 0, the in-memory database will only be\n\t\t\t\t\t\tsaved when mosquitto exits or when receiving the\n\t\t\t\t\t\tSIGUSR1 signal. Note that this setting only has an\n\t\t\t\t\t\teffect if the built-in persistence is enabled. Defaults\n\t\t\t\t\t\tto 1800 seconds (30 minutes).\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>autosave_on_changes</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If <replaceable>true</replaceable>, mosquitto will\n\t\t\t\t\t\tcount the number of subscription changes, retained\n\t\t\t\t\t\tmessages received and queued messages and if the total\n\t\t\t\t\t\texceeds <option>autosave_interval</option> then the\n\t\t\t\t\t\tin-memory database will be saved to disk. If\n\t\t\t\t\t\t<replaceable>false</replaceable>, mosquitto will save\n\t\t\t\t\t\tthe in-memory database to disk by treating\n\t\t\t\t\t\t<option>autosave_interval</option> as a time in\n\t\t\t\t\t\tseconds.</para>\n\n\t\t\t\t\t<para>Applies to built-in persistence only.</para>\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>check_retain_source</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>This option affects the scenario when a client\n\t\t\t\t\t\tsubscribes to a topic that has retained messages. It is\n\t\t\t\t\t\tpossible that the client that published the retained\n\t\t\t\t\t\tmessage to the topic had access at the time they\n\t\t\t\t\t\tpublished, but that access has been subsequently\n\t\t\t\t\t\tremoved. If <option>check_retain_source</option> is set\n\t\t\t\t\t\tto true, the default, the source of a retained message\n\t\t\t\t\t\twill be checked for access rights before it is\n\t\t\t\t\t\trepublished. When set to false, no check will be made\n\t\t\t\t\t\tand the retained message will always be\n\t\t\t\t\t\tpublished.</para>\n\t\t\t\t\t<para>This option applies globally, regardless of the\n\t\t\t\t\t\t<option>per_listener_settings</option> option.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>clientid_prefixes</option> <replaceable>prefix</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>This option is deprecated and will be removed in a\n\t\t\t\t\t\tfuture version.</para>\n\t\t\t\t\t<para>If defined, only clients that have a clientid with a\n\t\t\t\t\t\tprefix that matches clientid_prefixes will be allowed\n\t\t\t\t\t\tto connect to the broker. For example, setting\n\t\t\t\t\t\t\"secure-\" here would mean a client \"secure-client\"\n\t\t\t\t\t\tcould connect but another with clientid \"mqtt\"\n\t\t\t\t\t\tcouldn't. By default, all client ids are valid.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal. Note that currently\n\t\t\t\t\t\tconnected clients will be unaffected by any\n\t\t\t\t\t\tchanges.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>connection_messages</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If set to <replaceable>true</replaceable>, the log\n\t\t\t\t\t\twill include entries when clients connect and\n\t\t\t\t\t\tdisconnect. If set to <replaceable>false</replaceable>,\n\t\t\t\t\t\tthese entries will not appear.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>enable_control_api</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If set to <replaceable>true</replaceable>, the\n\t\t\t\t\t<option>$CONTROL/broker/v1</option> will be enabled. This currently\n\t\t\t\t\tallows interrogating which plugins are enabled, but in the future\n\t\t\t\t\twill allow access to configuring general broker options. If you enable\n\t\t\t\t\tthis you should ensure you have authentication and appropriate access\n\t\t\t\t\tcontrol configured, i.e. only allowing access to\n\t\t\t\t\t<option>$CONTROL/broker/#</option> for a limited set of users.\n\t\t\t\t</para>\n\t\t\t\t\t<para>This option applies globally.</para>\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>global_max_clients</option> <replaceable>count</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe maximum number of client sessions to allow across\n\t\t\t\t\t\tthe whole broker. In this context a client session means\n\t\t\t\t\t\teither a client currently connected via the network, or\n\t\t\t\t\t\ta client that has clean_session = False (MQTT v3.x) and\n\t\t\t\t\t\tis disconnected, or has disconnected and still hasn't\n\t\t\t\t\t\texceeded its session expiry interval (MQTT v5).\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tSee also the <option>max_connections</option> setting, which applies to\n\t\t\t\t\t\tlisteners. If you set <option>global_max_clients</option>\n\t\t\t\t\t\tto 1000 and <option>max_connections</option> on a\n\t\t\t\t\t\tlistener to 10, then that means only 10 simultaneous\n\t\t\t\t\t\tconnections will be allowed at once, with an overall\n\t\t\t\t\t\tmaximum of 1000 client sessions.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\t\t\t\t\t<para>Defaults to -1 (unlimited)</para>\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>global_max_connections</option> <replaceable>count</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe maximum number of currently connected clients to\n\t\t\t\t\t\tallow across the whole broker.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tSee also the <option>global_max_clients</option> and\n\t\t\t\t\t\t<option>max_connections</option> settings.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\t\t\t\t\t<para>Defaults to -1 (unlimited)</para>\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>global_plugin</option> <replaceable>file path</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tLoad an external module to extend broker features. This loads plugins\n\t\t\t\t\t\tfor use across all listeners, regardless of the value of the\n\t\t\t\t\t\t<option>per_listener_settings</option> option. This option functions\n\t\t\t\t\t\tthe same as the <option>plugin</option> option in all other ways.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf you set <option>per_listener_settings true</option>, then define both a\n\t\t\t\t\t\t<option>global_plugin</option> and a <option>plugin</option> (which will be\n\t\t\t\t\t\tattached to a specific listener), then the global plugin will always be\n\t\t\t\t\t\tprocessed first.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf you set <option>per_listener_settings false</option>, then\n\t\t\t\t\t\t<option>global_plugin</option> behaves identically to\n\t\t\t\t\t\t<option>plugin</option>.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>include_dir</option> <replaceable>dir</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>External configuration files may be included by using\n\t\t\t\t\t\tthe include_dir option. This defines a directory that\n\t\t\t\t\t\twill be searched for config files. All files that end\n\t\t\t\t\t\tin '.conf' will be loaded as a configuration file. It\n\t\t\t\t\t\tis best to have this as the last option in the main\n\t\t\t\t\t\tfile. This option will only be processed from the main\n\t\t\t\t\t\tconfiguration file. The directory specified must not\n\t\t\t\t\t\tcontain the main configuration file.</para>\n\t\t\t\t\t<para>The configuration files in\n\t\t\t\t\t\t<option>include_dir</option> are loaded in case\n\t\t\t\t\t\tsensitive alphabetical order, with the upper case of\n\t\t\t\t\t\teach letter ordered before the lower case of the same\n\t\t\t\t\t\tletter.</para>\n\t\t\t\t\t\t<example title=\"Load Order for include_dir\" label=\"Load Order for include_dir\">\n\t\t\t\t\t\t\t<para>Given the files\n\t\t\t\t\t\t\t\t<replaceable>b.conf</replaceable>,\n\t\t\t\t\t\t\t\t<replaceable>A.conf</replaceable>,\n\t\t\t\t\t\t\t\t<replaceable>01.conf</replaceable>,\n\t\t\t\t\t\t\t\t<replaceable>a.conf</replaceable>,\n\t\t\t\t\t\t\t\t<replaceable>B.conf</replaceable>, and\n\t\t\t\t\t\t\t\t<replaceable>00.conf</replaceable> inside\n\t\t\t\t\t\t\t\t<option>include_dir</option>, the config files\n\t\t\t\t\t\t\t\twould be loaded in this order:</para>\n\t\t\t\t\t\t<programlisting language=\"config\">\n00.conf\n01.conf\nA.conf\na.conf\nB.conf\nb.conf\n</programlisting></example>\n\t\t\t\t\t<para>If this option is used multiple times, then each\n\t\t\t\t\t\t<option>include_dir</option> option is processed\n\t\t\t\t\t\tcompletely in the order that they are written in the\n\t\t\t\t\t\tmain configuration file.</para>\n\t\t\t\t\t\t<example title=\"Load Order for Multiple include_dir\" label=\"Load Order for Multiple include_dir\">\n\t\t\t\t\t\t\t<para>Assuming a directory\n\t\t\t\t\t\t\t\t<replaceable>one.d</replaceable> containing\n\t\t\t\t\t\t\t\tfiles <replaceable>B.conf</replaceable> and\n\t\t\t\t\t\t\t\t<replaceable>C.conf</replaceable>, and a second\n\t\t\t\t\t\t\t\tdirectory <replaceable>two.d</replaceable>\n\t\t\t\t\t\t\t\tcontaining files\n\t\t\t\t\t\t\t\t<replaceable>A.conf</replaceable> and\n\t\t\t\t\t\t\t\t<replaceable>D.conf</replaceable>, and a\n\t\t\t\t\t\t\t\tconfig:</para>\n\t\t\t\t\t\t<programlisting language=\"config\">\ninclude_dir one.d\ninclude_dir two.d\n</programlisting><para>Then the config files would be loaded in this order:</para>\n\t\t\t\t\t\t<programlisting language=\"config\">\n# files from one.d\nB.conf\nC.conf\n# files from two.d\nA.conf\nD.conf\n</programlisting></example>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>log_dest</option> <replaceable>destinations</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Send log messages to a particular destination.\n\t\t\t\t\t\tPossible destinations are: <option>stdout</option>\n\t\t\t\t\t\t<option>stderr</option> <option>syslog</option>\n\t\t\t\t\t\t<option>topic</option> <option>file</option>\n\t\t\t\t\t\t<option>dlt</option> <option>android</option>.</para>\n\t\t\t\t\t<para><option>stdout</option> and\n\t\t\t\t\t\t<option>stderr</option> log to the console on the\n\t\t\t\t\t\tnamed output.</para>\n\t\t\t\t\t<para><option>syslog</option> uses the userspace syslog\n\t\t\t\t\t\tfacility which usually ends up in /var/log/messages or\n\t\t\t\t\t\tsimilar.</para>\n\t\t\t\t\t<para><option>topic</option> logs to the broker topic\n\t\t\t\t\t\t'$SYS/broker/log/&lt;severity&gt;', where severity is\n\t\t\t\t\t\tone of E, W, N, I, M which are error,\n\t\t\t\t\t\twarning, notice, information and message. Message type\n\t\t\t\t\t\tseverity is used by the subscribe and unsubscribe\n\t\t\t\t\t\tlog_type options and publishes log messages at\n\t\t\t\t\t\t$SYS/broker/log/M/subscribe and\n\t\t\t\t\t\t$SYS/broker/log/M/unsubscribe. Debug messages are never\n\t\t\t\t\t\tlogged on topics.</para>\n\t\t\t\t\t<para>The <option>file</option> destination requires an\n\t\t\t\t\t\tadditional parameter which is the file to be logged to,\n\t\t\t\t\t\te.g. \"log_dest file /var/log/mosquitto.log\". The file\n\t\t\t\t\t\twill be closed and reopened when the broker receives a\n\t\t\t\t\t\tHUP signal. Only a single file destination may be\n\t\t\t\t\t\tconfigured.</para>\n\t\t\t\t\t<para>The <option>dlt</option> destination is for the\n\t\t\t\t\t\tautomotive `Diagnostic Log and Trace` tool. This\n\t\t\t\t\t\trequires that Mosquitto has been compiled with DLT\n\t\t\t\t\t\tsupport.</para>\n\t\t\t\t\t<para>The <option>android</option> destination is for\n\t\t\t\t\t\tAndroid only, and will output to the logd daemon.</para>\n\t\t\t\t\t<para>Use \"log_dest none\" if you wish to disable logging.\n\t\t\t\t\t\tDefaults to stderr. This option may be specified\n\t\t\t\t\t\tmultiple times.</para>\n\t\t\t\t\t<para>Note that if the broker is running as a Windows\n\t\t\t\t\t\tservice it will default to \"log_dest none\" and neither\n\t\t\t\t\t\tstdout nor stderr logging is available.</para>\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>log_facility</option> <replaceable>local facility</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If using syslog logging (not on Windows), messages\n\t\t\t\t\t\twill be logged to the \"daemon\" facility by default. Use\n\t\t\t\t\t\tthe <option>log_facility</option> option to choose\n\t\t\t\t\t\twhich of local0 to local7 to log to instead. The option\n\t\t\t\t\t\tvalue should be an integer value, e.g. \"log_facility 5\"\n\t\t\t\t\t\tto use local5.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>log_timestamp</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Boolean value, if set to\n\t\t\t\t\t\t<replaceable>true</replaceable> a timestamp value will\n\t\t\t\t\t\tbe added to each log entry. The default is\n\t\t\t\t\t\t<replaceable>true</replaceable>.</para>\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>log_timestamp_format</option> <replaceable>format</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the format of the log timestamp. If left unset,\n\t\t\t\t\t\tthis is the number of seconds since the Unix epoch.\n\t\t\t\t\t\tThis option is a free text string which will be passed\n\t\t\t\t\t\tto the strftime function as the format specifier. To\n\t\t\t\t\t\tget an ISO 8601 datetime, for example:</para>\n\t\t\t\t\t<programlisting language=\"config\">\nlog_timestamp_format %Y-%m-%dT%H:%M:%S\n\t\t\t\t\t</programlisting>\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>log_type</option> <replaceable>types</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Choose types of messages to log. Possible types are:\n\t\t\t\t\t\t<replaceable>debug</replaceable>,\n\t\t\t\t\t\t<replaceable>error</replaceable>,\n\t\t\t\t\t\t<replaceable>warning</replaceable>,\n\t\t\t\t\t\t<replaceable>notice</replaceable>,\n\t\t\t\t\t\t<replaceable>information</replaceable>,\n\t\t\t\t\t\t<replaceable>subscribe</replaceable>,\n\t\t\t\t\t\t<replaceable>unsubscribe</replaceable>,\n\t\t\t\t\t\t<replaceable>websockets</replaceable>,\n\t\t\t\t\t\t<replaceable>none</replaceable>,\n\t\t\t\t\t\t<replaceable>all</replaceable>.</para>\n\t\t\t\t\t<para>Defaults to <replaceable>error</replaceable>,\n\t\t\t\t\t\t<replaceable>warning</replaceable>, <replaceable>notice\n\t\t\t\t\t\t</replaceable>and\n\t\t\t\t\t\t<replaceable>information</replaceable>. This option\n\t\t\t\t\t\tmay be specified multiple times. Note that the\n\t\t\t\t\t\t<replaceable>debug </replaceable>type (used for\n\t\t\t\t\t\tdecoding incoming/outgoing network packets) is never\n\t\t\t\t\t\tlogged in topics.</para>\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>max_inflight_bytes</option> <replaceable>count</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Outgoing QoS 1 and 2 messages will be allowed in flight until this byte\n\t\t\t\t\t\tlimit is reached. This allows control of outgoing message rate based on\n\t\t\t\t\t\tmessage size rather than message count. If the limit is set to 100,\n\t\t\t\t\t\tmessages of over 100 bytes are still allowed, but only a single message\n\t\t\t\t\t\tcan be in flight at once. Defaults to 0. (No limit).</para>\n\t\t\t\t\t<para>See also the <option>max_inflight_messages</option> option.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>max_inflight_messages</option> <replaceable>count</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The maximum number of outgoing QoS 1 or 2 messages that can be\n\t\t\t\t\t\tin the process of being transmitted simultaneously.\n\t\t\t\t\t\tThis includes messages currently going through\n\t\t\t\t\t\thandshakes and messages that are being retried.\n\t\t\t\t\t\tDefaults to 20. Set to 0 for no maximum. If set to 1,\n\t\t\t\t\t\tthis will guarantee in-order delivery of\n\t\t\t\t\t\tmessages.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>max_keepalive</option> <replaceable>value</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>For MQTT v5 clients, it is possible to have the\n\t\t\t\t\t\tserver send a \"server keepalive\" value that will\n\t\t\t\t\t\toverride the keepalive value set by the client. This\n\t\t\t\t\t\tis intended to be used as a mechanism to say that the\n\t\t\t\t\t\tserver will disconnect the client earlier than it\n\t\t\t\t\t\tanticipated, and that the client should use the new\n\t\t\t\t\t\tkeepalive value. The max_keepalive option allows you to\n\t\t\t\t\t\tspecify that clients may only connect with keepalive\n\t\t\t\t\t\tless than or equal to this value, otherwise they will\n\t\t\t\t\t\tbe sent a server keepalive telling them to use\n\t\t\t\t\t\tmax_keepalive. This only applies to MQTT v5 clients.\n\t\t\t\t\t\tThe maximum value is 65535. Set to 0 to allow infinite\n\t\t\t\t\t\tkeepalive. Defaults to 0.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tSet to 0 to allow clients to set keepalive = 0, which\n\t\t\t\t\t\tmeans no keepalive checks are made and the client will\n\t\t\t\t\t\tnever be disconnected by the broker if no messages are\n\t\t\t\t\t\treceived. You should be very sure this is the behaviour\n\t\t\t\t\t\tthat you want.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tFor MQTT v3.1.1 and v3.1 clients, there is no mechanism\n\t\t\t\t\t\tto tell the client what keepalive value they should use.\n\t\t\t\t\t\tIf an MQTT v3.1.1 or v3.1 client specifies a keepalive\n\t\t\t\t\t\ttime greater than max_keepalive they will be sent a\n\t\t\t\t\t\tCONNACK message with the \"identifier rejected\" reason\n\t\t\t\t\t\tcode, and disconnected.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>max_packet_size</option> <replaceable>value</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>For MQTT v5 clients, it is possible to have the\n\t\t\t\t\t\tserver send a \"maximum packet size\" value that will\n\t\t\t\t\t\tinstruct the client it will not accept MQTT packets\n\t\t\t\t\t\twith size greater than <option>value</option> bytes.\n\t\t\t\t\t\tThis applies to the full MQTT packet, not just the\n\t\t\t\t\t\tpayload. Setting this option to a positive value will\n\t\t\t\t\t\tset the maximum packet size to that number of bytes. If\n\t\t\t\t\t\ta client sends a packet which is larger than this\n\t\t\t\t\t\tvalue, it will be disconnected. This applies to all\n\t\t\t\t\t\tclients regardless of the protocol version they are\n\t\t\t\t\t\tusing, but v3.1.1 and earlier clients will of course\n\t\t\t\t\t\tnot have received the maximum packet size information.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tDefaults to 2000000 bytes since 2.1. Earlier versions\n\t\t\t\t\t\tdefaulted to no limit.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>This option applies to all clients, not just those\n\t\t\t\t\t\tusing MQTT v5, but it is not possible to notify clients\n\t\t\t\t\t\tusing MQTT v3.1.1 or MQTT v3.1 of the limit.</para>\n\n\t\t\t\t\t<para>Setting below 20 bytes is forbidden because it is\n\t\t\t\t\t\tlikely to interfere with normal client operation even\n\t\t\t\t\t\twith small payloads.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>max_queued_bytes</option> <replaceable>count</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The number of outgoing QoS 1 and 2 messages above those currently in-flight will be\n\t\t\t\t\t\tqueued (per client) by the broker. Once this limit has been reached, subsequent\n\t\t\t\t\t\tmessages will be silently dropped. This is an important option if you are sending\n\t\t\t\t\t\tmessages at a high rate and/or have clients who are slow to respond or may be offline\n\t\t\t\t\t\tfor extended periods of time. Defaults to 0. (No maximum).</para>\n\t\t\t\t\t<para>See also the\n\t\t\t\t\t\t<option>max_queued_messages</option> option.\n\t\t\t\t\t\tIf both max_queued_messages and max_queued_bytes are specified,\n\t\t\t\t\t\tpackets will be queued until the first limit is reached.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>max_queued_messages</option> <replaceable>count</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The maximum number of QoS 1 or 2 messages to hold in the\n\t\t\t\t\t\tqueue (per client) above those messages that are currently\n\t\t\t\t\t\tin flight. Defaults to 1000. Set to 0 for no maximum (not\n\t\t\t\t\t\trecommended). See also the\n\t\t\t\t\t\t<option>queue_qos0_messages</option> and\n\t\t\t\t\t\t<option>max_queued_bytes</option> options.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>memory_limit</option> <replaceable>limit</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThis option sets the maximum number of heap memory bytes that the broker\n\t\t\t\t\t\twill allocate, and hence sets a hard limit on memory use by the broker.\n\t\t\t\t\t\tMemory requests that exceed this value will be denied. The effect will\n\t\t\t\t\t\tvary depending on what has been denied. If an incoming message is being\n\t\t\t\t\t\tprocessed, then the message will be dropped and the publishing client\n\t\t\t\t\t\twill be disconnected. If an outgoing message is being sent, then the\n\t\t\t\t\t\tindividual message will be dropped and the receiving client will be\n\t\t\t\t\t\tdisconnected. Defaults to no limit.</para>\n\t\t\t\t\t<para>This option is only available if memory tracking support is compiled\n\t\t\t\t\t\tin.</para>\n\t\t\t\t\t<para>Reloaded on reload signal. Setting to a lower value and reloading will\n\t\t\t\t\t\tnot result in memory being freed.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>message_size_limit</option> <replaceable>limit</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>This option sets the maximum publish payload size\n\t\t\t\t\t\tthat the broker will allow. Received messages that\n\t\t\t\t\t\texceed this size will not be accepted by the broker. This means that the\n\t\t\t\t\t\tmessage will not be forwarded on to subscribing clients, but the QoS flow\n\t\t\t\t\t\twill be completed for QoS 1 or QoS 2 messages. MQTT v5 clients using QoS 1\n\t\t\t\t\t\tor QoS 2 will receive a PUBACK or PUBREC with the \"implementation specific\n\t\t\t\t\t\terror\" reason code.</para>\n\n\t\t\t\t\t<para>The default value is 0, which means that all valid MQTT\n\t\t\t\t\t\tmessages are accepted. MQTT imposes a maximum payload\n\t\t\t\t\t\tsize of 268435455 bytes.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>password_file</option> <replaceable>file path</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\t<emphasis>Note:</emphasis> It is suggested that you use the\n\t\t\t\t\t\t<option>mosquitto_password_file</option> plugin instead of this\n\t\t\t\t\t\toption. Using plugins for authentication and authorisation\n\t\t\t\t\t\tallows greater control of what listeners they are applied to,\n\t\t\t\t\t\twithout the need for the deprecated\n\t\t\t\t\t\t<option>per_listener_settings</option> option.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>Set the path to a password file. If defined, the\n\t\t\t\t\t\tcontents of the file are used to control client access\n\t\t\t\t\t\tto the broker. The file can be created using the\n\t\t\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_passwd-1.html\">mosquitto_passwd</link></refentrytitle><manvolnum>1</manvolnum></citerefentry>\n\t\t\t\t\t\tutility. If mosquitto is compiled without TLS support\n\t\t\t\t\t\t(it is recommended that TLS support is included), then\n\t\t\t\t\t\tthe password file should be a text file with each line\n\t\t\t\t\t\tin the format \"username:password\", where the colon and\n\t\t\t\t\t\tpassword are optional but recommended. If\n\t\t\t\t\t\t<option>allow_anonymous</option> is set to\n\t\t\t\t\t\t<replaceable>false</replaceable>, only users defined in\n\t\t\t\t\t\tthis file will be able to connect. Setting\n\t\t\t\t\t\t<option>allow_anonymous</option> to\n\t\t\t\t\t\t<replaceable>true</replaceable> when\n\t\t\t\t\t\t<replaceable>password_file</replaceable>is defined is\n\t\t\t\t\t\tvalid and could be used with acl_file to have e.g. read\n\t\t\t\t\t\tonly guest/anonymous accounts and defined users that\n\t\t\t\t\t\tcan publish.</para>\n\n\t\t\t\t\t<para>If <option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>true</replaceable>, this option applies to\n\t\t\t\t\t\tthe current listener being configured only. If\n\t\t\t\t\t\t<option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>false</replaceable>, this option applies\n\t\t\t\t\t\tto all listeners.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal. The currently loaded\n\t\t\t\t\t\tusername and password data will be freed and reloaded.\n\t\t\t\t\t\tClients that are already connected will not be\n\t\t\t\t\t\taffected.</para>\n\t\t\t\t\t<para>See also\n\t\t\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_passwd-1.html\">mosquitto_passwd</link></refentrytitle><manvolnum>1</manvolnum></citerefentry> and\n\t\t\t\t\t\t<ulink url=\"https://mosquitto.org/documentation/dynamic-security/\"/>\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>per_listener_settings</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If <replaceable>true</replaceable>, then\n\t\t\t\t\t\tauthentication and access control settings will be\n\t\t\t\t\t\tcontrolled on a per-listener basis. The following\n\t\t\t\t\t\toptions are affected:</para>\n\t\t\t\t\t<para><option>password_file</option>,\n\t\t\t\t\t\t<option>acl_file</option>, <option>psk_file</option>,\n\t\t\t\t\t\t<option>allow_anonymous</option>,\n\t\t\t\t\t\t<option>allow_zero_length_clientid</option>,\n\t\t\t\t\t\t<option>auto_id_prefix</option>.</para>\n\t\t\t\t\t\t<option>plugin</option>,\n\t\t\t\t\t\t<option>plugin_opt_*</option>,\n\t\t\t\t\t<para>Note that if set to true, then a durable client (i.e.\n\t\t\t\t\t\twith clean session set to false) that has disconnected\n\t\t\t\t\t\twill use the ACL settings defined for the listener that\n\t\t\t\t\t\tit was most recently connected to.</para>\n\t\t\t\t\t<para>The default behaviour is for this to be set to\n\t\t\t\t\t\t<replaceable>false</replaceable>, which maintains the\n\t\t\t\t\t\tsettings behaviour from previous versions of\n\t\t\t\t\t\tmosquitto.</para>\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>persistence</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf <replaceable>true</replaceable>, then built-in persistence\n\t\t\t\t\t\tis enabled. It is recommended that a plugin based persistence\n\t\t\t\t\t\tis used instead.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf enabled, connection, subscription and message data\n\t\t\t\t\t\twill be written to disk in mosquitto.db at the location\n\t\t\t\t\t\tdictated by persistence_location. When mosquitto is\n\t\t\t\t\t\trestarted, it will reload the information stored in\n\t\t\t\t\t\tmosquitto.db. The data will be written to disk when\n\t\t\t\t\t\tmosquitto closes and also at periodic intervals as\n\t\t\t\t\t\tdefined by autosave_interval. Writing of the persistence\n\t\t\t\t\t\tdatabase may also be forced by sending mosquitto the\n\t\t\t\t\t\tSIGUSR1 signal. If <replaceable>false</replaceable>,\n\t\t\t\t\t\tthe data will be stored in memory only. Defaults to\n\t\t\t\t\t\t<replaceable>false</replaceable>.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>The persistence file may change its format in a new\n\t\t\t\t\t\tversion. The broker can currently read all old formats,\n\t\t\t\t\t\tbut will only save in the latest format. It should always\n\t\t\t\t\t\tbe safe to upgrade, but cautious users may wish to take a\n\t\t\t\t\t\tcopy of the persistence file before installing a new\n\t\t\t\t\t\tversion so that they can roll back to an earlier version\n\t\t\t\t\t\tif necessary.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>persistence_file</option> <replaceable>file name</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe filename to use for the built-in persistent database.\n\t\t\t\t\t\tDefaults to mosquitto.db.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>persistence_location</option> <replaceable>path</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe path where plugins should store any persistence\n\t\t\t\t\t\tdata, and the path where the built-in persistence will\n\t\t\t\t\t\tstore its data. If not given, then the current directory\n\t\t\t\t\t\tis used.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>persistent_client_expiration</option> <replaceable>duration</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThis option allows the session of persistent clients (those with clean\n\t\t\t\t\t\tsession set to false) <emphasis>that are not currently connected</emphasis> to be removed if they\n\t\t\t\t\t\tdo not reconnect within a certain time frame. This is a non-standard option\n\t\t\t\t\t\tin MQTT v3.1. MQTT v3.1.1 and v5.0 allow brokers to remove client sessions.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tBadly designed clients may set clean session to false whilst using a randomly\n\t\t\t\t\t\tgenerated client id. This leads to persistent clients that connect once and\n\t\t\t\t\t\tnever reconnect. This option allows these clients to be removed. This option\n\t\t\t\t\t\tallows persistent clients (those with clean session set to false) to be\n\t\t\t\t\t\tremoved if they do not reconnect within a certain time frame.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe expiration period should be an integer followed\n\t\t\t\t\t\tby one of s h d w m y for second, hour, day, week, month and year\n\t\t\t\t\t\trespectively. For example:\n\t\t\t\t\t</para>\n\t\t\t\t\t<itemizedlist mark=\"circle\">\n\t\t\t\t\t\t<listitem><para>persistent_client_expiration 2m</para></listitem>\n\t\t\t\t\t\t<listitem><para>persistent_client_expiration 14d</para></listitem>\n\t\t\t\t\t\t<listitem><para>persistent_client_expiration 1y</para></listitem>\n\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tAlthough it is possible to specify the expiration time in seconds, this is not a\n\t\t\t\t\t\tsuggestion that you should use a short expiration interval. If you have a system\n\t\t\t\t\t\twhere you think the clients should be automatically expired in a short time you\n\t\t\t\t\t\tshould see about fixing the clients instead where possible.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tAs this is a non-standard option, the default if not\n\t\t\t\t\t\tset is to never expire persistent clients.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>pid_file</option> <replaceable>file path</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Write a pid file to the file specified. If not given\n\t\t\t\t\t\t(the default), no pid file will be written. If the pid\n\t\t\t\t\t\tfile cannot be written, mosquitto will exit.</para>\n\t\t\t\t\t<para>If mosquitto is being automatically started by an\n\t\t\t\t\t\tinit script it will usually be required to write a pid\n\t\t\t\t\t\tfile. This should then be configured as e.g.\n\t\t\t\t\t\t/var/run/mosquitto/mosquitto.pid</para>\n\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>plugin_opt_*</option> <replaceable>value</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tOptions to be passed to the most recent\n\t\t\t\t\t\t<option>plugin</option> defined in the\n\t\t\t\t\t\tconfiguration file. See the specific\n\t\t\t\t\t\tplugin instructions for details of what\n\t\t\t\t\t\toptions are available.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>Applies to the current plugin/global_plugin being configured.</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThis is also available as the <option>auth_opt_*</option>\n\t\t\t\t\t\toption, but this use is deprecated and will be removed\n\t\t\t\t\t\tin a future version.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>plugin</option> <replaceable>file path</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Specify an external module to use for authentication,\n\t\t\t\t\t\taccess control, and other features. This allows custom\n\t\t\t\t\t\tusername/password and access control functions to be\n\t\t\t\t\t\tcreated and other behaviour to be modified.</para>\n\t\t\t\t\t<para>Can be specified multiple times to load multiple\n\t\t\t\t\t\tplugins. The plugins will be processed in the order\n\t\t\t\t\t\tthat they are specified.</para>\n\t\t\t\t\t<para>If <option>password_file</option>, or\n\t\t\t\t\t\t<option>acl_file</option> are used in the config file\n\t\t\t\t\t\talongsize <option>plugin</option>, the plugin\n\t\t\t\t\t\tchecks will run before the built in checks.</para>\n\t\t\t\t\t<para>Not currently reloaded on reload signal.</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf <option>per_listener_settings</option> is set to\n\t\t\t\t\t\t<replaceable>true</replaceable>, this plugin will be\n\t\t\t\t\t\tloaded for the current listener only.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>See also\n\t\t\t\t\t\t<ulink url=\"https://mosquitto.org/documentation/dynamic-security/\"/>\n\t\t\t\t\t\tand the <option>global_plugin</option> option.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThis is also available as the <option>auth_plugin</option>\n\t\t\t\t\t\toption, but this use is deprecated and will be removed\n\t\t\t\t\t\tin a future version.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>psk_file</option> <replaceable>file path</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the path to a pre-shared-key file. This option\n\t\t\t\t\t\trequires a listener to be have PSK support enabled. If\n\t\t\t\t\t\tdefined, the contents of the file are used to control\n\t\t\t\t\t\tclient access to the broker. Each line should be in the\n\t\t\t\t\t\tformat \"identity:key\", where the key is a hexadecimal\n\t\t\t\t\t\tstring with no leading \"0x\". A client connecting to a\n\t\t\t\t\t\tlistener that has PSK support enabled must provide a\n\t\t\t\t\t\tmatching identity and PSK to allow the encrypted\n\t\t\t\t\t\tconnection to proceed.</para>\n\n\t\t\t\t\t<para>If <option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>true</replaceable>, this option applies to\n\t\t\t\t\t\tthe current listener being configured only. If\n\t\t\t\t\t\t<option>per_listener_settings</option> is\n\t\t\t\t\t\t<replaceable>false</replaceable>, this option applies\n\t\t\t\t\t\tto all listeners.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal. The currently loaded\n\t\t\t\t\t\tidentity and key data will be freed and reloaded.\n\t\t\t\t\t\tClients that are already connected will not be\n\t\t\t\t\t\taffected.</para>\n\t\t\t\t</listitem> </varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>queue_qos0_messages</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set to <replaceable>true</replaceable> to queue\n\t\t\t\t\t\tmessages with QoS 0 when a persistent client is\n\t\t\t\t\t\tdisconnected. When bridges topics are configured with QoS level 1 or 2 incoming\n\t\t\t\t\t\tQoS 0 messages for these topics are also queued.\n\t\t\t\t\t\tThese messages are included in the limit\n\t\t\t\t\t\timposed by max_queued_messages. Defaults to\n\t\t\t\t\t\t<replaceable>false</replaceable>.</para>\n\t\t\t\t\t<para>Note that the MQTT v3.1.1 spec states that only QoS 1\n\t\t\t\t\t\tand 2 messages should be saved in this situation so\n\t\t\t\t\t\tthis is a non-standard option.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>retain_available</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If set to false, then retained messages are not\n\t\t\t\t\t\tsupported. Clients that send a message with the retain\n\t\t\t\t\t\tbit will be disconnected if this option is set to\n\t\t\t\t\t\tfalse. Defaults to true.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>retain_expiry_interval</option> <replaceable>minutes</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThe default behaviour of mosquitto is to remove retained\n\t\t\t\t\t\tmessages that have reached their message-expiry-interval\n\t\t\t\t\t\tproperty the next time that that message is accessed -\n\t\t\t\t\t\teither by being replaced by a new message, or on the\n\t\t\t\t\t\tnext subscription that matches the message. If you have\n\t\t\t\t\t\ta pattern of publishing many retained messages with a\n\t\t\t\t\t\tmessage-expiry-interval, but that are not subscribed to,\n\t\t\t\t\t\tthen the expired retained messages will remain in\n\t\t\t\t\t\tmemory. This option configures the broker to\n\t\t\t\t\t\tperiodically check the retained tree for expired\n\t\t\t\t\t\tmessages.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>\n\t\t\t\t\t\tDefaults to off. Setting to a value greater than zero\n\t\t\t\t\t\tmeans the broker will make a check at an interval of\n\t\t\t\t\t\tthat number of minutes.\n\t\t\t\t\t</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>set_tcp_nodelay</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If set to true, the TCP_NODELAY option will be set on\n\t\t\t\t\t\t\tclient sockets to disable Nagle's algorithm. This\n\t\t\t\t\t\t\thas the effect of reducing latency of some messages\n\t\t\t\t\t\t\tat potentially increasing the number of TCP packets\n\t\t\t\t\t\t\tbeing sent. Defaults to false.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>sys_interval</option> <replaceable>seconds</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The integer number of seconds between updates of the\n\t\t\t\t\t\t$SYS subscription hierarchy, which provides status\n\t\t\t\t\t\tinformation about the broker. If unset, defaults to 10\n\t\t\t\t\t\tseconds.</para>\n\t\t\t\t\t<para>Set to 0 to disable publishing the $SYS hierarchy\n\t\t\t\t\t\tcompletely.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>upgrade_outgoing_qos</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The MQTT specification requires that the QoS of a\n\t\t\t\t\t\tmessage delivered to a subscriber is never upgraded to\n\t\t\t\t\t\tmatch the QoS of the subscription. Enabling this option\n\t\t\t\t\t\tchanges this behaviour. If\n\t\t\t\t\t\t<option>upgrade_outgoing_qos</option> is set\n\t\t\t\t\t\t<replaceable>true</replaceable>, messages sent to a\n\t\t\t\t\t\tsubscriber will always match the QoS of its\n\t\t\t\t\t\tsubscription. This is a non-standard option not\n\t\t\t\t\t\tprovided for by the spec. Defaults to\n\t\t\t\t\t\t<replaceable>false</replaceable>.</para>\n\n\t\t\t\t\t<para>This option applies globally.</para>\n\n\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>user</option> <replaceable>username</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>When run as root, change to this user and its primary\n\t\t\t\t\t\tgroup on startup. If set to \"mosquitto\" or left unset,\n\t\t\t\t\t\tand if the \"mosquitto\" user does not exist, then\n\t\t\t\t\t\tmosquitto will change to the \"nobody\" user instead.\n\t\t\t\t\t\tIf this is set to another value and mosquitto is unable\n\t\t\t\t\t\tto change to this user and group, it will exit with an\n\t\t\t\t\t\terror. The user specified must have read/write access\n\t\t\t\t\t\tto the persistence database if it is to be written. If\n\t\t\t\t\t\trun as a non-root user, this setting has no effect.\n\t\t\t\t\t\tDefaults to mosquitto.</para>\n\t\t\t\t\t<para>This setting has no effect on Windows and so you\n\t\t\t\t\t\tshould run mosquitto as the user you wish it to run\n\t\t\t\t\t\tas.</para>\n\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Listeners</title>\n\t\t<para>The network ports that mosquitto listens on can be controlled\n\t\t\tusing listeners. The default listener options can be overridden and\n\t\t\tfurther listeners can be created.</para>\n\t\t<refsect2>\n\t\t\t<title>General Options</title>\n\t\t\t<variablelist>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>accept_protocol_versions</option> <replaceable>versions</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tAccepted protocol versions. This sets what versions\n\t\t\t\t\t\t\tof the MQTT protocol will be accepted on this\n\t\t\t\t\t\t\tlistener. Can be any combination of 3, 4, 5 in a\n\t\t\t\t\t\t\tcomma separated list, e.g.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<programlisting>\n# Allow v5.0 only:\nlistener 1883\naccept_protocol_versions 5\n\n# Allow v3.1 and v3.1.1:\nlistener 1884\naccept_protocol_versions 3, 4</programlisting>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tDefaults to allowing all versions.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>Reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bind_address</option> <replaceable>address</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>This option is deprecated and will be removed in a\n\t\t\t\t\t\tfuture version. Use the <option>listener</option> instead.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>Listen for incoming network connections on the\n\t\t\t\t\t\t\tspecified IP address/hostname only. This is useful\n\t\t\t\t\t\t\tto restrict access to certain network interfaces.\n\t\t\t\t\t\t\tTo restrict access to mosquitto to the local host\n\t\t\t\t\t\t\tonly, use \"bind_address localhost\". This only\n\t\t\t\t\t\t\tapplies to the default listener. Use the\n\t\t\t\t\t\t\t<option>listener</option> option to control other\n\t\t\t\t\t\t\tlisteners.</para>\n\n\t\t\t\t\t\t<para>It is recommended to use an explicit\n\t\t\t\t\t\t\t<option>listener</option> rather than rely on the\n\t\t\t\t\t\t\timplicit default listener options like this.</para>\n\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bind_interface</option> <replaceable>device</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Listen for incoming network connections only on\n\t\t\t\t\t\t\tthe specified interface. This is similar to the\n\t\t\t\t\t\t\t<option>bind_address</option> option but is useful\n\t\t\t\t\t\t\twhen an interface has multiple addresses or the\n\t\t\t\t\t\t\taddress may change.</para>\n\t\t\t\t\t\t<para>If used at the same time as the\n\t\t\t\t\t\t\t<option>bind_address</option> for the default\n\t\t\t\t\t\t\tlistener, or the <replaceable>bind\n\t\t\t\t\t\t\taddress/host</replaceable> part of the\n\t\t\t\t\t\t\t<option>listener</option>, then <option>bind_interface</option>\n\t\t\t\t\t\t\twill take priority.</para>\n\t\t\t\t\t\t<para>This option is not available on Windows and AIX.</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>enable_proxy_protocol</option> [ 2 | 1 ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tEnable PROXY protocol support for this listener.\n\t\t\t\t\t\t\tVersions 1 and 2 are supported, if you have the\n\t\t\t\t\t\t\tchoice then version 2 is recommended because it\n\t\t\t\t\t\t\tgives you access to TLS information, and support\n\t\t\t\t\t\t\tUnix sockets.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThis option requires the use of a load\n\t\t\t\t\t\t\tbalancer/proxy such as HAProxy in front of\n\t\t\t\t\t\t\tMosquitto, with the proxy configured to use the\n\t\t\t\t\t\t\tPROXY protocol.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe PROXY protocol is used to send information about\n\t\t\t\t\t\t\tclient socket connections from the proxy to the\n\t\t\t\t\t\t\tbroker. Without this, the connection information and\n\t\t\t\t\t\t\tlogs in the broker will contain the IP address and\n\t\t\t\t\t\t\tport of the proxy itself, rather than the client.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tEnabling this option should only be done when a\n\t\t\t\t\t\t\ttrusted proxy is placed in front of Mosquitto, and\n\t\t\t\t\t\t\twhen no other external access to Mosquitto is\n\t\t\t\t\t\t\tpossible. Giving external access to a listener with\n\t\t\t\t\t\t\tthis option enabled is a security risk, particularly\n\t\t\t\t\t\t\tif you are using client certificates on the proxy.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tWhen using PROXY version 2, this option can be used\n\t\t\t\t\t\t\twith TLS termination on the proxy. In this case, TLS\n\t\t\t\t\t\t\tinformation will be passed to the broker as well.\n\t\t\t\t\t\t\tUse <option>proxy_protocol_v2_require_tls</option>\n\t\t\t\t\t\t\tto reject clients that do not use TLS - this is strongly\n\t\t\t\t\t\t\trecommended. Enabling <option>require_certificate</option>\n\t\t\t\t\t\t\ton the listener will result in the broker checking the TLS\n\t\t\t\t\t\t\tinformation provided to ensure that the client has provided\n\t\t\t\t\t\t\ta valid certificate. Enabling <option>use_identity_as_username</option>\n\t\t\t\t\t\t\tas well will result in the <replaceable>commonName</replaceable>\n\t\t\t\t\t\t\tvalue from the certificate being used as the client username instead\n\t\t\t\t\t\t\tof any provided in the CONNECT packet. In both cases, the listener\n\t\t\t\t\t\t\tmust not have TLS configured.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIt is not possible to use the <option>use_subject_as_username</option>,\n\t\t\t\t\t\t\t<option>certfile</option>, or <option>keyfile</option> options\n\t\t\t\t\t\t\tin conjunction with <option>enable_proxy_protocol</option>.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>http_dir</option> <replaceable>directory</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>When a listener is using the http_api protocol,\n\t\t\t\t\t\t\tit is possible to serve http data as well. Set\n\t\t\t\t\t\t\t<option>http_dir</option> to a directory which\n\t\t\t\t\t\t\tcontains the files you wish to serve. If this\n\t\t\t\t\t\t\toption is not specified, then no normal http\n\t\t\t\t\t\t\tconnections will be possible.</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThis option is also available for websockets\n\t\t\t\t\t\t\tlisteners if Mosquitto is\n\t\t\t\t\t\t\tcompiled with websockets support provided by\n\t\t\t\t\t\t\tlibwebsockets. This is not the default, and will be\n\t\t\t\t\t\t\tremoved in version 3.0.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>listener</option> <replaceable>port</replaceable> <replaceable><optional>bind address/host/unix socket path</optional></replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Listen for incoming network connection on the\n\t\t\t\t\t\t\tspecified port. A second optional argument allows\n\t\t\t\t\t\t\tthe listener to be bound to a specific ip\n\t\t\t\t\t\t\taddress/hostname. If this variable is used and\n\t\t\t\t\t\t\tneither the global <option>bind_address</option>\n\t\t\t\t\t\t\tnor <option>port</option> options are used then the\n\t\t\t\t\t\t\tdefault listener will not be started.</para>\n\n\t\t\t\t\t\t<para>The <option>bind address/host</option> option\n\t\t\t\t\t\t\tallows this listener to be bound to a specific IP\n\t\t\t\t\t\t\taddress by passing an IP address or hostname. For\n\t\t\t\t\t\t\twebsockets listeners, it is only possible to pass\n\t\t\t\t\t\t\tan IP address here.</para>\n\n\t\t\t\t\t\t<para>On systems that support Unix Domain Sockets, this\n\t\t\t\t\t\t\toption can also be used to create a Unix socket rather\n\t\t\t\t\t\t\tthan opening a TCP socket. In this case, the port must\n\t\t\t\t\t\t\tbe set to 0, and the unix socket path must be given.</para>\n\n\t\t\t\t\t\t<para>This option may be specified multiple times. See\n\t\t\t\t\t\t\talso the <option>mount_point</option>\n\t\t\t\t\t\t\toption.</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>listener_allow_anonymous</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tBoolean value that determines whether clients that\n\t\t\t\t\t\t\tconnect without providing a username are allowed to\n\t\t\t\t\t\t\tconnect to this specific listener. If set, this overrides\n\t\t\t\t\t\t\tthe value set by <option>allow_anonymous</option>\n\n\t\t\t\t\t\t\tIf set to <replaceable>false</replaceable>\n\t\t\t\t\t\t\tthen another means of connection should be created to\n\t\t\t\t\t\t\tcontrol authenticated client access.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf not explicitly set, the value from <option>allow_anonymous</option> will be used.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>auto_id_prefix</option> <replaceable>prefix</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThis option allows you to set a string that will be prefixed\n\t\t\t\t\t\t\tto the automatically generated client ids (i.e. for when a\n\t\t\t\t\t\t\tclient connects without providing a client id) to aid\n\t\t\t\t\t\t\tvisibility in logs. Defaults to <option>auto-</option>.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>max_connections</option> <replaceable>count</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Limit the total number of clients connected for\n\t\t\t\t\t\t\tthe current listener. Set to <literal>-1</literal>\n\t\t\t\t\t\t\tto have \"unlimited\" connections. Note that other\n\t\t\t\t\t\t\tlimits may be imposed that are outside the control\n\t\t\t\t\t\t\tof mosquitto. See e.g.\n\t\t\t\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"http://linux.die.net/man/5/limits.conf\">limits.conf</link></refentrytitle></citerefentry>.</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>max_qos</option> <replaceable>value</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Limit the QoS value allowed for clients connecting to this\n\t\t\t\t\t\t\tlistener. Defaults to 2, which means any QoS can be\n\t\t\t\t\t\t\tused. Set to 0 or 1 to limit to those QoS values.\n\t\t\t\t\t\t\tThis makes use of an MQTT v5 feature to notify\n\t\t\t\t\t\t\tclients of the limitation. MQTT v3.1.1 clients will\n\t\t\t\t\t\t\tnot be aware of the limitation. Clients publishing\n\t\t\t\t\t\t\tto this listener with a too-high QoS will be\n\t\t\t\t\t\t\tdisconnected.</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>max_topic_alias_broker</option> <replaceable>number</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>When publishing to MQTT v5 clients, Mosquitto can\n\t\t\t\t\t\tcreate topic aliases on a first come first serve basis,\n\t\t\t\t\t\ti.e. the topics that are published to a client first have\n\t\t\t\t\t\taliases created. This option controls the number of aliases\n\t\t\t\t\t\tper client. It applies per listener.</para>\n\n\t\t\t\t\t\t<para>Note that this behaviour is not guaranteed to remain\n\t\t\t\t\t\tthe same. It is possible that future versions introduce\n\t\t\t\t\t\tmechanisms for controlling which topics receive aliases.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>This option sets the maximum number topic aliases\n\t\t\t\t\t\tthat Mosquitto will create for an MQTT v5 client,\n\t\t\t\t\t\teven if the client allows more.</para>\n\n\t\t\t\t\t\t<para>For example, if the client sets\n\t\t\t\t\t\ttopic-alias-maximum to 100 and this option is set to\n\t\t\t\t\t\t10, the broker will create at most 10 topic aliases.\n\t\t\t\t\t\tLikewise, if the client sets topic-alias-maximum to\n\t\t\t\t\t\t20 and this option is set to 100, then the broker\n\t\t\t\t\t\twill create at most 20 topic aliases.</para>\n\n\t\t\t\t\t\t<para>Defaults to 10. Maximum of 65535. Set to 0 to\n\t\t\t\t\t\tdisable broker to client topic aliases completely.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>max_topic_alias</option> <replaceable>number</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>This option sets the maximum number topic aliases\n\t\t\t\t\t\t\tthat an MQTT v5 client is allowed to create. This option\n\t\t\t\t\t\t\tapplies per listener. Defaults to 10. Set to 0 to\n\t\t\t\t\t\t\tdisallow topic aliases from clients. The maximum value possible is 65535.</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>mount_point</option> <replaceable>topic prefix</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>This option is used with the listener option to\n\t\t\t\t\t\t\tisolate groups of clients. When a client connects\n\t\t\t\t\t\t\tto a listener which uses this option, the string\n\t\t\t\t\t\t\targument is attached to the start of all topics for\n\t\t\t\t\t\t\tthis client. This prefix is removed when any\n\t\t\t\t\t\t\tmessages are sent to the client. This means a\n\t\t\t\t\t\t\tclient connected to a listener with mount point\n\t\t\t\t\t\t\t<replaceable>example</replaceable> can only see\n\t\t\t\t\t\t\tmessages that are published in the topic hierarchy\n\t\t\t\t\t\t\t<replaceable>example</replaceable> and below.</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>port</option> <replaceable>port number</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>This option is deprecated and will be removed in a\n\t\t\t\t\t\tfuture version. Use the <option>listener</option> instead.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>Set the network port for the default listener to\n\t\t\t\t\t\t\tlisten on. Defaults to 1883.</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\n\t\t\t\t\t\t<para>It is recommended to use an explicit\n\t\t\t\t\t\t\t<option>listener</option> rather than rely on the\n\t\t\t\t\t\t\timplicit default listener options like this.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>protocol</option> <replaceable>value</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tSet the protocol to accept for the current listener. Can\n\t\t\t\t\t\t\tbe <option>mqtt</option>, the default,\n\t\t\t\t\t\t\t<option>websockets</option>, or <option>http_api</option>.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\t<option>mqtt</option>: the standard MQTT listener.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\t<option>websockets</option>: MQTT tunnelled over WebSockets.\n\t\t\t\t\t\t\tIf the legacy websockets support using libwebsockets is used,\n\t\t\t\t\t\t\tthen only a reduced TLS feature set is available, namely\n\t\t\t\t\t\t\t<option>cafile</option>, <option>certfile</option>,\n\t\t\t\t\t\t\t<option>keyfile</option>, <option>ciphers</option>, and\n\t\t\t\t\t\t\t<option>ciphers_tls1.3</option>.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\t<option>http_api</option>: This starts the listener as a very\n\t\t\t\t\t\t\tsimple webserver (see the <option>http_dir</option> option)\n\t\t\t\t\t\t\tthat can also serve some HTTP API requests. TLS is supported\n\t\t\t\t\t\t\tfor this listener, however only the <option>certfile</option> and\n\t\t\t\t\t\t\t<option>keyfile</option> options are allowed. Authentication is\n\t\t\t\t\t\t\tnot currently possible - to use this listener it is strongly\n\t\t\t\t\t\t\trecommended to bind the listener to the loopback interface and\n\t\t\t\t\t\t\tthen configure a reverse proxy with the appropriate encryption\n\t\t\t\t\t\t\tand authentication.\n\t\t\t\t\t\t</para>\n\n\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>proxy_protocol_v2_require_tls</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tWhen a listener is using the PROXY protocol, this\n\t\t\t\t\t\t\toption can be used to require that the client\n\t\t\t\t\t\t\tconnection is using TLS. If set to true, then the\n\t\t\t\t\t\t\tPROXY protocol header must contain a TLS\n\t\t\t\t\t\t\tindicator. If it does not, the connection will be\n\t\t\t\t\t\t\tclosed. Defaults to false.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>socket_domain</option> [ ipv4 | ipv6 ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>By default, a listener will attempt to listen on\n\t\t\t\t\t\t\tall supported IP protocol versions. If you do not\n\t\t\t\t\t\t\thave an IPv4 or IPv6 interface you may wish to\n\t\t\t\t\t\t\tdisable support for either of those protocol\n\t\t\t\t\t\t\tversions. In particular, note that due to the\n\t\t\t\t\t\t\tlimitations of the websockets library, it will only\n\t\t\t\t\t\t\tever attempt to open IPv6 sockets if IPv6 support\n\t\t\t\t\t\t\tis compiled in, and so will fail if IPv6 is not\n\t\t\t\t\t\t\tavailable.</para>\n\t\t\t\t\t\t<para>Set to <option>ipv4</option> to force the\n\t\t\t\t\t\t\tlistener to only use IPv4, or set to\n\t\t\t\t\t\t\t<option>ipv6</option> to force the listener to only\n\t\t\t\t\t\t\tuse IPv6. If you want support for both IPv4 and\n\t\t\t\t\t\t\tIPv6, then do not use the\n\t\t\t\t\t\t\t<option>socket_domain</option> option.</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>use_username_as_clientid</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Set <option>use_username_as_clientid</option> to\n\t\t\t\t\t\t\ttrue to replace the clientid that a client\n\t\t\t\t\t\t\tconnected with its username. This allows\n\t\t\t\t\t\t\tauthentication to be tied to the clientid, which\n\t\t\t\t\t\t\tmeans that it is possible to prevent one client\n\t\t\t\t\t\t\tdisconnecting another by using the same\n\t\t\t\t\t\t\tclientid. Defaults to false.</para>\n\t\t\t\t\t\t<para>If a client connects with no username it will be\n\t\t\t\t\t\t\tdisconnected as not authorised when this option is\n\t\t\t\t\t\t\tset to true. Do not use in conjunction with\n\t\t\t\t\t\t\t<option>clientid_prefixes</option>.</para>\n\t\t\t\t\t\t<para>This does not apply globally, but on a per-listener basis.</para>\n\t\t\t\t\t\t<para>See also\n\t\t\t\t\t\t\t<option>use_identity_as_username</option>.</para>\n\t\t\t\t\t\t<para>Not reloaded on reload signal.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>packet_buffer_size</option> <replaceable>size</replaceable></term>\n\t\t\t\t\t<term><option>websockets_headers_size</option> <replaceable>size</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tChange the size of the buffer used when reading from\n\t\t\t\t\t\t\tthe network before the size of the MQTT packet is\n\t\t\t\t\t\t\tknown. Defaults to 4096. Packets received that are\n\t\t\t\t\t\t\tsmaller than this value in principle only need a\n\t\t\t\t\t\t\tsingle read() call, making reading packets more\n\t\t\t\t\t\t\tefficient.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThis also operates as the option that sets the size\n\t\t\t\t\t\t\tof the buffer used by websockets when reading the\n\t\t\t\t\t\t\tinitial header. If you are passing large header data\n\t\t\t\t\t\t\tsuch as cookies then you may need to increase this\n\t\t\t\t\t\t\tvalue.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>websockets_log_level</option> <replaceable>level</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Change the websockets logging level. This is a\n\t\t\t\t\t\t\tglobal option, it is not possible to set per\n\t\t\t\t\t\t\tlistener. This is an integer that is interpreted by\n\t\t\t\t\t\t\tlibwebsockets as a bit mask for its lws_log_levels\n\t\t\t\t\t\t\tenum. See the libwebsockets documentation for more\n\t\t\t\t\t\t\tdetails.</para>\n\t\t\t\t\t\t<para>To use this option, <option>log_type\n\t\t\t\t\t\t\t\twebsockets</option> must also be enabled.\n\t\t\t\t\t\t\tDefaults to 0.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>websockets_origin</option> <replaceable>level</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf set, this will be compared to the http origin\n\t\t\t\t\t\t\theader when a connection attempts to upgrade to\n\t\t\t\t\t\t\tWebSockets. Only matching origins will be allowed.\n\t\t\t\t\t\t\tUse the exact string provided by the browser in the\n\t\t\t\t\t\t\torigin header, e.g.\n\t\t\t\t\t\t\t<replaceable>http://example.com:8080</replaceable>.\n\t\t\t\t\t\t\tCan be specified multiple times per listener.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf not set, connections from any origin will be allowed.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tRequires libwebsockets 3.1.0 or later.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t</variablelist>\n\t\t</refsect2>\n\t\t<refsect2>\n\t\t\t<title>Certificate based SSL/TLS Support</title>\n\t\t\t<para>The following options are available for all listeners to\n\t\t\t\tconfigure certificate based SSL support. See also\n\t\t\t\t\"Pre-shared-key based SSL/TLS support\".</para>\n\t\t\t<variablelist>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>cafile</option> <replaceable>file path</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para><option>cafile</option> is used to define the\n\t\t\t\t\t\t\tpath to a file containing the PEM encoded CA\n\t\t\t\t\t\t\tcertificates that are trusted when checking incoming\n\t\t\t\t\t\t\tclient certificates.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>capath</option> <replaceable>directory path</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para><option>capath</option> is used to define a\n\t\t\t\t\t\t\tdirectory that contains PEM encoded CA certificates\n\t\t\t\t\t\t\tthat are trusted when checking incoming client\n\t\t\t\t\t\t\tcertificates. For <option>capath</option> to\n\t\t\t\t\t\t\twork correctly, the certificates files must have\n\t\t\t\t\t\t\t\".pem\" as the file ending and you must run\n\t\t\t\t\t\t\t\"openssl rehash &lt;path to capath&gt;\" each time\n\t\t\t\t\t\t\tyou add/remove a certificate.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para><option>capath</option> is not supported for websockets.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>certfile</option> <replaceable>file path</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tPath to the PEM encoded server certificate. This\n\t\t\t\t\t\t\toption and <option>keyfile</option> must be present\n\t\t\t\t\t\t\tto enable certificate based TLS encryption.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe certificate pointed to by this option will be\n\t\t\t\t\t\t\treloaded when Mosquitto receives a SIGHUP signal.\n\t\t\t\t\t\t\tThis can be used to load new certificates prior to\n\t\t\t\t\t\t\tthe existing ones expiring.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>ciphers</option> <replaceable>cipher:list</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe list of allowed ciphers for this listener, for\n\t\t\t\t\t\t\tTLS v1.2 and earlier only, each separated with\n\t\t\t\t\t\t\ta colon. Available ciphers can be obtained using\n\t\t\t\t\t\t\tthe \"openssl ciphers\" command.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>ciphers_tls1.3</option> <replaceable>cipher:list</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe list of allowed ciphersuites for this listener,\n\t\t\t\t\t\t\tfor TLS v1.3, each separated with a colon.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>crlfile</option> <replaceable>file path</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>If you have <option>require_certificate</option>\n\t\t\t\t\t\t\tset to <replaceable>true</replaceable>, you can\n\t\t\t\t\t\t\tcreate a certificate revocation list file to revoke\n\t\t\t\t\t\t\taccess to particular client certificates. If you\n\t\t\t\t\t\t\thave done this, use crlfile to point to the PEM\n\t\t\t\t\t\t\tencoded revocation file.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>dhparamfile</option> <replaceable>file path</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>To allow the use of ephemeral DH key exchange,\n\t\t\t\t\t\t\twhich provides forward security, the listener must\n\t\t\t\t\t\t\tload DH parameters. This can be specified with the\n\t\t\t\t\t\t\tdhparamfile option. The dhparamfile can be\n\t\t\t\t\t\t\tgenerated with the command e.g.</para>\n\t\t\t\t\t\t<programlisting>\nopenssl dhparam -out dhparam.pem 2048</programlisting>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>disable_client_cert_date_checks</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf set true, <option>disable_client_cert_date_checks</option> will change the certificate\n\t\t\t\t\t\t\tverification behaviour to allow client certificates that are expired or are not yet\n\t\t\t\t\t\t\tvalid, when using <option>require_certificate true</option>.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tDefaults to <option>false</option>\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>keyfile</option> <replaceable>file path</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf <option>tls_keyform</option> equals \"pem\" this is the\n\t\t\t\t\t\t\tpath to the PEM encoded server key. This option\n\t\t\t\t\t\t\tand <option>certfile</option> must be present\n\t\t\t\t\t\t\tto enable certificate based TLS encryption. If\n\t\t\t\t\t\t\t<option>tls_keyform</option> is \"engine\" this represents\n\t\t\t\t\t\t\tthe engine handle of the private key.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe private key pointed to by this option will be\n\t\t\t\t\t\t\treloaded when Mosquitto receives a SIGHUP signal.\n\t\t\t\t\t\t\tThis can be used to load new keys prior to\n\t\t\t\t\t\t\tthe existing ones expiring.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>require_certificate</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>By default an SSL/TLS enabled listener will\n\t\t\t\t\t\t\toperate in a similar fashion to a https enabled web\n\t\t\t\t\t\t\tserver, in that the server has a certificate signed\n\t\t\t\t\t\t\tby a CA and the client will verify that it is a\n\t\t\t\t\t\t\ttrusted certificate. The overall aim is encryption\n\t\t\t\t\t\t\tof the network traffic. By setting\n\t\t\t\t\t\t\t<option>require_certificate</option> to\n\t\t\t\t\t\t\t<replaceable>true</replaceable>, a client connecting\n\t\t\t\t\t\t\tto this listener must provide a valid certificate in\n\t\t\t\t\t\t\torder for the network connection to proceed. This\n\t\t\t\t\t\t\tallows access to the broker to be controlled outside\n\t\t\t\t\t\t\tof the mechanisms provided by MQTT.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>tls_engine</option> <replaceable>engine</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>A valid openssl engine id. These can be listed with\n\t\t\t\t\t\topenssl engine command.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>tls_engine_kpass_sha1</option> <replaceable>engine_kpass_sha1</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>SHA1 of the private key password when using an\n\t\t\t\t\t\t\tTLS engine. Some TLS engines such as the TPM\n\t\t\t\t\t\t\tengine may require the use of a password in order\n\t\t\t\t\t\t\tto be accessed. This option allows a hex encoded\n\t\t\t\t\t\t\tSHA1 hash of the password to the engine directly,\n\t\t\t\t\t\t\tinstead of the user being prompted for the\n\t\t\t\t\t\t\tpassword.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>tls_keyform</option> [ pem | engine ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Specifies the type of private key in use when\n\t\t\t\t\t\t\tmaking TLS connections.. This can be \"pem\" or\n\t\t\t\t\t\t\t\"engine\". This parameter is useful when a TPM\n\t\t\t\t\t\t\tmodule is being used and the private key has been\n\t\t\t\t\t\t\tcreated with it. Defaults to \"pem\", which means\n\t\t\t\t\t\t\tnormal private key files are used.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>tls_version</option> <replaceable>version</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tConfigure the minimum version of the TLS protocol to be\n\t\t\t\t\t\t\tused for this listener. Possible values are\n\t\t\t\t\t\t\t<replaceable>tlsv1.3</replaceable> and\n\t\t\t\t\t\t\t<replaceable>tlsv1.2</replaceable>.\n\t\t\t\t\t\t\tIf left unset, the default is to allow TLS v1.3 and v1.2.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIn Mosquitto version 1.6.x and earlier, this\n\t\t\t\t\t\t\toption set the only TLS protocol version that\n\t\t\t\t\t\t\twas allowed, rather than the minimum.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>use_identity_as_username</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>If <option>require_certificate</option> is\n\t\t\t\t\t\t\t<replaceable>true</replaceable>, you may set\n\t\t\t\t\t\t\t<option>use_identity_as_username</option> to\n\t\t\t\t\t\t\t<replaceable>true</replaceable> to use the CN value\n\t\t\t\t\t\t\tfrom the client certificate as a username. If this\n\t\t\t\t\t\t\tis <replaceable>true</replaceable>, the\n\t\t\t\t\t\t\t<option>password_file</option> option will not be\n\t\t\t\t\t\t\tused for this listener.</para>\n\t\t\t\t\t\t<para>This takes priority over\n\t\t\t\t\t\t\t<option>use_subject_as_username</option> if both\n\t\t\t\t\t\t\tare set to <replaceable>true</replaceable>.</para>\n\t\t\t\t\t\t<para>See also\n\t\t\t\t\t\t\t<option>use_subject_as_username</option></para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>use_subject_as_username</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>If <option>require_certificate</option> is\n\t\t\t\t\t\t\t<replaceable>true</replaceable>, you may set\n\t\t\t\t\t\t\t<option>use_subject_as_username</option> to\n\t\t\t\t\t\t\t<replaceable>true</replaceable> to use the complete subject value\n\t\t\t\t\t\t\tfrom the client certificate as a username. If this\n\t\t\t\t\t\t\tis <replaceable>true</replaceable>, the\n\t\t\t\t\t\t\t<option>password_file</option> option will not be\n\t\t\t\t\t\t\tused for this listener.</para>\n\t\t\t\t\t\t<para>The subject will be generated in a form similar\n\t\t\t\t\t\t\tto <option>CN=test client,OU=Production,O=Server,L=Nottingham,ST=Nottinghamshire,C=GB</option>.</para>\n\t\t\t\t\t\t<para>See also\n\t\t\t\t\t\t\t<option>use_identity_as_username</option></para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t</variablelist>\n\t\t</refsect2>\n\t\t<refsect2>\n\t\t\t<title>Pre-shared-key based SSL/TLS Support</title>\n\t\t\t<para>The following options are available for all listeners to\n\t\t\t\tconfigure pre-shared-key based SSL support. See also\n\t\t\t\t\"Certificate based SSL/TLS support\".</para>\n\t\t\t<variablelist>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>ciphers</option> <replaceable>cipher:list</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>When using PSK, the encryption ciphers used will\n\t\t\t\t\t\t\tbe chosen from the list of available PSK ciphers.\n\t\t\t\t\t\t\tIf you want to control which ciphers are available,\n\t\t\t\t\t\t\tuse this option. The list of available ciphers can\n\t\t\t\t\t\t\tbe obtained using the \"openssl ciphers\" command and\n\t\t\t\t\t\t\tshould be provided in the same format as the output\n\t\t\t\t\t\t\tof that command.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>psk_hint</option> <replaceable>hint</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>The <option>psk_hint</option> option enables\n\t\t\t\t\t\t\tpre-shared-key support for this listener and also\n\t\t\t\t\t\t\tacts as an identifier for this listener. The hint\n\t\t\t\t\t\t\tis sent to clients and may be used locally to aid\n\t\t\t\t\t\t\tauthentication. The hint is a free form string that\n\t\t\t\t\t\t\tdoesn't have much meaning in itself, so feel free\n\t\t\t\t\t\t\tto be creative.</para>\n\t\t\t\t\t\t<para>If this option is provided, see\n\t\t\t\t\t\t\t<option>psk_file</option> to define the pre-shared\n\t\t\t\t\t\t\tkeys to be used or create a security plugin to\n\t\t\t\t\t\t\thandle them.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>tls_version</option> <replaceable>version</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tConfigure the minimum version of the TLS protocol to be\n\t\t\t\t\t\t\tused for this listener. Possible values are\n\t\t\t\t\t\t\t<replaceable>tlsv1.3</replaceable> and\n\t\t\t\t\t\t\t<replaceable>tlsv1.2</replaceable>.\n\t\t\t\t\t\t\tIf left unset, the default is to allow TLS v1.3 and v1.2.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>In Mosquitto version 1.6.x and earlier, this\n\t\t\t\t\t\t\toption set the only TLS protocol version that\n\t\t\t\t\t\t\twas allowed, rather than the minimum.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>use_identity_as_username</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Set <option>use_identity_as_username</option> to\n\t\t\t\t\t\t\thave the psk identity sent by the client used as\n\t\t\t\t\t\t\tits username. The username will be checked as\n\t\t\t\t\t\t\tnormal, so <option>password_file</option> or\n\t\t\t\t\t\t\tanother means of authentication checking must be\n\t\t\t\t\t\t\tused. No password will be used.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t</variablelist>\n\t\t</refsect2>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Configuring Bridges</title>\n\t\t<para>Multiple bridges (connections to other brokers) can be configured\n\t\tusing the following variables.</para>\n\t\t<para>Reloaded on reload signal.</para>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>address</option> <replaceable>address[:port]</replaceable> <replaceable>[address[:port]]</replaceable></term>\n\t\t\t\t<term><option>addresses</option> <replaceable>address[:port]</replaceable> <replaceable>[address[:port]]</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Specify the address and optionally the port of the\n\t\t\t\t\t\tbridge to connect to. This must be given for each\n\t\t\t\t\t\tbridge connection. If the port is not specified, the\n\t\t\t\t\t\tdefault of 1883 is used.</para>\n\t\t\t\t\t<para>If you use an IPv6 address, then the port is not\n\t\t\t\t\t\toptional.</para>\n\t\t\t\t\t<para>Multiple host addresses can be specified on the\n\t\t\t\t\t\taddress config. See the <option>round_robin</option>\n\t\t\t\t\t\toption for more details on the behaviour of bridges\n\t\t\t\t\t\twith multiple addresses.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_attempt_unsubscribe</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If a bridge has topics that have \"out\" direction, the\n\t\t\t\t\t\tdefault behaviour is to send an unsubscribe request to\n\t\t\t\t\t\tthe remote broker on that topic. This means that\n\t\t\t\t\t\tchanging a topic direction from \"in\" to \"out\" will not\n\t\t\t\t\t\tkeep receiving incoming messages. Sending these\n\t\t\t\t\t\tunsubscribe requests is not always desirable, setting\n\t\t\t\t\t\t<option>bridge_attempt_unsubscribe</option> to\n\t\t\t\t\t\t<replaceable>false</replaceable> will disable sending\n\t\t\t\t\t\tthe unsubscribe request. Defaults to\n\t\t\t\t\t\t<replaceable>true</replaceable>.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_bind_address</option> <replaceable>ip address</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf you need to have the bridge connect over a particular\n\t\t\t\t\t\tnetwork interface, use bridge_bind_address to tell the\n\t\t\t\t\t\tbridge which local IP address the socket should bind to,\n\t\t\t\t\t\te.g. <option>bridge_bind_address 192.168.1.10</option>.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_max_packet_size</option> <replaceable>value</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf you wish to restrict the size of messages sent to a\n\t\t\t\t\t\tremote bridge, use this option. This sets the maximum\n\t\t\t\t\t\tnumber of bytes for the total message, including headers\n\t\t\t\t\t\tand payload. Note that MQTT v5 brokers may provide their\n\t\t\t\t\t\town maximum-packet-size property. In this case, the\n\t\t\t\t\t\tsmaller of the two limits will be used. Set to 0 for\n\t\t\t\t\t\t\"unlimited\".\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_max_topic_alias</option> <replaceable>value</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If the bridge is using MQTT v5, this option sets the\n\t\t\t\t\tmaximum number of topic aliases that the bridge will\n\t\t\t\t\tallow the remote broker to configure. Defaults to 10,\n\t\t\t\t\tmaximum of 65535. Set to 0 to disable incoming topic\n\t\t\t\t\taliases completely.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_outgoing_retain</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Some MQTT brokers do not allow retained messages. MQTT v5 gives\n\t\t\t\t\t\ta mechanism for brokers to tell clients that they do not support\n\t\t\t\t\t\tretained messages, but this is not possible for MQTT v3.1.1 or v3.1.\n\t\t\t\t\t\tIf you need to bridge to a v3.1.1 or v3.1 broker that does not support\n\t\t\t\t\t\tretained messages, set the <option>bridge_outgoing_retain</option>\n\t\t\t\t\t\toption to <replaceable>false</replaceable>. This will remove the\n\t\t\t\t\t\tretain bit on all outgoing messages to that bridge, regardless of any\n\t\t\t\t\t\tother setting. Defaults to <replaceable>true</replaceable>.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_protocol_version</option> <replaceable>version</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the version of the MQTT protocol to use with for\n\t\t\t\t\t\tthis bridge. Can be one of\n\t\t\t\t\t\t<replaceable>mqttv50</replaceable>,\n\t\t\t\t\t\t<replaceable>mqttv311</replaceable> or\n\t\t\t\t\t\t<replaceable>mqttv31</replaceable>. Defaults to\n\t\t\t\t\t\t<replaceable>mqttv311</replaceable>.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_receive_maximum</option> <replaceable>count</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf the bridge is using MQTT v5.0 then use\n\t\t\t\t\t\t<option>bridge_receive_maximum</option>\n\t\t\t\t\t\tto limit the number QoS 1 or 2 messages that can be\n\t\t\t\t\t\tin-flight at once. Must be 1-65535. Defaults to\n\t\t\t\t\t\t<option>max_inflight_messages</option>, which defaults\n\t\t\t\t\t\tto 20.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_session_expiry_interval</option> <replaceable>interval</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf the bridge is using MQTT v5.0 then use\n\t\t\t\t\t\t<option>bridge_session_expiry_interval</option>\n\t\t\t\t\t\tto set the session expiry interval. Set to\n\t\t\t\t\t\t<replaceable>0</replaceable> to have the session expire\n\t\t\t\t\t\timmediately when the connection drops. Set to\n\t\t\t\t\t\t<replaceable>0xFFFFFFFF</replaceable> to have an infinite\n\t\t\t\t\t\texpiry interval. Otherwise the expiry interval is set to\n\t\t\t\t\t\tthe number of seconds that you specify. Defaults to 0.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_tcp_keepalive</option> <replaceable>idle</replaceable> <replaceable>interval</replaceable> <replaceable>counter</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tSet TCP keepalive parameters for this bridge connection.\n\t\t\t\t\t\tUse this option to allow you to set a long MQTT\n\t\t\t\t\t\tkeepalive value, whilst still being able to detect the\n\t\t\t\t\t\tconnection dropping in a reasonable time.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThis may be useful when bridging to services that bill\n\t\t\t\t\t\tfor PINGREQ messages.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>Disabled by default.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_tcp_user_timeout</option> <replaceable>timeout</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIt specifies the maximum amount of time (in milliseconds) that\n\t\t\t\t\t\ttransmitted data may remain unacknowledged at TCP level.\n\t\t\t\t\t\tPopular Linux distributions seem to set this time to \"up to 20\n\t\t\t\t\t\tminutes with system defaults\" (from Linux tcp man page).\n\t\t\t\t\t\tThe default time is related to tcp_retries2.\n\t\t\t\t\t\tReducing this value helps detecting dropped connections faster.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tBe aware that when used in combination with TCP Keepalive, it might\n\t\t\t\t\t\tchange the latter's behavior.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tYou can find more details about this setting at:\n\t\t\t\t\t\t<ulink url=\"https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/\"/>\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tOnly available on Linux.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>cleansession</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the clean session option for this bridge. Setting\n\t\t\t\t\t\tto <replaceable>false</replaceable> (the default),\n\t\t\t\t\t\tmeans that all subscriptions on the remote broker are\n\t\t\t\t\t\tkept in case of the network connection dropping. If set\n\t\t\t\t\t\tto <replaceable>true</replaceable>, all subscriptions\n\t\t\t\t\t\tand messages on the remote broker will be cleaned up if\n\t\t\t\t\t\tthe connection drops. Note that setting to\n\t\t\t\t\t\t<replaceable>true</replaceable> may cause a large\n\t\t\t\t\t\tamount of retained messages to be sent each time the\n\t\t\t\t\t\tbridge reconnects.</para>\n\t\t\t\t\t<para>If you are using bridges with\n\t\t\t\t\t\t<option>cleansession</option> set to\n\t\t\t\t\t\t<replaceable>false</replaceable> (the default), then\n\t\t\t\t\t\tyou may get unexpected behaviour from incoming topics\n\t\t\t\t\t\tif you change what topics you are subscribing to. This\n\t\t\t\t\t\tis because the remote broker keeps the subscription for\n\t\t\t\t\t\tthe old topic. If you have this problem, connect your\n\t\t\t\t\t\tbridge with <option>cleansession</option> set to\n\t\t\t\t\t\t<replaceable>true</replaceable>, then reconnect with\n\t\t\t\t\t\tcleansession set to <replaceable>false</replaceable> as\n\t\t\t\t\t\tnormal.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>local_cleansession</option> [ true | false]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The regular <option>cleansession</option> covers both the local subscriptions\n\t\t\t\t\tand the remote subscriptions. local_cleansession allows splitting this.\n\t\t\t\t\tSetting <replaceable>false</replaceable> will mean that the local connection\n\t\t\t\t\twill preserve subscription, independent of the remote connection.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>Defaults to the value of bridge.cleansession unless explicitly specified.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>connection</option> <replaceable>name</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>This variable marks the start of a new bridge\n\t\t\t\t\t\tconnection. It is also used to give the bridge a name\n\t\t\t\t\t\twhich is used as the client id on the remote\n\t\t\t\t\t\tbroker.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>keepalive_interval</option> <replaceable>seconds</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the number of seconds after which the bridge\n\t\t\t\t\t\tshould send a ping if no other traffic has occurred.\n\t\t\t\t\t\tDefaults to 60. A minimum value of 5 seconds\n\t\t\t\t\t\tis allowed.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>idle_timeout</option> <replaceable>seconds</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the amount of time a bridge using the lazy start\n\t\t\t\t\t\ttype must be idle before it will be stopped. Defaults\n\t\t\t\t\t\tto 60 seconds.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>local_clientid</option> <replaceable>id</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the clientid to use on the local broker. If not\n\t\t\t\t\t\tdefined, this defaults to\n\t\t\t\t\t\t<option>local.&lt;remote_clientid&gt;</option>. If you are\n\t\t\t\t\t\tbridging a broker to itself, it is important that\n\t\t\t\t\t\tlocal_clientid and remote_clientid do not match.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>local_password</option> <replaceable>password</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Configure the password to be used when connecting\n\t\t\t\t\t\tthis bridge to the local broker. This may be important\n\t\t\t\t\t\twhen authentication and ACLs are being used.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>local_username</option> <replaceable>username</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Configure the username to be used when connecting\n\t\t\t\t\t\tthis bridge to the local broker. This may be important\n\t\t\t\t\t\twhen authentication and ACLs are being used.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>notifications</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If set to <replaceable>true</replaceable>, publish\n\t\t\t\t\t\tnotification messages to the local and remote brokers\n\t\t\t\t\t\tgiving information about the state of the bridge\n\t\t\t\t\t\tconnection. Retained messages are published to the\n\t\t\t\t\t\ttopic $SYS/broker/connection/&lt;remote_clientid&gt;/state\n\t\t\t\t\t\tunless otherwise set with\n\t\t\t\t\t\t<option>notification_topic</option>s. If the message\n\t\t\t\t\t\tis 1 then the connection is active, or 0 if the\n\t\t\t\t\t\tconnection has failed. Defaults to\n\t\t\t\t\t\t<replaceable>true</replaceable>.</para>\n\t\t\t\t\t<para>This uses the Last Will and Testament (LWT) feature.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t <varlistentry>\n\t\t\t\t<term><option>notifications_local_only</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If set to <replaceable>true</replaceable>, only publish\n\t\t\t\t\t\tnotification messages to the local broker giving\n\t\t\t\t\t\tinformation about the state of the bridge connection.\n\t\t\t\t\t\tDefaults to <replaceable>false</replaceable>.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>notification_topic</option> <replaceable>topic</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Choose the topic on which notifications will be\n\t\t\t\t\t\tpublished for this bridge. If not set the messages will\n\t\t\t\t\t\tbe sent on the topic\n\t\t\t\t\t\t$SYS/broker/connection/&lt;remote_clientid&gt;/state.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>remote_clientid</option> <replaceable>id</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the client id for this bridge connection. If not\n\t\t\t\t\t\tdefined, this defaults to 'name.hostname', where name\n\t\t\t\t\t\tis the connection name and hostname is the hostname of\n\t\t\t\t\t\tthis computer.</para>\n\t\t\t\t\t<para>This replaces the old \"clientid\" option to avoid\n\t\t\t\t\t\tconfusion with local/remote sides of the bridge.\n\t\t\t\t\t\t\"clientid\" remains valid for the time being.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>remote_password</option> <replaceable>value</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Configure a password for the bridge. This is used for\n\t\t\t\t\t\tauthentication purposes when connecting to a broker\n\t\t\t\t\t\tthat supports MQTT v3.1 and up and requires a username\n\t\t\t\t\t\tand/or password to connect. This option is only valid\n\t\t\t\t\t\tif a remote_username is also supplied.</para>\n\t\t\t\t\t<para>This replaces the old \"password\" option to avoid\n\t\t\t\t\t\tconfusion with local/remote sides of the bridge.\n\t\t\t\t\t\t\"password\" remains valid for the time being.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>remote_username</option> <replaceable>name</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Configure a username for the bridge. This is used for\n\t\t\t\t\t\tauthentication purposes when connecting to a broker\n\t\t\t\t\t\tthat supports MQTT v3.1 and up and requires a username\n\t\t\t\t\t\tand/or password to connect. See also the\n\t\t\t\t\t\t<option>remote_password</option> option.</para>\n\t\t\t\t\t<para>This replaces the old \"username\" option to avoid\n\t\t\t\t\t\tconfusion with local/remote sides of the bridge.\n\t\t\t\t\t\t\"username\" remains valid for the time being.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>restart_timeout</option> <replaceable>base cap [stable]</replaceable></term>\n\t\t\t\t<term><option>restart_timeout</option> <replaceable>constant</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the amount of time a bridge using the automatic\n\t\t\t\t\t\tstart type will wait until attempting to reconnect.</para>\n\t\t\t\t\t<para>This option can be configured to use a constant delay\n\t\t\t\t\t\ttime in seconds, or to use a backoff mechanism based on\n\t\t\t\t\t\t\"Decorrelated Jitter\", which adds a degree of\n\t\t\t\t\t\trandomness to when the restart occurs, starting at the\n\t\t\t\t\t\tbase and increasing up to the cap. The backoff time will be reset to\n\t\t\t\t\t\t<replaceable>base</replaceable> after a successful connection.\n\t\t\t\t\t\tWhen <replaceable>stable</replaceable> is specified, the backoff time\n\t\t\t\t\t\twill be reset only if the connection remains open for at least\n\t\t\t\t\t\t<replaceable>stable</replaceable> seconds. Base has a minimum of 1 second\n\t\t\t\t\t\tand a maximum of 3600 seconds. Cap has a minimum of base, and a maximum of\n\t\t\t\t\t\t7200 seconds.</para>\n\t\t\t\t\t<para>Set a constant timeout of 20 seconds:</para>\n\t\t\t\t\t\t<programlisting language=\"config\">\nrestart_timeout 20</programlisting>\n\t\t\t\t\t<para>Set backoff with a base (start value) of 10 seconds and a cap (upper\n\t\t\t\t\t\tlimit) of 60 seconds:</para>\n\t\t\t\t\t\t<programlisting language=\"config\">\nrestart_timeout 10 60</programlisting>\n\t\t\t\t\t<para>Same as previous example, but wait for the connection to be stable for\n\t\t\t\t\tat least 30 seconds before resetting the backoff:</para>\n\t\t\t\t\t\t<programlisting language=\"config\">\nrestart_timeout 10 60 30</programlisting>\n\t\t\t\t\t<para>Defaults to jitter with a base of 5 seconds and cap\n\t\t\t\t\t\tof 30 seconds.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>round_robin</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If the bridge has more than one address given in the\n\t\t\t\t\t\taddress/addresses configuration, the round_robin option\n\t\t\t\t\t\tdefines the behaviour of the bridge on a failure of the\n\t\t\t\t\t\tbridge connection. If round_robin is\n\t\t\t\t\t\t<replaceable>false</replaceable>, the default value,\n\t\t\t\t\t\tthen the first address is treated as the main bridge\n\t\t\t\t\t\tconnection. If the connection fails, the other\n\t\t\t\t\t\tsecondary addresses will be attempted in turn. Whilst\n\t\t\t\t\t\tconnected to a secondary bridge, the bridge will\n\t\t\t\t\t\tperiodically attempt to reconnect to the main bridge\n\t\t\t\t\t\tuntil successful.</para>\n\t\t\t\t\t<para>If round_robin is <replaceable>true</replaceable>,\n\t\t\t\t\t\tthen all addresses are treated as equals. If a\n\t\t\t\t\t\tconnection fails, the next address will be tried and if\n\t\t\t\t\t\tsuccessful will remain connected until it fails.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>start_type</option> [ automatic | lazy | once ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the start type of the bridge. This controls how\n\t\t\t\t\t\tthe bridge starts and can be one of three types:\n\t\t\t\t\t\t<replaceable>automatic</replaceable>, <replaceable>lazy\n\t\t\t\t\t\t</replaceable>and <replaceable>once</replaceable>. Note\n\t\t\t\t\t\tthat RSMB provides a fourth start type \"manual\" which\n\t\t\t\t\t\tisn't currently supported by mosquitto.</para>\n\n\t\t\t\t\t<para><replaceable>automatic</replaceable> is the default\n\t\t\t\t\t\tstart type and means that the bridge connection will be\n\t\t\t\t\t\tstarted automatically when the broker starts and also\n\t\t\t\t\t\trestarted after a short delay (30 seconds) if the\n\t\t\t\t\t\tconnection fails.</para>\n\n\t\t\t\t\t<para>Bridges using the <replaceable>lazy</replaceable>\n\t\t\t\t\t\tstart type will be started automatically when the\n\t\t\t\t\t\tnumber of queued messages exceeds the number set with\n\t\t\t\t\t\tthe <option>threshold</option> option. It will be\n\t\t\t\t\t\tstopped automatically after the time set by the\n\t\t\t\t\t\t<option>idle_timeout</option> parameter. Use this start\n\t\t\t\t\t\ttype if you wish the connection to only be active when\n\t\t\t\t\t\tit is needed.</para>\n\n\t\t\t\t\t<para>A bridge using the <replaceable>once</replaceable>\n\t\t\t\t\t\tstart type will be started automatically when the\n\t\t\t\t\t\tbroker starts but will not be restarted if the\n\t\t\t\t\t\tconnection fails.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>threshold</option> <replaceable>count</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Set the number of messages that need to be queued for\n\t\t\t\t\t\ta bridge with lazy start type to be restarted.\n\t\t\t\t\t\tDefaults to 10 messages.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>topic</option> <replaceable>pattern</replaceable> [[[ out | in | both ] qos-level] local-prefix remote-prefix]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tDefine a topic pattern to be shared between the two\n\t\t\t\t\t\tbrokers. Any topics matching the pattern (which may\n\t\t\t\t\t\tinclude wildcards) are shared. The pattern may be\n\t\t\t\t\t\tenclosed in double quotes in case it contains a space.\n\t\t\t\t\t\tThe second parameter\n\t\t\t\t\t\tdefines the direction that the messages will be shared\n\t\t\t\t\t\tin, so it is possible to import messages from a remote\n\t\t\t\t\t\tbroker using <replaceable>in</replaceable>, export\n\t\t\t\t\t\tmessages to a remote broker using\n\t\t\t\t\t\t<replaceable>out</replaceable> or share messages in\n\t\t\t\t\t\tboth directions. If this parameter is not defined, the\n\t\t\t\t\t\tdefault of <replaceable>out</replaceable> is used. The\n\t\t\t\t\t\tQoS level defines the publish/subscribe QoS level used\n\t\t\t\t\t\tfor this topic and defaults to 0.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>The <replaceable>local-prefix</replaceable> and\n\t\t\t\t\t\t<replaceable>remote-prefix</replaceable> options allow\n\t\t\t\t\t\ttopics to be remapped when publishing to and receiving\n\t\t\t\t\t\tfrom remote brokers. This allows a topic tree from the\n\t\t\t\t\t\tlocal broker to be inserted into the topic tree of the\n\t\t\t\t\t\tremote broker at an appropriate place.</para>\n\t\t\t\t\t<para>For incoming topics, the bridge will prepend the\n\t\t\t\t\t\tpattern with the remote prefix and subscribe to the\n\t\t\t\t\t\tresulting topic on the remote broker. When a matching\n\t\t\t\t\t\tincoming message is received, the remote prefix will be\n\t\t\t\t\t\tremoved from the topic and then the local prefix\n\t\t\t\t\t\tadded.</para>\n\t\t\t\t\t<para>For outgoing topics, the bridge will prepend the\n\t\t\t\t\t\tpattern with the local prefix and subscribe to the\n\t\t\t\t\t\tresulting topic on the local broker. When an outgoing\n\t\t\t\t\t\tmessage is processed, the local prefix will be removed\n\t\t\t\t\t\tfrom the topic then the remote prefix added.</para>\n\t\t\t\t\t<para>When using topic mapping, an empty prefix can be\n\t\t\t\t\t\tdefined using the place marker\n\t\t\t\t\t\t<replaceable>\"\"</replaceable>. Using the empty marker\n\t\t\t\t\t\tfor the topic itself is also valid. The table below\n\t\t\t\t\t\tdefines what combination of empty or value is\n\t\t\t\t\t\tvalid. The <option>Full Local Topic</option> and\n\t\t\t\t\t\t<option>Full Remote Topic</option> show the resulting\n\t\t\t\t\t\ttopics that would be used on the local and remote ends\n\t\t\t\t\t\tof the bridge. For example, for the first table row if\n\t\t\t\t\t\tyou publish to <option>L/topic</option> on the local\n\t\t\t\t\t\tbroker, then the remote broker will receive a message\n\t\t\t\t\t\ton the topic <option>R/topic</option>.</para>\n\n\t\t\t\t\t<informaltable>\n\t\t\t\t\t\t<tgroup cols=\"6\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<row>\n\t\t\t\t\t\t\t\t\t<entry><emphasis>Pattern</emphasis></entry>\n\t\t\t\t\t\t\t\t\t<entry><emphasis>Local Prefix</emphasis></entry>\n\t\t\t\t\t\t\t\t\t<entry><emphasis>Remote Prefix</emphasis></entry>\n\t\t\t\t\t\t\t\t\t<entry><emphasis>Validity</emphasis></entry>\n\t\t\t\t\t\t\t\t\t<entry><emphasis>Full Local Topic</emphasis></entry>\n\t\t\t\t\t\t\t\t\t<entry><emphasis>Full Remote Topic</emphasis></entry>\n\t\t\t\t\t\t\t</row>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t<row><entry>pattern</entry><entry>L/</entry><entry>R/</entry><entry>valid</entry><entry>L/pattern</entry><entry>R/pattern</entry></row>\n\t\t\t\t\t\t\t\t<row><entry>pattern</entry><entry>L/</entry><entry>\"\"</entry><entry>valid</entry><entry>L/pattern</entry><entry>pattern</entry></row>\n\t\t\t\t\t\t\t\t<row><entry>pattern</entry><entry>\"\"</entry><entry>R/</entry><entry>valid</entry><entry>pattern</entry><entry>R/pattern</entry></row>\n\t\t\t\t\t\t\t\t<row><entry>pattern</entry><entry>\"\"</entry><entry>\"\"</entry><entry>valid (no remapping)</entry><entry>pattern</entry><entry>pattern</entry></row>\n\t\t\t\t\t\t\t\t<row><entry>\"\"</entry><entry>local</entry><entry>remote</entry><entry>valid (remap single local topic to remote)</entry><entry>local</entry><entry>remote</entry></row>\n\t\t\t\t\t\t\t\t<row><entry>\"\"</entry><entry>local</entry><entry>\"\"</entry><entry>invalid</entry><entry></entry><entry></entry></row>\n\t\t\t\t\t\t\t\t<row><entry>\"\"</entry><entry>\"\"</entry><entry>remote</entry><entry>invalid</entry><entry></entry><entry></entry></row>\n\t\t\t\t\t\t\t\t<row><entry>\"\"</entry><entry>\"\"</entry><entry>\"\"</entry><entry>invalid</entry><entry></entry><entry></entry></row>\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</tgroup>\n\t\t\t\t\t</informaltable>\n\t\t\t\t\t<para>To remap an entire topic tree, use e.g.:</para>\n\t\t\t\t\t<programlisting language=\"config\">\ntopic # both 2 local/topic/ remote/topic/</programlisting>\n\n\t\t\t\t\t<para>This option can be specified multiple times per\n\t\t\t\t\t\tbridge.</para>\n\t\t\t\t\t<para>Care must be taken to ensure that loops are not\n\t\t\t\t\t\tcreated with this option. If you are experiencing high\n\t\t\t\t\t\tCPU load from a broker, it is possible that you have a\n\t\t\t\t\t\tloop where each broker is forever forwarding each other\n\t\t\t\t\t\tthe same messages.</para>\n\t\t\t\t\t<para>See also the <option>cleansession</option> option if\n\t\t\t\t\t\tyou have messages arriving on unexpected topics when\n\t\t\t\t\t\tusing incoming topics.</para>\n\n\t\t\t\t\t<example title=\"Bridge Topic Remapping\" label=\"Bridge Topic Remapping\">\n\t\t\t\t\t\t<para>The configuration below connects a bridge to the\n\t\t\t\t\t\t\tbroker at <option>test.mosquitto.org</option>. It\n\t\t\t\t\t\t\tsubscribes to the remote topic\n\t\t\t\t\t\t\t<option>$SYS/broker/clients/total</option> and\n\t\t\t\t\t\t\trepublishes the messages received to the local topic\n\t\t\t\t\t\t\t<option>test/mosquitto/org/clients/total</option></para>\n\t\t\t\t\t\t<programlisting language=\"config\">\nconnection test-mosquitto-org\naddress test.mosquitto.org\ncleansession true\ntopic clients/total in 0 test/mosquitto/org/ $SYS/broker/\n</programlisting></example>\n\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>try_private</option> [ true | false ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If try_private is set to\n\t\t\t\t\t\t<replaceable>true</replaceable>, the bridge will\n\t\t\t\t\t\tattempt to indicate to the remote broker that it is a\n\t\t\t\t\t\tbridge not an ordinary client. If successful, this\n\t\t\t\t\t\tmeans that loop detection will be more effective and\n\t\t\t\t\t\tthat retained messages will be propagated correctly.\n\t\t\t\t\t\tNot all brokers support this feature so it may be\n\t\t\t\t\t\tnecessary to set <option>try_private</option> to\n\t\t\t\t\t\t<replaceable>false</replaceable> if your bridge does\n\t\t\t\t\t\tnot connect properly.</para>\n\t\t\t\t\t<para>Defaults to <replaceable>true</replaceable>.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>bridge_reload_type</option> [ lazy | immediate ]</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>If you change bridge options in the configuration file,\n\t\t\t\t\tthose configuration changes are applied during a bridge\n\t\t\t\t\treconnection. The <option>bridge_reload_type</option> option\n\t\t\t\t\tdetermines when that reconnection happens, and can be set to either\n\t\t\t\t\t<replaceable>lazy</replaceable> or <replaceable>immediate</replaceable>.</para>\n\n\t\t\t\t\t<para><replaceable>lazy</replaceable> is the default, and means\n\t\t\t\t\tthat any connected bridge will remain in its current state until\n\t\t\t\t\ta natural reconnection happens, at which point the new configuration\n\t\t\t\t\twill be used.</para>\n\n\t\t\t\t\t<para><replaceable>immediate</replaceable> forces a reconnection and so\n\t\t\t\t\tuses the new configuration straight away.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t\t<refsect2>\n\t\t\t<title>SSL/TLS Support</title>\n\t\t\t<para>The following options are available for all bridges to\n\t\t\t\tconfigure SSL/TLS support.</para>\n\t\t\t<variablelist>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_alpn</option> <replaceable>alpn</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Configure the application layer protocol negotiation\n\t\t\t\t\t\t\toption for the TLS session. Useful for brokers that support\n\t\t\t\t\t\t\tboth websockets and MQTT on the same port.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_cafile</option> <replaceable>file path</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>At least one of <option>bridge_cafile</option>, <option>bridge_capath</option>, or\n\t\t\t\t\t\t\t<option>bridge_tls_use_os_certs</option> must be provided to\n\t\t\t\t\t\t\tallow SSL/TLS support.</para>\n\t\t\t\t\t\t<para>bridge_cafile is used to define the path to a file\n\t\t\t\t\t\t\tcontaining the PEM encoded CA certificates that\n\t\t\t\t\t\t\thave signed the certificate for the remote broker.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_capath</option> <replaceable>file path</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>At least one of <option>bridge_cafile</option>, <option>bridge_capath</option>, or\n\t\t\t\t\t\t\t<option>bridge_tls_use_os_certs</option> must be provided to\n\t\t\t\t\t\t\tallow SSL/TLS support.</para>\n\t\t\t\t\t\t<para>bridge_capath is used to define the path to a\n\t\t\t\t\t\t\tdirectory containing the PEM encoded CA\n\t\t\t\t\t\t\tcertificates that have signed the certificate for\n\t\t\t\t\t\t\tthe remote broker. For bridge_capath to work\n\t\t\t\t\t\t\tcorrectly, the certificate files must have \".crt\"\n\t\t\t\t\t\t\tas the file ending and you must run \"openssl rehash\n\t\t\t\t\t\t\t&lt;path to bridge_capath&gt;\" each time you\n\t\t\t\t\t\t\tadd/remove a certificate.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_certfile</option> <replaceable>file path</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Path to the PEM encoded client certificate for\n\t\t\t\t\t\t\tthis bridge, if required by the remote\n\t\t\t\t\t\t\tbroker.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_identity</option> <replaceable>identity</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Pre-shared-key encryption provides an alternative\n\t\t\t\t\t\t\tto certificate based encryption. A bridge can be\n\t\t\t\t\t\t\tconfigured to use PSK with the\n\t\t\t\t\t\t\t<option>bridge_identity</option> and\n\t\t\t\t\t\t\t<option>bridge_psk</option> options. This is the\n\t\t\t\t\t\t\tclient identity used with PSK encryption. Only one\n\t\t\t\t\t\t\tof certificate and PSK based encryption can be used\n\t\t\t\t\t\t\ton one bridge at once.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_insecure</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>When using certificate based TLS, the bridge will\n\t\t\t\t\t\t\tattempt to verify the hostname provided in the\n\t\t\t\t\t\t\tremote certificate matches the host/address being\n\t\t\t\t\t\t\tconnected to. This may cause problems in testing\n\t\t\t\t\t\t\tscenarios, so <option>bridge_insecure</option> may\n\t\t\t\t\t\t\tbe set to <replaceable>true</replaceable> to\n\t\t\t\t\t\t\tdisable the hostname verification.</para>\n\t\t\t\t\t\t<para>Setting this option to\n\t\t\t\t\t\t\t<replaceable>true</replaceable> means that a\n\t\t\t\t\t\t\tmalicious third party could potentially impersonate\n\t\t\t\t\t\t\tyour server, so it should always be set to\n\t\t\t\t\t\t\t<replaceable>false</replaceable> in production\n\t\t\t\t\t\t\tenvironments.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_keyfile</option> <replaceable>file path</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Path to the PEM encoded private key for this\n\t\t\t\t\t\t\tbridge, if required by the remote broker.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_psk</option> <replaceable>key</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>Pre-shared-key encryption provides an alternative\n\t\t\t\t\t\t\tto certificate based encryption. A bridge can be\n\t\t\t\t\t\t\tconfigured to use PSK with the\n\t\t\t\t\t\t\t<option>bridge_identity</option> and\n\t\t\t\t\t\t\t<option>bridge_psk</option> options. This is the\n\t\t\t\t\t\t\tpre-shared-key in hexadecimal format with no \"0x\".\n\t\t\t\t\t\t\tOnly one of certificate and PSK based encryption\n\t\t\t\t\t\t\tcan be used on one bridge at once.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_require_ocsp</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>When set to true, the bridge requires OCSP on the TLS\n\t\t\t\t\t\t\tconnection it opens as client.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_tls_use_os_certs</option> [ true | false ]</term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>At least one of <option>bridge_cafile</option>, <option>bridge_capath</option>, or\n\t\t\t\t\t\t\t<option>bridge_tls_use_os_certs</option> must be provided to\n\t\t\t\t\t\t\tallow SSL/TLS support.</para>\n\t\t\t\t\t\t<para>Set <option>bridge_tls_use_os_certs</option>\n\t\t\t\t\t\t\tto true to enable TLS for this bridge, and to\n\t\t\t\t\t\t\tconfigure it to trust the default certificates\n\t\t\t\t\t\t\tprovided by openssl. This is typically a large\n\t\t\t\t\t\t\tnumber of certificates. Defaults to false.</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_tls_version</option> <replaceable>version</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tConfigure the version of the TLS protocol to be\n\t\t\t\t\t\t\tused for this bridge. Possible values are\n\t\t\t\t\t\t\t<replaceable>tlsv1.3</replaceable> and\n\t\t\t\t\t\t\t<replaceable>tlsv1.2</replaceable>. Defaults to\n\t\t\t\t\t\t\t<replaceable>tlsv1.2</replaceable>. The remote\n\t\t\t\t\t\t\tbroker must support the same version of TLS for the\n\t\t\t\t\t\t\tconnection to succeed.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_ciphers</option> <replaceable>cipher:list</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe list of allowed ciphers for this bridge, for\n\t\t\t\t\t\t\tTLS v1.2 and earlier only, each separated with\n\t\t\t\t\t\t\ta colon. Available ciphers can be obtained using\n\t\t\t\t\t\t\tthe \"openssl ciphers\" command.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>bridge_ciphers_tls1.3</option> <replaceable>cipher:list</replaceable></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe list of allowed ciphersuites for this bridge,\n\t\t\t\t\t\t\tfor TLS v1.3, each separated with a colon.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t</variablelist>\n\t\t</refsect2>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Files</title>\n\t\t<para>mosquitto.conf</para>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-bugs.xml\" />\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_passwd-1.html\">mosquitto_passwd</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-tls-7.html\">mosquitto-tls</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"http://linux.die.net/man/5/limits.conf\">limits.conf</link></refentrytitle>\n\t\t\t\t\t<manvolnum>5</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto_ctrl.1.meta",
    "content": ".. title: mosquitto_ctrl man page\n.. slug: mosquitto_ctrl-1\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto_ctrl.1.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto_ctrl\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\t<refmeta>\n\t\t<refentrytitle>mosquitto_ctrl</refentrytitle>\n\t\t<manvolnum>1</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Commands</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto_ctrl</refname>\n\t\t<refpurpose>a tool for initialising/configuring a Mosquitto broker instance</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_ctrl</command>\n\t\t\t<arg choice='opt'>options | <option>-o</option> <replaceable>config-file</replaceable></arg>\n\t\t\t<arg choice='plain'>module-name</arg>\n\t\t\t<arg choice='plain'>module-command</arg>\n\t\t\t<arg choice='opt'>command-options</arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>options:</command>\n\t\t\t<arg>auth-options</arg>\n\t\t\t<arg>connection-options</arg>\n\t\t\t<arg>mqtt-options</arg>\n\t\t\t<arg>output-options</arg>\n\t\t\t<group>\n\t\t\t\t<arg choice='plain'>tls-certificate-options</arg>\n\t\t\t\t<arg choice='plain'>tls-psk-options</arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>auth-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-u</option> <replaceable>username</replaceable></arg>\n\t\t\t<arg><option>-P</option> <replaceable>password</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>connection-options:</command>\n\t\t\t<sbr/>\n\t\t\t<group choice='req'>\n\t\t\t\t<arg choice='plain'>\n\t\t\t\t\t<arg><option>-h</option> <replaceable>hostname</replaceable></arg>\n\t\t\t\t\t<arg><option>--unix</option> <replaceable>socket path</replaceable></arg>\n\t\t\t\t\t<arg><option>-p</option> <replaceable>port-number</replaceable></arg>\n\t\t\t\t</arg>\n\t\t\t\t<arg choice='plain'><option>-L</option> <replaceable>URL</replaceable></arg>\n\t\t\t</group>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-A</option> <replaceable>bind-address</replaceable></arg>\n\t\t\t<sbr/>\n\t\t\t<arg><option>--proxy</option> <replaceable>socks-url</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>mqtt-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-c</option></arg>\n\t\t\t<arg><option>-i</option> <replaceable>client-id</replaceable></arg>\n\t\t\t<arg><option>-q</option> <replaceable>message-QoS</replaceable></arg>\n\t\t\t<arg><option>-V</option> <replaceable>protocol-version</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>output-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-d</option></arg>\n\t\t\t<arg><option>--quiet</option></arg>\n\t\t</cmdsynopsis>\n\t\t<xi:include href=\"common/synopsis-tls-certificate-options.xml\" />\n\t\t<xi:include href=\"common/synopsis-tls-psk-options.xml\" />\n\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_ctrl</command>\n\t\t\t<arg choice='opt'>connection-options</arg>\n\t\t\t(interactive shell mode)\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_ctrl</command>\n\t\t\t\t<arg><option>--help</option></arg>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>mosquitto_ctrl</command> is a tool for helping configure\n\t\t\ta Mosquitto broker instance.\n\t\t</para>\n\t\t<para>\n\t\t\tIt can be run primarily as a straightforward command line tool, as\n\t\t\tdescribed here, or as an interactive shell as described in\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_ctrl_shell-1.html\">mosquitto_ctrl_shell</link></refentrytitle> <manvolnum>1</manvolnum> </citerefentry>.\n\t\t\tThe interactive shell makes most operations very straightforward\n\t\t\tand is recommended for ease of use.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Encrypted Connections</title>\n\t\t<para>\n\t\t\t<command>mosquitto_ctrl</command> supports TLS encrypted\n\t\t\tconnections. It is strongly recommended that you use an encrypted\n\t\t\t\tconnection for all remote use of mosquitto_ctrl.\n\t\t</para>\n\t\t<para>\n\t\t\tTo enable TLS connections when using x509 certificates, one of\n\t\t\teither <option>--cafile</option> or <option>--capath</option> must\n\t\t\tbe provided as an option.\n\t\t</para>\n\t\t<para>\n\t\t\tTo enable TLS connections when using TLS-PSK, you must use the\n\t\t\t<option>--psk</option> and the <option>--psk-identity</option>\n\t\t\toptions.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Modules</title>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>Dynamic security</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tAuthentication, and role based access control with users\n\t\t\t\t\t\tand groups. Uses the <command>dynsec</command> module name. See:\n\t\t\t\t\t\t<citerefentry>\n\t\t\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_ctrl_dynsec-1.html\">mosquitto_ctrl_dynsec</link></refentrytitle>\n\t\t\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t\t\t</citerefentry>\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>External modules</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\t<command>mosquitto_ctrl</command> has the ability to load\n\t\t\t\t\t\texternal modules in the form of shared libraries. For example\n\t\t\t\t\t\tusing the module name <option>example</option> will try to load\n\t\t\t\t\t\tthe external module <option>mosquitto_ctrl_example.so</option>\n\t\t\t\t\t\tor <option>mosquitto_ctrl_example.dll</option>, depending on platform.\n\t\t\t\t\t\tThis allows new functionality to be added to Mosquitto by combining\n\t\t\t\t\t\ta plugin and mosquitto_ctrl module, without having to recompile any\n\t\t\t\t\t\tMosquitto source code.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Connection Options</title>\n\t\t<para>\n\t\t\tThe options below may be given on the command line, but may also\n\t\t\tbe placed in a config file located at\n\t\t\t<option>$XDG_CONFIG_HOME/mosquitto_ctrl</option> or\n\t\t\t<option>$HOME/.config/mosquitto_ctrl</option>.\n\t\t</para>\n\t\t<para>\n\t\t\tThe config file may be specified manually with the\n\t\t\t<option>-o <replaceable>config-file</replaceable></option>\n\t\t\toption.\n\t\t</para>\n\t\t<para>\n\t\t\tThe config file should have one pair of\n\t\t\t<option>-option <replaceable>value</replaceable></option>\n\t\t\tper line. The values in the config file will be used as defaults\n\t\t\tand can be overridden by using the command line. The exceptions to\n\t\t\tthis are the message type options, of which only one can be\n\t\t\tspecified. Note also that currently some options cannot be negated,\n\t\t\te.g. <option>-S</option>. Config file lines that have a\n\t\t\t<option>#</option> as the first character are treated as comments\n\t\t\tand not processed any further.\n\t\t</para>\n\t\t<variablelist>\n\t\t\t<xi:include href=\"common/option-bind.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-cafile.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-capath.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-cert.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-ciphers.xml\" />\n\t\t\t<xi:include href=\"common/option-debug.xml\" />\n\t\t\t<xi:include href=\"common/option-property.xml\" />\n\t\t\t<xi:include href=\"common/option-help.xml\" />\n\t\t\t<xi:include href=\"common/option-host.xml\" />\n\t\t\t<xi:include href=\"common/option-clientid.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-insecure.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-key.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-keyform.xml\" />\n\t\t\t<xi:include href=\"common/option-url.xml\" />\n\t\t\t<xi:include href=\"common/option-nodelay.xml\" />\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-o</option> <replaceable>config-file</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tProvide a path to a config file to load options from.\n\t\t\t\t\t\tThe config file should have one pair of\n\t\t\t\t\t\t<option>-option <replaceable>value</replaceable></option>\n\t\t\t\t\t\tper line. The values in the config file will be used as defaults\n\t\t\t\t\t\tand can be overridden by using the command line. The exceptions to\n\t\t\t\t\t\tthis are the message type options, of which only one can be\n\t\t\t\t\t\tspecified. Note also that currently some options cannot be negated,\n\t\t\t\t\t\te.g. <option>-S</option>. Config file lines that have a\n\t\t\t\t\t\t<option>#</option> as the first character are treated as comments\n\t\t\t\t\t\tand not processed any further.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<xi:include href=\"common/option-port.xml\" />\n\t\t\t<xi:include href=\"common/option-password.xml\" />\n\t\t\t<xi:include href=\"common/option-proxy.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-psk.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-psk-identity.xml\" />\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-q</option></term>\n\t\t\t\t<term><option>--qos</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tSpecify the quality of service to use for messages, from 0, 1 and 2. Defaults to 1.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<xi:include href=\"common/option-quiet.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-alpn.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-engine.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-engine-kpass-sha1.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-use-os-certs.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-version.xml\" />\n\t\t\t<xi:include href=\"common/option-username.xml\" />\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>--unix</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tConnect to a broker through a local unix domain socket\n\t\t\t\t\t\tinstead of a TCP socket. This is a replacement for\n\t\t\t\t\t\t<option>-h</option> and <option>-L</option>. For example:\n\t\t\t\t\t\t<option>mosquitto_ctrl --unix /tmp/mosquitto.sock ...</option>\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tSee the <option>socket_domain</option> option in\n\t\t\t\t\t\t<refentrytitle>\n\t\t\t\t\t\t\t<link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link>\n\t\t\t\t\t\t</refentrytitle><manvolnum>5</manvolnum>\n\t\t\t\t\t\tto configure Mosquitto to listen on a unix socket.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<xi:include href=\"common/option-protocol-version.xml\" />\n\t\t</variablelist>\n\t</refsect1>\n\n\t<refsect1 id='properties'>\n\t\t<title>Properties</title>\n\t\t<para>\n\t\t\tThe <option>-D</option> / <option>--property</option> option\n\t\t\tallows adding properties to different stages of the mosquitto_ctrl\n\t\t\trun. The properties supported for each command are as\n\t\t\tfollows:\n\t\t</para>\n\n\t\t<xi:include href=\"common/section-properties-connect.xml\" />\n\t\t<xi:include href=\"common/section-properties-publish.xml\" />\n\t\t<xi:include href=\"common/section-properties-disconnect.xml\" />\n\t\t<xi:include href=\"common/section-properties-will.xml\" />\n\t</refsect1>\n\n\t<xi:include href=\"common/section-exit-status.xml\" />\n\n\t<refsect1>\n\t\t<title>Environment Variables</title>\n\t\t<variablelist>\n\t\t\t<xi:include href=\"common/env-var-mosquitto-unsafe-allow-symlinks.xml\" />\n\t\t</variablelist>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-bugs.xml\" />\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_ctrl_shell-1.html\">mosquitto_ctrl_shell</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_rr-1.html\">mosquitto_rr</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_pub-1.html\">mosquitto_pub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_sub-1.html\">mosquitto_sub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"libmosquitto-3.html\">libmosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>3</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-tls-7.html\">mosquitto-tls</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto_ctrl_dynsec.1.meta",
    "content": ".. title: mosquitto_ctrl dynamic security man page\n.. slug: mosquitto_ctrl_dynsec-1\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto_ctrl_dynsec.1.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto_ctrl_dynsec\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\t<refmeta>\n\t\t<refentrytitle>mosquitto_ctrl_dynsec</refentrytitle>\n\t\t<manvolnum>1</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Commands</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto_ctrl_dynsec</refname>\n\t\t<refpurpose>mosquitto_ctrl module for controlling the Mosquitto Dynamic Security plugin.</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_ctrl</command>\n\t\t\t<arg choice='opt'>connection-options</arg>\n\t\t\t<arg choice='plain'>dynsec</arg>\n\t\t\t<arg choice='plain'>dynsec-command</arg>\n\t\t\t<arg choice='opt'>command-options</arg>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\tThis page describes the <command>dynsec</command> module for\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mosquitto_ctrl-1.html\">mosquitto_ctrl</link></refentrytitle>\n\t\t\t<manvolnum>1</manvolnum></citerefentry>. See the mosquitto_ctrl\n\t\t\tman page for details of the options for connecting to remote brokers,\n\t\t\tin particular since this module works with authentication and access\n\t\t\tcontrol, it is crucial that secure encrypted connections are used.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Commands</title>\n\n\t\t<refsect2>\n\t\t\t<title>Configuration file initialisation</title>\n\t\t\t<para></para>\n\t\t\t<itemizedlist mark=\"circle\">\n\t\t\t\t<listitem><para>mosquitto_ctrl dynsec init <literal>config-filename</literal> <literal>admin-user</literal> [admin-password]</para></listitem>\n\t\t\t</itemizedlist>\n\t\t</refsect2>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-bugs.xml\" />\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_rr-1.html\">mosquitto_rr</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_pub-1.html\">mosquitto_pub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_sub-1.html\">mosquitto_sub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"libmosquitto-3.html\">libmosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>3</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-tls-7.html\">mosquitto-tls</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto_ctrl_shell.1.meta",
    "content": ".. title: mosquitto_ctrl_shell man page\n.. slug: mosquitto_ctrl_shell-1\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto_ctrl_shell.1.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto_ctrl\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\t<refmeta>\n\t\t<refentrytitle>mosquitto_ctrl</refentrytitle>\n\t\t<manvolnum>1</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Commands</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto_ctrl_shell</refname>\n\t\t<refpurpose>a interactive shell for configuring a Mosquitto broker instance</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_ctrl</command>\n\t\t\t<arg><option>-h</option> <replaceable>hostname</replaceable></arg>\n\t\t\t<arg><option>-p</option> <replaceable>port-number</replaceable></arg>\n\t\t\t<arg><option>-u</option> <replaceable>username</replaceable></arg>\n\t\t\t<arg><option>-P</option> <replaceable>password</replaceable></arg>\n\t\t\t<arg><option>-i</option> <replaceable>client-id</replaceable></arg>\n\t\t\t<arg><option>--cafile</option> <replaceable>file</replaceable></arg>\n\t\t\t<arg><option>--capath</option> <replaceable>dir</replaceable></arg>\n\t\t\t<arg choice='opt'>\n\t\t\t\t<option>--cert</option> <replaceable>file</replaceable>\n\t\t\t\t<option>--key</option> <replaceable>file</replaceable>\n\t\t\t</arg>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>mosquitto_ctrl</command> is a tool for helping configure\n\t\t\ta Mosquitto broker instance.\n\t\t</para>\n\t\t<para>\n\t\t\tThis man page describes how to use the interactive shell. For\n\t\t\tinformation on the pure command line mode, see\n\t\t\t<citerefentry>\n\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_ctrl-1.html\">mosquitto_ctrl</link></refentrytitle>\n\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t</citerefentry>.\n\t\t\tThe interactive shell makes most operations very straightforward\n\t\t\tand is recommended for ease of use.\n\t\t</para>\n\t\t<para>\n\t\t\tTo run in interactive mode, run <command>mosquitto_ctrl</command>\n\t\t\twith no module or command, i.e. with at most the set of options\n\t\t\tdescribed in the synopsis above. The shell will start and you\n\t\t\twill be presented with a prompt.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Encrypted Connections</title>\n\t\t<para>\n\t\t\t<command>mosquitto_ctrl</command> supports TLS encrypted\n\t\t\tconnections. It is strongly recommended that you use an encrypted\n\t\t\tconnection for all remote use of mosquitto_ctrl.\n\t\t</para>\n\t\t<para>\n\t\t\tTo enable TLS connections, connect using the <option>mqtts://</option>\n\t\t\tor <option>wss://</option> scheme inside the shell, or use either\n\t\t\t<option>--cafile</option> or <option>--capath</option> when starting\n\t\t\tmosquitto_ctrl on the command line - this also allows custom CA\n\t\t\tcertificates to be used.\n\t\t</para>\n\t\t<para>\n\t\t\tClient certificates may be used for additional security. To\n\t\t\tenable this, use the <option>--cert</option> and\n\t\t\t<option>--key</option> options.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Example shell workflow</title>\n\t\t<example title=\"shell-create-group\">\n\t\t<para>\n\t\t\tThe typical workflow for using the interactive shell is to configure\n\t\t\tauthentication and connect to a broker, then switch to the mode you are\n\t\t\tinterested in and run its commands. For example, to create a new group\n\t\t\tin the dynamic-security plugin:\n\t\t</para>\n\n\t\t<programlisting language=\"shell\">\n$ mosquitto_ctrl\nmosquitto_ctrl shell v2.1.0\n&gt; auth\nusername: admin\npassword:\n&gt; connect mqtt://localhost\nmqtt://localhost:1883&gt; dynsec\nmqtt://localhost:1883|dynsec&gt; createGroup newgroup\nOK</programlisting>\n\t\t</example>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Shell Behaviour</title>\n\t\t<para>\n\t\t\tThe interactive shell operates in a set of different modes which\n\t\t\thave different commands available. The shell has tab completion for\n\t\t\tthe commands and their arguments, where possible. It should usually\n\t\t\tbe possible to press the tab key twice to be able to be shown a list\n\t\t\tof the currently available commmands or arguments.\n\t\t</para>\n\t\t<para>\n\t\t\tThe shell has a history of commands that can be accessed by pressing\n\t\t\tthe up and down arrow keys. The history is not saved to disk.\n\t\t</para>\n\t\t<para>\n\t\t\tHelp can be obtained for any command by typing <command>help</command>\n\t\t\tfollowed by the command name. For example: <command>help createGroup</command>.\n\t\t</para>\n\n\t\t<para>The different modes are:</para>\n\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><command>Pre-connection</command></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIn this mode it is possible to set the authentication\n\t\t\t\t\t\tdetails, connect to a broker, read help and exit.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><command>Post-connection</command></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIn this mode, mosquitto_ctrl is connected to a broker\n\t\t\t\t\t\tand you can switch between different control modules,\n\t\t\t\t\t\tview help or exit.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><command>dynsec</command></term>\n\t\t\t\t<listitem><para>\n\t\t\t\t\tAllows you to create, delete, and modify users, groups, roles, and ACLs.\n\t\t\t\t</para></listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><command>broker</command></term>\n\t\t\t\t<listitem><para>\n\t\t\t\t\tAllows you to view listener and plugin information.\n\t\t\t\t</para></listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Authentication</title>\n\t\t<para>\n\t\t\t<command>mosquitto_ctrl</command> supports authentication via\n\t\t\tusername and password, or via x509 client certificates. If you are\n\t\t\tusing username and password authentication, then you must set the\n\t\t\tusername and password before connecting. This can be done in one of\n\t\t\ttwo ways. The first is by using the <option>-u</option> and\n\t\t\t<option>-P</option> options on the command line. The second is by\n\t\t\tusing the <command>auth</command> command in the shell.\n\t\t</para>\n\t\t<para>\n\t\t\tAuthentication in the shell can done in one of two ways. The first\n\t\t\tis by using the <command>auth</command> command, which will then\n\t\t\tprompt for a username and password. The second is by using the\n\t\t\t<command>auth</command> <replaceable>username</replaceable> command,\n\t\t\twhich will then prompt for a password.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Connection</title>\n\t\t<para>\n\t\t\tTo connect to a broker, use the <command>connect</command> command.\n\t\t</para>\n\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem>\n\t\t\t\t<para>Connect to the broker at localhost on port 1883.</para>\n\t\t\t\t<programlisting language=\"shell\">&gt; connect</programlisting>\n\t\t\t</listitem>\n\t\t\t<listitem>\n\t\t\t\t<para>Connect to a specific broker using the default port 1883.</para>\n\t\t\t\t<programlisting language=\"shell\">&gt; connect mqtt://test.mosquitto.org</programlisting>\n\t\t\t</listitem>\n\t\t\t<listitem>\n\t\t\t\t<para>Connect to a specific broker using the specific port 1884.</para>\n\t\t\t\t<programlisting language=\"shell\">&gt; connect mqtt://test.mosquitto.org:1884</programlisting>\n\t\t\t</listitem>\n\t\t\t<listitem>\n\t\t\t\t<para>Connect to a specific broker using TLS.</para>\n\t\t\t\t<programlisting language=\"shell\">&gt; connect mqtts://test.mosquitto.org</programlisting>\n\t\t\t</listitem>\n\t\t\t<listitem>\n\t\t\t\t<para>Connect to a specific broker using websockets.</para>\n\t\t\t\t<programlisting language=\"shell\">&gt; connect ws://test.mosquitto.org</programlisting>\n\t\t\t</listitem>\n\t\t\t<listitem>\n\t\t\t\t<para>Connect to a specific broker using websockets over TLS.</para>\n\t\t\t\t<programlisting language=\"shell\">&gt; connect wss://test.mosquitto.org</programlisting>\n\t\t\t</listitem>\n\t\t</itemizedlist>\n\n\t\t<para>\n\t\t\tIf the <option>-h</option> option is used on the command line, the\n\t\t\tshell will immediately attempt to connect to the host specified.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Dynamic-security mode</title>\n\t\t<para>\n\t\t\tOnce connected, you can use the command <command>dynsec</command> to switch\n\t\t\tto the dynamic-security mode. This will only work if the broker has the\n\t\t\tdynamic-security plugin loaded, and you have permission to use it.\n\t\t</para>\n\t\t<para>\n\t\t\tThe dynamic-security mode has commands to create, delete,\n\t\t\tand modify users, groups, roles, and ACLs. The commands can be discovered by\n\t\t\tpressing the tab key twice. Usernames, group names, and role names can be\n\t\t\tauto-completed for the appropriate commands by pressing the tab key. Help\n\t\t\tis available for each command by using <command>help</command> followed by\n\t\t\tthe command name.\n\t\t</para>\n\t\t<para>\n\t\t\tTo leave the dynamic-security mode, use the <command>return</command> command,\n\t\t\tor use the <command>exit</command> command to exit the shell.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Broker mode</title>\n\t\t<para>\n\t\t\tUse the command <command>broker</command> to switch\n\t\t\tto the broker mode. This will only work if the broker has the\n\t\t\t<option>enable_control_api</option> option set to true.\n\t\t</para>\n\t\t<para>\n\t\t\tThe broker mode has the commands <command>listListeners</command>, to show\n\t\t\tcurrently configured listener configuration, and <command>listPlugins</command>,\n\t\t\tto show currently loaded plugins.\n\t\t</para>\n\t\t<para>\n\t\t\tTo leave the broker mode, use the <command>return</command> command,\n\t\t\tor use the <command>exit</command> command to exit the shell.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Connection Options</title>\n\t\t<para>The options below may be given on the command line</para>\n\t\t<variablelist>\n\t\t\t<xi:include href=\"common/option-tls-cafile.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-capath.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-cert.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-ciphers.xml\" />\n\t\t\t<xi:include href=\"common/option-help.xml\" />\n\t\t\t<xi:include href=\"common/option-host.xml\" />\n\t\t\t<xi:include href=\"common/option-clientid.xml\" />\n\t\t\t<xi:include href=\"common/option-tls-key.xml\" />\n\t\t\t<xi:include href=\"common/option-port.xml\" />\n\t\t\t<xi:include href=\"common/option-username.xml\" />\n\t\t</variablelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Exit Status</title>\n\t\t<para>\n\t\t\tmosquitto_ctrl returns zero on success, or non-zero on error.\n\t\t</para>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-bugs.xml\" />\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_ctrl-1.html\">mosquitto_ctrl</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_rr-1.html\">mosquitto_rr</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_pub-1.html\">mosquitto_pub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_sub-1.html\">mosquitto_sub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"libmosquitto-3.html\">libmosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>3</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-tls-7.html\">mosquitto-tls</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto_passwd.1.meta",
    "content": ".. title: mosquitto_passwd man page\n.. slug: mosquitto_passwd-1\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto_passwd.1.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto_passwd\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\t<refmeta>\n\t\t<refentrytitle>mosquitto_passwd</refentrytitle>\n\t\t<manvolnum>1</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Commands</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto_passwd</refname>\n\t\t<refpurpose>manage password files for mosquitto</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_passwd</command>\n\t\t\t<group>\n\t\t\t\t<arg choice='plain'><option>-H</option> <replaceable>hash</replaceable></arg>\n\t\t\t</group>\n\t\t\t<group>\n\t\t\t\t<arg choice='plain'><option>-c</option></arg>\n\t\t\t\t<arg choice='plain'><option>-D</option></arg>\n\t\t\t</group>\n\t\t\t<arg choice='plain'><replaceable>passwordfile</replaceable></arg>\n\t\t\t<arg choice='plain'><replaceable>username</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_passwd</command>\n\t\t\t<group>\n\t\t\t\t<arg choice='plain'><option>-H</option> <replaceable>hash</replaceable></arg>\n\t\t\t</group>\n\t\t\t<arg choice='plain'><option>-b</option></arg>\n\t\t\t<arg choice='plain'><replaceable>passwordfile</replaceable></arg>\n\t\t\t<arg choice='plain'><replaceable>username</replaceable></arg>\n\t\t\t<arg choice='plain'><replaceable>password</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_passwd</command>\n\t\t\t<arg choice='plain'><option>-U</option></arg>\n\t\t\t<arg choice='plain'><replaceable>passwordfile</replaceable></arg>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>mosquitto_passwd</command> is a tool for managing\n\t\t\tpassword files for the mosquitto MQTT broker.\n\t\t</para>\n\t\t<para>\n\t\t\tUsernames must not contain \":\". Passwords are stored in a similar\n\t\t\tformat to\n\t\t\t<citerefentry><refentrytitle><link xlink:href=\"https://linux.die.net/man/3/crypt\">crypt</link></refentrytitle><manvolnum>3</manvolnum></citerefentry>.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Options</title>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-b</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tRun in batch mode. This allows the password to be\n\t\t\t\t\t\tprovided at the command line which can be convenient\n\t\t\t\t\t\tbut should be used with care because the password will\n\t\t\t\t\t\tbe visible on the command line and in command\n\t\t\t\t\t\thistory.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-c</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tCreate a new password file. If the file already\n\t\t\t\t\t\texists, it will be overwritten. If the filename\n\t\t\t\t\t\tis specified as a dash <option>-</option>\n\t\t\t\t\t\tthen the output will be to stdout. This only really\n\t\t\t\t\t\tmakes sense with <option>-b</option>.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-D</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tDelete the specified user from the password file.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-H</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tChoose the hash to use. Can be one of\n\t\t\t\t\t\t<replaceable>argon2id</replaceable>,\n\t\t\t\t\t\t<replaceable>sha512-pbkdf2</replaceable>, or\n\t\t\t\t\t\t<replaceable>sha512</replaceable>. Defaults to\n\t\t\t\t\t\t<replaceable>argon2id</replaceable>. The\n\t\t\t\t\t\t<replaceable>sha512</replaceable> option is provided for\n\t\t\t\t\t\tcreating password files for use with Mosquitto 1.6\n\t\t\t\t\t\tand earlier.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-U</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThis option can be used to upgrade/convert a password\n\t\t\t\t\t\tfile with plain text passwords into one using hashed\n\t\t\t\t\t\tpasswords. It will modify the specified file. It does\n\t\t\t\t\t\tnot detect whether passwords are already hashed, so\n\t\t\t\t\t\tusing it on a password file that already contains\n\t\t\t\t\t\thashed passwords will generate new hashes based on the\n\t\t\t\t\t\told hashes and render the password file\n\t\t\t\t\t\tunusable.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>passwordfile</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The password file to modify.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>username</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The username to add/update/delete.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>password</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>The password to use when in batch mode.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\n\t<refsect1>\n\t\t<title>Exit Status</title>\n\t\t\t<para>\n\t\t\t\tmosquitto_passwd returns zero on success or non-zero on error.\n\t\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Examples</title>\n\t\t<para>Add a user to a new password file:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_passwd <literal>-c</literal> /etc/mosquitto/passwd <literal>ral</literal></para></listitem>\n\t\t</itemizedlist>\n\t\t<para>Add a user to an existing password file:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_passwd /etc/mosquitto/passwd <literal>ral</literal></para></listitem>\n\t\t</itemizedlist>\n\t\t<para>Add a user to an existing password file, passing the password on the command line:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_passwd -b /etc/mosquitto/passwd <literal>ral</literal> <literal>z2Dr0BsvtZ</literal></para></listitem>\n\t\t</itemizedlist>\n\t\t<para>Update the password for a user in an existing password file:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_passwd /etc/mosquitto/passwd <literal>ral</literal></para></listitem>\n\t\t</itemizedlist>\n\t\t<para>Add a user to an existing password file using the sha512 hash for Mosquitto 1.6 compatibility:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_passwd -H sha512 /etc/mosquitto/passwd <literal>ral</literal></para></listitem>\n\t\t</itemizedlist>\n\t\t<para>Delete a user from a password file</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_passwd <literal>-D</literal> /etc/mosquitto/passwd <literal>ral</literal></para></listitem>\n\t\t</itemizedlist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Environment Variables</title>\n\t\t<variablelist>\n\t\t\t<xi:include href=\"common/env-var-mosquitto-unsafe-allow-symlinks.xml\" />\n\t\t</variablelist>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-bugs.xml\" />\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-conf-5.html\">mosquitto.conf</link></refentrytitle>\n\t\t\t\t\t<manvolnum>5</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto_pub.1.meta",
    "content": ".. title: mosquitto_pub man page\n.. slug: mosquitto_pub-1\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto_pub.1.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<!DOCTYPE refentry [\n\t<!ENTITY commandname \"mosquitto_pub\">\n\t<!ENTITY from-version21 SYSTEM \"common/version-2.1.xml\">\n\t<!ENTITY options-intro SYSTEM \"common/options-intro.xml\">\n]>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto_pub\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\t<refmeta>\n\t\t<refentrytitle>mosquitto_pub</refentrytitle>\n\t\t<manvolnum>1</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Commands</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto_pub</refname>\n\t\t<refpurpose>an MQTT version 5/3.1.1/3.1 client for publishing simple messages</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_pub</command>\n\t\t\t<arg choice='opt'>options</arg>\n\t\t\t<arg choice='plain'><option>-t</option> <replaceable>topic</replaceable></arg>\n\t\t\t<arg choice='req'>payload-options</arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg>auth-options</arg>\n\t\t\t<arg>connection-options</arg>\n\t\t\t<arg>misc-options</arg>\n\t\t\t<arg>mqtt-options</arg>\n\t\t\t<arg>output-options</arg>\n\t\t\t<arg>publish-options</arg>\n\t\t\t<group>\n\t\t\t\t<arg choice='plain'>tls-certificate-options</arg>\n\t\t\t\t<arg choice='plain'>tls-psk-options</arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>auth-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-u</option> <replaceable>username</replaceable></arg>\n\t\t\t<arg><option>-P</option> <replaceable>password</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>connection-options</command>\n\t\t\t<sbr/>\n\t\t\t<group choice='req'>\n\t\t\t\t<arg choice='plain'>\n\t\t\t\t\t<arg><option>-h</option> <replaceable>hostname</replaceable></arg>\n\t\t\t\t\t<arg><option>--unix</option> <replaceable>socket path</replaceable></arg>\n\t\t\t\t\t<arg><option>-p</option> <replaceable>port-number</replaceable></arg>\n\t\t\t\t</arg>\n\t\t\t\t<arg choice='plain'><option>-L</option> <replaceable>URL</replaceable></arg>\n\t\t\t</group>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-A</option> <replaceable>bind-address</replaceable></arg>\n\t\t\t<arg><option>--nodelay</option></arg>\n\t\t\t<arg><option>-S</option></arg>\n\t\t\t<arg><option>--ws</option></arg>\n\t\t\t<sbr/>\n\t\t\t<arg><option>--proxy</option> <replaceable>socks-url</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>misc-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-o</option> <replaceable>config-file</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>mqtt-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-c</option></arg>\n\t\t\t<arg><option>-D</option> <replaceable>command</replaceable> <replaceable>identifier</replaceable> <replaceable>value</replaceable></arg>\n\t\t\t<arg><option>-i</option> <replaceable>client-id</replaceable></arg>\n\t\t\t<arg><option>-I</option> <replaceable>client-id-prefix</replaceable></arg>\n\t\t\t<arg><option>-k</option> <replaceable>keepalive-time</replaceable></arg>\n\t\t\t<arg><option>-x</option> <replaceable>session-expiry-interval</replaceable></arg>\n\t\t\t<arg><option>-V</option> <replaceable>protocol-version</replaceable></arg>\n\t\t\t<sbr/>\n\t\t\t<xi:include href=\"common/synopsis-will.xml\" />\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>output-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-d</option></arg>\n\t\t\t<arg><option>--quiet</option></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>payload-options:</command>\n\t\t\t<sbr/>\n\t\t\t<group choice='plain'>\n\t\t\t\t<arg choice='plain'><option>-f</option> <replaceable>file</replaceable></arg>\n\t\t\t\t<arg choice='plain'><option>-l</option></arg>\n\t\t\t\t<arg choice='plain'><option>-m</option> <replaceable>message</replaceable></arg>\n\t\t\t\t<arg choice='plain'><option>-n</option></arg>\n\t\t\t\t<arg choice='plain'><option>-s</option></arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>publish-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-q</option> <replaceable>message-QoS</replaceable></arg>\n\t\t\t<arg><option>-r</option></arg>\n\t\t\t<arg><option>--repeat</option> <replaceable>count</replaceable></arg>\n\t\t\t<arg><option>--repeat-delay</option> <replaceable>seconds</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<xi:include href=\"common/synopsis-tls-certificate-options.xml\" />\n\t\t<xi:include href=\"common/synopsis-tls-psk-options.xml\" />\n\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_pub</command>\n\t\t\t<group choice='plain'>\n\t\t\t\t<arg><option>--help</option></arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>mosquitto_pub</command> is a simple MQTT version 5/3.1.1\n\t\t\tclient that will publish a single message on a topic and exit.\n\t\t</para>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-encrypted-connections.xml\" />\n\n\t<refsect1>\n\t\t<title>Options</title>\n\t\t&options-intro;\n\n\t\t<refsect2>\n\t\t\t<title>The options</title>\n\t\t\t<variablelist>\n\t\t\t\t<xi:include href=\"common/option-bind.xml\" />\n\t\t\t\t<xi:include href=\"common/option-clean-session.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-cafile.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-capath.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-cert.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-ciphers.xml\" />\n\t\t\t\t<xi:include href=\"common/option-debug.xml\" />\n\t\t\t\t<xi:include href=\"common/option-property.xml\" />\n\t\t\t\t<xi:include href=\"common/option-payload-file.xml\" />\n\t\t\t\t<xi:include href=\"common/option-help.xml\" />\n\t\t\t\t<xi:include href=\"common/option-host.xml\" />\n\t\t\t\t<xi:include href=\"common/option-clientid.xml\" />\n\t\t\t\t<xi:include href=\"common/option-clientid-prefix.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-insecure.xml\" />\n\t\t\t\t<xi:include href=\"common/option-keepalive.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-key.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-keyform.xml\" />\n\t\t\t\t<xi:include href=\"common/option-url.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-l</option></term>\n\t\t\t\t\t<term><option>--stdin-line</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tSend messages read from stdin, splitting separate lines into separate messages.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-payload-message.xml\" />\n\t\t\t\t<xi:include href=\"common/option-payload-null.xml\" />\n\t\t\t\t<xi:include href=\"common/option-nodelay.xml\" />\n\t\t\t\t<xi:include href=\"common/option-no-tls.xml\" />\n\t\t\t\t<xi:include href=\"common/option-config-file.xml\" />\n\t\t\t\t<xi:include href=\"common/option-port.xml\" />\n\t\t\t\t<xi:include href=\"common/option-password.xml\" />\n\t\t\t\t<xi:include href=\"common/option-proxy.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-psk.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-psk-identity.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-q</option></term>\n\t\t\t\t\t<term><option>--qos</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tSpecify the quality of service to use for the message,\n\t\t\t\t\t\t\tfrom 0, 1 and 2. Defaults to 0.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-quiet.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-r</option></term>\n\t\t\t\t\t<term><option>--retain</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf retain is given, the message will be retained as a\n\t\t\t\t\t\t\t\"last known good\" value on the broker. See\n\t\t\t\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle><manvolnum>7</manvolnum></citerefentry>\n\t\t\t\t\t\t\tfor more information. Note that zero length payloads\n\t\t\t\t\t\t\tare never retained. If you send a zero length\n\t\t\t\t\t\t\tpayload retained message it will clear any retained\n\t\t\t\t\t\t\tmessage on the topic.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>--repeat</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf the publish mode is<option>-m</option>,\n\t\t\t\t\t\t\t<option>-f</option>, or <option>-s</option> (i.e. the modes\n\t\t\t\t\t\t\twhere only a single message is sent), then\n\t\t\t\t\t\t\t<option>--repeat</option> can be used to specify that the\n\t\t\t\t\t\t\tmessage will be published multiple times.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tSee also <option>--repeat-delay</option>.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>--repeat-delay</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf using <option>--repeat</option>, then the default\n\t\t\t\t\t\t\tbehaviour is to publish repeated messages as soon as the\n\t\t\t\t\t\t\tprevious message is delivered. Use\n\t\t\t\t\t\t\t<option>--repeat-delay</option> to specify the number of\n\t\t\t\t\t\t\tseconds to wait after the previous message was delivered\n\t\t\t\t\t\t\tbefore publishing the next. Does not need to be an integer\n\t\t\t\t\t\t\tnumber of seconds.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tNote that there is no guarantee as to the actual interval\n\t\t\t\t\t\t\tbetween messages, this option simply defines the minimum\n\t\t\t\t\t\t\ttime from delivery of one message to the start of the\n\t\t\t\t\t\t\tpublish of the next.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-payload-stdin-file.xml\" />\n\t\t\t\t<xi:include href=\"common/option-srv.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-t</option></term>\n\t\t\t\t\t<term><option>--topic</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe MQTT topic on which to publish the message. See\n\t\t\t\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle><manvolnum>7</manvolnum></citerefentry>\n\t\t\t\t\t\t\tfor more information on MQTT topics.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-tls-alpn.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-engine.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-engine-kpass-sha1.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-keylog.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-use-os-certs.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-version.xml\" />\n\t\t\t\t<xi:include href=\"common/option-username.xml\" />\n\t\t\t\t<xi:include href=\"common/option-unix-socket.xml\" />\n\t\t\t\t<xi:include href=\"common/option-protocol-version.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-payload.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-qos.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-retain.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-topic.xml\" />\n\t\t\t\t<xi:include href=\"common/option-websockets.xml\" />\n\t\t\t\t<xi:include href=\"common/option-session-expiry-interval.xml\" />\n\t\t\t</variablelist>\n\t\t</refsect2>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-wills.xml\" />\n\n\t<refsect1 id='properties'>\n\t\t<title>Properties</title>\n\t\t<para>\n\t\t\tThe <option>-D</option> / <option>--property</option> option\n\t\t\tallows adding properties to different stages of the mosquitto_pub\n\t\t\trun. The properties supported for each command are as follows:\n\t\t</para>\n\n\t\t<xi:include href=\"common/section-properties-connect.xml\" />\n\t\t<xi:include href=\"common/section-properties-publish.xml\" />\n\t\t<xi:include href=\"common/section-properties-disconnect.xml\" />\n\t\t<xi:include href=\"common/section-properties-will.xml\" />\n\t</refsect1>\n\n\t<xi:include href=\"common/section-exit-status.xml\" />\n\n\t<refsect1>\n\t\t<title>Examples</title>\n\t\t<para>Publish temperature information to localhost with QoS 1:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_pub <literal>-t</literal> sensors/temperature <literal>-m</literal> 32 <literal>-q</literal> 1</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>Publish timestamp and temperature information to a remote host on a non-standard port and QoS 0:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_pub <literal>-h</literal> 192.168.1.1\n\t\t\t<literal>-p</literal> 1885 <literal>-t</literal>\n\t\t\tsensors/temperature <literal>-m</literal> \"1266193804\n\t\t\t32\"</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>Publish light switch status. Message is set to retained because there may be a long period of time between light switch events:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_pub <literal>-r</literal> <literal>-t</literal> switches/kitchen_lights/status <literal>-m</literal> \"on\"</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>Send the contents of a file in two ways:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_pub <literal>-t</literal> my/topic <literal>-f</literal> ./data</para></listitem>\n\t\t\t<listitem><para>mosquitto_pub <literal>-t</literal> my/topic <literal>-s</literal> &lt; ./data</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>Send parsed electricity usage data from a Current Cost meter, reading from stdin with one line/reading as one message:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>read_cc128.pl | mosquitto_pub <literal>-t</literal> sensors/cc128 <literal>-l</literal></para></listitem>\n\t\t</itemizedlist>\n\t\t<para>Power on an appliance using zigbee2mqtt and an appropriately configured power socket:</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_pub <literal>-t</literal> zigbee2mqtt/power-microwave/set <literal>-m</literal> '{\"state\":\"ON\"}'</para></listitem>\n\t\t</itemizedlist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Files</title>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><filename>$XDG_CONFIG_HOME/mosquitto_pub</filename></term>\n\t\t\t\t<term><filename>$HOME/.config/mosquitto_pub</filename></term>\n\t\t\t\t<term><filename>$HOME/snap/mosquitto/current/.config/mosquitto_pub</filename> (for snap installs)</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tConfiguration file for default options.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-bugs.xml\" />\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_rr-1.html\">mosquitto_rr</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_sub-1.html\">mosquitto_sub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"libmosquitto-3.html\">libmosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>3</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-tls-7.html\">mosquitto-tls</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto_rr.1.meta",
    "content": ".. title: mosquitto_rr man page\n.. slug: mosquitto_rr-1\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto_rr.1.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<!DOCTYPE refentry [\n\t<!ENTITY commandname \"mosquitto_rr\">\n\t<!ENTITY from-version21 SYSTEM \"common/version-2.1.xml\">\n\t<!ENTITY options-intro SYSTEM \"common/options-intro.xml\">\n]>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto_rr\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\t<refmeta>\n\t\t<refentrytitle>mosquitto_rr</refentrytitle>\n\t\t<manvolnum>1</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Commands</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto_rr</refname>\n\t\t<refpurpose>an MQTT version 5/3.1.1 client for request/response messaging</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_rr</command>\n\t\t\t<arg choice='opt'>options</arg>\n\t\t\t<arg choice='plain'><option>-e</option> <replaceable>response-topic</replaceable></arg>\n\t\t\t<arg choice='plain' rep='repeat'><option>-t</option> <replaceable>message-topic</replaceable></arg>\n\t\t\t<arg choice='req'>payload-options</arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg>auth-options</arg>\n\t\t\t<arg>connection-options</arg>\n\t\t\t<arg>misc-options</arg>\n\t\t\t<arg>mqtt-options</arg>\n\t\t\t<arg>output-options</arg>\n\t\t\t<group>\n\t\t\t\t<arg choice='plain'>tls-certificate-options</arg>\n\t\t\t\t<arg choice='plain'>tls-psk-options</arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>auth-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-u</option> <replaceable>username</replaceable></arg>\n\t\t\t<arg><option>-P</option> <replaceable>password</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>connection-options:</command>\n\t\t\t<sbr/>\n\t\t\t<group choice='req'>\n\t\t\t\t<arg choice='plain'>\n\t\t\t\t\t<arg><option>-h</option> <replaceable>hostname</replaceable></arg>\n\t\t\t\t\t<arg><option>--unix</option> <replaceable>socket path</replaceable></arg>\n\t\t\t\t\t<arg><option>-p</option> <replaceable>port-number</replaceable></arg>\n\t\t\t\t</arg>\n\t\t\t\t<arg choice='plain'>\n\t\t\t\t\t<arg choice='plain'><option>-L</option> <replaceable>URL</replaceable></arg>\n\t\t\t\t</arg>\n\t\t\t</group>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-A</option> <replaceable>bind-address</replaceable></arg>\n\t\t\t<arg><option>--nodelay</option></arg>\n\t\t\t<arg><option>-S</option></arg>\n\t\t\t<arg><option>--ws</option></arg>\n\t\t\t<arg><option>--proxy</option> <replaceable>socks-url</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>misc-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>--latency</option></arg>\n\t\t\t<arg><option>-o</option> <replaceable>config-file</replaceable></arg>\n\t\t\t<arg><option>-W</option> <replaceable>message-processing-timeout</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>mqtt-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-c</option></arg>\n\t\t\t<arg><option>-D</option> <replaceable>command</replaceable> <replaceable>identifier</replaceable> <replaceable>value</replaceable></arg>\n\t\t\t<arg><option>-i</option> <replaceable>client-id</replaceable></arg>\n\t\t\t<arg><option>-I</option> <replaceable>client-id-prefix</replaceable></arg>\n\t\t\t<arg><option>-k</option> <replaceable>keepalive-time</replaceable></arg>\n\t\t\t<arg><option>-q</option> <replaceable>message-QoS</replaceable></arg>\n\t\t\t<arg><option>--retain-handling</option> always | new | never</arg>\n\t\t\t<arg><option>-V</option> <replaceable>protocol-version</replaceable></arg>\n\t\t\t<arg><option>-x</option> <replaceable>session-expiry-interval</replaceable></arg>\n\t\t\t<sbr/>\n\t\t\t<xi:include href=\"common/synopsis-will.xml\" />\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>output-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-d</option></arg>\n\t\t\t<arg><option>-N</option></arg>\n\t\t\t<arg><option>--pretty</option></arg>\n\t\t\t<arg><option>--quiet</option></arg>\n\t\t\t<arg><option>-R</option></arg>\n\t\t\t<arg><option>-v</option></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>payload-options:</command>\n\t\t\t<sbr/>\n\t\t\t<group choice='plain'>\n\t\t\t\t<arg choice='plain'><option>-f</option> <replaceable>file</replaceable></arg>\n\t\t\t\t<arg choice='plain'><option>-m</option> <replaceable>message</replaceable></arg>\n\t\t\t\t<arg choice='plain'><option>-n</option></arg>\n\t\t\t\t<arg choice='plain'><option>-s</option></arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t\t<xi:include href=\"common/synopsis-tls-certificate-options.xml\" />\n\t\t<xi:include href=\"common/synopsis-tls-psk-options.xml\" />\n\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_rr</command>\n\t\t\t<group choice='plain'>\n\t\t\t\t<arg><option>--help</option></arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>mosquitto_rr</command> is an MQTT version 5/3.1.1 client\n\t\t\tthat can be used to publish a request message and wait for a\n\t\t\tresponse. When using MQTT v5, which is the default, <command>mosquitto_rr</command>\n\t\t\twill use the Request-Response feature.\n\t\t</para>\n\t\t<para>\n\t\t\tThe important options are <option>-t</option>, <option>-e</option>,\n\t\t\tand one of <option>-f</option>, <option>-m</option>, <option>-n</option>,\n\t\t\tand <option>-s</option>.\n\t\t</para>\n\t\t<para>\n\t\t\tExample: <code>mosquitto_rr -t request-topic -e response-topic -m message</code>\n\t\t</para>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-encrypted-connections.xml\" />\n\n\t<refsect1>\n\t\t<title>Options</title>\n\t\t&options-intro;\n\n\t\t<refsect2>\n\t\t\t<title>The options</title>\n\t\t\t<variablelist>\n\t\t\t\t<xi:include href=\"common/option-bind.xml\" />\n\t\t\t\t<xi:include href=\"common/option-clean-session.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-cafile.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-capath.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-cert.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-ciphers.xml\" />\n\t\t\t\t<xi:include href=\"common/option-debug.xml\" />\n\t\t\t\t<xi:include href=\"common/option-property.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-e</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tResponse topic. The client will subscribe to this topic to wait for a response.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-payload-file.xml\" />\n\t\t\t\t<xi:include href=\"common/option-format.xml\" />\n\t\t\t\t<xi:include href=\"common/option-help.xml\" />\n\t\t\t\t<xi:include href=\"common/option-host.xml\" />\n\t\t\t\t<xi:include href=\"common/option-clientid.xml\" />\n\t\t\t\t<xi:include href=\"common/option-clientid-prefix.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-insecure.xml\" />\n\t\t\t\t<xi:include href=\"common/option-keepalive.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-key.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-keyform.xml\" />\n\t\t\t\t<xi:include href=\"common/option-url.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>--latency</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t&from-version21;\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf this option is specified, mosquitto_rr will print out\n\t\t\t\t\t\t\tthe latency between it starting to publish a request and\n\t\t\t\t\t\t\tthe response arriving. This number includes both the\n\t\t\t\t\t\t\tbroker and client processing times, as well as any\n\t\t\t\t\t\t\tinherent network latency.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThis can be used to measure message delivery latency\n\t\t\t\t\t\t\tvery simply by specifying an identical request and\n\t\t\t\t\t\t\tresponse topic.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe <option>--nodelay</option> option will be automatically\n\t\t\t\t\t\t\tused when <option>--latency</option> is in use.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-payload-message.xml\" />\n\t\t\t\t<xi:include href=\"common/option-format-no-eol.xml\" />\n\t\t\t\t<xi:include href=\"common/option-payload-null.xml\" />\n\t\t\t\t<xi:include href=\"common/option-nodelay.xml\" />\n\t\t\t\t<xi:include href=\"common/option-no-tls.xml\" />\n\t\t\t\t<xi:include href=\"common/option-config-file.xml\" />\n\t\t\t\t<xi:include href=\"common/option-port.xml\" />\n\t\t\t\t<xi:include href=\"common/option-password.xml\" />\n\t\t\t\t<xi:include href=\"common/option-format-pretty.xml\" />\n\t\t\t\t<xi:include href=\"common/option-proxy.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-psk.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-psk-identity.xml\" />\n\t\t\t\t<xi:include href=\"common/option-qos-incoming.xml\" />\n\t\t\t\t<xi:include href=\"common/option-quiet.xml\" />\n\t\t\t\t<xi:include href=\"common/option-hide-retain.xml\" />\n\t\t\t\t<xi:include href=\"common/option-retain-handling.xml\" />\n\t\t\t\t<xi:include href=\"common/option-srv.xml\" />\n\t\t\t\t<xi:include href=\"common/option-payload-stdin-file.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-t</option></term>\n\t\t\t\t\t<term><option>--topic</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe MQTT topic where the request message will be sent.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-tls-alpn.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-engine.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-engine-kpass-sha1.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-keylog.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-use-os-certs.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-version.xml\" />\n\t\t\t\t<xi:include href=\"common/option-username.xml\" />\n\t\t\t\t<xi:include href=\"common/option-unix-socket.xml\" />\n\t\t\t\t<xi:include href=\"common/option-format-verbose.xml\" />\n\t\t\t\t<xi:include href=\"common/option-protocol-version.xml\" />\n\t\t\t\t<xi:include href=\"common/option-timeout.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-payload.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-qos.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-retain.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-topic.xml\" />\n\t\t\t\t<xi:include href=\"common/option-websockets.xml\" />\n\t\t\t\t<xi:include href=\"common/option-session-expiry-interval.xml\" />\n\t\t\t</variablelist>\n\t\t</refsect2>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-output-format.xml\" />\n\t<xi:include href=\"common/section-wills.xml\" />\n\n\t<refsect1 id='properties'>\n\t\t<title>Properties</title>\n\t\t<para>\n\t\t\tThe <option>-D</option> / <option>--property</option> option\n\t\t\tallows adding properties to different stages of the mosquitto_rr\n\t\t\trun. The properties supported for each command are as\n\t\t\tfollows:\n\t\t</para>\n\n\t\t<xi:include href=\"common/section-properties-connect.xml\" />\n\t\t<xi:include href=\"common/section-properties-publish.xml\" />\n\t\t<xi:include href=\"common/section-properties-subscribe.xml\" />\n\t\t<xi:include href=\"common/section-properties-unsubscribe.xml\" />\n\t\t<xi:include href=\"common/section-properties-disconnect.xml\" />\n\t\t<xi:include href=\"common/section-properties-will.xml\" />\n\t</refsect1>\n\n\t<xi:include href=\"common/section-exit-status.xml\" />\n\n\t<refsect1>\n\t\t<title>Files</title>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><filename>$XDG_CONFIG_HOME/mosquitto_rr</filename></term>\n\t\t\t\t<term><filename>$HOME/.config/mosquitto_rr</filename></term>\n\t\t\t\t<term><filename>$HOME/snap/mosquitto/current/.config/mosquitto_rr</filename> (for snap installs)</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tConfiguration file for default options.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-bugs.xml\" />\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_pub-1.html\">mosquitto_pub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_sub-1.html\">mosquitto_sub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"libmosquitto-3.html\">libmosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>3</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-tls-7.html\">mosquitto-tls</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto_signal.1.meta",
    "content": ".. title: mosquitto_signal man page\n.. slug: mosquitto_signal-1\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto_signal.1.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto_signal\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\t<refmeta>\n\t\t<refentrytitle>mosquitto_signal</refentrytitle>\n\t\t<manvolnum>1</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Commands</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto_signal</refname>\n\t\t<refpurpose>a utility for sending signal events to Mosquitto brokers running on the local computer.</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_signal</command>\n\t\t\t<group choice='req'>\n\t\t\t\t<arg choice='plain'><option>-a</option></arg>\n\t\t\t\t<arg choice='plain'><option>-p</option> <replaceable>pid</replaceable></arg>\n\t\t\t</group>\n\t\t\t<arg choice='plain'><replaceable>signal</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_signal</command>\n\t\t\t<arg choice='plain'><option>--help</option></arg>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>mosquitto_signal</command>\n\t\t\tis a utility that can send named signals to one or all\n\t\t\tinstances of a Mosquitto broker running on the local\n\t\t\tcomputer.\n\t\t</para>\n\t\t<para>\n\t\t\tOn POSIX like systems, it is a convenient replacement for the\n\t\t\t<citerefentry>\n\t\t\t\t<refentrytitle><link xlink:href=\"https://man7.org/linux/man-pages/man1/kill.1.html\">kill</link></refentrytitle><manvolnum>1</manvolnum>\n\t\t\t</citerefentry>\n\t\t\tcommand, with the added enhancement of being able to use named\n\t\t\tsignals rather than generic system signals like SIGUSR1, and\n\t\t\twith the ability to signal multiple brokers at once.\n\t\t</para>\n\t\t<para>\n\t\t\tOn Windows, it is the only means of sending signals to a broker.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Options</title>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-a</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tSend the given signal to all processes matching the name\n\t\t\t\t\t\t<command>mosquitto</command>. As with all signal sending,\n\t\t\t\t\t\tonly those processes you have permission to send the signal\n\t\t\t\t\t\tto will receive the signal.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>-p</option> <replaceable>pid</replaceable></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tSend the signal to a specific process id. <command>mosquitto_signal</command>\n\t\t\t\t\t\twill always attempt to send the corresponding signal to the given\n\t\t\t\t\t\tprocess and will not check whether it appears to be a mosquitto\n\t\t\t\t\t\tprocess.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Signals</title>\n\t\t<para>\n\t\t\tThe following named signals are available for use.\n\t\t</para>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>config-reload</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tTrigger the broker to reload its configuration file, if in use.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tOn POSIX like systems this can also be triggered using the\n\t\t\t\t\t\t<option>SIGHUP</option> signal. Note that <option>SIGHUP</option>\n\t\t\t\t\t\tis used by other named signals.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>log-rotate</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tIf the broker is using <option>log_dest file</option>, then this\n\t\t\t\t\t\twill trigger the broker to close and reopen the log file.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tOn POSIX like systems this can also be triggered using the\n\t\t\t\t\t\t<option>SIGHUP</option> signal. Note that <option>SIGHUP</option>\n\t\t\t\t\t\tis used by other named signals.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>shutdown</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tTrigger the broker to quit.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tOn POSIX like systems this can also be triggered using the\n\t\t\t\t\t\t<option>SIGINT</option> signal.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>tree-print</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tTrigger the broker to print a representation of the subscription\n\t\t\t\t\t\tand retain trees to stdout. This is intended for debugging purposes\n\t\t\t\t\t\tonly.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tOn POSIX like systems this can also be triggered using the\n\t\t\t\t\t\t<option>SIGUSR2</option> signal. Note that <option>SIGUSR2</option>\n\t\t\t\t\t\tis used by other named signals.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t\t<varlistentry>\n\t\t\t\t<term><option>xtreport</option></term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tTrigger the broker to write some internal state information to\n\t\t\t\t\t\t<option>/tmp/xtmosquitto.kcg.&lt;pid&gt;.&lt;iteration&gt;</option>\n\t\t\t\t\t\ton POSIX systems or\n\t\t\t\t\t\t<option>xtmosquitto.kcg.&lt;pid&gt;.&lt;iteration&gt;</option>\n\t\t\t\t\t\ton Windows systems. This is intended for debugging purposes only.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tThis signal is only used by the broker when appropriate support\n\t\t\t\t\t\tis compiled in, which is not the case by default.\n\t\t\t\t\t</para>\n\t\t\t\t\t<para>\n\t\t\t\t\t\tOn POSIX like systems this can also be triggered using the\n\t\t\t\t\t\t<option>SIGRTMIN</option> signal.\n\t\t\t\t\t</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-bugs.xml\" />\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mosquitto_sub.1.meta",
    "content": ".. title: mosquitto_sub man page\n.. slug: mosquitto_sub-1\n.. category: man\n.. type: man\n.. pretty_url: False\n"
  },
  {
    "path": "man/mosquitto_sub.1.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<!DOCTYPE refentry [\n\t<!ENTITY commandname \"mosquitto_sub\">\n\t<!ENTITY from-version21 SYSTEM \"common/version-2.1.xml\">\n\t<!ENTITY options-intro SYSTEM \"common/options-intro.xml\">\n]>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mosquitto_sub\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\t<refmeta>\n\t\t<refentrytitle>mosquitto_sub</refentrytitle>\n\t\t<manvolnum>1</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Commands</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mosquitto_sub</refname>\n\t\t<refpurpose>an MQTT version 5/3.1.1/3.1 client for subscribing to topics</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_sub</command>\n\t\t\t<arg choice='opt'>options</arg>\n\t\t\t<group choice='req'>\n\t\t\t\t<arg choice='plain' rep='repeat'><option>-t</option> <replaceable>message-topic</replaceable></arg>\n\t\t\t\t<arg choice='plain' rep='repeat'><option>-U</option> <replaceable>unsubscribe-topic</replaceable></arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg>auth-options</arg>\n\t\t\t<arg>connection-options</arg>\n\t\t\t<arg>misc-options</arg>\n\t\t\t<arg>mqtt-options</arg>\n\t\t\t<arg>output-options</arg>\n\t\t\t<group>\n\t\t\t\t<arg choice='plain'>tls-certificate-options</arg>\n\t\t\t\t<arg choice='plain'>tls-psk-options</arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>auth-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-u</option> <replaceable>username</replaceable></arg>\n\t\t\t<arg><option>-P</option> <replaceable>password</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>connection-options:</command>\n\t\t\t<sbr/>\n\t\t\t<group choice='req'>\n\t\t\t\t<arg choice='plain'>\n\t\t\t\t\t<arg><option>-h</option> <replaceable>hostname</replaceable></arg>\n\t\t\t\t\t<arg><option>--unix</option> <replaceable>socket path</replaceable></arg>\n\t\t\t\t\t<arg><option>-p</option> <replaceable>port-number</replaceable></arg>\n\t\t\t\t</arg>\n\t\t\t\t<arg choice='plain'>\n\t\t\t\t\t<arg choice='plain'><option>-L</option> <replaceable>URL</replaceable></arg>\n\t\t\t\t</arg>\n\t\t\t</group>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-A</option> <replaceable>bind-address</replaceable></arg>\n\t\t\t<arg><option>--nodelay</option></arg>\n\t\t\t<arg><option>-S</option></arg>\n\t\t\t<arg><option>--ws</option></arg>\n\t\t\t<arg><option>--proxy</option> <replaceable>socks-url</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>misc-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-E</option></arg>\n\t\t\t<arg><option>-o</option> <replaceable>config-file</replaceable></arg>\n\t\t\t<arg><option>-W</option> <replaceable>message-processing-timeout</replaceable></arg>\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>mqtt-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-c</option></arg>\n\t\t\t<arg><option>-D</option> <replaceable>command</replaceable> <replaceable>identifier</replaceable> <replaceable>value</replaceable></arg>\n\t\t\t<arg><option>-i</option> <replaceable>client-id</replaceable></arg>\n\t\t\t<arg><option>-I</option> <replaceable>client-id-prefix</replaceable></arg>\n\t\t\t<arg><option>-k</option> <replaceable>keepalive-time</replaceable></arg>\n\t\t\t<arg><option>-q</option> <replaceable>message-QoS</replaceable></arg>\n\t\t\t<arg><option>--retain-as-published</option></arg>\n\t\t\t<arg><option>--retain-handling</option> always | new | never</arg>\n\t\t\t<arg><option>-V</option> <replaceable>protocol-version</replaceable></arg>\n\t\t\t<arg><option>-x</option> <replaceable>session-expiry-interval</replaceable></arg>\n\t\t\t<sbr/>\n\t\t\t<xi:include href=\"common/synopsis-will.xml\" />\n\t\t</cmdsynopsis>\n\t\t<cmdsynopsis>\n\t\t\t<command>output-options:</command>\n\t\t\t<sbr/>\n\t\t\t<arg><option>-d</option></arg>\n\t\t\t<arg><option>-C</option> <replaceable>msg-count</replaceable></arg>\n\t\t\t<arg><option>--message-rate</option></arg>\n\t\t\t<arg><option>-N</option></arg>\n\t\t\t<arg><option>--pretty</option></arg>\n\t\t\t<arg><option>--random-filter</option> <replaceable>chance</replaceable></arg>\n\t\t\t<arg><option>--remove-retained</option></arg>\n\t\t\t<group choice='opt'>\n\t\t\t\t<arg choice='plain'><option>-R</option></arg>\n\t\t\t\t<arg choice='plain'><option>--retained-only</option></arg>\n\t\t\t</group>\n\t\t\t<arg><option>--quiet</option></arg>\n\t\t\t<arg choice='opt' rep='repeat'><option>-T</option> <replaceable>filter-out</replaceable></arg>\n\t\t\t<arg><option>-v</option></arg>\n\t\t\t<group choice='opt'>\n\t\t\t\t<arg choice='plain'><option>-w</option></arg>\n\t\t\t\t<arg choice='plain'><option>--watch</option></arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t\t<xi:include href=\"common/synopsis-tls-certificate-options.xml\" />\n\t\t<xi:include href=\"common/synopsis-tls-psk-options.xml\" />\n\n\t\t<cmdsynopsis>\n\t\t\t<command>mosquitto_sub</command>\n\t\t\t<group choice='plain'>\n\t\t\t\t<arg><option>--help</option></arg>\n\t\t\t</group>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>mosquitto_sub</command> is a simple MQTT version 5/3.1.1\n\t\t\tclient that will subscribe to topics and print the messages that\n\t\t\tit receives.\n\t\t</para>\n\t\t<para>\n\t\t\tIn addition to subscribing to topics,\n\t\t\t<command>mosquitto_sub</command> can filter out received messages\n\t\t\tso they are not printed (see the <option>-T</option> option) or\n\t\t\tunsubscribe from topics (see the <option>-U</option> option).\n\t\t\tUnsubscribing from topics is useful for clients connecting with\n\t\t\tclean session set to false.\n\t\t</para>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-encrypted-connections.xml\" />\n\n\t<refsect1>\n\t\t<title>Options</title>\n\t\t&options-intro;\n\n\t\t<refsect2>\n\t\t\t<title>The options</title>\n\t\t\t<variablelist>\n\t\t\t\t<xi:include href=\"common/option-bind.xml\" />\n\t\t\t\t<xi:include href=\"common/option-clean-session.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-cafile.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-capath.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-cert.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-ciphers.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-C</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tDisconnect and exit the program immediately after the\n\t\t\t\t\t\t\tgiven count of messages have been received. This may be\n\t\t\t\t\t\t\tuseful in shell scripts where on a single status value\n\t\t\t\t\t\t\tis required, for example.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tCombine with <option>-R</option> or <option>--retain-handling never</option>\n\t\t\t\t\t\t\tto print only the first set of fresh messages (i.e. that\n\t\t\t\t\t\t\tdoes not have the retained flag set), or with\n\t\t\t\t\t\t\t<option>-T</option> to filter which topics are processed.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-debug.xml\" />\n\t\t\t\t<xi:include href=\"common/option-property.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-E</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf this option is given, <command>mosquitto_sub</command>\n\t\t\t\t\t\t\twill exit immediately that all of its subscriptions have\n\t\t\t\t\t\t\tbeen acknowledged by the broker. In conjunction with\n\t\t\t\t\t\t\t<option>-c</option> this allows a durable client session\n\t\t\t\t\t\t\tto be initialised on the broker for future use without\n\t\t\t\t\t\t\trequiring any messages to be received.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-format.xml\" />\n\t\t\t\t<xi:include href=\"common/option-help.xml\" />\n\t\t\t\t<xi:include href=\"common/option-host.xml\" />\n\t\t\t\t<xi:include href=\"common/option-clientid.xml\" />\n\t\t\t\t<xi:include href=\"common/option-clientid-prefix.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-insecure.xml\" />\n\t\t\t\t<xi:include href=\"common/option-keepalive.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-key.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-keyform.xml\" />\n\t\t\t\t<xi:include href=\"common/option-url.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>--message-rate</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t&from-version21;\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tInstead of printing the messages received, print a count\n\t\t\t\t\t\t\tof the messages received at one second intervals. Other\n\t\t\t\t\t\t\toptions related to output formatting are not valid when\n\t\t\t\t\t\t\tthis option is active.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-format-no-eol.xml\" />\n\t\t\t\t<xi:include href=\"common/option-nodelay.xml\" />\n\t\t\t\t<xi:include href=\"common/option-no-tls.xml\" />\n\t\t\t\t<xi:include href=\"common/option-config-file.xml\" />\n\t\t\t\t<xi:include href=\"common/option-port.xml\" />\n\t\t\t\t<xi:include href=\"common/option-password.xml\" />\n\t\t\t\t<xi:include href=\"common/option-format-pretty.xml\" />\n\t\t\t\t<xi:include href=\"common/option-proxy.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-psk.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-psk-identity.xml\" />\n\t\t\t\t<xi:include href=\"common/option-qos-incoming.xml\" />\n\t\t\t\t<xi:include href=\"common/option-quiet.xml\" />\n\t\t\t\t<xi:include href=\"common/option-hide-retain.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>--random-filter</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThis option can be used to reduce the proportion of\n\t\t\t\t\t\t\tmessages that mosquitto_sub prints. The default\n\t\t\t\t\t\t\tbehaviour is to print all incoming messages. Setting the\n\t\t\t\t\t\t\t<replaceable>chance</replaceable> to a floating point value\n\t\t\t\t\t\t\tbetween 0.1 and 100.0 will ensure that on average that\n\t\t\t\t\t\t\tpercentage of messages will be printed.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>--remove-retained</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf this argument is given, then when mosquitto_sub\n\t\t\t\t\t\t\treceives a message with the retained bit set, it will\n\t\t\t\t\t\t\tsend a message to the broker to clear that retained\n\t\t\t\t\t\t\tmessage. This applies to all received messages except\n\t\t\t\t\t\t\tthose that are filtered out by the <option>-T</option>\n\t\t\t\t\t\t\toption. This option still takes effect even if\n\t\t\t\t\t\t\t<option>-R</option> is used. See also the\n\t\t\t\t\t\t\t<option>--retain-as-published</option> and\n\t\t\t\t\t\t\t<option>--retained-only</option> options.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<example title=\"remove-retained-example-1\" label=\"1\">\n\t\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\t\tRemove all retained messages on the server,\n\t\t\t\t\t\t\t\tassuming we have access to do so, and then exit:\n\t\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t\t<programlisting language=\"config\">\n\tmosquitto_sub -t '#' --remove-retained --retained-only</programlisting>\n\t\t\t\t\t\t\t</example>\n\n\t\t\t\t\t\t\t<example title=\"remove-retained-example-2\" label=\"2\">\n\t\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\t\tRemove a whole tree, with the exception of a\n\t\t\t\t\t\t\t\tsingle topic:\n\t\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t\t<programlisting language=\"config\">\n\tmosquitto_sub -t 'bbc/#' -T bbc/bbc1 --remove-retained</programlisting>\n\t\t\t\t\t\t</example>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>--retained-only</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf this argument is given, only messages that are\n\t\t\t\t\t\t\treceived that have the retain bit set will be printed.\n\t\t\t\t\t\t\tMessages with retain set are \"stale\", in that it is not\n\t\t\t\t\t\t\tknown when they were originally published. With this\n\t\t\t\t\t\t\targument in use, the receipt of the first non-stale\n\t\t\t\t\t\t\tmessage will cause the client to exit. See also the\n\t\t\t\t\t\t\t<option>--retain-as-published</option> option.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>--retain-as-published</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf this argument is given, the subscriptions will\n\t\t\t\t\t\t\thave the \"retain as published\" option set. This means\n\t\t\t\t\t\t\tthat the retain flag on an incoming message will be\n\t\t\t\t\t\t\texactly as set by the publishing client, rather than\n\t\t\t\t\t\t\tindicating whether the message is fresh/stale.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThis option is not valid for MQTT v3.1/v3.1.1\n\t\t\t\t\t\t\tclients.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-retain-handling.xml\" />\n\t\t\t\t<xi:include href=\"common/option-srv.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-t</option></term>\n\t\t\t\t\t<term><option>--topic</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThe MQTT topic to subscribe to. See\n\t\t\t\t\t\t\t<citerefentry><refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle><manvolnum>7</manvolnum></citerefentry>\n\t\t\t\t\t\t\tfor more information on MQTT topics.</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThis option may be repeated to subscribe to multiple topics.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-T</option></term>\n\t\t\t\t\t<term><option>--filter-out</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tSuppress printing of topics that match the filter. This\n\t\t\t\t\t\t\tallows subscribing to a wildcard topic and only printing\n\t\t\t\t\t\t\ta partial set of the wildcard hierarchy.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tFor example, subscribe to the BBC tree, but suppress\n\t\t\t\t\t\t\toutput from Radio 3:\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<itemizedlist mark=\"circle\">\n\t\t\t\t\t\t\t<listitem><para>mosquitto_sub <literal>-t</literal>\n\t\t\t\t\t\t\t\t\tbbc/# <literal>-T</literal>\n\t\t\t\t\t\t\t\t\tbbc/radio3</para></listitem>\n\t\t\t\t\t\t</itemizedlist>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThis option may be repeated to filter out multiple\n\t\t\t\t\t\t\ttopics or topic trees.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-tls-alpn.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-engine.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-engine-kpass-sha1.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-keylog.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-use-os-certs.xml\" />\n\t\t\t\t<xi:include href=\"common/option-tls-version.xml\" />\n\t\t\t\t<xi:include href=\"common/option-username.xml\" />\n\t\t\t\t<xi:include href=\"common/option-unix-socket.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-U</option></term>\n\t\t\t\t\t<term><option>--unsubscribe</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tA topic that will be unsubscribed from. This may be used\n\t\t\t\t\t\t\ton its own or in conjunction with the <option>--topic</option>\n\t\t\t\t\t\t\toption and only makes sense when used in conjunction with\n\t\t\t\t\t\t\t<option>--clean-session</option>.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tIf used with <option>--topic</option> then subscriptions\n\t\t\t\t\t\t\twill be processed before unsubscriptions.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tNote that it is only possible to unsubscribe from\n\t\t\t\t\t\t\tsubscriptions that have previously been made. It is not\n\t\t\t\t\t\t\tpossible to punch holes in wildcard subscriptions. For\n\t\t\t\t\t\t\texample, subscribing to <option>sensors/#</option> and\n\t\t\t\t\t\t\tthen unsubscribing from\n\t\t\t\t\t\t\t<option>sensors/+/temperature</option> as shown below\n\t\t\t\t\t\t\twill still result in messages matching the\n\t\t\t\t\t\t\t<option>sensors/+/temperature</option> being delivered\n\t\t\t\t\t\t\tto the client.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<itemizedlist mark=\"circle\">\n\t\t\t\t\t\t\t<listitem><para>mosquitto_sub <literal>-t</literal> sensors/# <literal>-U</literal> sensors/+/temperature <literal>-v</literal></para></listitem>\n\t\t\t\t\t\t</itemizedlist>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tNote also that because retained messages are published\n\t\t\t\t\t\t\tby the broker on receipt of a SUBSCRIBE command,\n\t\t\t\t\t\t\tsubscribing and unsubscribing to the same topic may\n\t\t\t\t\t\t\tresult in messages being received at the client.\n\t\t\t\t\t\t</para>\n\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tThis option may be repeated to unsubscribe from multiple topics.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-format-verbose.xml\" />\n\t\t\t\t<xi:include href=\"common/option-protocol-version.xml\" />\n\t\t\t\t<xi:include href=\"common/option-timeout.xml\" />\n\t\t\t\t<varlistentry>\n\t\t\t\t\t<term><option>-w</option></term>\n\t\t\t\t\t<term><option>--watch</option></term>\n\t\t\t\t\t<listitem>\n\t\t\t\t\t\t&from-version21;\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tMessages will be printed on a fixed line number based on\n\t\t\t\t\t\t\tthe topic and order in which topics are received. Useful\n\t\t\t\t\t\t\tfor monitoring multiple topics that have single line\n\t\t\t\t\t\t\tpayloads. Unexpected behaviour will occur if there are\n\t\t\t\t\t\t\tmore topics than lines in the terminal, or if the\n\t\t\t\t\t\t\tpayload occupies more than a single line.\n\t\t\t\t\t\t\tThis can be used in conjuction with other output options\n\t\t\t\t\t\t\te.g. <option>-F</option>.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t\t<para>\n\t\t\t\t\t\t\tRequires ANSI escape code support in the terminal.\n\t\t\t\t\t\t</para>\n\t\t\t\t\t</listitem>\n\t\t\t\t</varlistentry>\n\t\t\t\t<xi:include href=\"common/option-will-payload.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-qos.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-retain.xml\" />\n\t\t\t\t<xi:include href=\"common/option-will-topic.xml\" />\n\t\t\t\t<xi:include href=\"common/option-websockets.xml\" />\n\t\t\t\t<xi:include href=\"common/option-session-expiry-interval.xml\" />\n\t\t\t</variablelist>\n\t\t</refsect2>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-output-format.xml\" />\n\t<xi:include href=\"common/section-wills.xml\" />\n\n\t<refsect1 id='properties'>\n\t\t<title>Properties</title>\n\t\t<para>\n\t\t\tThe <option>-D</option> / <option>--property</option> option\n\t\t\tallows adding properties to different stages of the mosquitto_sub\n\t\t\trun. The properties supported for each command are as follows:\n\t\t</para>\n\n\t\t<xi:include href=\"common/section-properties-connect.xml\" />\n\t\t<xi:include href=\"common/section-properties-subscribe.xml\" />\n\t\t<xi:include href=\"common/section-properties-unsubscribe.xml\" />\n\t\t<xi:include href=\"common/section-properties-disconnect.xml\" />\n\t\t<xi:include href=\"common/section-properties-will.xml\" />\n\t</refsect1>\n\n\t<xi:include href=\"common/section-exit-status.xml\" />\n\n\t<refsect1>\n\t\t<title>Examples</title>\n\t\t<para>\n\t\t\tNote that these really are examples - the subscriptions will work if\n\t\t\tyou run them as shown, but there must be something publishing\n\t\t\tmessages on those topics for you to receive anything.\n\t\t</para>\n\t\t<para>\n\t\t\tSubscribe to temperature information on localhost with QoS 1:\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_sub <literal>-t</literal> sensors/temperature <literal>-q</literal> 1</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>\n\t\t\tSubscribe to hard drive temperature updates on multiple\n\t\t\tmachines/hard drives. This expects each machine to be publishing its\n\t\t\thard drive temperature to\n\t\t\tsensors/machines/HOSTNAME/temperature/HD_NAME.\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_sub <literal>-t</literal> sensors/machines/+/temperature/+</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>\n\t\t\tSubscribe to all broker status messages:\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_sub <literal>-v</literal> <literal>-t</literal> \\$SYS/#</para></listitem>\n\t\t</itemizedlist>\n\n\t\t<para>\n\t\t\tSpecify the output format as \"ISO-8601 date : topic : payload in hex\"\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_sub <literal>-F '@Y-@m-@dT@H:@M:@S@z : %t : %x'</literal> <literal>-t</literal> '#'</para></listitem>\n\t\t</itemizedlist>\n\n\t\t<para>\n\t\t\tSpecify the output format as \"seconds since epoch.nanoseconds : retained flag : qos : mid : payload length\"\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_sub <literal>-F '%@s.@N : %r : %q : %m : %l'</literal> <literal>-q 2</literal> <literal>-t</literal> '#'</para></listitem>\n\t\t</itemizedlist>\n\n\t\t<para>\n\t\t\tTopic and payload output, but with colour where supported.\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>mosquitto_sub <literal>-F '\\e[92m%t \\e[96m%p\\e[0m'</literal> <literal>-q 2</literal> <literal>-t</literal> '#'</para></listitem>\n\t\t</itemizedlist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Files</title>\n\t\t<variablelist>\n\t\t\t<varlistentry>\n\t\t\t\t<term><filename>$XDG_CONFIG_HOME/mosquitto_sub</filename></term>\n\t\t\t\t<term><filename>$HOME/.config/mosquitto_sub</filename></term>\n\t\t\t\t<term><filename>$HOME/snap/mosquitto/current/.config/mosquitto_sub</filename> (for snap installs)</term>\n\t\t\t\t<listitem>\n\t\t\t\t\t<para>Configuration file for default options.</para>\n\t\t\t\t</listitem>\n\t\t\t</varlistentry>\n\t\t</variablelist>\n\t</refsect1>\n\n\t<xi:include href=\"common/section-bugs.xml\" />\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mqtt-7.html\">mqtt</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_pub-1.html\">mosquitto_pub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_rr-1.html\">mosquitto_rr</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"libmosquitto-3.html\">libmosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>3</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-tls-7.html\">mosquitto-tls</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "man/mqtt.7.meta",
    "content": ".. title: MQTT man page\n.. slug: mqtt-7\n.. category: man\n.. type: man\n.. pretty_url: False\n.. hide_title: True\n"
  },
  {
    "path": "man/mqtt.7.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<?xml-stylesheet type=\"text/xsl\" href=\"manpage.xsl\"?>\n\n<refentry xml:id=\"mqtt\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t<refmeta>\n\t\t<refentrytitle>mqtt</refentrytitle>\n\t\t<manvolnum>7</manvolnum>\n\t\t<refmiscinfo class=\"source\">Mosquitto Project</refmiscinfo>\n\t\t<refmiscinfo class=\"manual\">Conventions and miscellaneous</refmiscinfo>\n\t</refmeta>\n\n\t<refnamediv>\n\t\t<refname>mqtt</refname>\n\t\t<refpurpose>MQ Telemetry Transport</refpurpose>\n\t</refnamediv>\n\n\t<refsynopsisdiv>\n\t\t<cmdsynopsis>\n\t\t\t<command>MQTT</command>\n\t\t</cmdsynopsis>\n\t</refsynopsisdiv>\n\n\t<refsect1>\n\t\t<title>Description</title>\n\t\t<para>\n\t\t\t<command>MQTT</command> is a lightweight publish/subscribe\n\t\t\tmessaging protocol. It is useful for use with low power sensors, but\n\t\t\tis applicable to many scenarios.</para> <para>This manual describes\n\t\t\tsome of the features of MQTT version 3.1.1/3.1, to assist end users in\n\t\t\tgetting the most out of the protocol. For more complete information on\n\t\t\tMQTT, see <uri type=\"webpage\">https://mqtt.org/</uri>.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Publish/Subscribe</title>\n\t\t<para>\n\t\t\tThe MQTT protocol is based on the principle of publishing\n\t\t\tmessages and subscribing to topics, or \"pub/sub\". Multiple clients\n\t\t\tconnect to a broker and subscribe to topics that they are interested\n\t\t\tin. Clients also connect to the broker and publish messages to topics.\n\t\t\tMany clients may subscribe to the same topics and do with the\n\t\t\tinformation as they please. The broker and MQTT act as a simple, common\n\t\t\tinterface for everything to connect to. This means that you if you have\n\t\t\tclients that dump subscribed messages to a database, to Twitter,\n\t\t\tor even a simple text file, then it becomes very simple to add\n\t\t\tnew sensors or other data input to a database, Twitter or so on.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Topics/Subscriptions</title>\n\t\t<para>\n\t\t\tMessages in MQTT are published on topics. There is no need to\n\t\t\tconfigure a topic, publishing on it is enough. Topics are treated as a\n\t\t\thierarchy, using a slash (/) as a separator. This allows sensible\n\t\t\tarrangement of common themes to be created, much in the same way as a\n\t\t\tfilesystem. For example, multiple computers may all publish their\n\t\t\thard drive temperature information on the following topic, with their\n\t\t\town computer and hard drive name being replaced as appropriate:\n\t\t</para>\n\t\t<itemizedlist>\n\t\t\t<listitem><para>sensors/COMPUTER_NAME/temperature/HARDDRIVE_NAME</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>\n\t\t\tClients can receive messages by creating subscriptions. A\n\t\t\tsubscription may be to an explicit topic, in which case only messages\n\t\t\tto that topic will be received, or it may include wildcards. Two\n\t\t\twildcards are available, <option>+</option> or <option>#</option>.\n\t\t</para>\n\t\t<para>\n\t\t\t<option>+</option> can be used as a wildcard for a single level\n\t\t\tof hierarchy. It could be used with the topic above to get information\n\t\t\ton all computers and hard drives as follows:\n\t\t</para>\n\t\t<itemizedlist>\n\t\t\t<listitem><para>sensors/+/temperature/+</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>\n\t\t\tAs another example, for a topic of \"a/b/c/d\", the following\n\t\t\texample subscriptions will match:\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>a/b/c/d</para></listitem>\n\t\t\t<listitem><para>+/b/c/d</para></listitem>\n\t\t\t<listitem><para>a/+/c/d</para></listitem>\n\t\t\t<listitem><para>a/+/+/d</para></listitem>\n\t\t\t<listitem><para>+/+/+/+</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>\n\t\t\tThe following subscriptions will not match:\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>a/b/c</para></listitem>\n\t\t\t<listitem><para>b/+/c/d</para></listitem>\n\t\t\t<listitem><para>+/+/+</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>\n\t\t\t<option>#</option> can be used as a wildcard for all remaining levels of\n\t\t\thierarchy. This means that it must be the final character in a\n\t\t\tsubscription. With a topic of \"a/b/c/d\", the following example\n\t\t\tsubscriptions will match:\n\t\t</para>\n\t\t<itemizedlist mark=\"circle\">\n\t\t\t<listitem><para>a/b/c/d</para></listitem>\n\t\t\t<listitem><para>#</para></listitem>\n\t\t\t<listitem><para>a/#</para></listitem>\n\t\t\t<listitem><para>a/b/#</para></listitem>\n\t\t\t<listitem><para>a/b/c/#</para></listitem>\n\t\t\t<listitem><para>+/b/c/#</para></listitem>\n\t\t</itemizedlist>\n\t\t<para>\n\t\t\tZero length topic levels are valid, which can lead to some\n\t\t\tslightly non-obvious behaviour. For example, a topic of \"a//topic\"\n\t\t\twould correctly match against a subscription of \"a/+/topic\".\n\t\t\tLikewise, zero length topic levels can exist at both the beginning\n\t\t\tand the end of a topic string, so \"/a/topic\" would match against a\n\t\t\tsubscription of \"+/a/topic\", \"#\" or \"/#\", and a topic \"a/topic/\"\n\t\t\twould match against a subscription of \"a/topic/+\" or\n\t\t\t\"a/topic/#\".\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Quality of Service</title>\n\t\t<para>\n\t\t\tMQTT defines three levels of Quality of Service (QoS). The QoS\n\t\t\tdefines how hard the broker/client will try to ensure that a message is\n\t\t\treceived. Messages may be sent at any QoS level, and clients may\n\t\t\tattempt to subscribe to topics at any QoS level. This means that the\n\t\t\tclient chooses the maximum QoS it will receive. For example, if a\n\t\t\tmessage is published at QoS 2 and a client is subscribed with QoS 0,\n\t\t\tthe message will be delivered to that client with QoS 0. If a second\n\t\t\tclient is also subscribed to the same topic, but with QoS 2, then it\n\t\t\twill receive the same message but with QoS 2. For a second example, if\n\t\t\ta client is subscribed with QoS 2 and a message is published on QoS 0,\n\t\t\tthe client will receive it on QoS 0.\n\t\t</para>\n\t\t<para>\n\t\t\tHigher levels of QoS are more reliable, but involve higher\n\t\t\tlatency and have higher bandwidth requirements.\n\t\t</para>\n\t\t<itemizedlist>\n\t\t\t<listitem><para>0: The broker/client will deliver the message once, with no confirmation.</para></listitem>\n\t\t\t<listitem><para>1: The broker/client will deliver the message at least once, with confirmation required.</para></listitem>\n\t\t\t<listitem><para>2: The broker/client will deliver the message exactly once by using a four step handshake.</para></listitem>\n\t\t</itemizedlist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Retained Messages</title>\n\t\t<para>\n\t\t\tAll messages may be set to be retained. This means that the\n\t\t\tbroker will keep the message even after sending it to all current\n\t\t\tsubscribers. If a new subscription is made that matches the topic of\n\t\t\tthe retained message, then the message will be sent to the client. This\n\t\t\tis useful as a \"last known good\" mechanism. If a topic is only updated\n\t\t\tinfrequently, then without a retained message, a newly subscribed\n\t\t\tclient may have to wait a long time to receive an update. With a\n\t\t\tretained message, the client will receive an instant update.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Clean session / Durable connections</title>\n\t\t<para>\n\t\t\tOn connection, a client sets the \"clean session\" flag, which is\n\t\t\tsometimes also known as the \"clean start\" flag. If clean session is set\n\t\t\tto false, then the connection is treated as durable. This means that\n\t\t\twhen the client disconnects, any subscriptions it has will remain and\n\t\t\tany subsequent QoS 1 or 2 messages will be stored until it connects\n\t\t\tagain in the future. If clean session is true, then all subscriptions\n\t\t\twill be removed for the client when it disconnects.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Wills</title>\n\t\t<para>\n\t\t\tWhen a client connects to a broker, it may inform the broker that\n\t\t\tit has a will. This is a message that it wishes the broker to send when\n\t\t\tthe client disconnects unexpectedly. The will message has a topic,\n\t\t\tQoS and retain status just the same as any other message.\n\t\t</para>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>See Also</title>\n\t\t<simplelist type=\"inline\">\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-7.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>7</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto-8.html\">mosquitto</link></refentrytitle>\n\t\t\t\t\t<manvolnum>8</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_pub-1.html\">mosquitto_pub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<citerefentry>\n\t\t\t\t\t<refentrytitle><link xlink:href=\"mosquitto_sub-1.html\">mosquitto_sub</link></refentrytitle>\n\t\t\t\t\t<manvolnum>1</manvolnum>\n\t\t\t\t</citerefentry>\n\t\t\t</member>\n\t\t</simplelist>\n\t</refsect1>\n\n\t<refsect1>\n\t\t<title>Author</title>\n\t\t<para>Roger Light <email>roger@atchoo.org</email></para>\n\t</refsect1>\n</refentry>\n"
  },
  {
    "path": "misc/currentcost/cc128_log_mysql.pl",
    "content": "#!/usr/bin/perl\n\n# Log CurrentCost power meter data to a mysql database.\n# Assumes data is coming in on MQTT topic sensors/cc128\n# and in format timestamp,temperature,ch1_data\n# e.g. 1276605752,12.7,86\n\n# To create database, table and user:\n#\n# CREATE DATABASE powermeter;\n# USE 'powermeter';\n# CREATE TABLE powermeter (\n#   `id` INT NOT NULL auto_increment,\n#   `timestamp` INT NOT NULL,\n#   `temperature` FLOAT NOT NULL DEFAULT 0.0,\n#   `ch1` INT NOT NULL DEFAULT 0,\n#   PRIMARY KEY (`id`),\n#   UNIQUE KEY `timestamp` (`timestamp`)\n# ) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n#\n# CREATE USER 'powermeter'@'localhost' IDENTIFIED BY '<your password>';\n# GRANT ALL ON powermeter.* to 'powermeter'@'localhost';\n\nuse strict;\nuse DBI();\nuse FileHandle;\n\nlocal $| = 1;\n\nmy $dbname = \"powermeter\";\nmy $dbhost = \"localhost\";\nmy $dbusername = \"powermeter\";\nmy $dbpassword = \"<your password>\";\nmy $dbtable = \"powermeter\";\n\nmy $subclient = \"mosquitto_sub -t sensors/cc128\";\nopen(SUB, \"$subclient|\");\nSUB->autoflush(1);\n\nmy $dbh = DBI->connect(\"DBI:mysql:database=$dbname;host=$dbhost\",\n\t\t\"$dbusername\", \"$dbpassword\", {'RaiseError' => 1});\n\nmy $query = \"INSERT INTO powermeter (timestamp, temperature, ch1) VALUES (?,?,?)\";\n\nmy @vals;\nmy ($timestamp, $temperature, $ch1);\nwhile (my $line = <SUB>) {\n\t@vals = split(/,/, $line);\n\t$timestamp = @vals[0];\n\t$temperature = @vals[1];\n\t$ch1 = @vals[2];\n\t$dbh->do($query, undef, $timestamp, $temperature, $ch1);\n}\n$dbh->disconnect();\n\n"
  },
  {
    "path": "misc/currentcost/cc128_parse.pl",
    "content": "#!/usr/bin/perl -w\n\n# Read raw cc128 data and republish without xml.\n# Probably only works if you have a single channel.\n\nuse strict;\nuse HTTP::Date \"str2time\";\nuse FileHandle;\n\nlocal $| = 1;\n\nmy $subclient = \"mosquitto_sub -t sensors/cc128/raw -q 1\";\nmy $pubclient = \"mosquitto_pub -t sensors/cc128 -q 1 -l\";\nmy $pubclient_ch1 = \"mosquitto_pub -t sensors/cc128/ch1 -q 1 -l\";\n\nopen(SUB, \"$subclient|\");\nopen(PUB, \"|$pubclient\");\nopen(PUB_CH1, \"|$pubclient_ch1\");\n\nSUB->autoflush(1);\nPUB->autoflush(1);\nPUB_CH1->autoflush(1);\n\nwhile (my $line = <SUB>) {\n\t#<msg><src>CC128-v0.12</src><dsb>00002</dsb><time>00:02:12</time><tmpr>15.7</tmpr><sensor>0</sensor><id>03112</id><type>1</type><ch1><watts>00108</watts></ch1></msg>\n\tif ($line =~ m#<time>(.*)</time><tmpr> *([\\-\\d.]+)</tmpr><sensor>0</sensor><id>[0-9]*</id><type>1</type><ch1><watts>0*(\\d+)</watts></ch1></msg.*#){\n\t\tmy $reading_time = $1;\n\t\tmy $temp = $2;\n\t\tmy $watts = $3;\n\n\t\tmy $now = time;\n\t\tmy ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst,$r_stamp);\n\n\t\t($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = localtime($now);\n\t\t$year += 1900;\n\t\t$month += 1;\n\t\t$r_stamp = str2time(\"$year-$month-$mday $reading_time\");\n\t\tif($r_stamp > $now){\n\t\t\t$r_stamp -= 86400;\n\t\t}\n\n\t\tprint PUB \"$r_stamp,$temp,$watts\\n\";\n\t\tprint PUB_CH1 \"$r_stamp $watts\\n\";\n\t}\n}\n"
  },
  {
    "path": "misc/currentcost/cc128_read.pl",
    "content": "#!/usr/bin/perl -w\n\n# Reads data from a Current Cost device via serial port.\n# Spawns\n\nuse strict;\nuse Device::SerialPort qw( :PARAM :STAT 0.07 );\n\nmy $pubclient = \"mosquitto_pub -t sensors/cc128/raw -q 1 -l\";\nmy $PORT = \"/dev/ttyUSB0\";\nlocal $| = 1;\n\nmy $ob = Device::SerialPort->new($PORT);\n$ob->baudrate(57600);\n$ob->write_settings;\n\nopen(SERIAL, \"+<$PORT\");\nopen(MQTT, \"|$pubclient\");\nwhile (my $line = <SERIAL>) {\n\tprint(MQTT \"$line\");\n}\n\nclose(MQTT);\n"
  },
  {
    "path": "misc/currentcost/cc128_read.py",
    "content": "#!/usr/bin/python -u\n\nimport mosquitto\nimport serial\n\nusb = serial.Serial(port='/dev/ttyUSB0', baudrate=57600)\n\nmosq = mosquitto.Mosquitto()\nmosq.connect(\"localhost\")\nmosq.loop_start()\n\nrunning = True\ntry:\n    while running:\n        line = usb.readline()\n        mosq.publish(\"sensors/cc128/raw\", line)\nexcept usb.SerialException, e:\n    running = False\n\nmosq.disconnect()\nmosq.loop_stop()\n\n"
  },
  {
    "path": "misc/currentcost/gnome-panel/CurrentCostMQTT.py",
    "content": "#!/usr/bin/env python\n\nimport gnomeapplet\nimport gtk\nimport mosquitto\nimport sys\n\nclass CurrentCostMQTT(gnomeapplet.Applet):\n    def on_message(self, mosq, obj, msg):\n        # Message format is \"power\"\n        self.label.set_text(msg.payload+\"W\")\n\n    def set_label(self, val):\n        self.label.set_text(val)\n\n    def on_change_background(self, applet, type, color, pixmap):\n        applet.set_style(None)\n        applet.modify_style(gtk.RcStyle())\n\n        if type == gnomeapplet.COLOR_BACKGROUND:\n            applet.modify_bg(gtk.STATE_NORMAL, color)\n        elif type == gnomeapplet.PIXMAP_BACKGROUND:\n            style = applet.get_style().copy()\n            style.bg_pixmap[gtk.STATE_NORMAL] = pixmap\n            applet.set_style(style)\n\n    def show_menu(self, widget, event):\n        print \"menu\"\n\n    def __init__(self, applet, iid):\n        self.applet = applet\n        self.label = gtk.Label(\"0W\")\n        self.event_box = gtk.EventBox()\n        self.event_box.add(self.label)\n        self.event_box.set_events(gtk.gdk.BUTTON_PRESS_MASK)\n        self.event_box.connect(\"button_press_event\", self.show_menu)\n        self.applet.add(self.event_box)\n        self.applet.set_background_widget(applet)\n        self.applet.show_all()\n        self.mosq = mosquitto.Mosquitto()\n        self.mosq.on_message = self.on_message\n        self.mosq.connect(\"localhost\")\n        self.mosq.loop_start()\n        self.mosq.subscribe(\"sensors/cc128/ch1\", 0)\n        self.applet.connect('change-background', self.on_change_background)\n\ndef CurrentCostMQTT_factory(applet, iid):\n    CurrentCostMQTT(applet, iid)\n    return gtk.TRUE\n\nif len(sys.argv) == 2:\n    if sys.argv[1] == \"-d\": #Debug mode\n        main_window = gtk.Window(gtk.WINDOW_TOPLEVEL)\n        main_window.set_title(\"Python Applet\")\n        main_window.connect(\"destroy\", gtk.main_quit)\n        app = gnomeapplet.Applet()\n        CurrentCostMQTT_factory(app,None)\n        app.reparent(main_window)\n        main_window.show_all()\n        gtk.main()\n        sys.exit()\n\nif __name__ == '__main__':\n    gnomeapplet.bonobo_factory(\"OAFIID:CurrentCostMQTT_Factory\", gnomeapplet.Applet.__gtype__, \"MQTT\", \"0\", CurrentCostMQTT_factory)\n\n"
  },
  {
    "path": "misc/currentcost/gnome-panel/CurrentCostMQTT.server",
    "content": "<oaf_info>\n\t<oaf_server iid=\"OAFIID:CurrentCostMQTT_Factory\"\n\t\t    type=\"exe\"\n\t\t    location=\"CurrentCostMQTT.py\">\n\n\t\t<oaf_attribute name=\"repo_ids\" type=\"stringv\">\n\t\t\t<item value=\"IDL:Bonobo/GenericFactory:1.0\"/>\n\t\t\t<item value=\"IDL:Bonobo/Unknown:1.0\"/>\n\t\t</oaf_attribute>\n\t\t<oaf_attribute name=\"name\" type=\"string\" value=\"CurrentCost MQTT display\"/>\n\t\t<oaf_attribute name=\"description\" type=\"string\" value=\"Display energy usage published over MQTT\"/>\n\t</oaf_server>\n\n\t<oaf_server iid=\"OAFIID:CurrentCostMQTT\"\n\t\t    type=\"factory\"\n\t\t    location=\"OAFIID:CurrentCostMQTT_Factory\">\n\n\t\t<oaf_attribute name=\"repo_ids\" type=\"stringv\">\n\t\t\t<item value=\"IDL:GNOME/Vertigo/PanelAppletShell:1.0\"/>\n\t\t\t<item value=\"IDL:Bonobo/Control:1.0\"/>\n\t\t\t<item value=\"IDL:Bonobo/Unknown:1.0\"/>\n\t\t</oaf_attribute>\n\t\t<oaf_attribute name=\"name\" type=\"string\" value=\"CurrentCost MQTT display\"/>\n\t\t<oaf_attribute name=\"description\" type=\"string\" value=\"Display energy usage published over MQTT\"/>\n\t\t<oaf_attribute name=\"panel:category\" type=\"string\" value=\"Utility\"/>\n\t\t<oaf_attribute name=\"panel:icon\" type=\"string\" value=\"currentcost.png\"/>\n\t</oaf_server>\n</oaf_info>\n"
  },
  {
    "path": "misc/letsencrypt/mosquitto-copy.sh",
    "content": "#!/bin/sh\n\n# This is an example deploy renewal hook for certbot that copies newly updated\n# certificates to the Mosquitto certificates directory and sets the ownership\n# and permissions so only the mosquitto user can access them, then signals\n# Mosquitto to reload certificates.\n\n# RENEWED_DOMAINS will match the domains being renewed for that certificate, so\n# may be just \"example.com\", or multiple domains \"www.example.com example.com\"\n# depending on your certificate.\n\n# Place this script in /etc/letsencrypt/renewal-hooks/deploy/ and make it\n# executable after editing it to your needs.\n\n# Set which domain this script will be run for\nMY_DOMAIN=example.com\n# Set the directory that the certificates will be copied to.\nCERTIFICATE_DIR=/etc/mosquitto/certs\n\nfor D in ${RENEWED_DOMAINS}; do\n\tif [ \"${D}\" = \"${MY_DOMAIN}\" ]; then\n\t\t# Copy new certificate to Mosquitto directory\n\t\tcp ${RENEWED_LINEAGE}/fullchain.pem ${CERTIFICATE_DIR}/server.pem\n\t\tcp ${RENEWED_LINEAGE}/privkey.pem ${CERTIFICATE_DIR}/server.key\n\n\t\t# Set ownership to Mosquitto\n\t\tchown mosquitto: ${CERTIFICATE_DIR}/server.pem ${CERTIFICATE_DIR}/server.key\n\n\t\t# Ensure permissions are restrictive\n\t\tchmod 0600 ${CERTIFICATE_DIR}/server.pem ${CERTIFICATE_DIR}/server.key\n\n\t\t# Tell Mosquitto to reload certificates and configuration\n\t\tpkill -HUP -x mosquitto\n\tfi\ndone\n"
  },
  {
    "path": "mosquitto.conf",
    "content": "# Config file for mosquitto\n#\n# See mosquitto.conf(5) for more information.\n#\n# Default values are shown, uncomment to change.\n#\n# Use the # character to indicate a comment, but only if it is the\n# very first character on the line.\n\n# =================================================================\n# General configuration\n# =================================================================\n\n# Use per listener security settings.\n#\n# It is recommended this option be set before any other options.\n#\n# If this option is set to true, then all authentication and access control\n# options are controlled on a per listener basis. The following options are\n# affected:\n#\n# acl_file\n# allow_anonymous - use listener_allow_anonymous instead\n# allow_zero_length_clientid\n# auto_id_prefix - use listener_auto_id_prefix instead\n# password_file\n# plugin - use plugin_load and plugin_use instead\n# plugin_opt_*\n# psk_file\n#\n# Note that if set to true, then a durable client (i.e. with clean session set\n# to false) that has disconnected will use the ACL settings defined for the\n# listener that it was most recently connected to.\n#\n# The default behaviour is for this to be set to false, which maintains the\n# setting behaviour from previous versions of mosquitto.\n#per_listener_settings false\n\n\n# This option controls whether a client is allowed to connect with a zero\n# length client id or not. This option only affects clients using MQTT v3.1.1\n# and later. If set to false, clients connecting with a zero length client id\n# are disconnected. If set to true, clients will be allocated a client id by\n# the broker. This means it is only useful for clients with clean session set\n# to true.\n#allow_zero_length_clientid true\n\n# If allow_zero_length_clientid is true, this option allows you to set a prefix\n# to automatically generated client ids to aid visibility in logs.\n# Defaults to 'auto-'\n#auto_id_prefix auto-\n\n# This option affects the scenario when a client subscribes to a topic that has\n# retained messages. It is possible that the client that published the retained\n# message to the topic had access at the time they published, but that access\n# has been subsequently removed. If check_retain_source is set to true, the\n# default, the source of a retained message will be checked for access rights\n# before it is republished. When set to false, no check will be made and the\n# retained message will always be published. This affects all listeners.\n#check_retain_source true\n\n# The maximum number of client sessions to allow across the whole broker. In\n# this context a client session means either a client currently connected via\n# the network, or a client that has clean_session = False (MQTT v3.x) and is\n# disconnected, or has disconnected and still hasn't exceeded its session\n# expiry interval (MQTT v5).\n#\n# See also the global_max_connections setting, and the max_connections setting,\n# which applies to listeners. If you set global_max_clients to 1000 and\n# max_connections on a listener to 10, then that means only 10 simultaneous\n# connections will be allowed at once, with an overall maximum of 1000 client\n# sessions.\n#\n#global_max_clients -1\n\n# The maximum number of currently connected clients to allow across the whole\n# broker.\n# See also the global_max_clients and max_connections settings.\n#global_max_connections -1\n\n# QoS 1 and 2 messages will be allowed inflight per client until this limit\n# is exceeded.  Defaults to 0. (No maximum)\n# See also max_inflight_messages\n#max_inflight_bytes 0\n\n# The maximum number of QoS 1 and 2 messages currently inflight per\n# client.\n# This includes messages that are partway through handshakes and\n# those that are being retried. Defaults to 20. Set to 0 for no\n# maximum. Setting to 1 will guarantee in-order delivery of QoS 1\n# and 2 messages.\n#max_inflight_messages 20\n\n# For MQTT v5 clients, it is possible to have the server send a \"server\n# keepalive\" value that will override the keepalive value set by the client.\n# This is intended to be used as a mechanism to say that the server will\n# disconnect the client earlier than it anticipated, and that the client should\n# use the new keepalive value. The max_keepalive option allows you to specify\n# that clients may only connect with keepalive less than or equal to this\n# value, otherwise they will be sent a server keepalive telling them to use\n# max_keepalive. This only applies to MQTT v5 clients. The maximum value is\n# 65535. Set to 0 to allow infinite keepalive. Defaults to 0.\n#\n# Set to 0 to allow clients to set keepalive = 0, which means no keepalive\n# checks are made and the client will never be disconnected by the broker if no\n# messages are received. You should be very sure this is the behaviour that you\n# want.\n#\n# For MQTT v3.1.1 and v3.1 clients, there is no mechanism to tell the client\n# what keepalive value they should use. If an MQTT v3.1.1 or v3.1 client\n# specifies a keepalive time greater than max_keepalive they will be sent a\n# CONNACK message with the \"identifier rejected\" reason code, and disconnected.\n#\n#max_keepalive 0\n\n# For MQTT v5 clients, it is possible to have the server send a \"maximum packet\n# size\" value that will instruct the client it will not accept MQTT packets\n# with size greater than max_packet_size bytes. This applies to the full MQTT\n# packet, not just the payload. Setting this option to a positive value will\n# set the maximum packet size to that number of bytes. If a client sends a\n# packet which is larger than this value, it will be disconnected. This applies\n# to all clients regardless of the protocol version they are using, but v3.1.1\n# and earlier clients will of course not have received the maximum packet size\n# information. Defaults to no limit. Setting below 20 bytes is forbidden\n# because it is likely to interfere with ordinary client operation, even with\n# very small payloads.\n#max_packet_size 0\n\n# QoS 1 and 2 messages above those currently in-flight will be queued per\n# client until this limit is exceeded.  Defaults to 0. (No maximum)\n# See also max_queued_messages.\n# If both max_queued_messages and max_queued_bytes are specified, packets will\n# be queued until the first limit is reached.\n#max_queued_bytes 0\n\n# Set the maximum QoS supported. Clients publishing at a QoS higher than\n# specified here will be disconnected.\n#max_qos 2\n\n# The maximum number of QoS 1 and 2 messages to hold in a queue per client\n# above those that are currently in-flight.  Defaults to 1000. Set\n# to 0 for no maximum (not recommended).\n# See also queue_qos0_messages.\n# See also max_queued_bytes.\n#max_queued_messages 1000\n#\n# This option sets the maximum number of heap memory bytes that the broker will\n# allocate, and hence sets a hard limit on memory use by the broker.  Memory\n# requests that exceed this value will be denied. The effect will vary\n# depending on what has been denied. If an incoming message is being processed,\n# then the message will be dropped and the publishing client will be\n# disconnected. If an outgoing message is being sent, then the individual\n# message will be dropped and the receiving client will be disconnected.\n# Defaults to no limit.\n#memory_limit 0\n\n# This option sets the maximum publish payload size that the broker will allow.\n# Received messages that exceed this size will not be accepted by the broker.\n# The default value is 0, which means that all valid MQTT messages are\n# accepted. MQTT imposes a maximum payload size of 268435455 bytes.\n#message_size_limit 0\n\n# This option allows the session of persistent clients (those with clean\n# session set to false) that are not currently connected to be removed if they\n# do not reconnect within a certain time frame. This is a non-standard option\n# in MQTT v3.1. MQTT v3.1.1 and v5.0 allow brokers to remove client sessions.\n#\n# Badly designed clients may set clean session to false whilst using a randomly\n# generated client id. This leads to persistent clients that connect once and\n# never reconnect. This option allows these clients to be removed.  This option\n# allows persistent clients (those with clean session set to false) to be\n# removed if they do not reconnect within a certain time frame.\n#\n# The expiration period should be an integer followed by one of h d w m y for\n# hour, day, week, month and year respectively. For example\n#\n# persistent_client_expiration 2m\n# persistent_client_expiration 14d\n# persistent_client_expiration 1y\n#\n# The default if not set is to never expire persistent clients.\n#persistent_client_expiration\n\n# Write process id to a file. Default is a blank string which means\n# a pid file shouldn't be written.\n# This should be set to /var/run/mosquitto/mosquitto.pid if mosquitto is\n# being run automatically on boot with an init script and\n# start-stop-daemon or similar.\n#pid_file\n\n# Set to true to queue messages with QoS 0 when a persistent client is\n# disconnected. These messages are included in the limit imposed by\n# max_queued_messages and max_queued_bytes\n# Defaults to false.\n# This is a non-standard option for the MQTT v3.1 spec but is allowed in\n# v3.1.1.\n#queue_qos0_messages false\n\n# Set to false to disable retained message support. If a client publishes a\n# message with the retain bit set, it will be disconnected if this is set to\n# false.\n#retain_available true\n\n# Disable Nagle's algorithm on client sockets. This has the effect of reducing\n# latency of individual messages at the potential cost of increasing the number\n# of packets being sent.\n#set_tcp_nodelay false\n\n# Time in seconds between updates of the $SYS tree.\n# Set to 0 to disable the publishing of the $SYS tree.\n#sys_interval 10\n\n# The MQTT specification requires that the QoS of a message delivered to a\n# subscriber is never upgraded to match the QoS of the subscription. Enabling\n# this option changes this behaviour. If upgrade_outgoing_qos is set true,\n# messages sent to a subscriber will always match the QoS of its subscription.\n# This is a non-standard option explicitly disallowed by the spec.\n#upgrade_outgoing_qos false\n\n# When run as root, drop privileges to this user and its primary\n# group.\n# Set to root to stay as root, but this is not recommended.\n# If set to \"mosquitto\", or left unset, and the \"mosquitto\" user does not exist\n# then it will drop privileges to the \"nobody\" user instead.\n# If run as a non-root user, this setting has no effect.\n# Note that on Windows this has no effect and so mosquitto should be started by\n# the user you wish it to run as.\n#user mosquitto\n\n# =================================================================\n# Listeners\n# =================================================================\n\n# Listen on a port/ip address combination. By using this variable\n# multiple times, mosquitto can listen on more than one port. If\n# this variable is used and neither bind_address nor port given,\n# then the default listener will not be started.\n# The port number to listen on must be given. Optionally, an ip\n# address or host name may be supplied as a second argument. In\n# this case, mosquitto will attempt to bind the listener to that\n# address and so restrict access to the associated network and\n# interface. By default, mosquitto will listen on all interfaces.\n# Note that for a websockets listener it is not possible to bind to a host\n# name.\n#\n# On systems that support Unix Domain Sockets, it is also possible\n# to create a # Unix socket rather than opening a TCP socket. In\n# this case, the port number should be set to 0 and a unix socket\n# path must be provided, e.g.\n# listener 0 /tmp/mosquitto.sock\n#\n# listener port-number [ip address/host name/unix socket path]\n#listener\n\n# By default, a listener will attempt to listen on all supported IP protocol\n# versions. If you do not have an IPv4 or IPv6 interface you may wish to\n# disable support for either of those protocol versions. In particular, note\n# that due to the limitations of the websockets library, it will only ever\n# attempt to open IPv6 sockets if IPv6 support is compiled in, and so will fail\n# if IPv6 is not available.\n#\n# Set to `ipv4` to force the listener to only use IPv4, or set to `ipv6` to\n# force the listener to only use IPv6. If you want support for both IPv4 and\n# IPv6, then do not use the socket_domain option.\n#\n#socket_domain\n\n# Bind the listener to a specific interface. This is similar to\n# the [ip address/host name] part of the listener definition, but is useful\n# when an interface has multiple addresses or the address may change. If used\n# with the [ip address/host name] part of the listener definition, then the\n# bind_interface option will take priority.\n# Not available on Windows.\n#\n# Example: bind_interface eth0\n#bind_interface\n\n# When a listener is using the websockets protocol, it is possible to serve\n# http data as well. Set http_dir to a directory which contains the files you\n# wish to serve. If this option is not specified, then no normal http\n# connections will be possible.\n#http_dir\n\n# The maximum number of client connections to allow. This is\n# a per listener setting. Use global_max_clients if you wish to enforce a\n# client limit across the whole broker.\n# Default is -1, which means unlimited connections, unless otherwise limited by\n# global_max_clients.\n# Note that other process limits mean that unlimited connections\n# are not really possible. Typically the default maximum number of\n# connections possible is around 1024.\n#max_connections -1\n\n# The listener can be restricted to operating within a topic hierarchy using\n# the mount_point option. This is achieved be prefixing the mount_point string\n# to all topics for any clients connected to this listener. This prefixing only\n# happens internally to the broker; the client will not see the prefix.\n#mount_point\n\n# Choose the protocol to use when listening.\n# This can be either mqtt, websockets, or http_api.\n#\n# mqtt: A standard MQTT based listener\n# websockets: MQTT tunnelled over WebSockets. If the legacy websockets support\n#             using libwebsockets is used, then only a reduced TLS feature set\n#             is available.\n# http_api: This starts the listener as a very simple webserver that can also\n#           serve some HTTP API requests. TLS is supported for this listener,\n#           however only the certfile and keyfile options are allowed.\n#           Authentication is not currently possible - to use this listener it\n#           is strongly recommended to bind the listener to the loopback\n#           interface and then configure a reverse proxy with the appropriate\n#           encryption and authentication.\n#protocol mqtt\n\n# Accepted protocol versions. This sets what versions of the MQTT protocol will\n# be accepted on this listener. Can be any combination of 3, 4, 5 in a comma\n# separated list, e.g.\n#\n# # Allow v5.0 only:\n# listener 1883\n# accept_protocol_versions 5\n#\n# # Allow v3.1 and v3.1.1:\n# listener 1884\n# accept_protocol_versions 3, 4\n#\n# Defaults to allowing all versions.\n#accept_protocol_versions 3,4,5\n\n# If you wish to allow unauthenticated connections for a specific listener, use\n# the listener_allow_anonymous option set to true. If not set, the value\n# determined by the global allow_anonymous option will be used. If\n# listener_allow_anonymous is set at the same time as allow_anonymous, the\n# value set by listener_allow_anonymous will always take priority.\n#listener_allow_anonymous\n\n# This option allows you to set a prefix to automatically generated client ids\n# (i.e. for when a client connects without providing a client id) to aid\n# visibility in logs.\n# Defaults to 'auto-'\n#listener_auto_id_prefix auto-\n\n# Set use_username_as_clientid to true to replace the clientid that a client\n# connected with with its username. This allows authentication to be tied to\n# the clientid, which means that it is possible to prevent one client\n# disconnecting another by using the same clientid.\n# If a client connects with no username it will be disconnected as not\n# authorised when this option is set to true.\n# Do not use in conjunction with clientid_prefixes.\n# See also use_identity_as_username.\n# This does not apply globally, but on a per-listener basis.\n#use_username_as_clientid\n\n# Change the size of the buffer used when reading from the network before the\n# size of the MQTT packet is known. Defaults to 4096. Packets received that are\n# smaller than this value in principle only need a single read() call, making\n# reading packets more efficient. \n#\n# This also operates as the option that sets the size of the buffer used by\n# websockets when reading the initial header.  If you are passing large\n# header data such as cookies then you may need to increase this value.\n#packet_buffer_size\n\n# Enforce origin checking for websockets connections. This should be set to the\n# string of the host that you wish to allow connections from, as set as the\n# Origin header in the http request.\n# For example: https://example.com:8080\n# If left unset will allow connections from any origin.\n# May be set multiple times.\n#websockets_origin\n\n# -----------------------------------------------------------------\n# Certificate based SSL/TLS support\n# -----------------------------------------------------------------\n# The following options can be used to enable certificate based SSL/TLS support\n# for this listener. Note that the recommended port for MQTT over TLS is 8883,\n# but this must be set manually.\n#\n# See also the mosquitto-tls man page and the \"Pre-shared-key based SSL/TLS\n# support\" section. Only one of certificate or PSK encryption support can be\n# enabled for any listener.\n\n# Both of certfile and keyfile must be defined to enable certificate based\n# TLS encryption.\n\n# Path to the PEM encoded server certificate.\n#certfile\n\n# Path to the PEM encoded keyfile.\n#keyfile\n\n# Configure the minimum version of the TLS protocol to be used for this listener.\n# Possible values are tlsv1.3 and tlsv1.2.\n#tls_version tlsv1.2\n\n# If you wish to control which encryption ciphers are used, use the ciphers\n# option. The list of available ciphers can be obtained using the \"openssl\n# ciphers\" command and should be provided in the same format as the output of\n# that command. This applies to TLS 1.2 and earlier versions only. Use\n# ciphers_tls1.3 for TLS v1.3.\n#ciphers\n\n# Choose which TLS v1.3 ciphersuites are used for this listener.\n# Defaults to \"TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256\"\n#ciphers_tls1.3\n\n# If you have require_certificate set to true, you can create a certificate\n# revocation list file to revoke access to particular client certificates. If\n# you have done this, use crlfile to point to the PEM encoded revocation file.\n#crlfile\n\n# To allow the use of ephemeral DH key exchange, which provides forward\n# security, the listener must load DH parameters. This can be specified with\n# the dhparamfile option. The dhparamfile can be generated with the command\n# e.g. \"openssl dhparam -out dhparam.pem 2048\"\n#dhparamfile\n\n# By default an TLS enabled listener will operate in a similar fashion to a\n# https enabled web server, in that the server has a certificate signed by a CA\n# and the client will verify that it is a trusted certificate. The overall aim\n# is encryption of the network traffic. By setting require_certificate to true,\n# the client must provide a valid certificate in order for the network\n# connection to proceed. This allows access to the broker to be controlled\n# outside of the mechanisms provided by MQTT.\n#require_certificate false\n\n# If set true, disable_client_cert_date_checks will change the certificate\n# verification behaviour to allow client certificates that are expired or are\n# not yet valid, when using `require_certificate true`.\n#disable_client_cert_date_checks false\n\n# cafile and capath define methods of accessing the PEM encoded\n# Certificate Authority certificates that will be considered trusted when\n# checking incoming client certificates.\n# cafile defines the path to a file containing the CA certificates.\n# capath defines a directory that will be searched for files\n# containing the CA certificates. For capath to work correctly, the\n# certificate files must have \".crt\" as the file ending and you must run\n# \"openssl rehash <path to capath>\" each time you add/remove a certificate.\n# capath is not supported for websockets.\n#cafile\n#capath\n\n\n# If require_certificate is true, you may set use_identity_as_username to true\n# to use the CN value from the client certificate as a username. If this is\n# true, the password_file option will not be used for this listener.\n#use_identity_as_username false\n\n# -----------------------------------------------------------------\n# Pre-shared-key based SSL/TLS support\n# -----------------------------------------------------------------\n# The following options can be used to enable PSK based SSL/TLS support for\n# this listener. Note that the recommended port for MQTT over TLS is 8883, but\n# this must be set manually.\n#\n# See also the mosquitto-tls man page and the \"Certificate based SSL/TLS\n# support\" section. Only one of certificate or PSK encryption support can be\n# enabled for any listener.\n\n# The psk_hint option enables pre-shared-key support for this listener and also\n# acts as an identifier for this listener. The hint is sent to clients and may\n# be used locally to aid authentication. The hint is a free form string that\n# doesn't have much meaning in itself, so feel free to be creative.\n# If this option is provided, see psk_file to define the pre-shared keys to be\n# used or create a security plugin to handle them.\n#psk_hint\n\n# When using PSK, the encryption ciphers used will be chosen from the list of\n# available PSK ciphers. If you want to control which ciphers are available,\n# use the \"ciphers\" option.  The list of available ciphers can be obtained\n# using the \"openssl ciphers\" command and should be provided in the same format\n# as the output of that command.\n#ciphers\n\n# Set use_identity_as_username to have the psk identity sent by the client used\n# as its username. Authentication will be carried out using the PSK rather than\n# the MQTT username/password and so password_file will not be used for this\n# listener.\n#use_identity_as_username false\n\n\n# =================================================================\n# Persistence\n# =================================================================\n\n# If persistence is enabled, save the in-memory database to disk\n# every autosave_interval seconds. If set to 0, the persistence\n# database will only be written when mosquitto exits. See also\n# autosave_on_changes.\n# Note that writing of the persistence database can be forced by\n# sending mosquitto a SIGUSR1 signal.\n#autosave_interval 1800\n\n# If true, mosquitto will count the number of subscription changes, retained\n# messages received and queued messages and if the total exceeds\n# autosave_interval then the in-memory database will be saved to disk.\n# If false, mosquitto will save the in-memory database to disk by treating\n# autosave_interval as a time in seconds.\n#autosave_on_changes false\n\n# Save persistent message data to disk (true/false).\n# This saves information about all messages, including\n# subscriptions, currently in-flight messages and retained\n# messages.\n# retained_persistence is a synonym for this option.\n#persistence false\n\n# The filename to use for the persistent database, not including\n# the path.\n#persistence_file mosquitto.db\n\n# Location for persistent database.\n# Default is an empty string (current directory).\n# Set to e.g. /var/lib/mosquitto if running as a proper service on Linux or\n# similar.\n#\n# Can also be defined by setting the MOSQUITTO_PERSISTENCE_LOCATION environment\n# variable prior to running the broker. If both the config file option and the\n# environment variable are set, the environment variable takes precedent.\n#\n# This can also be used by plugins using the mosquitto_persistence_location() function.\n#persistence_location\n\n\n# =================================================================\n# Logging\n# =================================================================\n\n# Places to log to. Use multiple log_dest lines for multiple\n# logging destinations.\n# Possible destinations are: stdout stderr syslog topic file dlt\n#\n# stdout and stderr log to the console on the named output.\n#\n# syslog uses the userspace syslog facility which usually ends up\n# in /var/log/messages or similar.\n#\n# topic logs to the broker topic '$SYS/broker/log/<severity>',\n# where severity is one of D, E, W, N, I, M which are debug, error,\n# warning, notice, information and message. Message type severity is used by\n# the subscribe/unsubscribe log_types and publishes log messages to\n# $SYS/broker/log/M/susbcribe or $SYS/broker/log/M/unsubscribe.\n#\n# The file destination requires an additional parameter which is the file to be\n# logged to, e.g. \"log_dest file /var/log/mosquitto.log\". The file will be\n# closed and reopened when the broker receives a HUP signal. Only a single file\n# destination may be configured.\n#\n# The dlt destination is for the automotive `Diagnostic Log and Trace` tool.\n# This requires that Mosquitto has been compiled with DLT support.\n#\n# The android destination is for logging into the Android logd logging daemon.\n# This options is only available when building for the Android target.\n#\n# Note that if the broker is running as a Windows service it will default to\n# \"log_dest none\" and neither stdout nor stderr logging is available.\n# Use \"log_dest none\" if you wish to disable logging.\n#log_dest stderr\n\n# Types of messages to log. Use multiple log_type lines for logging\n# multiple types of messages.\n# Possible types are: debug, error, warning, notice, information,\n# none, subscribe, unsubscribe, websockets, all.\n# Note that debug type messages are for decoding the incoming/outgoing\n# network packets. They are not logged in \"topics\".\n#log_type error\n#log_type warning\n#log_type notice\n#log_type information\n\n\n# If set to true, client connection and disconnection messages will be included\n# in the log.\n#connection_messages true\n\n# If using syslog logging (not on Windows), messages will be logged to the\n# \"daemon\" facility by default. Use the log_facility option to choose which of\n# local0 to local7 to log to instead. The option value should be an integer\n# value, e.g. \"log_facility 5\" to use local5.\n#log_facility\n\n# If set to true, add a timestamp value to each log message.\n#log_timestamp true\n\n# Set the format of the log timestamp. If left unset, this is the number of\n# seconds since the Unix epoch.\n# This is a free text string which will be passed to the strftime function. To\n# get an ISO 8601 datetime, for example:\n# log_timestamp_format %Y-%m-%dT%H:%M:%S\n#log_timestamp_format\n\n# Change the websockets logging level. This is a global option, it is not\n# possible to set per listener. This is an integer that is interpreted by\n# libwebsockets as a bit mask for its lws_log_levels enum. See the\n# libwebsockets documentation for more details. \"log_type websockets\" must also\n# be enabled.\n#websockets_log_level 0\n\n\n# =================================================================\n# Security\n# =================================================================\n\n# If set, only clients that have a matching prefix on their\n# clientid will be allowed to connect to the broker. By default,\n# all clients may connect.\n# For example, setting \"secure-\" here would mean a client \"secure-\n# client\" could connect but another with clientid \"mqtt\" couldn't.\n#clientid_prefixes\n\n# Boolean value that determines whether clients that connect\n# without providing a username are allowed to connect. If set to\n# false then a password file should be created (see the\n# password_file option) to control authenticated client access.\n#\n# Defaults to false, unless there are no listeners defined in the configuration\n# file, in which case it is set to true, but connections are only allowed from\n# the local machine.\n#allow_anonymous false\n\n# -----------------------------------------------------------------\n# Default authentication and topic access control\n# -----------------------------------------------------------------\n\n# Control access to the broker using a password file. This file can be\n# generated using the mosquitto_passwd utility. If TLS support is not compiled\n# into mosquitto (it is recommended that TLS support should be included) then\n# plain text passwords are used, in which case the file should be a text file\n# with lines in the format:\n# username:password\n# The password (and colon) may be omitted if desired, although this\n# offers very little in the way of security.\n#\n# See the TLS client require_certificate and use_identity_as_username options\n# for alternative authentication options. If a plugin is used as well as\n# password_file, the plugin check will be made first.\n#password_file\n\n# Access may also be controlled using a pre-shared-key file. This requires\n# TLS-PSK support and a listener configured to use it. The file should be text\n# lines in the format:\n# identity:key\n# The key should be in hexadecimal format without a leading \"0x\".\n# If an plugin is used as well, the plugin check will be made first.\n#psk_file\n\n# Control access to topics on the broker using an access control list\n# file. If this parameter is defined then only the topics listed will\n# have access.\n# If the first character of a line of the ACL file is a # it is treated as a\n# comment.\n# Topic access is added with lines of the format:\n#\n# topic [read|write|readwrite|deny] <topic>\n#\n# The access type is controlled using \"read\", \"write\", \"readwrite\" or \"deny\".\n# This parameter is optional (unless <topic> contains a space character) - if\n# not given then the access is read/write.  <topic> can contain the + or #\n# wildcards as in subscriptions.\n#\n# The \"deny\" option can used to explicitly deny access to a topic that would\n# otherwise be granted by a broader read/write/readwrite statement. Any \"deny\"\n# topics are handled before topics that grant read/write access.\n#\n# The first set of topics are applied to anonymous clients, assuming\n# allow_anonymous is true. User specific topic ACLs are added after a\n# user line as follows:\n#\n# user <username>\n#\n# The username referred to here is the same as in password_file. It is\n# not the clientid.\n#\n#\n# If is also possible to define ACLs based on pattern substitution within the\n# topic. The patterns available for substitution are:\n#\n# %c to match the client id of the client\n# %u to match the username of the client\n#\n# The substitution pattern must be the only text for that level of hierarchy.\n#\n# The form is the same as for the topic keyword, but using pattern as the\n# keyword.\n# Pattern ACLs apply to all users even if the \"user\" keyword has previously\n# been given.\n#\n# If using bridges with usernames and ACLs, connection messages can be allowed\n# with the following pattern:\n# pattern write $SYS/broker/connection/%c/state\n#\n# pattern [read|write|readwrite] <topic>\n#\n# Example:\n#\n# pattern write sensor/%u/data\n#\n# If an plugin is used as well as acl_file, the plugin check will be\n# made first.\n#acl_file\n\n# -----------------------------------------------------------------\n# External authentication and topic access plugin options\n# -----------------------------------------------------------------\n\n# External authentication and access control can be supported with the\n# plugin option. This is a path to a loadable plugin. See also the\n# plugin_opt_* options described below.\n#\n# The plugin option can be specified multiple times to load multiple\n# plugins. The plugins will be processed in the order that they are specified\n# here. If the plugin option is specified alongside either of\n# password_file or acl_file then the plugin checks will be made first.\n#\n# If the per_listener_settings option is false, the plugin will be apply to all\n# listeners. If per_listener_settings is true, then the plugin will apply to\n# the current listener being defined only.\n#\n# This option is also available as `auth_plugin`, but this use is deprecated\n# and will be removed in the future.\n#\n#plugin <path to plugin>\n\n# If the plugin option above is used, define options to pass to the\n# plugin here as described by the plugin instructions. All options named\n# using the format plugin_opt_* will be passed to the plugin, for example:\n#\n# This option is also available as `auth_opt_*`, but this use is deprecated\n# and will be removed in the future.\n#\n# plugin_opt_db_host\n# plugin_opt_db_port\n# plugin_opt_db_username\n# plugin_opt_db_password\n\n# The `plugin` option is affected by the `per_listener_settings` option. If you\n# want to set `per_listener_settings true` but still want to use a plugin work\n# across all listeners, then use the `global_plugin` option. This option\n# functions the same as the `plugin` option in all other ways.\n#\n# If you set `per_listener_settings true`, then define both a `global_plugin`\n# and a `plugin` (which will be attached to a specific listener), then the\n# global plugin will always be processed first.\n#\n# If you set `per_listener_settings false`, then `global_plugin` behaves\n# identically to `plugin`.\n#\n#global_plugin <path to plugin>\n\n# =================================================================\n# Bridges\n# =================================================================\n\n# A bridge is a way of connecting multiple MQTT brokers together.\n# Create a new bridge using the \"connection\" option as described below. Set\n# options for the bridges using the remaining parameters. You must specify the\n# address and at least one topic to subscribe to.\n#\n# Each connection must have a unique name.\n#\n# The address line may have multiple host address and ports specified. See\n# below in the round_robin description for more details on bridge behaviour if\n# multiple addresses are used. Note that if you use an IPv6 address, then you\n# are required to specify a port.\n#\n# The direction that the topic will be shared can be chosen by\n# specifying out, in or both, where the default value is out.\n# The QoS level of the bridged communication can be specified with the next\n# topic option. The default QoS level is 0, to change the QoS the topic\n# direction must also be given.\n#\n# The local and remote prefix options allow a topic to be remapped when it is\n# bridged to/from the remote broker. This provides the ability to place a topic\n# tree in an appropriate location.\n#\n# For more details see the mosquitto.conf man page.\n#\n# Multiple topics can be specified per connection, but be careful\n# not to create any loops.\n#\n# If you are using bridges with cleansession set to false (the default), then\n# you may get unexpected behaviour from incoming topics if you change what\n# topics you are subscribing to. This is because the remote broker keeps the\n# subscription for the old topic. If you have this problem, connect your bridge\n# with cleansession set to true, then reconnect with cleansession set to false\n# as normal.\n#connection <name>\n#address <host>[:<port>] [<host>[:<port>]]\n#topic <topic> [[[out | in | both] qos-level] local-prefix remote-prefix]\n\n# If you need to have the bridge connect over a particular network interface,\n# use bridge_bind_address to tell the bridge which local IP address the socket\n# should bind to, e.g. `bridge_bind_address 192.168.1.10`\n#bridge_bind_address\n\n# If a bridge has topics that have \"out\" direction, the default behaviour is to\n# send an unsubscribe request to the remote broker on that topic. This means\n# that changing a topic direction from \"in\" to \"out\" will not keep receiving\n# incoming messages. Sending these unsubscribe requests is not always\n# desirable, setting bridge_attempt_unsubscribe to false will disable sending\n# the unsubscribe request.\n#bridge_attempt_unsubscribe true\n\n# Set the version of the MQTT protocol to use with for this bridge. Can be one\n# of mqttv50, mqttv311 or mqttv31. Defaults to mqttv311.\n#bridge_protocol_version mqttv311\n\n# If the bridge is using MQTT v5.0 then use bridge_receive_maximum to\n# limit the number QoS 1 or 2 messages that can be in-flight at once.\n# Must be 1-65535. Defaults to the same value as max_inflight_messages,\n# which defaults to 20.\n#bridge_receive_maximum 20\n\n# Set the clean session variable for this bridge.\n# When set to true, when the bridge disconnects for any reason, all\n# messages and subscriptions will be cleaned up on the remote\n# broker. Note that with cleansession set to true, there may be a\n# significant amount of retained messages sent when the bridge\n# reconnects after losing its connection.\n# When set to false, the subscriptions and messages are kept on the\n# remote broker, and delivered when the bridge reconnects.\n#cleansession false\n\n# If the bridge is using MQTT v5.0 then use bridge_session_expiry_interval to\n# set the session expiry interval. Set to 0 to have the session expire\n# immediately when the connection drops. Set to 0xFFFFFFFF to have an infinite\n# expiry interval. Otherwise the expiry interval is set to the number of\n# seconds that you specify.\n# Defaults to 0.\n#bridge_session_expiry_interval 0\n\n# Set the amount of time a bridge using the lazy start type must be idle before\n# it will be stopped. Defaults to 60 seconds.\n#idle_timeout 60\n\n# Set the MQTT keepalive interval (PING) for this bridge connection, in\n# seconds.\n#keepalive_interval 60\n\n# Set TCP keepalive parameters for this bridge connection.\n# Disabled by default.\n#bridge_tcp_keepalive <idle> <interval> <counter>\n\n# Change the maximum amount of time (in milliseconds) that transmitted data\n# may remain unacknowledged at TCP level.\n# Popular Linux distributions seem to set this time to \"up to 20 minutes with\n# system defaults\" (from Linux tcp man page). Reducing this value helps\n# detecting dropped connections faster.\n# When used together with TCP Keepalive, it might change it's behavior.\n# More details at: https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/\n# Only available on Linux.\n#bridge_tcp_user_timeout <ms>\n\n# Set the clientid to use on the local broker. If not defined, this defaults to\n# 'local.<clientid>'. If you are bridging a broker to itself, it is important\n# that local_clientid and clientid do not match.\n#local_clientid\n\n# If set to true, publish notification messages to the local and remote brokers\n# giving information about the state of the bridge connection. Retained\n# messages are published to the topic $SYS/broker/connection/<clientid>/state\n# unless the notification_topic option is used.\n# If the message is 1 then the connection is active, or 0 if the connection has\n# failed.\n# This uses the last will and testament feature.\n#notifications true\n\n# If set to true, only publish notification messages to the local broker giving\n# information about the state of the bridge connection. Defaults to false.\n#notifications_local_only false\n\n# Choose the topic on which notification messages for this bridge are\n# published. If not set, messages are published on the topic\n# $SYS/broker/connection/<clientid>/state\n#notification_topic\n\n# Set the client id to use on the remote end of this bridge connection. If not\n# defined, this defaults to 'name.hostname' where name is the connection name\n# and hostname is the hostname of this computer.\n# This replaces the old \"clientid\" option to avoid confusion. \"clientid\"\n# remains valid for the time being.\n#remote_clientid\n\n# Set the password to use when connecting to a broker that requires\n# authentication. This option is only used if remote_username is also set.\n# This replaces the old \"password\" option to avoid confusion. \"password\"\n# remains valid for the time being.\n#remote_password\n\n# Set the username to use when connecting to a broker that requires\n# authentication.\n# This replaces the old \"username\" option to avoid confusion. \"username\"\n# remains valid for the time being.\n#remote_username\n\n# Set the amount of time a bridge using the automatic start type will wait\n# until attempting to reconnect.\n# This option can be configured to use a constant delay time in seconds, or to\n# use a backoff mechanism based on \"Decorrelated Jitter\", which adds a degree\n# of randomness to when the restart occurs.\n# Minimum of 1, maximum of 3600\n#\n# Set a constant timeout of 20 seconds:\n# restart_timeout 20\n#\n# Set backoff with a base (start value) of 10 seconds and a cap (upper limit) of\n# 60 seconds:\n# restart_timeout 10 60\n#\n# Same as previous example, but wait for the connection to be stable for\n# at least 30 seconds before resetting the backoff:\n# restart_timeout 10 60 30\n#\n# Defaults to jitter with a base of 5 and cap of 30\n#restart_timeout 5 30\n\n# If the bridge has more than one address given in the address/addresses\n# configuration, the round_robin option defines the behaviour of the bridge on\n# a failure of the bridge connection. If round_robin is false, the default\n# value, then the first address is treated as the main bridge connection. If\n# the connection fails, the other secondary addresses will be attempted in\n# turn. Whilst connected to a secondary bridge, the bridge will periodically\n# attempt to reconnect to the main bridge until successful.\n# If round_robin is true, then all addresses are treated as equals. If a\n# connection fails, the next address will be tried and if successful will\n# remain connected until it fails\n#round_robin false\n\n# Set the start type of the bridge. This controls how the bridge starts and\n# can be one of three types: automatic, lazy and once. Note that RSMB provides\n# a fourth start type \"manual\" which isn't currently supported by mosquitto.\n#\n# \"automatic\" is the default start type and means that the bridge connection\n# will be started automatically when the broker starts and also restarted\n# after a short delay (30 seconds) if the connection fails.\n#\n# Bridges using the \"lazy\" start type will be started automatically when the\n# number of queued messages exceeds the number set with the \"threshold\"\n# parameter. It will be stopped automatically after the time set by the\n# \"idle_timeout\" parameter. Use this start type if you wish the connection to\n# only be active when it is needed.\n#\n# A bridge using the \"once\" start type will be started automatically when the\n# broker starts but will not be restarted if the connection fails.\n#start_type automatic\n\n# Set the number of messages that need to be queued for a bridge with lazy\n# start type to be restarted. Defaults to 10 messages.\n# Must be less than max_queued_messages.\n#threshold 10\n\n# If try_private is set to true, the bridge will attempt to indicate to the\n# remote broker that it is a bridge not an ordinary client. If successful, this\n# means that loop detection will be more effective and that retained messages\n# will be propagated correctly. Not all brokers support this feature so it may\n# be necessary to set try_private to false if your bridge does not connect\n# properly.\n#try_private true\n\n# Some MQTT brokers do not allow retained messages. MQTT v5 gives a mechanism\n# for brokers to tell clients that they do not support retained messages, but\n# this is not possible for MQTT v3.1.1 or v3.1. If you need to bridge to a\n# v3.1.1 or v3.1 broker that does not support retained messages, set the\n# bridge_outgoing_retain option to false. This will remove the retain bit on\n# all outgoing messages to that bridge, regardless of any other setting.\n#bridge_outgoing_retain true\n\n# If you wish to restrict the size of messages sent to a remote bridge, use the\n# bridge_max_packet_size option. This sets the maximum number of bytes for\n# the total message, including headers and payload.\n# Note that MQTT v5 brokers may provide their own maximum-packet-size property.\n# In this case, the smaller of the two limits will be used.\n# Set to 0 for \"unlimited\".\n#bridge_max_packet_size 0\n\n# If the bridge is using MQTT v5, this option sets the maximum number of topic\n# aliases that the bridge will allow the remote broker to configure. Defaults\n# to 10, maximum of 65535. Set to 0 to disable incoming topic aliases\n# completely.\n#bridge_max_topic_alias 10\n\n# If you change bridge options in the configuration file, those configuration\n# changes are applied during a bridge reconnection. The bridge_reload_type\n# option determines when that reconnection happens, and can be set to either\n# lazy or immediate.\n#\n# lazy is the default, and means that any connected bridge will remain in its\n# current state until a natural reconnection happens, at which point the new\n# configuration is used.\n#\n# immediate forces a reconnection and so uses the new configuration straight\n# away.\n#bridge_reload_type lazy\n\n# When publishing to MQTT v5 clients, Mosquitto can create topic aliases on a\n# first come first serve basis, i.e. the topics that are published to a client\n# first have aliases created. The max_topic_alias_broker option controls the\n# number of aliases per client. It applies per listener.\n#\n# Note that this behaviour is not guaranteed to remain the same. It is possible\n# that future versions introduce mechanisms for controlling which topics\n# receive aliases.\n#\n# This option sets the maximum number topic aliases that Mosquitto will create\n# for an MQTT v5 client, even if the client allows more.\n#\n# For example, if the client sets topic-alias-maximum to 100 and this option is\n# set to 10, the broker will create at most 10 topic aliases.  Likewise, if the\n# client sets topic-alias-maximum to 20 and this option is set to 100, then the\n# broker will create at most 20 topic aliases.\n# \n# Defaults to 10. Maximum of 65535. Set to 0 to disable broker to client topic\n# aliases completely.\n#max_topic_alias_broker 10\n\n# The max_topic_alias option sets the maximum number topic aliases that an MQTT\n# v5 client is allowed to create. This option applies per listener. Defaults to\n# 10. Set to 0 to disallow topic aliases from clients. The maximum value\n# possible is 65535.\n#max_topic_alias 10\n\n\n# -----------------------------------------------------------------\n# Certificate based SSL/TLS support\n# -----------------------------------------------------------------\n# To enable TLS support, the bridge must be configured to trust some\n# certificate authority certificates. This can be done in three ways, by\n# defining at least one of bridge_cafile, bridge_capath, or\n# bridge_tls_use_os_certs.\n\n# Use bridge_cafile or bridge_capath to explicitly choose which certificates to\n# trust for this bridge.\n# bridge_cafile defines the path to a file containing the\n# Certificate Authority certificates that have signed the remote broker\n# certificate.\n# bridge_capath defines a directory that will be searched for files containing\n# the CA certificates. For bridge_capath to work correctly, the certificate\n# files must have \".crt\" as the file ending and you must run \"openssl rehash\n# <path to capath>\" each time you add/remove a certificate.\n#bridge_cafile\n#bridge_capath\n\n# Set bridge_tls_use_os_certs to true (default is false) to configure this\n# bridge to use the default certificates as configured in openssl.\n#bridge_tls_use_os_certs false\n\n\n# If the remote broker has more than one protocol available on its port, e.g.\n# MQTT and WebSockets, then use bridge_alpn to configure which protocol is\n# requested. Note that WebSockets support for bridges is not yet available.\n#bridge_alpn\n\n# Require the use of Online Certificate Status Protocol (OCSP) for this bridge\n#bridge_require_ocsp false\n\n# When using certificate based encryption, bridge_insecure disables\n# verification of the server hostname in the server certificate. This can be\n# useful when testing initial server configurations, but makes it possible for\n# a malicious third party to impersonate your server through DNS spoofing, for\n# example. Use this option in testing only. If you need to resort to using this\n# option in a production environment, your setup is at fault and there is no\n# point using encryption.\n#bridge_insecure false\n\n# Path to the PEM encoded client certificate, if required by the remote broker.\n#bridge_certfile\n\n# Path to the PEM encoded client private key, if required by the remote broker.\n#bridge_keyfile\n\n# Configure the version of the TLS protocol to be used for this bridge.\n# Possible values are tlsv1.3 and tlsv1.2. Defaults to tlsv1.2.\n# The remote broker must support the same version of TLS for the connection to succeed.\n#bridge_tls_version\n\n# If you wish to control which encryption ciphers are used, use the ciphers\n# option. The list of available ciphers can be optained using the \"openssl\n# ciphers\" command and should be provided in the same format as the output of\n# that command. This applies to TLS 1.2 and earlier versions only. Use\n# bridge_ciphers_tls1.3 for TLS v1.3.\n#bridge_ciphers\n\n# Choose which TLS v1.3 ciphersuites are used for this bridge.\n# Defaults to \"TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256\"\n#bridge_ciphers_tls1.3\n\n# -----------------------------------------------------------------\n# PSK based SSL/TLS support\n# -----------------------------------------------------------------\n# Pre-shared-key encryption provides an alternative to certificate based\n# encryption. A bridge can be configured to use PSK with the bridge_identity\n# and bridge_psk options. These are the client PSK identity, and pre-shared-key\n# in hexadecimal format with no \"0x\". Only one of certificate and PSK based\n# encryption can be used on one\n# bridge at once.\n#bridge_identity\n#bridge_psk\n\n\n# =================================================================\n# Broker control\n# =================================================================\n\n# Enable the $CONTROL/broker/# topic API\n#enable_control_api false\n\n# =================================================================\n# External config files\n# =================================================================\n\n# External configuration files may be included by using the\n# include_dir option. This defines a directory that will be searched\n# for config files. All files that end in '.conf' will be loaded as\n# a configuration file. It is best to have this as the last option\n# in the main file. This option will only be processed from the main\n# configuration file. The directory specified must not contain the\n# main configuration file.\n# Files within include_dir will be loaded sorted in case-sensitive\n# alphabetical order, with capital letters ordered first. If this option is\n# given multiple times, all of the files from the first instance will be\n# processed before the next instance. See the man page for examples.\n#include_dir\n"
  },
  {
    "path": "plugins/CMakeLists.txt",
    "content": "function(add_mosquitto_plugin_no_install PLUGIN_NAME SRCLIST INCLIST LINKLIST)\n\tadd_library(${PLUGIN_NAME} MODULE ${SRCLIST})\n\ttarget_include_directories(${PLUGIN_NAME} PRIVATE\n\t\t${INCLIST}\n\t\t\"${mosquitto_SOURCE_DIR}/\"\n\t\t\"${mosquitto_SOURCE_DIR}/common\"\n\t\t\"${mosquitto_SOURCE_DIR}/include\"\n\t)\n\n\tset_target_properties(${PLUGIN_NAME} PROPERTIES\n\t\tPREFIX \"\"\n\t\tPOSITION_INDEPENDENT_CODE 1\n\t)\n\n\ttarget_link_libraries(${PLUGIN_NAME} PRIVATE\n\t\t${LINKLIST}\n\t\tcommon-options\n\t\tmosquitto\n\t)\nendfunction()\n\nfunction(add_mosquitto_plugin PLUGIN_NAME SRCLIST INCLIST LINKLIST)\n\tadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${SRCLIST}\" \"${INCLIST}\" \"${LINKLIST}\")\n\n\tif(WIN32)\n\t\tinstall(TARGETS ${PLUGIN_NAME}\n\t\t\tDESTINATION \"${CMAKE_INSTALL_BINDIR}\")\n\telse()\n\t\tinstall(TARGETS ${PLUGIN_NAME}\n\t\t\tRUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n\t\t\tLIBRARY DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n\t\t)\n\tendif()\nendfunction()\n\n\noption(WITH_PLUGIN_ACL_FILE \"Build acl-file plugin?\" ON)\noption(WITH_PLUGIN_DYNAMIC_SECURITY \"Build dynamic-security plugin?\" ON)\noption(WITH_PLUGIN_EXAMPLES \"Build example plugins?\" ON)\noption(WITH_PLUGIN_PERSIST_SQLITE \"Build persist-sqlite plugin?\" ON)\noption(WITH_PLUGIN_PASSWORD_FILE \"Build password-file plugin?\" ON)\noption(WITH_PLUGIN_SPARKPLUG_AWARE \"Build sparkplug-aware plugin?\" ON)\n\nif(WITH_PLUGIN_ACL_FILE)\n\tadd_subdirectory(acl-file)\nendif()\n\nif(WITH_PLUGIN_DYNAMIC_SECURITY)\n\tadd_subdirectory(dynamic-security)\nendif()\n\nif (WITH_PLUGIN_EXAMPLES)\n\tadd_subdirectory(examples)\nendif()\n\nif(WITH_PLUGIN_PASSWORD_FILE)\n\tadd_subdirectory(password-file)\nendif()\n\nif (WITH_PLUGIN_PERSIST_SQLITE)\n\tfind_package(SQLite3 REQUIRED)\n\tadd_subdirectory(persist-sqlite)\nendif()\n\nif(WITH_PLUGIN_SPARKPLUG_AWARE)\n\tadd_subdirectory(sparkplug-aware)\nendif()\n"
  },
  {
    "path": "plugins/Makefile",
    "content": "R=..\ninclude ${R}/config.mk\n\nDIRS= \\\n\t\tacl-file \\\n\t\tdynamic-security \\\n\t\texamples \\\n\t\tpassword-file \\\n\t\tsparkplug-aware\n\nifeq ($(WITH_SQLITE),yes)\nDIRS+=persist-sqlite\nendif\n\n.PHONY : all binary check clean reallyclean test test-compile install uninstall\n\nall :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d}; done\n\nbinary :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\nclean :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\nreallyclean :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\ntest-compile :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\ncheck : test\ntest :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\ninstall :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\nuninstall :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n"
  },
  {
    "path": "plugins/README.md",
    "content": "# Plugins\n\nThis directory contains plugins for use with Mosquitto.\n\n## Dynamic security\nThis is a fully functioning plugin that implements authentication and access\ncontrol, with configuration via a $CONTROL topic. See the readme in\ndynamic-security for more information.\n\n## Examples / Add properties\nThis is an **example** plugin that demonstrates adding MQTT v5 properties to\nmessages, and how to get client information.\n\n## Examples / Authenticate by IP address\nThis is an **example** plugin that demonstrates a basic authentication callback\nthat allows clients based on their IP address. Password based authentication is\npreferred over this very simple type of access control.\n\n## Examples / Client lifetime stats\nThis is an **example** plugin that collects counts of how long client sessions\nlast, in different time buckets of 0, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1k,\n2k, 5k, 10k, 20k, 50k, 100k, 200k, 500k, 1M, 2M, 5M, 10M, 20M, 50M, 100M, 200M,\n500M seconds. It periodically publishes the counts at\n`$SYS/broker/client/lifetimes/<bucket>`.\n\n## Examples / Client properties\nThis is an **example** plugin that demonstrates some of the functions for\nretrieving client information such as client id and username.\n\n## Examples / Connection state\nThis is an **example** plugin to demonstrate the use of the connect and\ndisconnect events. It publishes messages to\n$SYS/broker/connection/client/<client id>/state for every client that connects\nto the broker, to indicate the connection state of that client.\n\n## Examples / Deferred authentication\nThis is an **example** plugin to demonstrate how a plugin can carry out\ndelayed basic authentication. This method should be used where the plugin\nsends an authentication request to an external server so that if there is a\ndelay in getting a response it does not block the broker. The plugin may spawn\nextra threads to handle the authentication requests, but the call to\n`mosquitto_complete_basic_auth()` must happen in the main Mosquitto thread.\n\n## Examples / Message timestamp\nThis is an **example** plugin to demonstrate how it is possible to attach MQTT\nv5 properties to messages after they have been received, and before they are\nsent on to subscribers.\n\nThis plugin attaches a user-property property to each message which contains\nthe ISO-8601 timestamp of the time the message was received by the broker. This\nmeans it is possible for MQTT v5 clients to see how old a retained message is,\nfor example.\n\n## Examples / Payload modification\nThis is an **example** plugin to demonstrate how it is possible to modify the\npayload of messages after they have been received, and before they are sent on\nto subscribers.\n\nIf you are considering using this feature, you should be very certain you have\nverified the payload is the correct format before modifying it.\n\nThis plugin adds the text string \"hello \" to the beginning of each payload, so\nwith anything other than simple plain text messages it will corrupt the payload\ncontents.\n\n## Examples / Payload size stats\nThis is an **example** plugin that collects counts of payload message sizes, in\ntime buckets of 0, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1k, 2k, 5k, 10k, 20k,\n50k, 100k, 200k, 500k, 1M, 2M, 5M, 10M, 20M, 50M, 100M, 200M, 500M bytes. It\nperiodically publishes the counts at `$SYS/broker/publish/sizes/<bucket>`.\n\n## Examples / Print IP on publish\nThis is an **example** plugin that prints out client ID and IP address of any\nclient that publishes on a particular topic.\n\n## Examples / Topic modification\nThis is an **example** plugin to demonstrate how it is possible to modify the\ntopic of messages after they have been received, and before they are sent on\nto subscribers.\n\nThis plugin removes the `/uplink` end part of topics that match the pattern\n`device/+/data/uplink`, so devices publishing to `device/0001/data/uplink` will\neffectively be publishing to `device/0001/data`.\n\n## Examples / Wildcard temp\nThis is an **example** plugin that denies access to the `#` subscription topic\nonly. This prevents clients from discovering which topics are active and\nreduces outgoing bandwidth. If clients connect with username `wildcard` and\nsubscribes to `#` they will be allowed 20 seconds of access, after which the\nsubscription will be silently removed.\n"
  },
  {
    "path": "plugins/acl-file/CMakeLists.txt",
    "content": "set(PLUGIN_NAME mosquitto_acl_file)\n\nset(SRCLIST\n\tacl_check.c\n\tacl_parse.c\n\tplugin.c\n)\n\nset(INCLIST ${mosquitto_SOURCE_DIR}/src)\nset(LINKLIST libmosquitto_common)\n\nadd_mosquitto_plugin(\"${PLUGIN_NAME}\" \"${SRCLIST}\" \"${INCLIST}\" \"${LINKLIST}\")\n"
  },
  {
    "path": "plugins/acl-file/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_acl_file\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=-I${R}/src\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=${LIBMOSQ_COMMON}\n\n# Objects for this plugin only, built from source in this directory\nOBJS = \\\n\tacl_check.o \\\n\tacl_parse.o \\\n\tplugin.o\n\n# Objects from e.g. the common directory that are not in this directory\nOBJS_EXTERNAL =\n\nall : binary\n\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/acl-file/acl_check.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <ctype.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"acl_file.h\"\n#include \"mosquitto.h\"\n\n\nint acl_file__check(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\tstruct acl__user *acl_user;\n\tstruct acl__entry *acl_root;\n\tbool result;\n\tstruct acl_file_data *data = userdata;\n\tconst char *clientid;\n\tconst char *username;\n\n\tUNUSED(event);\n\n\t// FIXME if(ed->client->bridge) return MOSQ_ERR_SUCCESS;\n\tif(ed->access == MOSQ_ACL_SUBSCRIBE || ed->access == MOSQ_ACL_UNSUBSCRIBE){\n\t\treturn MOSQ_ERR_SUCCESS;                                                                        /* FIXME - implement ACL subscription strings. */\n\n\t}\n\tclientid = mosquitto_client_id(ed->client);\n\tusername = mosquitto_client_username(ed->client);\n\n\tif(!data->acl_file && !data->acl_users && !data->acl_patterns){\n\t\treturn MOSQ_ERR_PLUGIN_IGNORE;\n\t}\n\n\tif(username){\n\t\tHASH_FIND(hh, data->acl_users, username, strlen(username), acl_user);\n\t}else{\n\t\tacl_user = &data->acl_anon;\n\t}\n\tif(!acl_user && !data->acl_patterns){\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n\n\tif(acl_user){\n\t\tacl_root = acl_user->acl;\n\t}else{\n\t\tacl_root = NULL;\n\t}\n\n\t/* Loop through all ACLs for this client. ACL denials are iterated over first. */\n\twhile(acl_root){\n\t\t/* Loop through the topic looking for matches to this ACL. */\n\n\t\t/* If subscription starts with $, acl_root->topic must also start with $. */\n\t\tif(ed->topic[0] == '$' && acl_root->topic[0] != '$'){\n\t\t\tacl_root = acl_root->next;\n\t\t\tcontinue;\n\t\t}\n\t\tmosquitto_topic_matches_sub(acl_root->topic, ed->topic, &result);\n\t\tif(result){\n\t\t\tif(acl_root->access == MOSQ_ACL_NONE){\n\t\t\t\t/* Access was explicitly denied for this topic. */\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t\tif(ed->access & acl_root->access){\n\t\t\t\t/* And access is allowed. */\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}\n\t\tacl_root = acl_root->next;\n\t}\n\n\tacl_root = data->acl_patterns;\n\n\tif(acl_root){\n\t\t/* We are using pattern based acls. Check whether the username or\n\t\t * client id contains a + or # and if so deny access.\n\t\t *\n\t\t * Without this, a malicious client may configure its username/client\n\t\t * id to bypass ACL checks (or have a username/client id that cannot\n\t\t * publish or receive messages to its own place in the hierarchy).\n\t\t */\n\t\tif(username && strpbrk(username, \"+#\")){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_NOTICE, \"ACL denying access to client with dangerous username \\\"%s\\\"\", username);\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\n\t\tif(clientid && strpbrk(clientid, \"+#\")){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_NOTICE, \"ACL denying access to client with dangerous client id \\\"%s\\\"\", clientid);\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\t}\n\n\t/* Loop through all pattern ACLs. ACL denial patterns are iterated over first. */\n\tif(!clientid){\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n\n\twhile(acl_root){\n\t\tif(acl_root->ucount && !username){\n\t\t\tacl_root = acl_root->next;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(mosquitto_topic_matches_sub_with_pattern(acl_root->topic, ed->topic, clientid, username, &result)){\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\t\tif(result){\n\t\t\tif(acl_root->access == MOSQ_ACL_NONE){\n\t\t\t\t/* Access was explicitly denied for this topic pattern. */\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t\tif(ed->access & acl_root->access){\n\t\t\t\t/* And access is allowed. */\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}\n\n\t\tacl_root = acl_root->next;\n\t}\n\n\treturn MOSQ_ERR_ACL_DENIED;\n}\n"
  },
  {
    "path": "plugins/acl-file/acl_parse.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <ctype.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <string.h>\n#include <utlist.h>\n\n#include \"mosquitto.h\"\n#include \"acl_file.h\"\n\n\nstatic int acl__add_to_user(struct acl__user *acl_user, const char *topic, int access)\n{\n\tstruct acl__entry *acl;\n\n\tacl = mosquitto_calloc(1, sizeof(struct acl__entry));\n\tif(!acl){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tacl->access = access;\n\n\tacl->topic = mosquitto_strdup(topic);\n\tif(!acl->topic){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t/* Add acl to user acl list */\n\tif(access == MOSQ_ACL_NONE){\n\t\t/* Put \"deny\" acls at front of the list */\n\t\tDL_PREPEND(acl_user->acl, acl);\n\t}else{\n\t\tDL_APPEND(acl_user->acl, acl);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic struct acl__user *acl__find_or_create_user(struct acl_file_data *data, const char *user, unsigned user_hashv)\n{\n\tif(user == NULL){\n\t\treturn &data->acl_anon;\n\t}else{\n\t\tstruct acl__user *acl_user=NULL;\n\n\t\tHASH_FIND_BYHASHVALUE(hh, data->acl_users, user, strlen(user), user_hashv, acl_user);\n\n\t\tif(!acl_user){\n\t\t\tacl_user = mosquitto_calloc(1, sizeof(struct acl__user));\n\t\t\tif(!acl_user){\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tif(user){\n\t\t\t\tacl_user->username = mosquitto_strdup(user);\n\t\t\t\tif(!acl_user->username){\n\t\t\t\t\tmosquitto_FREE(acl_user);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t\tHASH_ADD_KEYPTR(hh, data->acl_users, acl_user->username, strlen(acl_user->username), acl_user);\n\t\t}\n\n\t\treturn acl_user;\n\t}\n}\n\n\nstatic int acl__add(struct acl_file_data *data, const char *user, unsigned user_hashv, const char *topic, int access)\n{\n\tstruct acl__user *acl_user=NULL;\n\n\tif(!data || !topic){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tacl_user = acl__find_or_create_user(data, user, user_hashv);\n\tif(!acl_user){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\treturn acl__add_to_user(acl_user, topic, access);\n}\n\n\nstatic int acl__add_pattern(struct acl_file_data *data, const char *topic, int access)\n{\n\tstruct acl__entry *acl, *acl_tail;\n\tchar *local_topic;\n\tchar *s;\n\n\tif(!data| !topic){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tlocal_topic = mosquitto_strdup(topic);\n\tif(!local_topic){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tacl = mosquitto_malloc(sizeof(struct acl__entry));\n\tif(!acl){\n\t\tmosquitto_FREE(local_topic);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tacl->access = access;\n\tacl->topic = local_topic;\n\tacl->next = NULL;\n\n\tacl->ccount = 0;\n\ts = local_topic;\n\twhile(s){\n\t\ts = strstr(s, \"%c\");\n\t\tif(s){\n\t\t\tacl->ccount++;\n\t\t\ts+=2;\n\t\t}\n\t}\n\n\tacl->ucount = 0;\n\ts = local_topic;\n\twhile(s){\n\t\ts = strstr(s, \"%u\");\n\t\tif(s){\n\t\t\tacl->ucount++;\n\t\t\ts+=2;\n\t\t}\n\t}\n\n\tif(acl->ccount == 0 && acl->ucount == 0){\n\t\tmosquitto_log_printf(MOSQ_LOG_WARNING,\n\t\t\t\t\"Warning: ACL pattern '%s' does not contain '%%c' or '%%u'.\",\n\t\t\t\ttopic);\n\t}\n\n\tif(data->acl_patterns){\n\t\tacl_tail = data->acl_patterns;\n\t\tif(access == MOSQ_ACL_NONE){\n\t\t\t/* Put \"deny\" acls at front of the list */\n\t\t\tacl->next = acl_tail;\n\t\t\tdata->acl_patterns = acl;\n\t\t}else{\n\t\t\twhile(acl_tail->next){\n\t\t\t\tacl_tail = acl_tail->next;\n\t\t\t}\n\t\t\tacl_tail->next = acl;\n\t\t}\n\t}else{\n\t\tdata->acl_patterns = acl;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint acl_file__parse(struct acl_file_data *data)\n{\n\tFILE *aclfptr = NULL;\n\tchar *token;\n\tchar *user = NULL;\n\tchar *topic;\n\tchar *access_s;\n\tint access;\n\tint rc = MOSQ_ERR_SUCCESS;\n\tsize_t slen;\n\tint topic_pattern;\n\tchar *saveptr = NULL;\n\tchar *buf = NULL;\n\tint buflen = 256;\n\tunsigned user_hashv = 0;\n\n\tif(!data){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!data->acl_file){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tbuf = mosquitto_calloc((size_t)buflen, 1);\n\tif(buf == NULL){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\taclfptr = mosquitto_fopen(data->acl_file, \"rt\", true);\n\tif(!aclfptr){\n\t\tmosquitto_FREE(buf);\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Unable to open acl_file \\\"%s\\\".\", data->acl_file);\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\t/* topic [read|write] <topic>\n\t * user <user>\n\t */\n\n\twhile(mosquitto_fgets(&buf, &buflen, aclfptr)){\n\t\tslen = strlen(buf);\n\t\twhile(slen > 0 && isspace((unsigned char)buf[slen-1])){\n\t\t\tbuf[slen-1] = '\\0';\n\t\t\tslen = strlen(buf);\n\t\t}\n\t\tif(buf[0] == '#'){\n\t\t\tcontinue;\n\t\t}\n\t\ttoken = strtok_r(buf, \" \", &saveptr);\n\t\tif(token){\n\t\t\tif(!strcmp(token, \"topic\") || !strcmp(token, \"pattern\")){\n\t\t\t\tif(!strcmp(token, \"topic\")){\n\t\t\t\t\ttopic_pattern = 0;\n\t\t\t\t}else{\n\t\t\t\t\ttopic_pattern = 1;\n\t\t\t\t}\n\n\t\t\t\taccess_s = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\tif(!access_s){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Empty topic in acl_file \\\"%s\\\".\", data->acl_file);\n\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ttoken = strtok_r(NULL, \"\", &saveptr);\n\t\t\t\tif(token){\n\t\t\t\t\ttopic = mosquitto_trimblanks(token);\n\t\t\t\t}else{\n\t\t\t\t\ttopic = access_s;\n\t\t\t\t\taccess_s = NULL;\n\t\t\t\t}\n\t\t\t\tif(access_s){\n\t\t\t\t\tif(!strcmp(access_s, \"read\")){\n\t\t\t\t\t\taccess = MOSQ_ACL_READ;\n\t\t\t\t\t}else if(!strcmp(access_s, \"write\")){\n\t\t\t\t\t\taccess = MOSQ_ACL_WRITE;\n\t\t\t\t\t}else if(!strcmp(access_s, \"readwrite\")){\n\t\t\t\t\t\taccess = MOSQ_ACL_READ | MOSQ_ACL_WRITE;\n\t\t\t\t\t}else if(!strcmp(access_s, \"deny\")){\n\t\t\t\t\t\taccess = MOSQ_ACL_NONE;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Invalid topic access type \\\"%s\\\" in acl_file \\\"%s\\\".\", access_s, data->acl_file);\n\t\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\taccess = MOSQ_ACL_READ | MOSQ_ACL_WRITE;\n\t\t\t\t}\n\t\t\t\trc = mosquitto_sub_topic_check(topic);\n\t\t\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Invalid ACL topic \\\"%s\\\" in acl_file \\\"%s\\\".\", topic, data->acl_file);\n\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif(topic_pattern == 0){\n\t\t\t\t\trc = acl__add(data, user, user_hashv, topic, access);\n\t\t\t\t}else{\n\t\t\t\t\trc = acl__add_pattern(data, topic, access);\n\t\t\t\t}\n\t\t\t\tif(rc){\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}else if(!strcmp(token, \"user\")){\n\t\t\t\ttoken = strtok_r(NULL, \"\", &saveptr);\n\t\t\t\tif(token){\n\t\t\t\t\ttoken = mosquitto_trimblanks(token);\n\t\t\t\t\tif(slen == 0){\n\t\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Missing username in acl_file \\\"%s\\\".\", data->acl_file);\n\t\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_FREE(user);\n\t\t\t\t\tuser = mosquitto_strdup(token);\n\t\t\t\t\tif(!user){\n\t\t\t\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tHASH_VALUE(user, strlen(user), user_hashv);\n\t\t\t\t}else{\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Missing username in acl_file \\\"%s\\\".\", data->acl_file);\n\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Invalid line in acl_file \\\"%s\\\": %s.\", data->acl_file, buf);\n\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tmosquitto_FREE(buf);\n\tmosquitto_FREE(user);\n\tfclose(aclfptr);\n\n\treturn rc;\n}\n\n\nstatic void acl__free_entries(struct acl__entry *entry)\n{\n\twhile(entry){\n\t\tstruct acl__entry *next = entry->next;\n\n\t\tmosquitto_FREE(entry->topic);\n\t\tmosquitto_FREE(entry);\n\n\t\tentry = next;\n\t}\n}\n\n\nvoid acl_file__cleanup(struct acl_file_data *data)\n{\n\tstruct acl__user *user, *user_tmp;\n\n\tHASH_ITER(hh, data->acl_users, user, user_tmp){\n\t\tHASH_DELETE(hh, data->acl_users, user);\n\t\tmosquitto_FREE(user->username);\n\t\tacl__free_entries(user->acl);\n\t\tmosquitto_FREE(user);\n\t}\n\n\tacl__free_entries(data->acl_anon.acl);\n\tdata->acl_anon.acl = NULL;\n\n\tacl__free_entries(data->acl_patterns);\n\tdata->acl_patterns = NULL;\n}\n\n\nint acl_file__reload(int event, void *event_data, void *userdata)\n{\n\tstruct acl_file_data *data = userdata;\n\n\tUNUSED(event);\n\tUNUSED(event_data);\n\n\tacl_file__cleanup(data);\n\treturn acl_file__parse(data);\n}\n"
  },
  {
    "path": "plugins/acl-file/plugin.c",
    "content": "/*\nCopyright (c) 2023 Cedalo Gmbh\n*/\n\n#include \"config.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n#include \"acl_file.h\"\n\n#define PLUGIN_NAME \"acl-file\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int handle_options(struct acl_file_data *data, struct mosquitto_opt *options, int option_count)\n{\n\tfor(int i=0; i<option_count; i++){\n\t\tif(!strcmp(options[i].key, \"acl_file\")){\n\t\t\tmosquitto_FREE(data->acl_file);\n\t\t\tdata->acl_file = mosquitto_strdup(options[i].value);\n\t\t\tif(!data->acl_file){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}else{\n\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, PLUGIN_NAME \": Error: Unknown option '%s'.\", options[i].key);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *options, int option_count)\n{\n\tstruct acl_file_data *data;\n\tint rc;\n\n\tUNUSED(options);\n\tUNUSED(option_count);\n\n\tdata = mosquitto_calloc(1, sizeof(struct acl_file_data));\n\tif(!data){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\t*user_data = data;\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, NULL);\n\n\trc = handle_options(data, options, option_count);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = acl_file__parse(data);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_ACL_CHECK, acl_file__check, NULL, data);\n\tif(rc){\n\t\treturn rc;\n\t}\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_RELOAD, acl_file__reload, NULL, data);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *options, int option_count)\n{\n\tstruct acl_file_data *data = user_data;\n\n\tUNUSED(options);\n\tUNUSED(option_count);\n\n\tmosquitto_callback_unregister(mosq_pid, MOSQ_EVT_ACL_CHECK, acl_file__check, NULL);\n\tmosquitto_callback_unregister(mosq_pid, MOSQ_EVT_RELOAD, acl_file__reload, NULL);\n\n\tacl_file__cleanup(data);\n\tmosquitto_FREE(data->acl_file);\n\tmosquitto_FREE(data);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/acl-file/test.conf",
    "content": "listener 1883\nallow_anonymous true\nplugin ./mosquitto_acl_file.so\nplugin_opt_acl_file ./acl_file\n"
  },
  {
    "path": "plugins/acl-file/test.sh",
    "content": "VG=\"valgrind --log-file=vglog\"\n${VG} ../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/dynamic-security/CMakeLists.txt",
    "content": "if(WITH_TLS)\n\tset(PLUGIN_NAME mosquitto_dynamic_security)\n\n\tset(SRCLIST\n\t\tacl.c\n\t\tauth.c\n\t\tclients.c\n\t\tclientlist.c\n\t\tconfig.c\n\t\tconfig_init.c\n\t\tcontrol.c\n\t\tdefault_acl.c\n\t\tdetails.c\n\t\tdynamic_security.h\n\t\tgroups.c\n\t\tgrouplist.c\n\t\t../../common/json_help.c ../../common/json_help.h\n\t\tkicklist.c\n\t\tplugin.c\n\t\troles.c\n\t\trolelist.c\n\t\ttick.c\n\t)\n\n\tset(INCLIST\n\t\t\"${CJSON_INCLUDE_DIRS}\"\n\t\t\"${mosquitto_SOURCE_DIR}/lib\"\n\t\t\"${mosquitto_SOURCE_DIR}/src\"\n\t)\n\n\tset(LINKLIST\n\t\tlibmosquitto_common\n\t\tcJSON\n\t\tOpenSSL::SSL\n\t)\n\n\tif(ARGON2_FOUND)\n\t\tset(LINKLIST\n\t\t\t\"${LINKLIST}\"\n\t\t\targon2\n\t\t)\n\tendif()\n\tadd_mosquitto_plugin(\"${PLUGIN_NAME}\" \"${SRCLIST}\" \"${INCLIST}\" \"${LINKLIST}\")\nendif()\n"
  },
  {
    "path": "plugins/dynamic-security/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_dynamic_security\n\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=-I${R}/lib/ -I${R}/src/ -I${R}/plugins/common\nLOCAL_LIBADD+=-lcjson -lm $(LIBMOSQ_COMMON)\nLOCAL_LDFLAGS+=\n\nWITH_PW_CACHE:=yes\nifeq ($(WITH_PW_CACHE),yes)\n        LOCAL_CPPFLAGS+=-DWITH_PW_CACHE\nendif\n\nOBJS = \\\n\tacl.o \\\n\tauth.o \\\n\tclients.o \\\n\tclientlist.o \\\n\tconfig.o \\\n\tconfig_init.o \\\n\tcontrol.o \\\n\tdefault_acl.o \\\n\tdetails.o \\\n\tgroups.o \\\n\tgrouplist.o \\\n\tkicklist.o \\\n\tplugin.o \\\n\troles.o \\\n\trolelist.o \\\n\ttick.o\n\nOBJS_EXTERNAL = \\\n\tjson_help.o\n\nEXTRA_DEPS:=dynamic_security.h\n\nifeq ($(WITH_TLS),yes)\nALL_DEPS:= binary\nelse\nALL_DEPS:=\nendif\n\nall : ${ALL_DEPS}\n\njson_help.o : ${R}/common/json_help.c ${R}/common/json_help.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/dynamic-security/README.md",
    "content": "# Mosquitto Dynamic Security\n\nThis document describes a topic based mechanism for controlling security in\nMosquitto. JSON commands are published to topics like `$CONTROL/<feature>/v1`\n\n## Clients\n\nWhen a client connects to Mosquitto, it can optionally provide a username. The\nusername maps the client instance to a client on the broker, if it exists.\nMultiple clients can make use of the same username, and hence the same broker\nclient.\n\n## Groups\n\nBroker clients can be defined as belonging to zero or more broker groups.\n\n## Roles\n\nRoles can be applied to a client or a group, and define what that client/group\nis allowed to do, for example what topics it may or may not publish or\nsubscribe to.\n\n## Commands\n\n### Set default ACL access\n\nSets the default access behaviour for the different ACL types, assuming there\nare no matching ACLs for a topic.\n\nBy default, publishClientSend and subscribe default to deny, and\npublishClientReceive and unsubscribe default to allow.\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"setDefaultACLAccess\",\n\t\t\t\"acls\":[\n\t\t\t\t{ \"acltype\": \"publishClientSend\", \"allow\": false },\n\t\t\t\t{ \"acltype\": \"publishClientReceive\", \"allow\": true },\n\t\t\t\t{ \"acltype\": \"subscribe\", \"allow\": false },\n\t\t\t\t{ \"acltype\": \"unsubscribe\", \"allow\": true }\n\t\t\t]\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec setDefaultACLAccess subscribe deny\n```\n\n### Get default ACL access\n\nGets the default access behaviour for the different ACL types.\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"getDefaultACLAccess\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec getDefaultACLAccess\n```\n\n## Create Client\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"createClient\",\n\t\t\t\"username\": \"new username\",\n\t\t\t\"password\": \"new password\",\n\t\t\t\"clientid\": \"\", # Optional\n\t\t\t\"textname\": \"\", # Optional\n\t\t\t\"textdescription\": \"\", # Optional\n\t\t\t\"groups\": [\n\t\t\t\t{ \"groupname\": \"group\", \"priority\": 1 }\n\t\t\t], # Optional, groups must exist\n\t\t\t\"roles\": [\n\t\t\t\t{ \"rolename\": \"role\", \"priority\": -1 }\n\t\t\t] # Optional, roles must exist\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec createClient username password\n```\n\n## Delete Client\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"deleteClient\",\n\t\t\t\"username\": \"username to delete\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec deleteClient username\n```\n\n## Enable Client\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"enableClient\",\n\t\t\t\"username\": \"username to enable\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec enableClient username\n```\n\n## Disable Client\n\nStop a client from being able to log in, and kick any clients with matching\nusername that are currently connected.\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"disableClient\",\n\t\t\t\"username\": \"username to disable\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec disableClient username\n```\n\n## Get Client\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"getClient\",\n\t\t\t\"username\": \"required username\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec getClient username\n```\n\n## List Clients\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"listClients\",\n\t\t\t\"verbose\": false,\n\t\t\t\"count\": -1, # -1 for all, or a positive integer for a limited count\n\t\t\t\"offset\": 0 # Where in the list to start\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec listClients 10 20\n```\n\n## Modify Existing Client\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"modifyClient\",\n\t\t\t\"username\": \"username to modify\"\n\t\t\t\"clientid\": \"new clientid, or empty string to clear\", # Optional\n\t\t\t\"password\": \"new password\", # Optional\n\t\t\t\"textname\": \"\", # Optional\n\t\t\t\"textdescription\": \"\", # Optional\n\t\t\t\"roles\": [\n\t\t\t\t{ \"rolename\": \"role\", \"priority\": 1 }\n\t\t\t], # Optional\n\t\t\t\"groups\": [\n\t\t\t\t{ \"groupname\": \"group\", \"priority\": 1 }\n\t\t\t], # Optional\n\t\t}\n\t]\n}\n```\n\nModifying clients isn't currently possible with mosquitto_ctrl.\n\n## Set Client id\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"setClientId\",\n\t\t\t\"username\": \"username to change\",\n\t\t\t\"clientid\": \"new clientid\" # Optional, if blank or missing then client id will be removed.\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec setClientId username clientId\n```\n\n## Set Client Password\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"setClientPassword\",\n\t\t\t\"username\": \"username to change\",\n\t\t\t\"password\": \"new password\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec setClientPassword username password\n```\n\n## Add Client Role\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"addClientRole\",\n\t\t\t\"username\": \"client to add role to\",\n\t\t\t\"rolename\": \"role to add\",\n\t\t\t\"priority\": -1 # Optional priority\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec addClientRole username rolename\n```\n\n## Remove Client Role\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"removeClientRole\",\n\t\t\t\"username\": \"client to remove role from\",\n\t\t\t\"rolename\": \"role to remove\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec removeClientRole username rolename\n```\n\n## Add Client to a Group\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"addGroupClient\",\n\t\t\t\"groupname\": \"group to add client to\",\n\t\t\t\"username\": \"client to add to group\",\n\t\t\t\"priority\": -1 # Priority of the group for the client\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec addGroupClient groupname username\n```\n\n## Create Group\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"createGroup\",\n\t\t\t\"groupname\": \"new group\",\n\t\t\t\"roles\": [\n\t\t\t\t{ \"rolename\": \"role\", \"priority\": 1 }\n\t\t\t] # Optional, roles must exist\n\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec createGroup groupname\n```\n\n## Delete Group\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"deleteGroup\",\n\t\t\t\"groupname: \"group to delete\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec deleteGroup groupname\n```\n\n## Get Group\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"getGroup\",\n\t\t\t\"groupname: \"group to get\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec getGroup groupname\n```\n\n## List Groups\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"listGroups\",\n\t\t\t\"verbose\": false,\n\t\t\t\"count\": -1, # -1 for all, or a positive integer for a limited count\n\t\t\t\"offset\": 0 # Where in the list to start\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec listGroups\n```\n\n## Modify Group\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"modifyGroup\",\n\t\t\t\"groupname\": \"group to modify\",\n\t\t\t\"textname\": \"\", # Optional\n\t\t\t\"textdescription\": \"\", # Optional\n\t\t\t\"roles\": [\n\t\t\t\t{ \"rolename\": \"role\", \"priority\": 1 }\n\t\t\t], # Optional\n\t\t\t\"clients\": [\n\t\t\t\t{ \"username\": \"client\", \"priority\": 1 }\n\t\t\t] # Optional\n\n\t\t}\n\t]\n}\n```\n\nModifying groups isn't currently possible with mosquitto_ctrl.\n\n## Remove Client from a Group\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"removeGroupClient\",\n\t\t\t\"groupname\": \"group to remove client from\",\n\t\t\t\"username\": \"client to remove from group\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec removeGroupClient groupname username\n```\n\n## Add Group Role\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"addGroupRole\",\n\t\t\t\"groupname\": \"group to add role to\",\n\t\t\t\"rolename\": \"role to add\",\n\t\t\t\"priority\": -1 # Optional priority\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec addGroupRole groupname rolename\n```\n\n## Remove Group Role\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"removeGroupRole\",\n\t\t\t\"groupname\": \"group\",\n\t\t\t\"rolename\": \"role\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec removeGroupRole groupname rolename\n```\n\n## Set Group for Anonymous Clients\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"setAnonymousGroup\",\n\t\t\t\"groupname\": \"group\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec setAnonymousGroup groupname\n```\n\n## Get Group for Anonymous Clients\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"getAnonymousGroup\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec getAnonymousGroup\n```\n\n## Create Role\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"createRole\",\n\t\t\t\"rolename\": \"new role\",\n\t\t\t\"textname\": \"\", # Optional\n\t\t\t\"textdescription\": \"\", # Optional\n\t\t\t\"acls\": [\n\t\t\t\t{ \"acltype\": \"subscribePattern\", \"topic\": \"topic/#\", \"priority\": -1, \"allow\": true}\n\t\t\t] # Optional\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec createRole rolename\n```\n\n## Get Role\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"getRole\",\n\t\t\t\"rolename\": \"role\",\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec getRole rolename\n```\n\n## List Roles\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"listRoles\",\n\t\t\t\"verbose\": false,\n\t\t\t\"count\": -1, # -1 for all, or a positive integer for a limited count\n\t\t\t\"offset\": 0 # Where in the list to start\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec listRoles\n```\n\n## Modify Role\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"modifyRole\",\n\t\t\t\"rolename\": \"role to modify\"\n\t\t\t\"textname\": \"\", # Optional\n\t\t\t\"textdescription\": \"\", # Optional\n\t\t\t\"acls\": [\n\t\t\t\t{ \"acltype\": \"subscribePattern\", \"topic\": \"topic/#\", \"priority\": -1, \"allow\": true }\n\t\t\t] # Optional\n\t\t}\n\t]\n}\n```\n\nModifying roles isn't currently possible with mosquitto_ctrl.\n\n## Delete Role\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"deleteRole\",\n\t\t\t\"rolename\": \"role\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec deleteRole rolename\n```\n\n## Add Role ACL\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"addRoleACL\",\n\t\t\t\"rolename\": \"role\",\n\t\t\t\"acltype\": \"subscribePattern\",\n\t\t\t\"topic\": \"topic/#\",\n\t\t\t\"priority\": -1,\n\t\t\t\"allow\": true\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec addRoleACL rolename subscribeLiteral topic/# deny\n```\n\n## Remove Role ACL\n\nCommand:\n```\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"command\": \"removeRoleACL\",\n\t\t\t\"rolename\": \"role\",\n\t\t\t\"acltype\": \"subscribePattern\",\n\t\t\t\"topic\": \"topic/#\"\n\t\t}\n\t]\n}\n```\n\nmosquitto_ctrl example:\n```\nmosquitto_ctrl dynsec removeRoleACL rolename subscribeLiteral topic/#\n```\n"
  },
  {
    "path": "plugins/dynamic-security/acl.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto.h\"\n\n#include \"dynamic_security.h\"\n\ntypedef int (*MOSQ_FUNC_acl_check)(struct dynsec__data *data, struct mosquitto_evt_acl_check *, struct dynsec__rolelist *);\n\n\n/* FIXME - CACHE! */\n\n\n/* ################################################################\n * #\n * # ACL check - publish broker to client\n * #\n * ################################################################ */\n\n\nstatic int acl_check_publish_c_recv(struct dynsec__data *data, struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist)\n{\n\tstruct dynsec__rolelist *rolelist, *rolelist_tmp = NULL;\n\tstruct dynsec__acl *acl, *acl_tmp = NULL;\n\tbool result;\n\tconst char *clientid, *username;\n\n\tUNUSED(data);\n\n\tclientid = mosquitto_client_id(ed->client);\n\tusername = mosquitto_client_username(ed->client);\n\n\tHASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){\n\t\tHASH_ITER(hh, rolelist->role->acls.publish_c_recv, acl, acl_tmp){\n\t\t\tif(mosquitto_topic_matches_sub_with_pattern(acl->topic, ed->topic, clientid, username, &result)){\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t\tif(result){\n\t\t\t\tif(acl->allow){\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}else{\n\t\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_NOT_FOUND;\n}\n\n\n/* ################################################################\n * #\n * # ACL check - publish client to broker\n * #\n * ################################################################ */\n\n\nstatic int acl_check_publish_c_send(struct dynsec__data *data, struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist)\n{\n\tstruct dynsec__rolelist *rolelist, *rolelist_tmp = NULL;\n\tstruct dynsec__acl *acl, *acl_tmp = NULL;\n\tbool result;\n\tconst char *clientid, *username;\n\n\tUNUSED(data);\n\n\tclientid = mosquitto_client_id(ed->client);\n\tusername = mosquitto_client_username(ed->client);\n\n\tHASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){\n\t\tHASH_ITER(hh, rolelist->role->acls.publish_c_send, acl, acl_tmp){\n\t\t\tif(mosquitto_topic_matches_sub_with_pattern(acl->topic, ed->topic, clientid, username, &result)){\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t\tif(result){\n\t\t\t\tif(acl->allow){\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}else{\n\t\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_NOT_FOUND;\n}\n\n\n/* ################################################################\n * #\n * # ACL check - subscribe\n * #\n * ################################################################ */\n\n\nstatic int acl_check_subscribe(struct dynsec__data *data, struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist)\n{\n\tstruct dynsec__rolelist *rolelist, *rolelist_tmp = NULL;\n\tstruct dynsec__acl *acl, *acl_tmp = NULL;\n\tsize_t len;\n\tbool result;\n\tconst char *clientid, *username;\n\tbool has_wildcard;\n\n\tUNUSED(data);\n\n\tlen = strlen(ed->topic);\n\thas_wildcard = (strpbrk(ed->topic, \"+#\") != NULL);\n\n\tclientid = mosquitto_client_id(ed->client);\n\tusername = mosquitto_client_username(ed->client);\n\n\tHASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){\n\t\tif(rolelist->role->allow_wildcard_subs == false && has_wildcard == true){\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\t\tHASH_FIND(hh, rolelist->role->acls.subscribe_literal, ed->topic, len, acl);\n\t\tif(acl){\n\t\t\tif(acl->allow){\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t}\n\t\tHASH_ITER(hh, rolelist->role->acls.subscribe_pattern, acl, acl_tmp){\n\t\t\tif(mosquitto_sub_matches_acl_with_pattern(acl->topic, ed->topic, clientid, username, &result)){\n\t\t\t\t/* Invalid input, so deny */\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t\tif(result){\n\t\t\t\tif(acl->allow){\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}else{\n\t\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_NOT_FOUND;\n}\n\n\n/* ################################################################\n * #\n * # ACL check - unsubscribe\n * #\n * ################################################################ */\n\n\nstatic int acl_check_unsubscribe(struct dynsec__data *data, struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist)\n{\n\tstruct dynsec__rolelist *rolelist, *rolelist_tmp = NULL;\n\tstruct dynsec__acl *acl, *acl_tmp = NULL;\n\tsize_t len;\n\tbool result;\n\tconst char *clientid, *username;\n\n\tUNUSED(data);\n\n\tlen = strlen(ed->topic);\n\n\tclientid = mosquitto_client_id(ed->client);\n\tusername = mosquitto_client_username(ed->client);\n\n\tHASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){\n\t\tHASH_FIND(hh, rolelist->role->acls.unsubscribe_literal, ed->topic, len, acl);\n\t\tif(acl){\n\t\t\tif(acl->allow){\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t}\n\t\tHASH_ITER(hh, rolelist->role->acls.unsubscribe_pattern, acl, acl_tmp){\n\t\t\tif(mosquitto_sub_matches_acl_with_pattern(acl->topic, ed->topic, clientid, username, &result)){\n\t\t\t\t/* Invalid input, so deny */\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t\tif(result){\n\t\t\t\tif(acl->allow){\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t}else{\n\t\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_NOT_FOUND;\n}\n\n\n/* ################################################################\n * #\n * # ACL check - generic check\n * #\n * ################################################################ */\n\n\nstatic int acl_check(struct dynsec__data *data, struct mosquitto_evt_acl_check *ed, MOSQ_FUNC_acl_check check, bool acl_default_access)\n{\n\tstruct dynsec__client *client;\n\tstruct dynsec__grouplist *grouplist, *grouplist_tmp = NULL;\n\tconst char *username;\n\tint rc;\n\n\tusername = mosquitto_client_username(ed->client);\n\n\tif(username){\n\t\tclient = dynsec_clients__find(data, username);\n\t\tif(client == NULL){\n\t\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t\t}\n\n\t\t/* Client roles */\n\t\trc = check(data, ed, client->rolelist);\n\t\tif(rc != MOSQ_ERR_NOT_FOUND){\n\t\t\treturn rc;\n\t\t}\n\n\t\tHASH_ITER(hh, client->grouplist, grouplist, grouplist_tmp){\n\t\t\trc = check(data, ed, grouplist->group->rolelist);\n\t\t\tif(rc != MOSQ_ERR_NOT_FOUND){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}else if(data->anonymous_group){\n\t\t/* If we have a group for anonymous users, use that for checking. */\n\t\trc = check(data, ed, data->anonymous_group->rolelist);\n\t\tif(rc != MOSQ_ERR_NOT_FOUND){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tif(acl_default_access == false){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}else{\n\t\tif(!strncmp(ed->topic, \"$CONTROL\", strlen(\"$CONTROL\"))){\n\t\t\t/* We never give fall through access to $CONTROL topics, they must\n\t\t\t * be granted explicitly. */\n\t\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n}\n\n\n/* ################################################################\n * #\n * # ACL check - plugin callback\n * #\n * ################################################################ */\n\n\nint dynsec__acl_check_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\tstruct dynsec__data *data = userdata;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\t/* ACL checks are made in the order below until a match occurs, at which\n\t * point the decision is made.\n\t *\n\t * User roles in priority order highest to lowest.\n\t *    Roles have their ACLs checked in priority order, highest to lowest\n\t * Groups are processed in priority order highest to lowest\n\t *    Group roles are processed in priority order, highest to lowest\n\t *       Roles have their ACLs checked in priority order, highest to lowest\n\t */\n\n\tswitch(ed->access){\n\t\tcase MOSQ_ACL_SUBSCRIBE:\n\t\t\treturn acl_check(data, event_data, acl_check_subscribe, data->default_access.subscribe);\n\t\t\tbreak;\n\t\tcase MOSQ_ACL_UNSUBSCRIBE:\n\t\t\treturn acl_check(data, event_data, acl_check_unsubscribe, data->default_access.unsubscribe);\n\t\t\tbreak;\n\t\tcase MOSQ_ACL_WRITE: /* Client to broker */\n\t\t\treturn acl_check(data, event_data, acl_check_publish_c_send, data->default_access.publish_c_send);\n\t\t\tbreak;\n\t\tcase MOSQ_ACL_READ:\n\t\t\treturn acl_check(data, event_data, acl_check_publish_c_recv, data->default_access.publish_c_recv);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n"
  },
  {
    "path": "plugins/dynamic-security/auth.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <openssl/bio.h>\n#include <openssl/buffer.h>\n#include <openssl/evp.h>\n#include <openssl/rand.h>\n\n#include \"dynamic_security.h\"\n\n\n/* ################################################################\n * #\n * # Username/password check\n * #\n * ################################################################ */\n\n\nint dynsec_auth__basic_auth_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tstruct dynsec__data *data = userdata;\n\tstruct dynsec__client *client;\n\tconst char *clientid;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tif(ed->username == NULL || ed->password == NULL){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}\n\n\tclient = dynsec_clients__find(data, ed->username);\n\tif(client){\n\t\tif(client->disabled){\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t\tif(client->clientid){\n\t\t\tclientid = mosquitto_client_id(ed->client);\n\t\t\tif(clientid == NULL || strcmp(client->clientid, clientid)){\n\t\t\t\treturn MOSQ_ERR_AUTH;\n\t\t\t}\n\t\t}\n\t\tif(mosquitto_pw_verify(client->pw, ed->password) == MOSQ_ERR_SUCCESS){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}else{\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}\n}\n"
  },
  {
    "path": "plugins/dynamic-security/clientlist.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <uthash.h>\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\n/* ################################################################\n * #\n * # Plugin global variables\n * #\n * ################################################################ */\n\n/* ################################################################\n * #\n * # Function declarations\n * #\n * ################################################################ */\n\n/* ################################################################\n * #\n * # Local variables\n * #\n * ################################################################ */\n\n\n/* ################################################################\n * #\n * # Utility functions\n * #\n * ################################################################ */\n\n\nstatic int dynsec_clientlist__cmp(void *a, void *b)\n{\n\tstruct dynsec__clientlist *clientlist_a = a;\n\tstruct dynsec__clientlist *clientlist_b = b;\n\n\treturn strcmp(clientlist_a->client->username, clientlist_b->client->username);\n}\n\n\nvoid dynsec_clientlist__kick_all(struct dynsec__data *data, struct dynsec__clientlist *base_clientlist)\n{\n\tstruct dynsec__clientlist *clientlist, *clientlist_tmp;\n\n\tHASH_ITER(hh, base_clientlist, clientlist, clientlist_tmp){\n\t\tdynsec_kicklist__add(data, clientlist->client->username);\n\t}\n}\n\n\ncJSON *dynsec_clientlist__all_to_json(struct dynsec__clientlist *base_clientlist)\n{\n\tstruct dynsec__clientlist *clientlist, *clientlist_tmp;\n\tcJSON *j_clients, *j_client;\n\n\tj_clients = cJSON_CreateArray();\n\tif(j_clients == NULL){\n\t\treturn NULL;\n\t}\n\n\tHASH_ITER(hh, base_clientlist, clientlist, clientlist_tmp){\n\t\tj_client = cJSON_CreateObject();\n\t\tif(j_client == NULL){\n\t\t\tcJSON_Delete(j_clients);\n\t\t\treturn NULL;\n\t\t}\n\t\tcJSON_AddItemToArray(j_clients, j_client);\n\n\t\tif(cJSON_AddStringToObject(j_client, \"username\", clientlist->client->username) == NULL\n\t\t\t\t|| (clientlist->priority != -1 && cJSON_AddIntToObject(j_client, \"priority\", clientlist->priority) == NULL)\n\t\t\t\t){\n\n\t\t\tcJSON_Delete(j_clients);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn j_clients;\n}\n\n\nint dynsec_clientlist__add(struct dynsec__clientlist **base_clientlist, struct dynsec__client *client, int priority)\n{\n\tstruct dynsec__clientlist *clientlist;\n\n\tHASH_FIND(hh, *base_clientlist, client->username, strlen(client->username), clientlist);\n\tif(clientlist != NULL){\n\t\t/* Client is already in the group */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tclientlist = mosquitto_malloc(sizeof(struct dynsec__clientlist));\n\tif(clientlist == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tclientlist->client = client;\n\tclientlist->priority = priority;\n\tHASH_ADD_KEYPTR_INORDER(hh, *base_clientlist, client->username, strlen(client->username), clientlist, dynsec_clientlist__cmp);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid dynsec_clientlist__cleanup(struct dynsec__clientlist **base_clientlist)\n{\n\tstruct dynsec__clientlist *clientlist, *clientlist_tmp;\n\n\tHASH_ITER(hh, *base_clientlist, clientlist, clientlist_tmp){\n\t\tHASH_DELETE(hh, *base_clientlist, clientlist);\n\t\tmosquitto_free(clientlist);\n\t}\n}\n\n\nvoid dynsec_clientlist__remove(struct dynsec__clientlist **base_clientlist, struct dynsec__client *client)\n{\n\tstruct dynsec__clientlist *clientlist;\n\n\tHASH_FIND(hh, *base_clientlist, client->username, strlen(client->username), clientlist);\n\tif(clientlist){\n\t\tHASH_DELETE(hh, *base_clientlist, clientlist);\n\t\tmosquitto_free(clientlist);\n\t}\n}\n"
  },
  {
    "path": "plugins/dynamic-security/clients.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <uthash.h>\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\nstruct connection_array_context {\n\tconst char *username;\n\tcJSON *j_connections;\n};\n\n/* ################################################################\n * #\n * # Function declarations\n * #\n * ################################################################ */\n\nstatic int dynsec__remove_client_from_all_groups(struct dynsec__data *data, const char *username);\nstatic void client__remove_all_roles(struct dynsec__client *client);\n\n\n/* ################################################################\n * #\n * # Local variables\n * #\n * ################################################################ */\n\n\n/* ################################################################\n * #\n * # Utility functions\n * #\n * ################################################################ */\n\n\nstatic int client_cmp(void *a, void *b)\n{\n\tstruct dynsec__client *client_a = a;\n\tstruct dynsec__client *client_b = b;\n\n\treturn strcmp(client_a->username, client_b->username);\n}\n\nstruct dynsec__client *dynsec_clients__find(struct dynsec__data *data, const char *username)\n{\n\tstruct dynsec__client *client = NULL;\n\n\tif(username){\n\t\tHASH_FIND(hh, data->clients, username, strlen(username), client);\n\t}\n\treturn client;\n}\n\n\nstatic void client__free_item(struct dynsec__data *data, struct dynsec__client *client)\n{\n\tstruct dynsec__client *client_found;\n\tif(client == NULL){\n\t\treturn;\n\t}\n\n\tclient_found = dynsec_clients__find(data, client->username);\n\tif(client_found){\n\t\tHASH_DEL(data->clients, client_found);\n\t}\n\tdynsec_rolelist__cleanup(&client->rolelist);\n\tdynsec__remove_client_from_all_groups(data, client->username);\n\tmosquitto_pw_cleanup(client->pw);\n\tmosquitto_free(client->text_name);\n\tmosquitto_free(client->text_description);\n\tmosquitto_free(client->clientid);\n\tmosquitto_free(client);\n}\n\n\nvoid dynsec_clients__cleanup(struct dynsec__data *data)\n{\n\tstruct dynsec__client *client, *client_tmp;\n\n\tHASH_ITER(hh, data->clients, client, client_tmp){\n\t\tclient__free_item(data, client);\n\t}\n}\n\n\n/* ################################################################\n * #\n * # Config file load and save\n * #\n * ################################################################ */\n\n\nint dynsec_clients__config_load(struct dynsec__data *data, cJSON *tree)\n{\n\tcJSON *j_clients, *j_client = NULL, *j_roles, *j_role;\n\tstruct dynsec__client *client;\n\tstruct dynsec__role *role;\n\tint priority;\n\n\tj_clients = cJSON_GetObjectItem(tree, \"clients\");\n\tif(j_clients == NULL){\n\t\treturn 0;\n\t}\n\n\tif(cJSON_IsArray(j_clients) == false){\n\t\treturn 1;\n\t}\n\tcJSON_ArrayForEach(j_client, j_clients){\n\t\tif(cJSON_IsObject(j_client) == true){\n\t\t\t/* Username */\n\t\t\tconst char *username;\n\t\t\tif(json_get_string(j_client, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsize_t username_len = strlen(username);\n\t\t\tif(username_len == 0){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(dynsec_clients__find(data, username)){\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tclient = mosquitto_calloc(1, sizeof(struct dynsec__client) + username_len + 1);\n\t\t\tif(client == NULL){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tstrncpy(client->username, username, username_len);\n\n\t\t\tbool disabled;\n\t\t\tif(json_get_bool(j_client, \"disabled\", &disabled, false, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\tclient->disabled = disabled;\n\t\t\t}\n\n\t\t\tconst char *password;\n\t\t\tif(json_get_string(j_client, \"encoded_password\", &password, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\tif(!client->pw && mosquitto_pw_new(&client->pw, MOSQ_PW_DEFAULT)){\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\tif(mosquitto_pw_decode(client->pw, password) == MOSQ_ERR_NOMEM){\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\t/* sha512-pbkdf2 only */\n\t\t\t\tint iterations;\n\t\t\t\tconst char *salt;\n\t\t\t\tjson_get_int(j_client, \"iterations\", &iterations, true, 0);\n\t\t\t\tif(json_get_string(j_client, \"salt\", &salt, false) == MOSQ_ERR_SUCCESS\n\t\t\t\t\t\t&& json_get_string(j_client, \"password\", &password, false) == MOSQ_ERR_SUCCESS\n\t\t\t\t\t\t&& iterations > 0){\n\n\t\t\t\t\tchar buf[1024];\n\t\t\t\t\tif(!client->pw && mosquitto_pw_new(&client->pw, MOSQ_PW_SHA512_PBKDF2)){\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tsnprintf(buf, sizeof(buf), \"$7$%d$%s$%s\", iterations, salt, password);\n\t\t\t\t\tmosquitto_pw_decode(client->pw, buf);\n\t\t\t\t}else{\n\t\t\t\t\tmosquitto_pw_set_valid(client->pw, false);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Client id */\n\t\t\tconst char *clientid;\n\t\t\tif(json_get_string(j_client, \"clientid\", &clientid, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\tclient->clientid = mosquitto_strdup(clientid);\n\t\t\t\tif(client->clientid == NULL){\n\t\t\t\t\tmosquitto_free(client);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Text name */\n\t\t\tconst char *textname;\n\t\t\tif(json_get_string(j_client, \"textname\", &textname, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\tclient->text_name = mosquitto_strdup(textname);\n\t\t\t\tif(client->text_name == NULL){\n\t\t\t\t\tmosquitto_free(client->clientid);\n\t\t\t\t\tmosquitto_free(client);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Text description */\n\t\t\tconst char *textdescription;\n\t\t\tif(json_get_string(j_client, \"textdescription\", &textdescription, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\tclient->text_description = mosquitto_strdup(textdescription);\n\t\t\t\tif(client->text_description == NULL){\n\t\t\t\t\tmosquitto_free(client->text_name);\n\t\t\t\t\tmosquitto_free(client->clientid);\n\t\t\t\t\tmosquitto_free(client);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Roles */\n\t\t\tj_roles = cJSON_GetObjectItem(j_client, \"roles\");\n\t\t\tif(j_roles && cJSON_IsArray(j_roles)){\n\t\t\t\tcJSON_ArrayForEach(j_role, j_roles){\n\t\t\t\t\tif(cJSON_IsObject(j_role)){\n\t\t\t\t\t\tconst char *rolename;\n\t\t\t\t\t\tif(json_get_string(j_role, \"rolename\", &rolename, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\t\t\t\tjson_get_int(j_role, \"priority\", &priority, true, -1);\n\t\t\t\t\t\t\tif(priority > PRIORITY_MAX){\n\t\t\t\t\t\t\t\tpriority = PRIORITY_MAX;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif(priority < -PRIORITY_MAX){\n\t\t\t\t\t\t\t\tpriority = -PRIORITY_MAX;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\trole = dynsec_roles__find(data, rolename);\n\t\t\t\t\t\t\tdynsec_rolelist__client_add(client, role, priority);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tHASH_ADD(hh, data->clients, username, username_len, client);\n\t\t}\n\t}\n\tHASH_SORT(data->clients, client_cmp);\n\n\treturn 0;\n}\n\n\nstatic int dynsec__config_add_clients(struct dynsec__data *data, cJSON *j_clients)\n{\n\tstruct dynsec__client *client, *client_tmp;\n\tcJSON *j_client, *j_roles;\n\n\tHASH_ITER(hh, data->clients, client, client_tmp){\n\t\tj_client = cJSON_CreateObject();\n\t\tif(j_client == NULL){\n\t\t\treturn 1;\n\t\t}\n\t\tcJSON_AddItemToArray(j_clients, j_client);\n\n\t\tif(cJSON_AddStringToObject(j_client, \"username\", client->username) == NULL\n\t\t\t\t|| (client->clientid && cJSON_AddStringToObject(j_client, \"clientid\", client->clientid) == NULL)\n\t\t\t\t|| (client->text_name && cJSON_AddStringToObject(j_client, \"textname\", client->text_name) == NULL)\n\t\t\t\t|| (client->text_description && cJSON_AddStringToObject(j_client, \"textdescription\", client->text_description) == NULL)\n\t\t\t\t|| (client->disabled && cJSON_AddBoolToObject(j_client, \"disabled\", true) == NULL)\n\t\t\t\t){\n\n\t\t\treturn 1;\n\t\t}\n\n\t\tj_roles = dynsec_rolelist__all_to_json(client->rolelist);\n\t\tif(j_roles == NULL){\n\t\t\treturn 1;\n\t\t}\n\t\tcJSON_AddItemToObject(j_client, \"roles\", j_roles);\n\n\t\tif(mosquitto_pw_is_valid(client->pw)){\n\t\t\tif(cJSON_AddStringToObject(j_client, \"encoded_password\", mosquitto_pw_get_encoded(client->pw)) == NULL){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\nint dynsec_clients__config_save(struct dynsec__data *data, cJSON *tree)\n{\n\tcJSON *j_clients;\n\n\tif((j_clients = cJSON_AddArrayToObject(tree, \"clients\")) == NULL){\n\t\treturn 1;\n\t}\n\tif(dynsec__config_add_clients(data, j_clients)){\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\n\nint dynsec_clients__process_create(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username, *password, *clientid = NULL;\n\tconst char *text_name, *text_description;\n\tstruct dynsec__client *client;\n\tint rc;\n\tcJSON *j_groups, *j_group;\n\tint priority;\n\tconst char *admin_clientid, *admin_username;\n\tsize_t username_len;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tusername_len = strlen(username);\n\tif(username_len == 0){\n\t\tmosquitto_control_command_reply(cmd, \"Empty username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)username_len) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"password\", &password, true) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing password\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"clientid\", &clientid, true) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing client id\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(clientid && mosquitto_validate_utf8(clientid, (int)strlen(clientid)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Client ID not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\n\tif(json_get_string(cmd->j_command, \"textname\", &text_name, true) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing textname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textdescription\", &text_description, true) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing textdescription\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client){\n\t\tmosquitto_control_command_reply(cmd, \"Client already exists\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tclient = mosquitto_calloc(1, sizeof(struct dynsec__client) + username_len + 1);\n\tif(client == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tstrncpy(client->username, username, username_len);\n\n\tif(text_name){\n\t\tclient->text_name = mosquitto_strdup(text_name);\n\t\tif(client->text_name == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\tclient__free_item(data, client);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\tif(text_description){\n\t\tclient->text_description = mosquitto_strdup(text_description);\n\t\tif(client->text_description == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\tclient__free_item(data, client);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\tif(password){\n\t\tif(mosquitto_pw_new(&client->pw, MOSQ_PW_DEFAULT)\n\t\t\t\t|| mosquitto_pw_hash_encoded(client->pw, password)\n\t\t\t\t){\n\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\tclient__free_item(data, client);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\tif(clientid && strlen(clientid) > 0){\n\t\tclient->clientid = mosquitto_strdup(clientid);\n\t\tif(client->clientid == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\tclient__free_item(data, client);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\trc = dynsec_rolelist__load_from_json(data, cmd->j_command, &client->rolelist);\n\tif(rc == MOSQ_ERR_SUCCESS || rc == ERR_LIST_NOT_FOUND){\n\t}else if(rc == MOSQ_ERR_NOT_FOUND){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\tclient__free_item(data, client);\n\t\treturn MOSQ_ERR_INVAL;\n\t}else{\n\t\tif(rc == MOSQ_ERR_INVAL){\n\t\t\tmosquitto_control_command_reply(cmd, \"'roles' not an array or missing/invalid rolename\");\n\t\t}else{\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t}\n\t\tclient__free_item(data, client);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* Must add user before groups, otherwise adding groups will fail */\n\tHASH_ADD_INORDER(hh, data->clients, username, username_len, client, client_cmp);\n\n\tj_groups = cJSON_GetObjectItem(cmd->j_command, \"groups\");\n\tif(j_groups && cJSON_IsArray(j_groups)){\n\t\tcJSON_ArrayForEach(j_group, j_groups){\n\t\t\tif(cJSON_IsObject(j_group)){\n\t\t\t\tconst char *groupname;\n\t\t\t\tif(json_get_string(j_group, \"groupname\", &groupname, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\t\tjson_get_int(j_group, \"priority\", &priority, true, -1);\n\t\t\t\t\tif(priority > PRIORITY_MAX){\n\t\t\t\t\t\tpriority = PRIORITY_MAX;\n\t\t\t\t\t}\n\t\t\t\t\trc = dynsec_groups__add_client(data, username, groupname, priority, false);\n\t\t\t\t\tif(rc == ERR_GROUP_NOT_FOUND){\n\t\t\t\t\t\tmosquitto_control_command_reply(cmd, \"Group not found\");\n\t\t\t\t\t\tclient__free_item(data, client);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\t\t\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\t\t\t\tclient__free_item(data, client);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tdynsec__config_batch_save(data);\n\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | createClient | username=%s | password=%s\",\n\t\t\tadmin_clientid, admin_username, username, password?\"*****\":\"no password\");\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_clients__process_delete(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username;\n\tstruct dynsec__client *client;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client){\n\t\tdynsec__remove_client_from_all_groups(data, username);\n\t\tclient__remove_all_roles(client);\n\t\tclient__free_item(data, client);\n\t\tdynsec__config_batch_save(data);\n\t\tmosquitto_control_command_reply(cmd, NULL);\n\n\t\t/* Enforce any changes */\n\t\tdynsec_kicklist__add(data, username);\n\n\t\tadmin_clientid = mosquitto_client_id(cmd->client);\n\t\tadmin_username = mosquitto_client_username(cmd->client);\n\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | deleteClient | username=%s\",\n\t\t\t\tadmin_clientid, admin_username, username);\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_clients__process_disable(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username;\n\tstruct dynsec__client *client;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tclient->disabled = true;\n\n\tdynsec_kicklist__add(data, username);\n\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | disableClient | username=%s\",\n\t\t\tadmin_clientid, admin_username, username);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_clients__process_enable(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username;\n\tstruct dynsec__client *client;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tclient->disabled = false;\n\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | enableClient | username=%s\",\n\t\t\tadmin_clientid, admin_username, username);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_clients__process_set_id(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username, *clientid;\n\tchar *clientid_heap = NULL;\n\tstruct dynsec__client *client;\n\tsize_t slen;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"clientid\", &clientid, true) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing client ID\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(clientid){\n\t\tslen = strlen(clientid);\n\t\tif(mosquitto_validate_utf8(clientid, (int)slen) != MOSQ_ERR_SUCCESS){\n\t\t\tmosquitto_control_command_reply(cmd, \"Client ID not valid UTF-8\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(slen > 0){\n\t\t\tclientid_heap = mosquitto_strdup(clientid);\n\t\t\tif(clientid_heap == NULL){\n\t\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}else{\n\t\t\tclientid_heap = NULL;\n\t\t}\n\t}\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client == NULL){\n\t\tmosquitto_free(clientid_heap);\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tmosquitto_free(client->clientid);\n\tclient->clientid = clientid_heap;\n\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\t/* Enforce any changes */\n\tdynsec_kicklist__add(data, username);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | setClientId | username=%s | clientid=%s\",\n\t\t\tadmin_clientid, admin_username, username, client->clientid);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int client__set_password(struct dynsec__client *client, const char *password)\n{\n\tif(!client->pw){\n\t\tif(mosquitto_pw_new(&client->pw, MOSQ_PW_DEFAULT)){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\treturn mosquitto_pw_hash_encoded(client->pw, password);\n}\n\n\nint dynsec_clients__process_set_password(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username, *password;\n\tstruct dynsec__client *client;\n\tint rc;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"password\", &password, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing password\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(strlen(password) == 0){\n\t\tmosquitto_control_command_reply(cmd, \"Empty password is not allowed\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\trc = client__set_password(client, password);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tdynsec__config_batch_save(data);\n\t\tmosquitto_control_command_reply(cmd, NULL);\n\n\t\t/* Enforce any changes */\n\t\tdynsec_kicklist__add(data, username);\n\n\t\tadmin_clientid = mosquitto_client_id(cmd->client);\n\t\tadmin_username = mosquitto_client_username(cmd->client);\n\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | setClientPassword | username=%s | password=******\",\n\t\t\t\tadmin_clientid, admin_username, username);\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t}\n\treturn rc;\n}\n\n\nstatic void client__add_new_roles(struct dynsec__client *client, struct dynsec__rolelist *base_rolelist)\n{\n\tstruct dynsec__rolelist *rolelist, *rolelist_tmp;\n\n\tHASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){\n\t\tdynsec_rolelist__client_add(client, rolelist->role, rolelist->priority);\n\t}\n}\n\n\nstatic void client__remove_all_roles(struct dynsec__client *client)\n{\n\tstruct dynsec__rolelist *rolelist, *rolelist_tmp;\n\n\tHASH_ITER(hh, client->rolelist, rolelist, rolelist_tmp){\n\t\tdynsec_rolelist__client_remove(client, rolelist->role);\n\t}\n}\n\n\nint dynsec_clients__process_modify(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username;\n\tchar *clientid = NULL;\n\tconst char *password = NULL;\n\tchar *text_name = NULL, *text_description = NULL;\n\tbool have_clientid = false, have_text_name = false, have_text_description = false, have_rolelist = false, have_password = false;\n\tstruct dynsec__client *client;\n\tstruct dynsec__group *group;\n\tstruct dynsec__rolelist *rolelist = NULL;\n\tconst char *str;\n\tint rc;\n\tint priority;\n\tcJSON *j_group, *j_groups;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"clientid\", &str, false) == MOSQ_ERR_SUCCESS){\n\t\thave_clientid = true;\n\t\tif(str && strlen(str) > 0){\n\t\t\tclientid = mosquitto_strdup(str);\n\t\t\tif(clientid == NULL){\n\t\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else{\n\t\t\tclientid = NULL;\n\t\t}\n\t}\n\n\tif(json_get_string(cmd->j_command, \"password\", &password, false) == MOSQ_ERR_SUCCESS){\n\t\tif(strlen(password) > 0){\n\t\t\thave_password = true;\n\t\t}\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textname\", &str, false) == MOSQ_ERR_SUCCESS){\n\t\thave_text_name = true;\n\t\ttext_name = mosquitto_strdup(str);\n\t\tif(text_name == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textdescription\", &str, false) == MOSQ_ERR_SUCCESS){\n\t\thave_text_description = true;\n\t\ttext_description = mosquitto_strdup(str);\n\t\tif(text_description == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\trc = dynsec_rolelist__load_from_json(data, cmd->j_command, &rolelist);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\thave_rolelist = true;\n\t}else if(rc == ERR_LIST_NOT_FOUND){\n\t\t/* There was no list in the JSON, so no modification */\n\t}else if(rc == MOSQ_ERR_NOT_FOUND){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto error;\n\t}else{\n\t\tif(rc == MOSQ_ERR_INVAL){\n\t\t\tmosquitto_control_command_reply(cmd, \"'roles' not an array or missing/invalid rolename\");\n\t\t}else{\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t}\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto error;\n\t}\n\n\tj_groups = cJSON_GetObjectItem(cmd->j_command, \"groups\");\n\tif(j_groups && cJSON_IsArray(j_groups)){\n\t\t/* Iterate through list to check all groups are valid */\n\t\tcJSON_ArrayForEach(j_group, j_groups){\n\t\t\tif(cJSON_IsObject(j_group)){\n\t\t\t\tconst char *groupname;\n\t\t\t\tif(json_get_string(j_group, \"groupname\", &groupname, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\t\tgroup = dynsec_groups__find(data, groupname);\n\t\t\t\t\tif(group == NULL){\n\t\t\t\t\t\tmosquitto_control_command_reply(cmd, \"'groups' contains an object with a 'groupname' that does not exist\");\n\t\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tmosquitto_control_command_reply(cmd, \"'groups' contains an object with an invalid 'groupname'\");\n\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdynsec__remove_client_from_all_groups(data, username);\n\t\tcJSON_ArrayForEach(j_group, j_groups){\n\t\t\tif(cJSON_IsObject(j_group)){\n\t\t\t\tconst char *groupname;\n\t\t\t\tif(json_get_string(j_group, \"groupname\", &groupname, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\t\tjson_get_int(j_group, \"priority\", &priority, true, -1);\n\t\t\t\t\tif(priority > PRIORITY_MAX){\n\t\t\t\t\t\tpriority = PRIORITY_MAX;\n\t\t\t\t\t}\n\t\t\t\t\tdynsec_groups__add_client(data, username, groupname, priority, false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif(have_password){\n\t\t/* FIXME - This is the one call that will result in modification on internal error - note that groups have already been modified */\n\t\trc = client__set_password(client, password);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\tmosquitto_kick_client_by_username(username, false);\n\t\t\t/* If this fails we have the situation that the password is set as\n\t\t\t * invalid, but the config isn't saved, so restarting the broker\n\t\t\t * *now* will mean the client can log in again. This might be\n\t\t\t * \"good\", but is inconsistent, so save the config to be\n\t\t\t * consistent. */\n\t\t\tdynsec__config_batch_save(data);\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\tif(have_clientid){\n\t\tmosquitto_free(client->clientid);\n\t\tclient->clientid = clientid;\n\t}\n\n\tif(have_text_name){\n\t\tmosquitto_free(client->text_name);\n\t\tclient->text_name = text_name;\n\t}\n\n\tif(have_text_description){\n\t\tmosquitto_free(client->text_description);\n\t\tclient->text_description = text_description;\n\t}\n\n\tif(have_rolelist){\n\t\tclient__remove_all_roles(client);\n\t\tclient__add_new_roles(client, rolelist);\n\t\tdynsec_rolelist__cleanup(&rolelist);\n\t}\n\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\t/* Enforce any changes */\n\tdynsec_kicklist__add(data, username);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | modifyClient | username=%s\",\n\t\t\tadmin_clientid, admin_username, username);\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tmosquitto_free(clientid);\n\tmosquitto_free(text_name);\n\tmosquitto_free(text_description);\n\tdynsec_rolelist__cleanup(&rolelist);\n\treturn rc;\n}\n\n\nstatic int dynsec__remove_client_from_all_groups(struct dynsec__data *data, const char *username)\n{\n\tstruct dynsec__grouplist *grouplist, *grouplist_tmp;\n\tstruct dynsec__client *client;\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client){\n\t\tHASH_ITER(hh, client->grouplist, grouplist, grouplist_tmp){\n\t\t\tdynsec_groups__remove_client(data, username, grouplist->group->groupname, false);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int dynsec__add_client_address(const struct mosquitto *client, void *context_ptr)\n{\n\tstruct connection_array_context *functor_context = (struct connection_array_context *)context_ptr;\n\tconst char *username = mosquitto_client_username(client);\n\n\tif((username == NULL && functor_context->username == NULL)\n\t\t\t|| (username && functor_context->username && !strcmp(functor_context->username, username))){\n\n\t\tcJSON *j_connection = cJSON_CreateObject();\n\t\tconst char *address;\n\t\tif(!j_connection){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tif((address = mosquitto_client_address(client)) && !cJSON_AddStringToObject(j_connection, \"address\", address)){\n\t\t\tcJSON_Delete(j_connection);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tcJSON_AddItemToArray(functor_context->j_connections, j_connection);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic cJSON *dynsec_connections__all_to_json(const char *username, const char *clientid)\n{\n\tstruct connection_array_context functor_context = { username, cJSON_CreateArray()};\n\n\tif(functor_context.j_connections == NULL){\n\t\treturn NULL;\n\t}\n\n\tif(clientid){\n\t\tconst struct mosquitto *client = mosquitto_client(clientid);\n\t\tif(client && dynsec__add_client_address(client, &functor_context) != MOSQ_ERR_SUCCESS){\n\t\t\tcJSON_Delete(functor_context.j_connections);\n\t\t\treturn NULL;\n\t\t}\n\t}else{\n\t\tif(mosquitto_apply_on_all_clients(&dynsec__add_client_address, &functor_context) != MOSQ_ERR_SUCCESS){\n\t\t\tcJSON_Delete(functor_context.j_connections);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn functor_context.j_connections;\n}\n\n\nstatic cJSON *add_client_to_json(struct dynsec__client *client, bool verbose)\n{\n\tcJSON *j_client = NULL;\n\n\tif(verbose){\n\t\tcJSON *j_groups, *j_roles, *j_connections;\n\n\t\tj_client = cJSON_CreateObject();\n\t\tif(j_client == NULL){\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif(cJSON_AddStringToObject(j_client, \"username\", client->username) == NULL\n\t\t\t\t|| (client->clientid && cJSON_AddStringToObject(j_client, \"clientid\", client->clientid) == NULL)\n\t\t\t\t|| (client->text_name && cJSON_AddStringToObject(j_client, \"textname\", client->text_name) == NULL)\n\t\t\t\t|| (client->text_description && cJSON_AddStringToObject(j_client, \"textdescription\", client->text_description) == NULL)\n\t\t\t\t|| (client->disabled && cJSON_AddBoolToObject(j_client, \"disabled\", client->disabled) == NULL)\n\t\t\t\t){\n\n\t\t\tcJSON_Delete(j_client);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tj_roles = dynsec_rolelist__all_to_json(client->rolelist);\n\t\tif(j_roles == NULL){\n\t\t\tcJSON_Delete(j_client);\n\t\t\treturn NULL;\n\t\t}\n\t\tcJSON_AddItemToObject(j_client, \"roles\", j_roles);\n\n\t\tj_groups = dynsec_grouplist__all_to_json(client->grouplist);\n\t\tif(j_groups == NULL){\n\t\t\tcJSON_Delete(j_client);\n\t\t\treturn NULL;\n\t\t}\n\t\tcJSON_AddItemToObject(j_client, \"groups\", j_groups);\n\n\t\tj_connections = dynsec_connections__all_to_json(client->username, client->clientid);\n\t\tif(j_connections == NULL){\n\t\t\tcJSON_Delete(j_client);\n\t\t\treturn NULL;\n\t\t}\n\t\tcJSON_AddItemToObject(j_client, \"connections\", j_connections);\n\t}else{\n\t\tj_client = cJSON_CreateString(client->username);\n\t\tif(j_client == NULL){\n\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn j_client;\n}\n\n\nint dynsec_clients__process_get(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username;\n\tstruct dynsec__client *client;\n\tcJSON *tree, *j_client, *j_data;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(cJSON_AddStringToObject(tree, \"command\", \"getClient\") == NULL\n\t\t\t|| (j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tj_client = add_client_to_json(client, true);\n\tif(j_client == NULL){\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToObject(j_data, \"client\", j_client);\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | getClient | username=%s\",\n\t\t\tadmin_clientid, admin_username, username);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_clients__process_list(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tbool verbose;\n\tstruct dynsec__client *client, *client_tmp;\n\tcJSON *tree, *j_clients, *j_client, *j_data;\n\tint i, count, offset;\n\tconst char *admin_clientid, *admin_username;\n\n\tjson_get_bool(cmd->j_command, \"verbose\", &verbose, true, false);\n\tjson_get_int(cmd->j_command, \"count\", &count, true, -1);\n\tjson_get_int(cmd->j_command, \"offset\", &offset, true, 0);\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(cJSON_AddStringToObject(tree, \"command\", \"listClients\") == NULL\n\t\t\t|| (j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL\n\t\t\t|| cJSON_AddIntToObject(j_data, \"totalCount\", (int)HASH_CNT(hh, data->clients)) == NULL\n\t\t\t|| (j_clients = cJSON_AddArrayToObject(j_data, \"clients\")) == NULL\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\ti = 0;\n\tHASH_ITER(hh, data->clients, client, client_tmp){\n\t\tif(i>=offset){\n\t\t\tj_client = add_client_to_json(client, verbose);\n\t\t\tif(j_client == NULL){\n\t\t\t\tcJSON_Delete(tree);\n\t\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tcJSON_AddItemToArray(j_clients, j_client);\n\n\t\t\tif(count >= 0){\n\t\t\t\tcount--;\n\t\t\t\tif(count <= 0){\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ti++;\n\t}\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | listClients | verbose=%s | count=%d | offset=%d\",\n\t\t\tadmin_clientid, admin_username, verbose?\"true\":\"false\", count, offset);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_clients__process_add_role(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username, *rolename;\n\tstruct dynsec__client *client;\n\tstruct dynsec__role *role;\n\tint priority;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Role name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tjson_get_int(cmd->j_command, \"priority\", &priority, true, -1);\n\tif(priority > PRIORITY_MAX){\n\t\tpriority = PRIORITY_MAX;\n\t}\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\trole = dynsec_roles__find(data, rolename);\n\tif(role == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(dynsec_rolelist__client_add(client, role, priority) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\t/* Enforce any changes */\n\tdynsec_kicklist__add(data, username);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | addClientRole | username=%s | rolename=%s | priority=%d\",\n\t\t\tadmin_clientid, admin_username, username, rolename, priority);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_clients__process_remove_role(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username, *rolename;\n\tstruct dynsec__client *client;\n\tstruct dynsec__role *role;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Role name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\trole = dynsec_roles__find(data, rolename);\n\tif(role == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tdynsec_rolelist__client_remove(client, role);\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\t/* Enforce any changes */\n\tdynsec_kicklist__add(data, username);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | removeClientRole | username=%s | rolename=%s\",\n\t\t\tadmin_clientid, admin_username, username, rolename);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/dynamic-security/config.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\n\nstatic int dynsec__general_config_load(struct dynsec__data *data, cJSON *tree)\n{\n\tcJSON *j_default_access;\n\n\tjson_get_int64(tree, \"changeIndex\", &data->changeindex, true, 0);\n\n\tj_default_access = cJSON_GetObjectItem(tree, \"defaultACLAccess\");\n\tif(j_default_access && cJSON_IsObject(j_default_access)){\n\t\tjson_get_bool(j_default_access, ACL_TYPE_PUB_C_SEND, &data->default_access.publish_c_send, true, false);\n\t\tjson_get_bool(j_default_access, ACL_TYPE_PUB_C_RECV, &data->default_access.publish_c_recv, true, false);\n\t\tjson_get_bool(j_default_access, ACL_TYPE_SUB_GENERIC, &data->default_access.subscribe, true, false);\n\t\tjson_get_bool(j_default_access, ACL_TYPE_UNSUB_GENERIC, &data->default_access.unsubscribe, true, false);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int dynsec__general_config_save(struct dynsec__data *data, cJSON *tree)\n{\n\tcJSON *j_default_access;\n\n\tcJSON_AddIntToObject(tree, \"changeIndex\", data->changeindex);\n\n\tj_default_access = cJSON_CreateObject();\n\tif(j_default_access == NULL){\n\t\treturn 1;\n\t}\n\tcJSON_AddItemToObject(tree, \"defaultACLAccess\", j_default_access);\n\n\tif(cJSON_AddBoolToObject(j_default_access, ACL_TYPE_PUB_C_SEND, data->default_access.publish_c_send) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_default_access, ACL_TYPE_PUB_C_RECV, data->default_access.publish_c_recv) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_default_access, ACL_TYPE_SUB_GENERIC, data->default_access.subscribe) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_default_access, ACL_TYPE_UNSUB_GENERIC, data->default_access.unsubscribe) == NULL\n\t\t\t){\n\n\t\treturn 1;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec__config_from_json(struct dynsec__data *data, const char *json_str)\n{\n\tcJSON *tree;\n\n\ttree = cJSON_Parse(json_str);\n\tif(tree == NULL){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error loading Dynamic security plugin config: File is not valid JSON.\");\n\t\treturn 1;\n\t}\n\n\tif(dynsec__general_config_load(data, tree)\n\t\t\t|| dynsec_roles__config_load(data, tree)\n\t\t\t|| dynsec_clients__config_load(data, tree)\n\t\t\t|| dynsec_groups__config_load(data, tree)\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\treturn 1;\n\t}\n\n\tcJSON_Delete(tree);\n\treturn 0;\n}\n\n\nint dynsec__config_load(struct dynsec__data *data)\n{\n\tFILE *fptr;\n\tlong flen_l;\n\tsize_t flen;\n\tchar *json_str;\n\tint rc;\n\n\t/* Load from file */\n\tfptr = fopen(data->config_file, \"rb\");\n\tif(fptr == NULL){\n\t\t/* Attempt to initialise a new config file */\n\t\tif(dynsec__config_init(data) == MOSQ_ERR_SUCCESS){\n\t\t\t/* If it works, try to open the file again */\n\t\t\tfptr = fopen(data->config_file, \"rb\");\n\t\t}\n\n\t\tif(fptr == NULL){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR,\n\t\t\t\t\t\"Error loading Dynamic security plugin config: File is not readable - check permissions.\");\n\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\n\tfseek(fptr, 0, SEEK_END);\n\tflen_l = ftell(fptr);\n\tif(flen_l < 0){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error loading Dynamic security plugin config: %s\", strerror(errno));\n\t\tfclose(fptr);\n\t\treturn 1;\n\t}else if(flen_l == 0){\n\t\tfclose(fptr);\n\t\treturn 0;\n\t}\n\tflen = (size_t)flen_l;\n\tfseek(fptr, 0, SEEK_SET);\n\tjson_str = mosquitto_calloc(flen+1, sizeof(char));\n\tif(json_str == NULL){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\tfclose(fptr);\n\t\treturn 1;\n\t}\n\tif(fread(json_str, 1, flen, fptr) != flen){\n\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Error loading Dynamic security plugin config: Unable to read file contents.\\n\");\n\t\tmosquitto_free(json_str);\n\t\tfclose(fptr);\n\t\treturn 1;\n\t}\n\tfclose(fptr);\n\n\trc = dynsec__config_from_json(data, json_str);\n\tfree(json_str);\n\treturn rc;\n}\n\n\nchar *dynsec__config_to_json(struct dynsec__data *data)\n{\n\tcJSON *tree;\n\tchar *json_str;\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\treturn NULL;\n\t}\n\n\tif(dynsec__general_config_save(data, tree)\n\t\t\t|| dynsec_clients__config_save(data, tree)\n\t\t\t|| dynsec_groups__config_save(data, tree)\n\t\t\t|| dynsec_roles__config_save(data, tree)){\n\n\t\tcJSON_Delete(tree);\n\t\treturn NULL;\n\t}\n\n\t/* Print json to string */\n\tjson_str = cJSON_Print(tree);\n\tcJSON_Delete(tree);\n\treturn json_str;\n}\n\n\nvoid dynsec__log_write_error(const char *msg)\n{\n\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error saving Dynamic security plugin config: %s\", msg);\n}\n\n\nint dynsec__write_json_config(FILE *fptr, void *user_data)\n{\n\tstruct dynsec__data *data = (struct dynsec__data *)user_data;\n\tchar *json_str;\n\tsize_t json_str_len;\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tjson_str = dynsec__config_to_json(data);\n\tif(json_str == NULL){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error saving Dynamic security plugin config: Out of memory.\\n\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tjson_str_len = strlen(json_str);\n\n\tif(fwrite(json_str, 1, json_str_len, fptr) != json_str_len){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error saving Dynamic security plugin config: Cannot write whole config (%ld) bytes to file %s\", json_str_len, data->config_file);\n\t\trc = MOSQ_ERR_UNKNOWN;\n\t}\n\n\tmosquitto_free(json_str);\n\treturn rc;\n}\n\n\nvoid dynsec__config_batch_save(struct dynsec__data *data)\n{\n\tdata->changeindex++;\n\tdata->need_save = true;\n}\n\n\nvoid dynsec__config_save(struct dynsec__data *data)\n{\n\tdata->need_save = false;\n\tmosquitto_write_file(data->config_file, true, &dynsec__write_json_config, data, &dynsec__log_write_error);\n}\n"
  },
  {
    "path": "plugins/dynamic-security/config_init.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <ctype.h>\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <openssl/rand.h>\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\nconst char pw_chars[] = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=_+[]{}@#~,./<>?\";\n\n\nstatic int add_default_access(cJSON *j_tree)\n{\n\tcJSON *j_default_access;\n\n\tj_default_access = cJSON_AddObjectToObject(j_tree, \"defaultACLAccess\");\n\tif(j_default_access == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\t/* Set default behaviour:\n\t * * Client can not publish to the broker by default.\n\t * * Broker *CAN* publish to the client by default.\n\t * * Client con not subscribe to topics by default.\n\t * * Client *CAN* unsubscribe from topics by default.\n\t */\n\tif(cJSON_AddBoolToObject(j_default_access, \"publishClientSend\", false) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_default_access, \"publishClientReceive\", true) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_default_access, \"subscribe\", false) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_default_access, \"unsubscribe\", true) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int get_password_from_init_file(struct dynsec__data *data, char **pw)\n{\n\tFILE *fptr;\n\tchar buf[1024];\n\tint pos;\n\n\tif(data->password_init_file == NULL){\n\t\t*pw = NULL;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tfptr = mosquitto_fopen(data->password_init_file, \"rt\", true);\n\tif(!fptr){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Unable to get initial password from '%s', file not accessible.\", data->password_init_file);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!fgets(buf, sizeof(buf), fptr)){\n\t\tfclose(fptr);\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Unable to get initial password from '%s', file empty.\", data->password_init_file);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tfclose(fptr);\n\n\tpos = (int)strlen(buf)-1;\n\twhile(pos >= 0 && isspace((unsigned char)buf[pos])){\n\t\tbuf[pos] = '\\0';\n\t\tpos--;\n\t}\n\tif(strlen(buf) == 0){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Unable to get initial password from '%s', password is empty.\", data->password_init_file);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\t*pw = strdup(buf);\n\tif(!*pw){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Unable to get initial password from '%s', out of memory.\", data->password_init_file);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\n/* Generate a password for the admin user\n *\n * Uses passwords from, in order:\n *\n * * The password defined in the plugin_opt_password_init_file file\n * * The contents of the MOSQUITTO_DYNSEC_PASSWORD environment variable\n * * Randomly generated passwords for \"admin\", \"user\", stored in plain text at '<plugin_opt_config_file>.pw'\n */\nstatic int generate_password(struct dynsec__data *data, cJSON *j_client, char **password)\n{\n\tstruct mosquitto_pw *pw;\n\tchar *pwenv;\n\n\tif(data->init_mode == dpwim_file){\n\t\tif(get_password_from_init_file(data, password)){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}else if(data->init_mode == dpwim_env){\n\t\tpwenv = getenv(\"MOSQUITTO_DYNSEC_PASSWORD\");\n\t\tif(pwenv == NULL || strlen(pwenv) < 12){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Not generating dynsec config, MOSQUITTO_DYNSEC_PASSWORD must be at least 12 characters.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\t*password = strdup(pwenv);\n\t\tif(*password == NULL){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}else{\n\t\tunsigned char vb;\n\t\tunsigned long v;\n\t\tsize_t len;\n\t\tconst size_t pwlen = 20;\n\t\t*password = malloc(pwlen+1);\n\t\tif(*password == NULL){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tlen = sizeof(pw_chars)-1;\n\t\tfor(size_t i=0; i<pwlen; i++){\n\t\t\tdo{\n\t\t\t\tif(RAND_bytes(&vb, 1) != 1){\n\t\t\t\t\tfree(*password);\n\t\t\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t\t\t}\n\t\t\t\tv = vb;\n\t\t\t}while(v >= (RAND_MAX - (RAND_MAX % len)));\n\t\t\t(*password)[i] = pw_chars[v%len];\n\t\t}\n\t\t(*password)[pwlen] = '\\0';\n\t}\n\n\tif(mosquitto_pw_new(&pw, MOSQ_PW_DEFAULT) != MOSQ_ERR_SUCCESS\n\t\t\t|| mosquitto_pw_hash_encoded(pw, *password) != MOSQ_ERR_SUCCESS\n\t\t\t|| cJSON_AddStringToObject(j_client, \"encoded_password\", mosquitto_pw_get_encoded(pw)) == NULL){\n\n\t\tmosquitto_pw_cleanup(pw);\n\t\tfree(*password);\n\t\t*password = NULL;\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tmosquitto_pw_cleanup(pw);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int client_role_add(cJSON *j_roles, const char *rolename)\n{\n\tcJSON *j_role;\n\n\tj_role = cJSON_CreateObject();\n\tif(j_role == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(j_roles, j_role);\n\tif(cJSON_AddStringToObject(j_role, \"rolename\", rolename) == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nstatic int client_add_admin(struct dynsec__data *data, FILE *pwfile, cJSON *j_clients)\n{\n\tcJSON *j_client, *j_roles;\n\tchar *password = NULL;\n\n\tj_client = cJSON_CreateObject();\n\tif(j_client == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tif(generate_password(data, j_client, &password)){\n\t\tcJSON_Delete(j_client);\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tcJSON_AddItemToArray(j_clients, j_client);\n\tif(cJSON_AddStringToObject(j_client, \"username\", \"admin\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_client, \"textname\", \"Admin user\") == NULL\n\t\t\t|| (j_roles = cJSON_AddArrayToObject(j_client, \"roles\")) == NULL\n\t\t\t){\n\n\t\tcJSON_Delete(j_client);\n\t\tfree(password);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(client_role_add(j_roles, \"super-admin\")\n\t\t\t|| client_role_add(j_roles, \"sys-observe\")\n\t\t\t|| client_role_add(j_roles, \"topic-observe\")){\n\n\t\tfree(password);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(data->init_mode == dpwim_random){\n\t\tfprintf(pwfile, \"admin %s\\n\", password);\n\t}\n\tfree(password);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int client_add_user(struct dynsec__data *data, FILE *pwfile, cJSON *j_clients)\n{\n\tcJSON *j_client, *j_roles;\n\tchar *password = NULL;\n\n\tif(data->init_mode != dpwim_random){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tj_client = cJSON_CreateObject();\n\tif(j_client == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(generate_password(data, j_client, &password)){\n\t\tcJSON_Delete(j_client);\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(cJSON_AddStringToObject(j_client, \"username\", \"democlient\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_client, \"textname\", \"Demonstration client with full read/write access to the '#' topic hierarchy.\") == NULL\n\t\t\t|| (j_roles = cJSON_AddArrayToObject(j_client, \"roles\")) == NULL\n\t\t\t){\n\n\t\tfree(password);\n\t\tcJSON_Delete(j_client);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(j_clients, j_client);\n\n\tif(client_role_add(j_roles, \"client\")){\n\t\tfree(password);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tfprintf(pwfile, \"democlient %s\\n\", password);\n\tfree(password);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int add_clients(struct dynsec__data *data, cJSON *j_tree)\n{\n\tcJSON *j_clients;\n\tchar *pwfile;\n\tsize_t len;\n\tFILE *fptr = NULL;\n\n\tif(data->init_mode == dpwim_random){\n\t\tlen = strlen(data->config_file) + 5;\n\t\tpwfile = malloc(len);\n\t\tif(pwfile == NULL){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tsnprintf(pwfile, len, \"%s.pw\", data->config_file);\n\t\tfptr = mosquitto_fopen(pwfile, \"wb\", true);\n\t\tfree(pwfile);\n\t\tif(fptr == NULL){\n\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\n\tj_clients = cJSON_AddArrayToObject(j_tree, \"clients\");\n\tif(j_clients == NULL){\n\t\tif(fptr){\n\t\t\tfclose(fptr);\n\t\t}\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(client_add_admin(data, fptr, j_clients)\n\t\t\t|| client_add_user(data, fptr, j_clients)\n\t\t\t){\n\n\t\tif(fptr){\n\t\t\tfclose(fptr);\n\t\t}\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(fptr){\n\t\tfclose(fptr);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int group_add_anon(cJSON *j_groups)\n{\n\tcJSON *j_group;\n\n\tj_group = cJSON_CreateObject();\n\tif(j_group == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tcJSON_AddItemToArray(j_groups, j_group);\n\tif(cJSON_AddStringToObject(j_group, \"groupname\", \"unauthenticated\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_group, \"textname\", \"Unauthenticated group\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_group, \"textdescription\", \"If unauthenticated access is allowed, this group can be used to define roles for clients that connect without a password.\") == NULL\n\t\t\t|| cJSON_AddArrayToObject(j_group, \"roles\") == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int add_groups(cJSON *j_tree)\n{\n\tcJSON *j_groups;\n\n\tj_groups = cJSON_AddArrayToObject(j_tree, \"groups\");\n\tif(j_groups == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\treturn group_add_anon(j_groups);\n}\n\n\nstatic int acl_add(cJSON *j_acls, const char *acltype, const char *topic, int priority, bool allow)\n{\n\tcJSON *j_acl;\n\n\tj_acl = cJSON_CreateObject();\n\tcJSON_AddItemToArray(j_acls, j_acl);\n\tif(cJSON_AddStringToObject(j_acl, \"acltype\", acltype) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_acl, \"topic\", topic) == NULL\n\t\t\t|| cJSON_AddNumberToObject(j_acl, \"priority\", priority) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_acl, \"allow\", allow) == NULL\n\t\t\t){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nstatic int add_role_with_full_permission(cJSON *j_roles, const char *role_name, const char *text_description, const char *topic_pattern)\n{\n\tcJSON *j_role, *j_acls;\n\n\tj_role = cJSON_CreateObject();\n\tif(j_role == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(j_roles, j_role);\n\n\tif(cJSON_AddStringToObject(j_role, \"rolename\", role_name) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_role, \"textdescription\", text_description) == NULL\n\t\t\t|| (j_acls = cJSON_AddArrayToObject(j_role, \"acls\")) == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(acl_add(j_acls, \"publishClientSend\", topic_pattern, 0, true)\n\t\t\t|| acl_add(j_acls, \"publishClientReceive\", topic_pattern, 0, true)\n\t\t\t|| acl_add(j_acls, \"subscribePattern\", topic_pattern, 0, true)\n\t\t\t|| acl_add(j_acls, \"unsubscribePattern\", topic_pattern, 0, true)){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int role_add_sys_notify(cJSON *j_roles)\n{\n\tcJSON *j_role, *j_acls;\n\n\tj_role = cJSON_CreateObject();\n\tif(j_role == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(j_roles, j_role);\n\n\tif(cJSON_AddStringToObject(j_role, \"rolename\", \"sys-notify\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_role, \"textdescription\",\n\t\t\t\"Allow bridges to publish connection state messages.\") == NULL\n\t\t\t|| (j_acls = cJSON_AddArrayToObject(j_role, \"acls\")) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(acl_add(j_acls, \"publishClientSend\", \"$SYS/broker/connection/%c/state\", 0, true)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int role_add_sys_observe(cJSON *j_roles)\n{\n\tcJSON *j_role, *j_acls;\n\n\tj_role = cJSON_CreateObject();\n\tif(j_role == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(j_roles, j_role);\n\n\tif(cJSON_AddStringToObject(j_role, \"rolename\", \"sys-observe\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_role, \"textdescription\",\n\t\t\t\"Observe the $SYS topic hierarchy.\") == NULL\n\t\t\t|| (j_acls = cJSON_AddArrayToObject(j_role, \"acls\")) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(acl_add(j_acls, \"publishClientReceive\", \"$SYS/#\", 0, true)\n\t\t\t|| acl_add(j_acls, \"subscribePattern\", \"$SYS/#\", 0, true)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int role_add_topic_observe(cJSON *j_roles)\n{\n\tcJSON *j_role, *j_acls;\n\n\tj_role = cJSON_CreateObject();\n\tif(j_role == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(j_roles, j_role);\n\n\tif(cJSON_AddStringToObject(j_role, \"rolename\", \"topic-observe\") == NULL\n\t\t\t|| cJSON_AddStringToObject(j_role, \"textdescription\",\n\t\t\t\"Read only access to the full application topic hierarchy.\") == NULL\n\t\t\t|| (j_acls = cJSON_AddArrayToObject(j_role, \"acls\")) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(acl_add(j_acls, \"publishClientReceive\", \"#\", 0, true)\n\t\t\t|| acl_add(j_acls, \"subscribePattern\", \"#\", 0, true)\n\t\t\t|| acl_add(j_acls, \"unsubscribePattern\", \"#\", 0, true)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int add_roles(cJSON *j_tree)\n{\n\tcJSON *j_roles;\n\n\tj_roles = cJSON_AddArrayToObject(j_tree, \"roles\");\n\tif(j_roles == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(add_role_with_full_permission(j_roles, \"client\", \"Read/write access to the full application topic hierarchy.\", \"#\")\n\t\t\t|| add_role_with_full_permission(j_roles, \"broker-admin\", \"Grants access to administer general broker configuration.\", \"$CONTROL/broker/#\")\n\t\t\t|| add_role_with_full_permission(j_roles, \"dynsec-admin\", \"Grants access to administer clients/groups/roles.\", \"$CONTROL/dynamic-security/#\")\n\t\t\t|| add_role_with_full_permission(j_roles, \"super-admin\", \"Grants access to administer all kind of broker controls\", \"$CONTROL/#\")\n\t\t\t|| role_add_sys_notify(j_roles) || role_add_sys_observe(j_roles) || role_add_topic_observe(j_roles)){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec__config_init(struct dynsec__data *data)\n{\n\tFILE *fptr;\n\tcJSON *j_tree;\n\tchar *json_str;\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"Dynamic security plugin config not found, generating a default config.\");\n\n\tif(data->password_init_file){\n\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"  Using admin password from file '%s'\", data->password_init_file);\n\t\tdata->init_mode = dpwim_file;\n\t}else if(getenv(\"MOSQUITTO_DYNSEC_PASSWORD\")){\n\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"  Using admin password from MOSQUITTO_DYNSEC_PASSWORD environment variable\");\n\t\tdata->init_mode = dpwim_env;\n\t}else{\n\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"  Generated passwords are at %s.pw\", data->config_file);\n\t\tdata->init_mode = dpwim_random;\n\t}\n\n\tj_tree = cJSON_CreateObject();\n\tif(j_tree == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(add_default_access(j_tree) != MOSQ_ERR_SUCCESS\n\t\t\t|| add_clients(data, j_tree) != MOSQ_ERR_SUCCESS\n\t\t\t|| add_groups(j_tree) != MOSQ_ERR_SUCCESS\n\t\t\t|| add_roles(j_tree) != MOSQ_ERR_SUCCESS\n\t\t\t|| cJSON_AddStringToObject(j_tree, \"anonymousGroup\", \"unauthenticated\") == NULL\n\t\t\t){\n\n\t\tcJSON_Delete(j_tree);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tjson_str = cJSON_Print(j_tree);\n\tcJSON_Delete(j_tree);\n\tif(json_str == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tfptr = mosquitto_fopen(data->config_file, \"wb\", true);\n\tif(fptr == NULL){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tfprintf(fptr, \"%s\", json_str);\n\tfree(json_str);\n\tfclose(fptr);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/dynamic-security/control.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\n#define RESPONSE_TOPIC \"$CONTROL/dynamic-security/v1/response\"\n\n\nstatic int dynsec__handle_command(struct mosquitto_control_cmd *cmd, void *userdata)\n{\n\tstruct dynsec__data *data = userdata;\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\t/* Plugin */\n\tif(!strcasecmp(cmd->command_name, \"setDefaultACLAccess\")){\n\t\trc = dynsec__process_set_default_acl_access(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"getDefaultACLAccess\")){\n\t\trc = dynsec__process_get_default_acl_access(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"getDetails\")){\n\t\trc = dynsec_details__process_get(data, cmd);\n\n\t\t/* Clients */\n\t}else if(!strcasecmp(cmd->command_name, \"createClient\")){\n\t\trc = dynsec_clients__process_create(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"deleteClient\")){\n\t\trc = dynsec_clients__process_delete(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"getClient\")){\n\t\trc = dynsec_clients__process_get(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"listClients\")){\n\t\trc = dynsec_clients__process_list(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"modifyClient\")){\n\t\trc = dynsec_clients__process_modify(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"setClientPassword\")){\n\t\trc = dynsec_clients__process_set_password(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"setClientId\")){\n\t\trc = dynsec_clients__process_set_id(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"addClientRole\")){\n\t\trc = dynsec_clients__process_add_role(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"removeClientRole\")){\n\t\trc = dynsec_clients__process_remove_role(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"enableClient\")){\n\t\trc = dynsec_clients__process_enable(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"disableClient\")){\n\t\trc = dynsec_clients__process_disable(data, cmd);\n\n\t\t/* Groups */\n\t}else if(!strcasecmp(cmd->command_name, \"addGroupClient\")){\n\t\trc = dynsec_groups__process_add_client(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"createGroup\")){\n\t\trc = dynsec_groups__process_create(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"deleteGroup\")){\n\t\trc = dynsec_groups__process_delete(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"getGroup\")){\n\t\trc = dynsec_groups__process_get(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"listGroups\")){\n\t\trc = dynsec_groups__process_list(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"modifyGroup\")){\n\t\trc = dynsec_groups__process_modify(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"removeGroupClient\")){\n\t\trc = dynsec_groups__process_remove_client(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"addGroupRole\")){\n\t\trc = dynsec_groups__process_add_role(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"removeGroupRole\")){\n\t\trc = dynsec_groups__process_remove_role(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"setAnonymousGroup\")){\n\t\trc = dynsec_groups__process_set_anonymous_group(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"getAnonymousGroup\")){\n\t\trc = dynsec_groups__process_get_anonymous_group(data, cmd);\n\n\t\t/* Roles */\n\t}else if(!strcasecmp(cmd->command_name, \"createRole\")){\n\t\trc = dynsec_roles__process_create(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"getRole\")){\n\t\trc = dynsec_roles__process_get(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"listRoles\")){\n\t\trc = dynsec_roles__process_list(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"modifyRole\")){\n\t\trc = dynsec_roles__process_modify(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"deleteRole\")){\n\t\trc = dynsec_roles__process_delete(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"addRoleACL\")){\n\t\trc = dynsec_roles__process_add_acl(data, cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"removeRoleACL\")){\n\t\trc = dynsec_roles__process_remove_acl(data, cmd);\n\n\t\t/* Unknown */\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Unknown command\");\n\t\trc = MOSQ_ERR_INVAL;\n\t}\n\n\treturn rc;\n}\n\n\nint dynsec_control_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_control *ed = event_data;\n\tstruct dynsec__data *data = userdata;\n\tint rc;\n\n\tUNUSED(event);\n\n\tdata->need_save = false;\n\trc = mosquitto_control_generic_callback(ed, RESPONSE_TOPIC, userdata, dynsec__handle_command);\n\tif(rc == MOSQ_ERR_SUCCESS && data->need_save){\n\t\tdynsec__config_save(data);\n\t}\n\treturn rc;\n}\n"
  },
  {
    "path": "plugins/dynamic-security/default_acl.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\n\nint dynsec__process_set_default_acl_access(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tcJSON *j_actions, *j_action;\n\tbool allow;\n\tconst char *admin_clientid, *admin_username;\n\tconst char *acltype;\n\n\tj_actions = cJSON_GetObjectItem(cmd->j_command, \"acls\");\n\tif(j_actions == NULL || !cJSON_IsArray(j_actions)){\n\t\tmosquitto_control_command_reply(cmd, \"Missing/invalid actions array\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\n\tcJSON_ArrayForEach(j_action, j_actions){\n\t\tif(json_get_string(j_action, \"acltype\", &acltype, false) == MOSQ_ERR_SUCCESS\n\t\t\t\t&& json_get_bool(j_action, \"allow\", &allow, false, false) == MOSQ_ERR_SUCCESS){\n\n\t\t\tif(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){\n\t\t\t\tdata->default_access.publish_c_send = allow;\n\t\t\t}else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){\n\t\t\t\tdata->default_access.publish_c_recv = allow;\n\t\t\t}else if(!strcasecmp(acltype, ACL_TYPE_SUB_GENERIC)){\n\t\t\t\tdata->default_access.subscribe = allow;\n\t\t\t}else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_GENERIC)){\n\t\t\t\tdata->default_access.unsubscribe = allow;\n\t\t\t}\n\t\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | setDefaultACLAccess | acltype=%s | allow=%s\",\n\t\t\t\t\tadmin_clientid, admin_username, acltype, allow?\"true\":\"false\");\n\t\t}\n\t}\n\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec__process_get_default_acl_access(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tcJSON *tree, *jtmp, *j_data, *j_acls, *j_acl;\n\tconst char *admin_clientid, *admin_username;\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | getDefaultACLAccess\",\n\t\t\tadmin_clientid, admin_username);\n\n\tif(cJSON_AddStringToObject(tree, \"command\", \"getDefaultACLAccess\") == NULL\n\t\t\t|| ((j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL)\n\n\t\t\t){\n\t\tgoto internal_error;\n\t}\n\n\tj_acls = cJSON_AddArrayToObject(j_data, \"acls\");\n\tif(j_acls == NULL){\n\t\tgoto internal_error;\n\t}\n\n\t/* publishClientSend */\n\tj_acl = cJSON_CreateObject();\n\tif(j_acl == NULL){\n\t\tgoto internal_error;\n\t}\n\tcJSON_AddItemToArray(j_acls, j_acl);\n\tif(cJSON_AddStringToObject(j_acl, \"acltype\", ACL_TYPE_PUB_C_SEND) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_acl, \"allow\", data->default_access.publish_c_send) == NULL\n\t\t\t){\n\n\t\tgoto internal_error;\n\t}\n\n\t/* publishClientReceive */\n\tj_acl = cJSON_CreateObject();\n\tif(j_acl == NULL){\n\t\tgoto internal_error;\n\t}\n\tcJSON_AddItemToArray(j_acls, j_acl);\n\tif(cJSON_AddStringToObject(j_acl, \"acltype\", ACL_TYPE_PUB_C_RECV) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_acl, \"allow\", data->default_access.publish_c_recv) == NULL\n\t\t\t){\n\n\t\tgoto internal_error;\n\t}\n\n\t/* subscribe */\n\tj_acl = cJSON_CreateObject();\n\tif(j_acl == NULL){\n\t\tgoto internal_error;\n\t}\n\tcJSON_AddItemToArray(j_acls, j_acl);\n\tif(cJSON_AddStringToObject(j_acl, \"acltype\", ACL_TYPE_SUB_GENERIC) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_acl, \"allow\", data->default_access.subscribe) == NULL\n\t\t\t){\n\n\t\tgoto internal_error;\n\t}\n\n\t/* unsubscribe */\n\tj_acl = cJSON_CreateObject();\n\tif(j_acl == NULL){\n\t\tgoto internal_error;\n\t}\n\tcJSON_AddItemToArray(j_acls, j_acl);\n\tif(cJSON_AddStringToObject(j_acl, \"acltype\", ACL_TYPE_UNSUB_GENERIC) == NULL\n\t\t\t|| cJSON_AddBoolToObject(j_acl, \"allow\", data->default_access.unsubscribe) == NULL\n\t\t\t){\n\n\t\tgoto internal_error;\n\t}\n\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\tif(cmd->correlation_data){\n\t\tjtmp = cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data);\n\t\tif(jtmp == NULL){\n\t\t\tgoto internal_error;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n\ninternal_error:\n\tcJSON_Delete(tree);\n\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\treturn MOSQ_ERR_NOMEM;\n}\n"
  },
  {
    "path": "plugins/dynamic-security/details.c",
    "content": "/*\nCopyright (c) 2025 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <uthash.h>\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\n\nint dynsec_details__process_get(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tcJSON *tree, *j_data;\n\tconst char *admin_clientid, *admin_username;\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL\n\t\t\t|| cJSON_AddStringToObject(tree, \"command\", \"getDetails\") == NULL\n\t\t\t|| (j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t|| cJSON_AddIntToObject(j_data, \"clientCount\", (int)HASH_CNT(hh, data->clients)) == NULL\n\t\t\t|| cJSON_AddIntToObject(j_data, \"groupCount\", (int)HASH_CNT(hh, data->groups)) == NULL\n\t\t\t|| cJSON_AddIntToObject(j_data, \"roleCount\", (int)HASH_CNT(hh, data->roles)) == NULL\n\t\t\t|| cJSON_AddIntToObject(j_data, \"changeIndex\", data->changeindex) == NULL\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | getDetails\",\n\t\t\tadmin_clientid, admin_username);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/dynamic-security/dynamic_security.h",
    "content": "#ifndef DYNAMIC_SECURITY_H\n#define DYNAMIC_SECURITY_H\n/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <cjson/cJSON.h>\n#include <uthash.h>\n#include \"mosquitto.h\"\n\n#define PRIORITY_MAX 100000\n\n/* ################################################################\n * #\n * # ACL types\n * #\n * ################################################################ */\n\n#define ACL_TYPE_PUB_C_RECV \"publishClientReceive\"\n#define ACL_TYPE_PUB_C_SEND \"publishClientSend\"\n#define ACL_TYPE_SUB_GENERIC \"subscribe\"\n#define ACL_TYPE_SUB_LITERAL \"subscribeLiteral\"\n#define ACL_TYPE_SUB_PATTERN \"subscribePattern\"\n#define ACL_TYPE_UNSUB_GENERIC \"unsubscribe\"\n#define ACL_TYPE_UNSUB_LITERAL \"unsubscribeLiteral\"\n#define ACL_TYPE_UNSUB_PATTERN \"unsubscribePattern\"\n\n/* ################################################################\n * #\n * # Error codes\n * #\n * ################################################################ */\n\n#define ERR_USER_NOT_FOUND 10000\n#define ERR_GROUP_NOT_FOUND 10001\n#define ERR_LIST_NOT_FOUND 10002\n\n/* ################################################################\n * #\n * # Datatypes\n * #\n * ################################################################ */\n\nstruct dynsec__clientlist {\n\tUT_hash_handle hh;\n\tstruct dynsec__client *client;\n\tint priority;\n};\n\nstruct dynsec__grouplist {\n\tUT_hash_handle hh;\n\tstruct dynsec__group *group;\n\tint priority;\n};\n\nstruct dynsec__rolelist {\n\tUT_hash_handle hh;\n\tstruct dynsec__role *role;\n\tint priority;\n\tchar rolename[];\n};\n\nstruct dynsec__kicklist {\n\tstruct dynsec__kicklist *next, *prev;\n\tchar username[];\n};\n\nstruct dynsec__client {\n\tUT_hash_handle hh;\n\tstruct mosquitto_pw *pw;\n\tstruct dynsec__rolelist *rolelist;\n\tstruct dynsec__grouplist *grouplist;\n\tchar *clientid;\n\tchar *text_name;\n\tchar *text_description;\n\tbool disabled;\n\tchar username[];\n};\n\nstruct dynsec__group {\n\tUT_hash_handle hh;\n\tstruct dynsec__rolelist *rolelist;\n\tstruct dynsec__clientlist *clientlist;\n\tchar *text_name;\n\tchar *text_description;\n\tchar groupname[];\n};\n\n\nstruct dynsec__acl {\n\tUT_hash_handle hh;\n\tint priority;\n\tbool allow;\n\tchar topic[];\n};\n\nstruct dynsec__acls {\n\tstruct dynsec__acl *publish_c_send;\n\tstruct dynsec__acl *publish_c_recv;\n\tstruct dynsec__acl *subscribe_literal;\n\tstruct dynsec__acl *subscribe_pattern;\n\tstruct dynsec__acl *unsubscribe_literal;\n\tstruct dynsec__acl *unsubscribe_pattern;\n};\n\nstruct dynsec__role {\n\tUT_hash_handle hh;\n\tstruct dynsec__acls acls;\n\tstruct dynsec__clientlist *clientlist;\n\tstruct dynsec__grouplist *grouplist;\n\tchar *text_name;\n\tchar *text_description;\n\tbool allow_wildcard_subs;\n\tchar rolename[];\n};\n\nstruct dynsec__acl_default_access {\n\tbool publish_c_send;\n\tbool publish_c_recv;\n\tbool subscribe;\n\tbool unsubscribe;\n};\n\nenum dynsec_pw_init_mode {\n\tdpwim_file = 1,\n\tdpwim_env = 2,\n\tdpwim_random = 3,\n};\n\nstruct dynsec__data {\n\tchar *config_file;\n\tchar *password_init_file;\n\tstruct dynsec__client *clients;\n\tstruct dynsec__group *groups;\n\tstruct dynsec__role *roles;\n\tstruct dynsec__group *anonymous_group;\n\tstruct dynsec__kicklist *kicklist;\n\tstruct dynsec__acl_default_access default_access;\n\tint64_t changeindex;\n\tint init_mode;\n\tbool need_save;\n};\n\n/* ################################################################\n * #\n * # Plugin Functions\n * #\n * ################################################################ */\n\nint dynsec__config_init(struct dynsec__data *data);\nvoid dynsec__config_save(struct dynsec__data *data);\nvoid dynsec__config_batch_save(struct dynsec__data *data);\nint dynsec__config_load(struct dynsec__data *data);\nchar *dynsec__config_to_json(struct dynsec__data *data);\nint dynsec__config_from_json(struct dynsec__data *data, const char *json_str);\nvoid dynsec__command_reply(cJSON *j_responses, struct mosquitto *context, const char *command, const char *error, const char *correlation_data);\nint dynsec_control_callback(int event, void *event_data, void *userdata);\n\n\n/* ################################################################\n * #\n * # ACL Functions\n * #\n * ################################################################ */\n\nint dynsec__acl_check_callback(int event, void *event_data, void *userdata);\nint dynsec__process_set_default_acl_access(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec__process_get_default_acl_access(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\n\n\n/* ################################################################\n * #\n * # Auth Functions\n * #\n * ################################################################ */\n\nint dynsec_auth__pw_hash(struct dynsec__client *client, const char *password, unsigned char *password_hash, int password_hash_len, bool new_password);\nint dynsec_auth__basic_auth_callback(int event, void *event_data, void *userdata);\n\n\n/* ################################################################\n * #\n * # Client Functions\n * #\n * ################################################################ */\n\nvoid dynsec_clients__cleanup(struct dynsec__data *data);\nint dynsec_clients__config_load(struct dynsec__data *data, cJSON *tree);\nint dynsec_clients__config_save(struct dynsec__data *data, cJSON *tree);\nint dynsec_clients__process_add_role(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_clients__process_create(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_clients__process_delete(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_clients__process_disable(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_clients__process_enable(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_clients__process_get(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_clients__process_list(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_clients__process_modify(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_clients__process_remove_role(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_clients__process_set_id(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_clients__process_set_password(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nstruct dynsec__client *dynsec_clients__find(struct dynsec__data *data, const char *username);\n\n\n/* ################################################################\n * #\n * # Client List Functions\n * #\n * ################################################################ */\n\ncJSON *dynsec_clientlist__all_to_json(struct dynsec__clientlist *base_clientlist);\nint dynsec_clientlist__add(struct dynsec__clientlist **base_clientlist, struct dynsec__client *client, int priority);\nvoid dynsec_clientlist__cleanup(struct dynsec__clientlist **base_clientlist);\nvoid dynsec_clientlist__remove(struct dynsec__clientlist **base_clientlist, struct dynsec__client *client);\nvoid dynsec_clientlist__kick_all(struct dynsec__data *data, struct dynsec__clientlist *base_clientlist);\n\n\n/* ################################################################\n * #\n * # Group Functions\n * #\n * ################################################################ */\n\nvoid dynsec_groups__cleanup(struct dynsec__data *data);\nint dynsec_groups__config_load(struct dynsec__data *data, cJSON *tree);\nint dynsec_groups__add_client(struct dynsec__data *data, const char *username, const char *groupname, int priority, bool update_config);\nint dynsec_groups__config_save(struct dynsec__data *data, cJSON *tree);\nint dynsec_groups__process_add_client(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__process_add_role(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__process_create(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__process_delete(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__process_get(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__process_list(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__process_modify(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__process_remove_client(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__process_remove_role(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__process_get_anonymous_group(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__process_set_anonymous_group(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_groups__remove_client(struct dynsec__data *data, const char *username, const char *groupname, bool update_config);\nstruct dynsec__group *dynsec_groups__find(struct dynsec__data *data, const char *groupname);\n\n\n/* ################################################################\n * #\n * # Group List Functions\n * #\n * ################################################################ */\n\ncJSON *dynsec_grouplist__all_to_json(struct dynsec__grouplist *base_grouplist);\nint dynsec_grouplist__add(struct dynsec__grouplist **base_grouplist, struct dynsec__group *group, int priority);\nvoid dynsec_grouplist__cleanup(struct dynsec__grouplist **base_grouplist);\nvoid dynsec_grouplist__remove(struct dynsec__grouplist **base_grouplist, struct dynsec__group *group);\n\n\n/* ################################################################\n * #\n * # Role Functions\n * #\n * ################################################################ */\n\nvoid dynsec_roles__cleanup(struct dynsec__data *data);\nint dynsec_roles__config_load(struct dynsec__data *data, cJSON *tree);\nint dynsec_roles__config_save(struct dynsec__data *data, cJSON *tree);\nint dynsec_roles__process_add_acl(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_roles__process_create(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_roles__process_delete(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_roles__process_get(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_roles__process_list(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_roles__process_modify(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nint dynsec_roles__process_remove_acl(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\nstruct dynsec__role *dynsec_roles__find(struct dynsec__data *data, const char *rolename);\n\n\n/* ################################################################\n * #\n * # Role List Functions\n * #\n * ################################################################ */\n\nint dynsec_rolelist__client_add(struct dynsec__client *client, struct dynsec__role *role, int priority);\nint dynsec_rolelist__client_remove(struct dynsec__client *client, struct dynsec__role *role);\nint dynsec_rolelist__group_add(struct dynsec__group *group, struct dynsec__role *role, int priority);\nvoid dynsec_rolelist__group_remove(struct dynsec__group *group, struct dynsec__role *role);\nint dynsec_rolelist__load_from_json(struct dynsec__data *data, cJSON *command, struct dynsec__rolelist **rolelist);\nvoid dynsec_rolelist__cleanup(struct dynsec__rolelist **base_rolelist);\ncJSON *dynsec_rolelist__all_to_json(struct dynsec__rolelist *base_rolelist);\n\n/* ################################################################\n * #\n * # Kick List Functions\n * #\n * ################################################################ */\n\nint dynsec_kicklist__add(struct dynsec__data *data, const char *username);\nvoid dynsec_kicklist__kick(struct dynsec__data *data);\nint dynsec__tick_callback(int event, void *event_data, void *userdata);\nvoid dynsec_kicklist__cleanup(struct dynsec__data *data);\n\n/* ################################################################\n * #\n * # Change Index Functions\n * #\n * ################################################################ */\n\nint dynsec_details__process_get(struct dynsec__data *data, struct mosquitto_control_cmd *cmd);\n\n#endif\n"
  },
  {
    "path": "plugins/dynamic-security/grouplist.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <uthash.h>\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\n\n/* ################################################################\n * #\n * # Plugin global variables\n * #\n * ################################################################ */\n\n\n/* ################################################################\n * #\n * # Function declarations\n * #\n * ################################################################ */\n\n\n/* ################################################################\n * #\n * # Local variables\n * #\n * ################################################################ */\n\n\n/* ################################################################\n * #\n * # Utility functions\n * #\n * ################################################################ */\n\n\nstatic int dynsec_grouplist__cmp(void *a, void *b)\n{\n\tint prio;\n\tstruct dynsec__grouplist *grouplist_a = a;\n\tstruct dynsec__grouplist *grouplist_b = b;\n\n\tprio = grouplist_b->priority - grouplist_a->priority;\n\tif(prio == 0){\n\t\treturn strcmp(grouplist_a->group->groupname, grouplist_b->group->groupname);\n\t}else{\n\t\treturn prio;\n\t}\n}\n\n\ncJSON *dynsec_grouplist__all_to_json(struct dynsec__grouplist *base_grouplist)\n{\n\tstruct dynsec__grouplist *grouplist, *grouplist_tmp;\n\tcJSON *j_groups, *j_group;\n\n\tj_groups = cJSON_CreateArray();\n\tif(j_groups == NULL){\n\t\treturn NULL;\n\t}\n\n\tHASH_ITER(hh, base_grouplist, grouplist, grouplist_tmp){\n\t\tj_group = cJSON_CreateObject();\n\t\tif(j_group == NULL){\n\t\t\tcJSON_Delete(j_groups);\n\t\t\treturn NULL;\n\t\t}\n\t\tcJSON_AddItemToArray(j_groups, j_group);\n\n\t\tif(cJSON_AddStringToObject(j_group, \"groupname\", grouplist->group->groupname) == NULL\n\t\t\t\t|| (grouplist->priority != -1 && cJSON_AddIntToObject(j_group, \"priority\", grouplist->priority) == NULL)\n\t\t\t\t){\n\n\t\t\tcJSON_Delete(j_groups);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn j_groups;\n}\n\n\nint dynsec_grouplist__add(struct dynsec__grouplist **base_grouplist, struct dynsec__group *group, int priority)\n{\n\tstruct dynsec__grouplist *grouplist;\n\n\tHASH_FIND(hh, *base_grouplist, group->groupname, strlen(group->groupname), grouplist);\n\tif(grouplist != NULL){\n\t\t/* Group is already in the list */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tgrouplist = mosquitto_malloc(sizeof(struct dynsec__grouplist));\n\tif(grouplist == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tgrouplist->group = group;\n\tgrouplist->priority = priority;\n\tHASH_ADD_KEYPTR_INORDER(hh, *base_grouplist, grouplist->group->groupname, strlen(grouplist->group->groupname), grouplist, dynsec_grouplist__cmp);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid dynsec_grouplist__cleanup(struct dynsec__grouplist **base_grouplist)\n{\n\tstruct dynsec__grouplist *grouplist, *grouplist_tmp;\n\n\tHASH_ITER(hh, *base_grouplist, grouplist, grouplist_tmp){\n\t\tHASH_DELETE(hh, *base_grouplist, grouplist);\n\t\tmosquitto_free(grouplist);\n\t}\n}\n\n\nvoid dynsec_grouplist__remove(struct dynsec__grouplist **base_grouplist, struct dynsec__group *group)\n{\n\tstruct dynsec__grouplist *grouplist;\n\n\tHASH_FIND(hh, *base_grouplist, group->groupname, strlen(group->groupname), grouplist);\n\tif(grouplist){\n\t\tHASH_DELETE(hh, *base_grouplist, grouplist);\n\t\tmosquitto_free(grouplist);\n\t}\n}\n"
  },
  {
    "path": "plugins/dynamic-security/groups.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <uthash.h>\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\n/* ################################################################\n * #\n * # Plugin global variables\n * #\n * ################################################################ */\n\n/* ################################################################\n * #\n * # Function declarations\n * #\n * ################################################################ */\n\nstatic int dynsec__remove_all_clients_from_group(struct dynsec__group *group);\nstatic int dynsec__remove_all_roles_from_group(struct dynsec__group *group);\nstatic cJSON *add_group_to_json(struct dynsec__group *group);\n\n\n/* ################################################################\n * #\n * # Local variables\n * #\n * ################################################################ */\n\n\n/* ################################################################\n * #\n * # Utility functions\n * #\n * ################################################################ */\n\n\nstatic void group__kick_all(struct dynsec__data *data, struct dynsec__group *group)\n{\n\tif(group == data->anonymous_group){\n\t\tdynsec_kicklist__add(data, NULL);\n\t}\n\tdynsec_clientlist__kick_all(data, group->clientlist);\n}\n\n\nstatic int group_cmp(void *a, void *b)\n{\n\tstruct dynsec__group *group_a = a;\n\tstruct dynsec__group *group_b = b;\n\n\treturn strcmp(group_a->groupname, group_b->groupname);\n}\n\n\nstruct dynsec__group *dynsec_groups__find(struct dynsec__data *data, const char *groupname)\n{\n\tstruct dynsec__group *group = NULL;\n\n\tif(groupname){\n\t\tHASH_FIND(hh, data->groups, groupname, strlen(groupname), group);\n\t}\n\treturn group;\n}\n\n\nstatic void group__free_item(struct dynsec__data *data, struct dynsec__group *group)\n{\n\tstruct dynsec__group *found_group = NULL;\n\n\tif(group == NULL){\n\t\treturn;\n\t}\n\n\tfound_group = dynsec_groups__find(data, group->groupname);\n\tif(found_group){\n\t\tHASH_DEL(data->groups, found_group);\n\t}\n\tdynsec__remove_all_clients_from_group(group);\n\tmosquitto_free(group->text_name);\n\tmosquitto_free(group->text_description);\n\tdynsec_rolelist__cleanup(&group->rolelist);\n\tmosquitto_free(group);\n}\n\n\nint dynsec_groups__process_add_role(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *groupname, *rolename;\n\tstruct dynsec__group *group;\n\tstruct dynsec__role *role;\n\tint priority;\n\tconst char *admin_clientid, *admin_username;\n\tint rc;\n\n\tif(json_get_string(cmd->j_command, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing groupname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Group name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Role name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tjson_get_int(cmd->j_command, \"priority\", &priority, true, -1);\n\tif(priority > PRIORITY_MAX){\n\t\tpriority = PRIORITY_MAX;\n\t}\n\tif(priority < -PRIORITY_MAX){\n\t\tpriority = -PRIORITY_MAX;\n\t}\n\n\tgroup = dynsec_groups__find(data, groupname);\n\tif(group == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Group not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\trole = dynsec_roles__find(data, rolename);\n\tif(role == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\n\trc = dynsec_rolelist__group_add(group, role, priority);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t/* Continue */\n\t}else if(rc == MOSQ_ERR_ALREADY_EXISTS){\n\t\tmosquitto_control_command_reply(cmd, \"Group is already in this role\");\n\t\treturn MOSQ_ERR_ALREADY_EXISTS;\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | addGroupRole | groupname=%s | rolename=%s | priority=%d\",\n\t\t\tadmin_clientid, admin_username, groupname, rolename, priority);\n\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\t/* Enforce any changes */\n\tgroup__kick_all(data, group);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid dynsec_groups__cleanup(struct dynsec__data *data)\n{\n\tstruct dynsec__group *group, *group_tmp = NULL;\n\n\tHASH_ITER(hh, data->groups, group, group_tmp){\n\t\tgroup__free_item(data, group);\n\t}\n\tdata->anonymous_group = NULL;\n}\n\n\n/* ################################################################\n * #\n * # Config file load\n * #\n * ################################################################ */\n\n\nint dynsec_groups__config_load(struct dynsec__data *data, cJSON *tree)\n{\n\tcJSON *j_groups, *j_group;\n\tcJSON *j_clientlist;\n\tcJSON *j_roles;\n\tconst char *groupname;\n\n\tstruct dynsec__group *group;\n\tstruct dynsec__role *role;\n\tint priority;\n\n\tj_groups = cJSON_GetObjectItem(tree, \"groups\");\n\tif(j_groups == NULL){\n\t\treturn 0;\n\t}\n\n\tif(cJSON_IsArray(j_groups) == false){\n\t\treturn 1;\n\t}\n\n\tcJSON_ArrayForEach(j_group, j_groups){\n\t\tif(cJSON_IsObject(j_group) == true){\n\t\t\t/* Group name */\n\t\t\tsize_t groupname_len;\n\t\t\tif(json_get_string(j_group, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tgroupname_len = strlen(groupname);\n\t\t\tif(groupname_len == 0){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(dynsec_groups__find(data, groupname)){\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tgroup = mosquitto_calloc(1, sizeof(struct dynsec__group) + groupname_len + 1);\n\t\t\tif(group == NULL){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tstrncpy(group->groupname, groupname, groupname_len+1);\n\n\t\t\t/* Text name */\n\t\t\tconst char *textname;\n\t\t\tif(json_get_string(j_group, \"textname\", &textname, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\tif(textname){\n\t\t\t\t\tgroup->text_name = mosquitto_strdup(textname);\n\t\t\t\t\tif(group->text_name == NULL){\n\t\t\t\t\t\tmosquitto_free(group);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Text description */\n\t\t\tconst char *textdescription;\n\t\t\tif(json_get_string(j_group, \"textdescription\", &textdescription, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\tif(textdescription){\n\t\t\t\t\tgroup->text_description = mosquitto_strdup(textdescription);\n\t\t\t\t\tif(group->text_description == NULL){\n\t\t\t\t\t\tmosquitto_free(group->text_name);\n\t\t\t\t\t\tmosquitto_free(group);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Roles */\n\t\t\tj_roles = cJSON_GetObjectItem(j_group, \"roles\");\n\t\t\tif(j_roles && cJSON_IsArray(j_roles)){\n\t\t\t\tcJSON *j_role;\n\n\t\t\t\tcJSON_ArrayForEach(j_role, j_roles){\n\t\t\t\t\tif(cJSON_IsObject(j_role)){\n\t\t\t\t\t\tconst char *rolename;\n\t\t\t\t\t\tif(json_get_string(j_role, \"rolename\", &rolename, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\t\t\t\tjson_get_int(j_role, \"priority\", &priority, true, -1);\n\t\t\t\t\t\t\tif(priority > PRIORITY_MAX){\n\t\t\t\t\t\t\t\tpriority = PRIORITY_MAX;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif(priority < -PRIORITY_MAX){\n\t\t\t\t\t\t\t\tpriority = -PRIORITY_MAX;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\trole = dynsec_roles__find(data, rolename);\n\t\t\t\t\t\t\tdynsec_rolelist__group_add(group, role, priority);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* This must go before clients are loaded, otherwise the group won't be found */\n\t\t\tHASH_ADD(hh, data->groups, groupname, groupname_len, group);\n\n\t\t\t/* Clients */\n\t\t\tj_clientlist = cJSON_GetObjectItem(j_group, \"clients\");\n\t\t\tif(j_clientlist && cJSON_IsArray(j_clientlist)){\n\t\t\t\tcJSON *j_client;\n\t\t\t\tcJSON_ArrayForEach(j_client, j_clientlist){\n\t\t\t\t\tif(cJSON_IsObject(j_client)){\n\t\t\t\t\t\tconst char *username;\n\t\t\t\t\t\tif(json_get_string(j_client, \"username\", &username, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\t\t\t\tjson_get_int(j_client, \"priority\", &priority, true, -1);\n\t\t\t\t\t\t\tif(priority > PRIORITY_MAX){\n\t\t\t\t\t\t\t\tpriority = PRIORITY_MAX;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif(priority < -PRIORITY_MAX){\n\t\t\t\t\t\t\t\tpriority = -PRIORITY_MAX;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdynsec_groups__add_client(data, username, group->groupname, priority, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tHASH_SORT(data->groups, group_cmp);\n\n\tif(json_get_string(tree, \"anonymousGroup\", &groupname, false) == MOSQ_ERR_SUCCESS){\n\t\tdata->anonymous_group = dynsec_groups__find(data, groupname);\n\t}\n\n\treturn 0;\n}\n\n\n/* ################################################################\n * #\n * # Config load and save\n * #\n * ################################################################ */\n\n\nstatic int dynsec__config_add_groups(struct dynsec__data *data, cJSON *j_groups)\n{\n\tstruct dynsec__group *group, *group_tmp = NULL;\n\tcJSON *j_group, *j_clients, *j_roles;\n\n\tHASH_ITER(hh, data->groups, group, group_tmp){\n\t\tj_group = cJSON_CreateObject();\n\t\tif(j_group == NULL){\n\t\t\treturn 1;\n\t\t}\n\t\tcJSON_AddItemToArray(j_groups, j_group);\n\n\t\tif(cJSON_AddStringToObject(j_group, \"groupname\", group->groupname) == NULL\n\t\t\t\t|| (group->text_name && cJSON_AddStringToObject(j_group, \"textname\", group->text_name) == NULL)\n\t\t\t\t|| (group->text_description && cJSON_AddStringToObject(j_group, \"textdescription\", group->text_description) == NULL)\n\t\t\t\t){\n\n\t\t\treturn 1;\n\t\t}\n\n\t\tj_roles = dynsec_rolelist__all_to_json(group->rolelist);\n\t\tif(j_roles == NULL){\n\t\t\treturn 1;\n\t\t}\n\t\tcJSON_AddItemToObject(j_group, \"roles\", j_roles);\n\n\t\tj_clients = dynsec_clientlist__all_to_json(group->clientlist);\n\t\tif(j_clients == NULL){\n\t\t\treturn 1;\n\t\t}\n\t\tcJSON_AddItemToObject(j_group, \"clients\", j_clients);\n\t}\n\n\treturn 0;\n}\n\n\nint dynsec_groups__config_save(struct dynsec__data *data, cJSON *tree)\n{\n\tcJSON *j_groups;\n\n\tj_groups = cJSON_CreateArray();\n\tif(j_groups == NULL){\n\t\treturn 1;\n\t}\n\tcJSON_AddItemToObject(tree, \"groups\", j_groups);\n\tif(dynsec__config_add_groups(data, j_groups)){\n\t\treturn 1;\n\t}\n\n\tif(data->anonymous_group\n\t\t\t&& cJSON_AddStringToObject(tree, \"anonymousGroup\", data->anonymous_group->groupname) == NULL){\n\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\n\nint dynsec_groups__process_create(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *groupname, *text_name, *text_description;\n\tstruct dynsec__group *group = NULL;\n\tint rc = MOSQ_ERR_SUCCESS;\n\tconst char *admin_clientid, *admin_username;\n\tsize_t groupname_len;\n\n\tif(json_get_string(cmd->j_command, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing groupname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tgroupname_len = strlen(groupname);\n\tif(groupname_len == 0){\n\t\tmosquitto_control_command_reply(cmd, \"Empty groupname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(groupname, (int)groupname_len) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Group name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textname\", &text_name, true) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing textname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textdescription\", &text_description, true) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing textdescription\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tgroup = dynsec_groups__find(data, groupname);\n\tif(group){\n\t\tmosquitto_control_command_reply(cmd, \"Group already exists\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tgroup = mosquitto_calloc(1, sizeof(struct dynsec__group) + groupname_len + 1);\n\tif(group == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tstrncpy(group->groupname, groupname, groupname_len+1);\n\tif(text_name){\n\t\tgroup->text_name = mosquitto_strdup(text_name);\n\t\tif(group->text_name == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\tgroup__free_item(data, group);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\tif(text_description){\n\t\tgroup->text_description = mosquitto_strdup(text_description);\n\t\tif(group->text_description == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\tgroup__free_item(data, group);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\trc = dynsec_rolelist__load_from_json(data, cmd->j_command, &group->rolelist);\n\tif(rc == MOSQ_ERR_SUCCESS || rc == ERR_LIST_NOT_FOUND){\n\t}else if(rc == MOSQ_ERR_NOT_FOUND){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\tgroup__free_item(data, group);\n\t\treturn MOSQ_ERR_INVAL;\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\tgroup__free_item(data, group);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_ADD_INORDER(hh, data->groups, groupname, groupname_len, group, group_cmp);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | createGroup | groupname=%s\",\n\t\t\tadmin_clientid, admin_username, groupname);\n\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_groups__process_delete(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *groupname;\n\tstruct dynsec__group *group;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing groupname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Group name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tgroup = dynsec_groups__find(data, groupname);\n\tif(group){\n\t\tif(group == data->anonymous_group){\n\t\t\tmosquitto_control_command_reply(cmd, \"Deleting the anonymous group is forbidden\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\t/* Enforce any changes */\n\t\tgroup__kick_all(data, group);\n\n\t\tdynsec__remove_all_roles_from_group(group);\n\t\tgroup__free_item(data, group);\n\t\tdynsec__config_batch_save(data);\n\t\tmosquitto_control_command_reply(cmd, NULL);\n\n\t\tadmin_clientid = mosquitto_client_id(cmd->client);\n\t\tadmin_username = mosquitto_client_username(cmd->client);\n\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | deleteGroup | groupname=%s\",\n\t\t\t\tadmin_clientid, admin_username, groupname);\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Group not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_groups__add_client(struct dynsec__data *data, const char *username, const char *groupname, int priority, bool update_config)\n{\n\tstruct dynsec__client *client;\n\tstruct dynsec__clientlist *clientlist;\n\tstruct dynsec__group *group;\n\tint rc;\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client == NULL){\n\t\treturn ERR_USER_NOT_FOUND;\n\t}\n\n\tgroup = dynsec_groups__find(data, groupname);\n\tif(group == NULL){\n\t\treturn ERR_GROUP_NOT_FOUND;\n\t}\n\n\tHASH_FIND(hh, group->clientlist, username, strlen(username), clientlist);\n\tif(clientlist != NULL){\n\t\t/* Client is already in the group */\n\t\treturn MOSQ_ERR_ALREADY_EXISTS;\n\t}\n\n\trc = dynsec_clientlist__add(&group->clientlist, client, priority);\n\tif(rc){\n\t\treturn rc;\n\t}\n\trc = dynsec_grouplist__add(&client->grouplist, group, priority);\n\tif(rc){\n\t\tdynsec_clientlist__remove(&group->clientlist, client);\n\t\treturn rc;\n\t}\n\n\tif(update_config){\n\t\tdynsec__config_batch_save(data);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_groups__process_add_client(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username, *groupname;\n\tint rc;\n\tint priority;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing groupname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Group name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tjson_get_int(cmd->j_command, \"priority\", &priority, true, -1);\n\tif(priority > PRIORITY_MAX){\n\t\tpriority = PRIORITY_MAX;\n\t}\n\tif(priority < -PRIORITY_MAX){\n\t\tpriority = -PRIORITY_MAX;\n\t}\n\n\trc = dynsec_groups__add_client(data, username, groupname, priority, true);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tadmin_clientid = mosquitto_client_id(cmd->client);\n\t\tadmin_username = mosquitto_client_username(cmd->client);\n\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | addGroupClient | groupname=%s | username=%s | priority=%d\",\n\t\t\t\tadmin_clientid, admin_username, groupname, username, priority);\n\n\t\tmosquitto_control_command_reply(cmd, NULL);\n\t}else if(rc == ERR_USER_NOT_FOUND){\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t}else if(rc == ERR_GROUP_NOT_FOUND){\n\t\tmosquitto_control_command_reply(cmd, \"Group not found\");\n\t}else if(rc == MOSQ_ERR_ALREADY_EXISTS){\n\t\tmosquitto_control_command_reply(cmd, \"Client is already in this group\");\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t}\n\n\t/* Enforce any changes */\n\tdynsec_kicklist__add(data, username);\n\n\treturn rc;\n}\n\n\nstatic int dynsec__remove_all_clients_from_group(struct dynsec__group *group)\n{\n\tstruct dynsec__clientlist *clientlist, *clientlist_tmp = NULL;\n\n\tHASH_ITER(hh, group->clientlist, clientlist, clientlist_tmp){\n\t\t/* Remove client stored group reference */\n\t\tdynsec_grouplist__remove(&clientlist->client->grouplist, group);\n\n\t\tHASH_DELETE(hh, group->clientlist, clientlist);\n\t\tmosquitto_free(clientlist);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int dynsec__remove_all_roles_from_group(struct dynsec__group *group)\n{\n\tstruct dynsec__rolelist *rolelist, *rolelist_tmp = NULL;\n\n\tHASH_ITER(hh, group->rolelist, rolelist, rolelist_tmp){\n\t\tdynsec_rolelist__group_remove(group, rolelist->role);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_groups__remove_client(struct dynsec__data *data, const char *username, const char *groupname, bool update_config)\n{\n\tstruct dynsec__client *client;\n\tstruct dynsec__group *group;\n\n\tclient = dynsec_clients__find(data, username);\n\tif(client == NULL){\n\t\treturn ERR_USER_NOT_FOUND;\n\t}\n\n\tgroup = dynsec_groups__find(data, groupname);\n\tif(group == NULL){\n\t\treturn ERR_GROUP_NOT_FOUND;\n\t}\n\n\tdynsec_clientlist__remove(&group->clientlist, client);\n\tdynsec_grouplist__remove(&client->grouplist, group);\n\n\tif(update_config){\n\t\tdynsec__config_batch_save(data);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_groups__process_remove_client(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *username, *groupname;\n\tint rc;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"username\", &username, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing username\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Username not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing groupname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Group name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trc = dynsec_groups__remove_client(data, username, groupname, true);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tadmin_clientid = mosquitto_client_id(cmd->client);\n\t\tadmin_username = mosquitto_client_username(cmd->client);\n\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | removeGroupClient | groupname=%s | username=%s\",\n\t\t\t\tadmin_clientid, admin_username, groupname, username);\n\n\t\tmosquitto_control_command_reply(cmd, NULL);\n\t}else if(rc == ERR_USER_NOT_FOUND){\n\t\tmosquitto_control_command_reply(cmd, \"Client not found\");\n\t}else if(rc == ERR_GROUP_NOT_FOUND){\n\t\tmosquitto_control_command_reply(cmd, \"Group not found\");\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t}\n\n\t/* Enforce any changes */\n\tdynsec_kicklist__add(data, username);\n\n\treturn rc;\n}\n\n\nstatic cJSON *add_group_to_json(struct dynsec__group *group)\n{\n\tcJSON *j_group, *jtmp, *j_clientlist, *j_client, *j_rolelist;\n\tstruct dynsec__clientlist *clientlist, *clientlist_tmp = NULL;\n\n\tj_group = cJSON_CreateObject();\n\tif(j_group == NULL){\n\t\treturn NULL;\n\t}\n\n\tif(cJSON_AddStringToObject(j_group, \"groupname\", group->groupname) == NULL\n\t\t\t|| (group->text_name && cJSON_AddStringToObject(j_group, \"textname\", group->text_name) == NULL)\n\t\t\t|| (group->text_description && cJSON_AddStringToObject(j_group, \"textdescription\", group->text_description) == NULL)\n\t\t\t|| (j_clientlist = cJSON_AddArrayToObject(j_group, \"clients\")) == NULL\n\t\t\t){\n\n\t\tcJSON_Delete(j_group);\n\t\treturn NULL;\n\t}\n\n\tHASH_ITER(hh, group->clientlist, clientlist, clientlist_tmp){\n\t\tj_client = cJSON_CreateObject();\n\t\tif(j_client == NULL){\n\t\t\tcJSON_Delete(j_group);\n\t\t\treturn NULL;\n\t\t}\n\t\tcJSON_AddItemToArray(j_clientlist, j_client);\n\n\t\tjtmp = cJSON_CreateStringReference(clientlist->client->username);\n\t\tif(jtmp == NULL){\n\t\t\tcJSON_Delete(j_group);\n\t\t\treturn NULL;\n\t\t}\n\t\tcJSON_AddItemToObject(j_client, \"username\", jtmp);\n\t}\n\n\tj_rolelist = dynsec_rolelist__all_to_json(group->rolelist);\n\tif(j_rolelist == NULL){\n\t\tcJSON_Delete(j_group);\n\t\treturn NULL;\n\t}\n\tcJSON_AddItemToObject(j_group, \"roles\", j_rolelist);\n\n\treturn j_group;\n}\n\n\nint dynsec_groups__process_list(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tbool verbose;\n\tcJSON *tree, *j_groups, *j_group, *j_data;\n\tstruct dynsec__group *group, *group_tmp = NULL;\n\tint i, count, offset;\n\tconst char *admin_clientid, *admin_username;\n\n\tjson_get_bool(cmd->j_command, \"verbose\", &verbose, true, false);\n\tjson_get_int(cmd->j_command, \"count\", &count, true, -1);\n\tjson_get_int(cmd->j_command, \"offset\", &offset, true, 0);\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(cJSON_AddStringToObject(tree, \"command\", \"listGroups\") == NULL\n\t\t\t|| (j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL\n\t\t\t|| cJSON_AddIntToObject(j_data, \"totalCount\", (int)HASH_CNT(hh, data->groups)) == NULL\n\t\t\t|| (j_groups = cJSON_AddArrayToObject(j_data, \"groups\")) == NULL\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\ti = 0;\n\tHASH_ITER(hh, data->groups, group, group_tmp){\n\t\tif(i>=offset){\n\t\t\tif(verbose){\n\t\t\t\tj_group = add_group_to_json(group);\n\t\t\t\tif(j_group == NULL){\n\t\t\t\t\tcJSON_Delete(tree);\n\t\t\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\tcJSON_AddItemToArray(j_groups, j_group);\n\n\t\t\t}else{\n\t\t\t\tj_group = cJSON_CreateString(group->groupname);\n\t\t\t\tif(j_group){\n\t\t\t\t\tcJSON_AddItemToArray(j_groups, j_group);\n\t\t\t\t}else{\n\t\t\t\t\tcJSON_Delete(tree);\n\t\t\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(count >= 0){\n\t\t\t\tcount--;\n\t\t\t\tif(count <= 0){\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ti++;\n\t}\n\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | listGroups | verbose=%s | count=%d | offset=%d\",\n\t\t\tadmin_clientid, admin_username, verbose?\"true\":\"false\", count, offset);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_groups__process_get(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *groupname;\n\tcJSON *tree, *j_group, *j_data;\n\tstruct dynsec__group *group;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing groupname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Group name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(cJSON_AddStringToObject(tree, \"command\", \"getGroup\") == NULL\n\t\t\t|| (j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tgroup = dynsec_groups__find(data, groupname);\n\tif(group){\n\t\tj_group = add_group_to_json(group);\n\t\tif(j_group == NULL){\n\t\t\tcJSON_Delete(tree);\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tcJSON_AddItemToObject(j_data, \"group\", j_group);\n\t}else{\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Group not found\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | getGroup | groupname=%s\",\n\t\t\tadmin_clientid, admin_username, groupname);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_groups__process_remove_role(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *groupname, *rolename;\n\tstruct dynsec__group *group;\n\tstruct dynsec__role *role;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing groupname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Group name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Role name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tgroup = dynsec_groups__find(data, groupname);\n\tif(group == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Group not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\trole = dynsec_roles__find(data, rolename);\n\tif(role == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tdynsec_rolelist__group_remove(group, role);\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\t/* Enforce any changes */\n\tgroup__kick_all(data, group);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | removeGroupRole | groupname=%s | rolename=%s\",\n\t\t\tadmin_clientid, admin_username, groupname, rolename);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_groups__process_modify(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *groupname = NULL;\n\tchar *text_name = NULL, *text_description = NULL;\n\tstruct dynsec__client *client = NULL;\n\tstruct dynsec__group *group = NULL;\n\tstruct dynsec__rolelist *rolelist = NULL;\n\tbool have_text_name = false, have_text_description = false, have_rolelist = false;\n\tconst char *str;\n\tint rc;\n\tint priority;\n\tcJSON *j_client, *j_clients;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing groupname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Group name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tgroup = dynsec_groups__find(data, groupname);\n\tif(group == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Group not found\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textname\", &str, false) == MOSQ_ERR_SUCCESS){\n\t\thave_text_name = true;\n\t\ttext_name = mosquitto_strdup(str);\n\t\tif(text_name == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textdescription\", &str, false) == MOSQ_ERR_SUCCESS){\n\t\thave_text_description = true;\n\t\ttext_description = mosquitto_strdup(str);\n\t\tif(text_description == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\trc = dynsec_rolelist__load_from_json(data, cmd->j_command, &rolelist);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t/* Apply changes below */\n\t\thave_rolelist = true;\n\t}else if(rc == ERR_LIST_NOT_FOUND){\n\t\t/* There was no list in the JSON, so no modification */\n\t\trolelist = NULL;\n\t}else if(rc == MOSQ_ERR_NOT_FOUND){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto error;\n\t}else{\n\t\tif(rc == MOSQ_ERR_INVAL){\n\t\t\tmosquitto_control_command_reply(cmd, \"'roles' not an array or missing/invalid rolename\");\n\t\t}else{\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t}\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto error;\n\t}\n\n\tj_clients = cJSON_GetObjectItem(cmd->j_command, \"clients\");\n\tif(j_clients && cJSON_IsArray(j_clients)){\n\t\t/* Iterate over array to check clients are valid before proceeding */\n\t\tcJSON_ArrayForEach(j_client, j_clients){\n\t\t\tif(cJSON_IsObject(j_client)){\n\t\t\t\tconst char *username;\n\t\t\t\tif(json_get_string(j_client, \"username\", &username, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\t\tclient = dynsec_clients__find(data, username);\n\t\t\t\t\tif(client == NULL){\n\t\t\t\t\t\tmosquitto_control_command_reply(cmd, \"'clients' contains an object with a 'username' that does not exist\");\n\t\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tmosquitto_control_command_reply(cmd, \"'clients' contains an object with an invalid 'username'\");\n\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/* Kick all clients in the *current* group */\n\t\tgroup__kick_all(data, group);\n\t\tdynsec__remove_all_clients_from_group(group);\n\n\t\t/* Now we can add the new clients to the group */\n\t\tcJSON_ArrayForEach(j_client, j_clients){\n\t\t\tif(cJSON_IsObject(j_client)){\n\t\t\t\tconst char *username;\n\t\t\t\tif(json_get_string(j_client, \"username\", &username, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\t\tjson_get_int(j_client, \"priority\", &priority, true, -1);\n\t\t\t\t\tif(priority > PRIORITY_MAX){\n\t\t\t\t\t\tpriority = PRIORITY_MAX;\n\t\t\t\t\t}\n\t\t\t\t\tif(priority < -PRIORITY_MAX){\n\t\t\t\t\t\tpriority = -PRIORITY_MAX;\n\t\t\t\t\t}\n\t\t\t\t\tdynsec_groups__add_client(data, username, groupname, priority, false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Apply remaining changes to group, note that user changes are already applied */\n\tif(have_text_name){\n\t\tmosquitto_free(group->text_name);\n\t\tgroup->text_name = text_name;\n\t}\n\n\tif(have_text_description){\n\t\tmosquitto_free(group->text_description);\n\t\tgroup->text_description = text_description;\n\t}\n\n\tif(have_rolelist){\n\t\tdynsec_rolelist__cleanup(&group->rolelist);\n\t\tgroup->rolelist = rolelist;\n\t}\n\n\t/* And save */\n\tdynsec__config_batch_save(data);\n\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\t/* Enforce any changes - kick any clients in the *new* group */\n\tgroup__kick_all(data, group);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | modifyGroup | groupname=%s\",\n\t\t\tadmin_clientid, admin_username, groupname);\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tmosquitto_free(text_name);\n\tmosquitto_free(text_description);\n\tdynsec_rolelist__cleanup(&rolelist);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | modifyGroup | groupname=%s\",\n\t\t\tadmin_clientid, admin_username, groupname);\n\n\treturn rc;\n}\n\n\nint dynsec_groups__process_set_anonymous_group(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *groupname;\n\tstruct dynsec__group *group = NULL;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"groupname\", &groupname, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing groupname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Group name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tgroup = dynsec_groups__find(data, groupname);\n\tif(group == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Group not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tdata->anonymous_group = group;\n\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\t/* Enforce any changes */\n\tdynsec_kicklist__add(data, NULL);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | setAnonymousGroup | groupname=%s\",\n\t\t\tadmin_clientid, admin_username, groupname);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_groups__process_get_anonymous_group(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tcJSON *tree, *j_data, *j_group;\n\tconst char *groupname;\n\tconst char *admin_clientid, *admin_username;\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(data->anonymous_group){\n\t\tgroupname = data->anonymous_group->groupname;\n\t}else{\n\t\tgroupname = \"\";\n\t}\n\n\tif(cJSON_AddStringToObject(tree, \"command\", \"getAnonymousGroup\") == NULL\n\t\t\t|| (j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL\n\t\t\t|| (j_group = cJSON_AddObjectToObject(j_data, \"group\")) == NULL\n\t\t\t|| cJSON_AddStringToObject(j_group, \"groupname\", groupname) == NULL\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | getAnonymousGroup\",\n\t\t\tadmin_clientid, admin_username);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/dynamic-security/kicklist.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <utlist.h>\n\n#include \"dynamic_security.h\"\n\n\nint dynsec_kicklist__add(struct dynsec__data *data, const char *username)\n{\n\tstruct dynsec__kicklist *kick;\n\tsize_t slen;\n\n\tif(username){\n\t\tslen = strlen(username);\n\t}else{\n\t\tslen = 0;\n\t}\n\tkick = malloc(sizeof(struct dynsec__kicklist)+slen+1);\n\tif(!kick){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tif(username){\n\t\tstrcpy(kick->username, username);\n\t}else{\n\t\tkick->username[0] = '\\0';\n\t}\n\tDL_APPEND(data->kicklist, kick);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid dynsec_kicklist__kick(struct dynsec__data *data)\n{\n\tstruct dynsec__kicklist *kick, *tmp;\n\n\tDL_FOREACH_SAFE(data->kicklist, kick, tmp){\n\t\tDL_DELETE(data->kicklist, kick);\n\t\tif(strlen(kick->username)){\n\t\t\tmosquitto_kick_client_by_username(kick->username, false);\n\t\t}else{\n\t\t\tmosquitto_kick_client_by_username(NULL, false);\n\t\t}\n\t\tfree(kick);\n\t}\n}\n\n\nvoid dynsec_kicklist__cleanup(struct dynsec__data *data)\n{\n\tstruct dynsec__kicklist *kick, *tmp;\n\n\tDL_FOREACH_SAFE(data->kicklist, kick, tmp){\n\t\tDL_DELETE(data->kicklist, kick);\n\t\tfree(kick);\n\t}\n}\n"
  },
  {
    "path": "plugins/dynamic-security/migrate_to_dynsec.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nMigration script to migrate from acl_file and password_file to the Dynamic Security Plugin.\n\"\"\"\n\nimport json\nimport argparse\nfrom pathlib import Path  # use Path to be able to handle Windows as well\nfrom dataclasses import dataclass, asdict, field\nfrom typing import Optional, Self\n\n\n#\n# Dynamic Security Plugin\n#\n\n\n@dataclass\nclass DynsecAclDefaultAccess:\n    publishClientSend: bool = False\n    publishClientReceive: bool = True\n    subscribe: bool = False\n    unsubscribe: bool = True\n\n\n@dataclass\nclass DynSecAcl:\n    acltype: str\n    priority: int\n    allow: bool\n    topic: str\n\n\n@dataclass\nclass DynSecRole:\n    rolename: str = \"\"\n    textname: str = \"\"\n    textdescription: str = \"\"\n    allowwildcardsubs: bool = True\n    acls: list[DynSecAcl] = field(default_factory=list)\n\n    @staticmethod\n    def create_role_with_permissions(\n        role_name: str,\n        text_description: str,\n        topic_pattern: str,\n        permissions: list[str],\n    ) -> Self:\n        return DynSecRole(\n            rolename=role_name,\n            textdescription=text_description,\n            acls=[\n                DynSecAcl(permission, 0, True, topic_pattern)\n                for permission in permissions\n            ],\n            allowwildcardsubs=True,\n        )\n\n    @staticmethod\n    def create_role_with_full_permissions(\n        role_name: str, text_description: str, topic_pattern: str\n    ) -> Self:\n        return DynSecRole.create_role_with_permissions(\n            role_name=role_name,\n            text_description=text_description,\n            topic_pattern=topic_pattern,\n            permissions=[\n                \"publishClientSend\",\n                \"publishClientReceive\",\n                \"subscribePattern\",\n                \"unsubscribePattern\",\n            ],\n        )\n\n\nDYNSEC_DEFAULT_ROLES: list[DynSecRole] = [\n    DynSecRole.create_role_with_full_permissions(\n        role_name=\"client\",\n        text_description=\"Read/write access to the full application topic hierarchy.\",\n        topic_pattern=\"#\",\n    ),\n    DynSecRole.create_role_with_full_permissions(\n        role_name=\"broker-admin\",\n        text_description=\"Grants access to administer general broker configuration.\",\n        topic_pattern=\"$CONTROL/broker/#\",\n    ),\n    DynSecRole.create_role_with_full_permissions(\n        role_name=\"dynsec-admin\",\n        text_description=\"Grants access to administer clients/groups/roles.\",\n        topic_pattern=\"$CONTROL/dynamic-security/#\",\n    ),\n    DynSecRole.create_role_with_full_permissions(\n        role_name=\"inspect-admin\",\n        text_description=\"Grants access to administer inspect data.\",\n        topic_pattern=\"$CONTROL/cedalo/inspect/#\",\n    ),\n    DynSecRole.create_role_with_full_permissions(\n        role_name=\"super-admin\",\n        text_description=\"Grants access to administer all kind of broker controls\",\n        topic_pattern=\"$CONTROL/#\",\n    ),\n    DynSecRole.create_role_with_permissions(\n        role_name=\"sys-notify\",\n        text_description=\"Allow bridges to publish connection state messages.\",\n        topic_pattern=r\"$SYS/broker/connection/%c/state\",\n        permissions=[\"publishClientSend\"],\n    ),\n    DynSecRole.create_role_with_permissions(\n        role_name=\"sys-observe\",\n        text_description=\"Observe the $SYS topic hierarchy.\",\n        topic_pattern=\"$SYS/#\",\n        permissions=[\"publishClientReceive\", \"subscribePattern\"],\n    ),\n    DynSecRole.create_role_with_permissions(\n        role_name=\"topic-observe\",\n        text_description=\"Read only access to the full application topic hierarchy.\",\n        topic_pattern=\"#\",\n        permissions=[\n            \"publishClientReceive\",\n            \"subscribePattern\",\n            \"unsubscribePattern\",\n        ],\n    ),\n]\n\n\n@dataclass\nclass DynSecClient:\n    username: str\n    rolelist: list[dict[str, str]]\n    password: Optional[str] = None\n    salt: Optional[str] = None\n    hash_algorithm_id: int = 7\n    iterations: int = 1000\n    encoded_password: Optional[int] = None\n    textname: str = \"\"\n    textdescription: str = \"\"\n    # clientid omitted as it cannot be set for the user in the ACL file\n    disabled: bool = False\n\n    def asdict(self) -> dict:\n        dynsec_client_as_dict = {\n            \"username\": self.username,\n            \"roles\": self.rolelist,\n            \"disabled\": self.disabled,\n        }\n\n        if self.encoded_password is not None:\n            dynsec_client_as_dict.update({\"encoded_password\": self.encoded_password})\n        else:\n            dynsec_client_as_dict.update(\n                {\n                    \"password\": self.password,\n                    \"salt\": self.salt,\n                    \"iterations\": self.iterations,\n                }\n            )\n\n        if self.textname is not None:\n            dynsec_client_as_dict.update({\"textname\": self.textname})\n\n        if self.textdescription is not None:\n            dynsec_client_as_dict.update({\"textdescription\": self.textdescription})\n\n        return dynsec_client_as_dict\n\n\n@dataclass\nclass DynSecGroup:\n    groupname: str\n    textname: str\n    textdescription: str\n    roles: list[DynSecRole]\n\n\nDYNSEC_DEFAULT_ANON_GROUP = DynSecGroup(\n    groupname=\"unauthenticated\",\n    textname=\"Unauthenticated group\",\n    textdescription=\"If unauthenticated access is allowed, this group can be used to define roles for clients that connect without a password.\",\n    roles=[],\n)\n\n\n@dataclass\nclass DynSecConfig:\n    defaultACLAccess: DynsecAclDefaultAccess = field(\n        default_factory=DynsecAclDefaultAccess\n    )\n    clients: list[DynSecClient] = field(default_factory=list)\n    groups: list[DynSecGroup] = field(\n        default_factory=lambda: [DYNSEC_DEFAULT_ANON_GROUP]\n    )\n    roles: list[DynSecRole] = field(default_factory=lambda: DYNSEC_DEFAULT_ROLES)\n    anonymousGroup: str = \"unauthenticated\"\n\n    def asdict(self) -> dict:\n        return {\n            \"defaultACLAccess\": asdict(self.defaultACLAccess),\n            \"clients\": [client.asdict() for client in self.clients],\n            \"groups\": [asdict(group) for group in self.groups],\n            \"roles\": [asdict(role) for role in self.roles],\n            \"anonymousGroup\": self.anonymousGroup,\n        }\n\n\n#\n# ACL file\n#\n\nACL_FILE_DYNSEC_MAP = {\n    \"read\": [\n        \"subscribePattern\",\n    ],\n    \"write\": [\"publishClientSend\"],\n    \"readwrite\": [\n        \"subscribePattern\",\n        \"publishClientSend\",\n    ],\n    \"deny\": [\n        \"subscribePattern\",\n        \"publishClientSend\",\n    ],\n}\n\n\ndef is_parent_topic(parent_topic: str, topic: str, user: str = None) -> bool:\n    if len(parent_topic) == 0 or len(topic) == 0:\n        return False\n\n    if (parent_topic.startswith(\"$\") and not topic.startswith(\"$\")) or (\n        not parent_topic.startswith(\"$\") and topic.startswith(\"$\")\n    ):\n        return False\n\n    tokens_sub_topic = parent_topic.split(\"/\")\n    tokens_topic = topic.split(\"/\")\n\n    if not \"#\" in parent_topic and len(tokens_sub_topic) != len(tokens_topic):\n        return False\n\n    for token_parent_topic, token_topic in zip(tokens_sub_topic, tokens_topic):\n        if token_parent_topic in (\"#\", \"+\"):\n            continue\n\n        if token_parent_topic in (r\"%c\", r\"%u\"):\n            if token_parent_topic == r\"%u\" and user is not None:\n                token_parent_topic.replace(r\"%u\", user)\n            else:\n                continue\n\n        if token_parent_topic != token_topic:\n            return False\n\n    return True\n\n\n@dataclass\nclass AclFileConfig:\n    global_acls: list[DynSecAcl] = field(default_factory=list)\n    user_acls: dict[str, list[DynSecAcl]] = field(default_factory=dict)\n\n    @staticmethod\n    def topic_or_pattern_sanity_check(acl_file_line: str, tokens: list[str]) -> None:\n        # At least topic/pattern keyword and the topic string must be provided\n        if len(tokens) < 2:\n            raise ValueError(\n                f'Invalid topic/pattern definition: \"{acl_file_line}\" (Too few arguments)'\n            )\n\n        # Topic is missing\n        if len(tokens) == 2 and tokens[1] in ACL_FILE_DYNSEC_MAP:\n            raise ValueError(\n                f'Invalid topic/pattern definition: \"{acl_file_line}\" (Topic missing)'\n            )\n\n        # Topic string contains at least one whitespace => access type is mandatory\n        if len(tokens) > 3 and tokens[1] not in ACL_FILE_DYNSEC_MAP:\n            raise ValueError(\n                f'Invalid topic/pattern definition: \"{acl_file_line}\" (Access type missing)'\n            )\n\n    @staticmethod\n    def parse_topic_or_pattern_acl(acl_file_line: str) -> list[DynSecAcl]:\n        tokens = acl_file_line.strip().split(\" \")\n\n        # Raises in case of an invalid definition\n        AclFileConfig.topic_or_pattern_sanity_check(acl_file_line, tokens)\n\n        return [\n            DynSecAcl(\n                acltype=permission,\n                priority=0 if len(tokens) == 2 or tokens[1] != \"deny\" else 1,\n                allow=True if len(tokens) == 2 else tokens[1] != \"deny\",\n                topic=tokens[1] if len(tokens) == 2 else \" \".join(tokens[2:]),\n            )\n            for permission in (\n                ACL_FILE_DYNSEC_MAP[\"readwrite\"]\n                if len(tokens) == 2\n                else ACL_FILE_DYNSEC_MAP[tokens[1]]\n            )\n        ]\n\n    @staticmethod\n    def parse_acl_file(acl_file_path: Path) -> Self:\n        acl_file_lines = acl_file_path.read_text(encoding=\"utf-8\").splitlines()\n\n        acl_file_config = AclFileConfig()\n        current_user: str = None\n\n        for line in acl_file_lines:\n            if line.startswith(\"#\"):\n                continue\n\n            if line.startswith(\"user\"):\n                current_user = line.replace(\"user \", \"\")\n                if current_user not in acl_file_config.user_acls:\n                    acl_file_config.user_acls[current_user] = []\n                continue\n\n            current_acls: list[DynSecAcl] = None\n            if line.startswith(\"pattern\") or line.startswith(\"topic\"):\n                current_acls = AclFileConfig.parse_topic_or_pattern_acl(line)\n\n            if current_acls is not None:\n                if not line.startswith(\"pattern\") and current_user is not None:\n                    acl_file_config.user_acls[current_user].extend(current_acls)\n                else:\n                    acl_file_config.global_acls.extend(current_acls)\n\n        return acl_file_config\n\n\n#\n# Password file\n#\n\n\n@dataclass\nclass PasswordFile:\n    username_password_map: dict[str, Optional[str]] = field(default_factory=dict)\n\n    def __getitem__(self, username: str):\n        return self.username_password_map[username]\n\n    def __setitem__(self, username: str, pw_data: str):\n        self.username_password_map[username] = pw_data\n\n    @staticmethod\n    def parse_password_file(pw_file_path: Path) -> Self:\n        pw_file_lines = pw_file_path.read_text(encoding=\"utf-8\").splitlines()\n\n        pw_file = PasswordFile()\n        for line in pw_file_lines:\n            [username, password] = line.strip().split(\":\")\n            pw_file[username] = password\n\n        return pw_file\n\n\n#\n# Migration\n#\n\n\ndef filter_used_deny_acls(acls: list[DynSecAcl], user: str = None) -> AclFileConfig:\n    deny_acls = list(filter(lambda dynsec_acl: not dynsec_acl.allow, acls))\n    allow_acls = list(filter(lambda dynsec_acl: dynsec_acl not in deny_acls, acls))\n    final_acls = []\n\n    for deny_acl in deny_acls:\n        for allow_acl in allow_acls:\n            if is_parent_topic(allow_acl.topic, deny_acl.topic, user):\n                final_acls.append(deny_acl)\n                break\n        if deny_acl not in final_acls:\n            print(f\"WARNING: Removing unused 'deny' ACL: {deny_acl}\")\n\n    final_acls.extend(allow_acls)\n    return final_acls\n\n\ndef migrate_to_dynsec(\n    acl_file_config: AclFileConfig, pw_file: PasswordFile\n) -> DynSecConfig:\n    dynsec_config = DynSecConfig()\n\n    for user in pw_file.username_password_map.keys():\n        user_rolename = f\"client-role-{user}\"\n\n        # ACL file\n        user_acls = acl_file_config.user_acls.get(user)\n        if user_acls is not None:\n            user_acls_filtered = filter_used_deny_acls(\n                acl_file_config.user_acls[user], user\n            )\n            dynsec_config.roles.append(\n                DynSecRole(rolename=user_rolename, acls=user_acls_filtered)\n            )\n\n        new_client = DynSecClient(\n            username=user,\n            rolelist=[{\"rolename\": user_rolename}] if user_acls is not None else [],\n        )\n\n        # Password file\n        user_password_details = pw_file[user]\n        if user_password_details.startswith(\"$argon2id\"):\n            new_client.encoded_password = user_password_details\n        else:\n            # leading \"$\" creates an empty string on split and hash algo ID is unused\n            #   -> omit first two elements from split\n            [iterations_string, new_client.salt, new_client.password] = (\n                user_password_details.split(\"$\")[2:]\n            )\n            new_client.iterations = int(iterations_string)\n\n        dynsec_config.clients.append(new_client)\n\n    return dynsec_config\n\n\ndef migrate_mosquitto_conf(\n    mosquitto_conf: str, dynsec_lib_path: Path, dynsec_config_path: Path\n) -> str:\n    migrated_mosquitto_conf: list[str] = []\n    for line in mosquitto_conf.splitlines():\n        if line.startswith(\"acl_file\"):\n            continue\n\n        if line.startswith(\"password_file\"):\n            dynsec_plugin_configuration = [\n                f\"plugin {str(dynsec_lib_path)}\",\n                f\"plugin_opt_config_file {str(dynsec_config_path)}\",\n            ]\n            migrated_mosquitto_conf.extend(dynsec_plugin_configuration)\n            continue\n\n        migrated_mosquitto_conf.append(line)\n\n    return \"\\n\".join(migrated_mosquitto_conf)\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--acl-file\", required=True, type=str, help=\"Path to ACL file\")\n    parser.add_argument(\n        \"--pw-file\", required=True, type=str, help=\"Path to password file\"\n    )\n    parser.add_argument(\n        \"--conf\", required=False, type=str, help=\"Path to mosquitto.conf file\"\n    )\n    parser.add_argument(\n        \"--dynsec-lib\",\n        required=False,\n        type=str,\n        help=\"Path to mosquitto_dynamic_security.so/.dll\",\n    )\n\n    args = parser.parse_args()\n    acl_file_path = Path(args.acl_file)\n    pw_file_path = Path(args.pw_file)\n\n    dynsec_config_file_path = Path(__file__).parent.resolve() / \"dynamic-security.json\"\n\n    if args.conf is not None:\n        if args.dynsec_lib is None:\n            print(\n                \"Error: Cannot migrate mosquitto.conf file. Reason: --dynsec-lib argument is missing\"\n            )\n            return\n\n        mosquitto_conf_path = Path(args.conf)\n        mosquitto_conf = mosquitto_conf_path.read_text(encoding=\"utf-8\")\n\n        # Migrate mosquitto.conf\n        migrated_mosquitto_conf = migrate_mosquitto_conf(\n            mosquitto_conf,\n            Path(args.dynsec_lib),\n            dynsec_config_file_path,\n        )\n\n        # Backup old mosquitto.conf and afterwards write migrated mosquitto.conf file\n        mosquitto_conf_path.with_suffix(\".conf.old.dynsec\").write_text(\n            mosquitto_conf, encoding=\"utf-8\"\n        )\n        mosquitto_conf_path.write_text(migrated_mosquitto_conf, encoding=\"utf-8\")\n\n    parsed_acl_file: AclFileConfig = AclFileConfig.parse_acl_file(acl_file_path)\n\n    # Add global ACLs to users\n    for _, user_acls in parsed_acl_file.user_acls.items():\n        user_acls.extend(parsed_acl_file.global_acls)\n\n    parsed_pw_file: PasswordFile = PasswordFile.parse_password_file(pw_file_path)\n\n    # Migrate config and write to file\n    dynsec_config = migrate_to_dynsec(parsed_acl_file, parsed_pw_file)\n    dynsec_config_file_path.write_text(\n        data=json.dumps(dynsec_config.asdict(), indent=4), encoding=\"utf-8\"\n    )\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "plugins/dynamic-security/plugin.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\n#ifndef WIN32\n#  include <strings.h>\n#endif\n\n#include \"mosquitto.h\"\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic struct dynsec__data dynsec_data;\nstatic mosquitto_plugin_id_t *plg_id = NULL;\n\n#ifdef WIN32\n#  include <winsock2.h>\n#  include <aclapi.h>\n#  include <io.h>\n#  include <lmcons.h>\n#  include <fcntl.h>\n#  define PATH_MAX MAX_PATH\n#else\n#  include <sys/stat.h>\n#  include <pwd.h>\n#  include <grp.h>\n#  include <unistd.h>\n#endif\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *options, int option_count)\n{\n\tint i;\n\tint rc;\n\n\tUNUSED(user_data);\n\n\tmemset(&dynsec_data, 0, sizeof(struct dynsec__data));\n\n\tfor(i=0; i<option_count; i++){\n\t\tif(!strcasecmp(options[i].key, \"config_file\")){\n\t\t\tdynsec_data.config_file = mosquitto_strdup(options[i].value);\n\t\t\tif(dynsec_data.config_file == NULL){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}else if(!strcasecmp(options[i].key, \"password_init_file\")){\n\t\t\tdynsec_data.password_init_file = mosquitto_strdup(options[i].value);\n\t\t\tif(dynsec_data.password_init_file == NULL){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}\n\tif(dynsec_data.config_file == NULL){\n\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Warning: Dynamic security plugin has no plugin_opt_config_file defined. The plugin will not be activated.\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tplg_id = identifier;\n\tmosquitto_plugin_set_info(identifier, \"dynamic-security\", NULL);\n\n\tdynsec__config_load(&dynsec_data);\n\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_CONTROL, dynsec_control_callback, \"$CONTROL/dynamic-security/v1\", &dynsec_data);\n\tif(rc == MOSQ_ERR_ALREADY_EXISTS){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Dynamic security plugin can currently only be loaded once.\");\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Note that this was previously incorrectly allowed but could cause problems with duplicate entries in the config.\");\n\t\tgoto error;\n\t}else if(rc == MOSQ_ERR_NOMEM){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\tgoto error;\n\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto error;\n\t}\n\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, dynsec_auth__basic_auth_callback, NULL, &dynsec_data);\n\tif(rc == MOSQ_ERR_ALREADY_EXISTS){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Dynamic security plugin can only be loaded once.\");\n\t\tgoto error;\n\t}else if(rc == MOSQ_ERR_NOMEM){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\tgoto error;\n\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto error;\n\t}\n\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, dynsec__acl_check_callback, NULL, &dynsec_data);\n\tif(rc == MOSQ_ERR_ALREADY_EXISTS){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Dynamic security plugin can only be loaded once.\");\n\t\tgoto error;\n\t}else if(rc == MOSQ_ERR_NOMEM){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\tgoto error;\n\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto error;\n\t}\n\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_TICK, dynsec__tick_callback, NULL, &dynsec_data);\n\tif(rc == MOSQ_ERR_NOMEM){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\tgoto error;\n\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto error;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tmosquitto_free(dynsec_data.config_file);\n\tdynsec_data.config_file = NULL;\n\treturn rc;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *options, int option_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(options);\n\tUNUSED(option_count);\n\n\tdynsec_groups__cleanup(&dynsec_data);\n\tdynsec_clients__cleanup(&dynsec_data);\n\tdynsec_roles__cleanup(&dynsec_data);\n\tdynsec_kicklist__cleanup(&dynsec_data);\n\n\tmosquitto_free(dynsec_data.config_file);\n\tdynsec_data.config_file = NULL;\n\n\tmosquitto_free(dynsec_data.password_init_file);\n\tdynsec_data.password_init_file = NULL;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_CONTROL, dynsec_control_callback, \"$CONTROL/dynamic-security/v1\");\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_BASIC_AUTH, dynsec_auth__basic_auth_callback, NULL);\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_ACL_CHECK, dynsec__acl_check_callback, NULL);\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_TICK, dynsec__tick_callback, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/dynamic-security/rolelist.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <string.h>\n#include <uthash.h>\n#include <utlist.h>\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\n\n/* ################################################################\n * #\n * # Utility functions\n * #\n * ################################################################ */\n\n\nstatic int rolelist_cmp(void *a, void *b)\n{\n\tint prio;\n\tstruct dynsec__rolelist *rolelist_a = a;\n\tstruct dynsec__rolelist *rolelist_b = b;\n\n\tprio = rolelist_b->priority - rolelist_a->priority;\n\tif(prio == 0){\n\t\treturn strcmp(rolelist_a->rolename, rolelist_b->rolename);\n\t}else{\n\t\treturn prio;\n\t}\n}\n\n\nstatic void dynsec_rolelist__free_item(struct dynsec__rolelist **base_rolelist, struct dynsec__rolelist *rolelist)\n{\n\tHASH_DELETE(hh, *base_rolelist, rolelist);\n\tmosquitto_free(rolelist);\n}\n\n\nvoid dynsec_rolelist__cleanup(struct dynsec__rolelist **base_rolelist)\n{\n\tstruct dynsec__rolelist *rolelist, *rolelist_tmp;\n\n\tHASH_ITER(hh, *base_rolelist, rolelist, rolelist_tmp){\n\t\tdynsec_rolelist__free_item(base_rolelist, rolelist);\n\t}\n}\n\n\nstatic int dynsec_rolelist__remove_role(struct dynsec__rolelist **base_rolelist, const struct dynsec__role *role)\n{\n\tstruct dynsec__rolelist *found_rolelist;\n\n\tHASH_FIND(hh, *base_rolelist, role->rolename, strlen(role->rolename), found_rolelist);\n\tif(found_rolelist){\n\t\tdynsec_rolelist__free_item(base_rolelist, found_rolelist);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n}\n\n\nint dynsec_rolelist__client_remove(struct dynsec__client *client, struct dynsec__role *role)\n{\n\tint rc;\n\tstruct dynsec__clientlist *found_clientlist;\n\n\trc = dynsec_rolelist__remove_role(&client->rolelist, role);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tHASH_FIND(hh, role->clientlist, client->username, strlen(client->username), found_clientlist);\n\tif(found_clientlist){\n\t\tHASH_DELETE(hh, role->clientlist, found_clientlist);\n\t\tmosquitto_free(found_clientlist);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n}\n\n\nvoid dynsec_rolelist__group_remove(struct dynsec__group *group, struct dynsec__role *role)\n{\n\tdynsec_rolelist__remove_role(&group->rolelist, role);\n\tdynsec_grouplist__remove(&role->grouplist, group);\n}\n\n\nstatic int dynsec_rolelist__add(struct dynsec__rolelist **base_rolelist, struct dynsec__role *role, int priority)\n{\n\tstruct dynsec__rolelist *rolelist;\n\tsize_t rolename_len;\n\n\tif(role == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\trolename_len = strlen(role->rolename);\n\tif(rolename_len == 0){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_FIND(hh, *base_rolelist, role->rolename, rolename_len, rolelist);\n\tif(rolelist){\n\t\treturn MOSQ_ERR_ALREADY_EXISTS;\n\t}else{\n\t\trolelist = mosquitto_calloc(1, sizeof(struct dynsec__rolelist) + rolename_len + 1);\n\t\tif(rolelist == NULL){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\trolelist->role = role;\n\t\trolelist->priority = priority;\n\t\tstrncpy(rolelist->rolename, role->rolename, rolename_len+1);\n\t\tHASH_ADD_INORDER(hh, *base_rolelist, rolename, rolename_len, rolelist, rolelist_cmp);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint dynsec_rolelist__client_add(struct dynsec__client *client, struct dynsec__role *role, int priority)\n{\n\tstruct dynsec__rolelist *rolelist;\n\tint rc;\n\n\trc = dynsec_rolelist__add(&client->rolelist, role, priority);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tHASH_FIND(hh, client->rolelist, role->rolename, strlen(role->rolename), rolelist);\n\tif(rolelist == NULL){\n\t\t/* This should never happen because the above add_role succeeded. */\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\trc = dynsec_clientlist__add(&role->clientlist, client, priority);\n\tif(rc){\n\t\tdynsec_rolelist__remove_role(&client->rolelist, role);\n\t}\n\n\treturn rc;\n}\n\n\nint dynsec_rolelist__group_add(struct dynsec__group *group, struct dynsec__role *role, int priority)\n{\n\tint rc;\n\n\trc = dynsec_rolelist__add(&group->rolelist, role, priority);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = dynsec_grouplist__add(&role->grouplist, group, priority);\n\tif(rc){\n\t\tdynsec_rolelist__remove_role(&group->rolelist, role);\n\t}\n\treturn rc;\n}\n\n\nint dynsec_rolelist__load_from_json(struct dynsec__data *data, cJSON *command, struct dynsec__rolelist **rolelist)\n{\n\tcJSON *j_roles, *j_role;\n\tint priority;\n\tstruct dynsec__role *role;\n\tconst char *rolename;\n\n\tj_roles = cJSON_GetObjectItem(command, \"roles\");\n\tif(j_roles){\n\t\tif(cJSON_IsArray(j_roles)){\n\t\t\tcJSON_ArrayForEach(j_role, j_roles){\n\t\t\t\tif(json_get_string(j_role, \"rolename\", &rolename, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\t\tjson_get_int(j_role, \"priority\", &priority, true, -1);\n\t\t\t\t\tif(priority > PRIORITY_MAX){\n\t\t\t\t\t\tpriority = PRIORITY_MAX;\n\t\t\t\t\t}\n\t\t\t\t\trole = dynsec_roles__find(data, rolename);\n\t\t\t\t\tif(role){\n\t\t\t\t\t\tdynsec_rolelist__add(rolelist, role, priority);\n\t\t\t\t\t}else{\n\t\t\t\t\t\tdynsec_rolelist__cleanup(rolelist);\n\t\t\t\t\t\treturn MOSQ_ERR_NOT_FOUND;\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}else{\n\t\treturn ERR_LIST_NOT_FOUND;\n\t}\n}\n\n\ncJSON *dynsec_rolelist__all_to_json(struct dynsec__rolelist *base_rolelist)\n{\n\tstruct dynsec__rolelist *rolelist, *rolelist_tmp;\n\tcJSON *j_roles, *j_role;\n\n\tj_roles = cJSON_CreateArray();\n\tif(j_roles == NULL){\n\t\treturn NULL;\n\t}\n\n\tHASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){\n\t\tj_role = cJSON_CreateObject();\n\t\tif(j_role == NULL){\n\t\t\tcJSON_Delete(j_roles);\n\t\t\treturn NULL;\n\t\t}\n\t\tcJSON_AddItemToArray(j_roles, j_role);\n\n\t\tif(cJSON_AddStringToObject(j_role, \"rolename\", rolelist->role->rolename) == NULL\n\t\t\t\t|| (rolelist->priority != -1 && cJSON_AddIntToObject(j_role, \"priority\", rolelist->priority) == NULL)\n\t\t\t\t){\n\n\t\t\tcJSON_Delete(j_roles);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn j_roles;\n}\n"
  },
  {
    "path": "plugins/dynamic-security/roles.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <stdio.h>\n#include <string.h>\n#include <uthash.h>\n#include <utlist.h>\n\n#ifndef WIN32\n#  include <strings.h>\n#endif\n\n#include \"dynamic_security.h\"\n#include \"json_help.h\"\n\nstatic cJSON *add_role_to_json(struct dynsec__role *role, bool verbose);\nstatic void role__remove_all_clients(struct dynsec__data *data, struct dynsec__role *role);\n\n\n/* ################################################################\n * #\n * # Utility functions\n * #\n * ################################################################ */\n\n\nstatic int role_cmp(void *a, void *b)\n{\n\tstruct dynsec__role *role_a = a;\n\tstruct dynsec__role *role_b = b;\n\n\treturn strcmp(role_a->rolename, role_b->rolename);\n}\n\n\nstatic void role__free_acl(struct dynsec__acl **acl, struct dynsec__acl *item)\n{\n\tHASH_DELETE(hh, *acl, item);\n\tmosquitto_free(item);\n}\n\n\nstatic void role__free_all_acls(struct dynsec__acl **acl)\n{\n\tstruct dynsec__acl *iter, *tmp = NULL;\n\n\tHASH_ITER(hh, *acl, iter, tmp){\n\t\trole__free_acl(acl, iter);\n\t}\n}\n\n\nstatic void role__free_item(struct dynsec__data *data, struct dynsec__role *role, bool remove_from_hash)\n{\n\tif(remove_from_hash){\n\t\tHASH_DEL(data->roles, role);\n\t}\n\tdynsec_clientlist__cleanup(&role->clientlist);\n\tdynsec_grouplist__cleanup(&role->grouplist);\n\tmosquitto_free(role->text_name);\n\tmosquitto_free(role->text_description);\n\trole__free_all_acls(&role->acls.publish_c_send);\n\trole__free_all_acls(&role->acls.publish_c_recv);\n\trole__free_all_acls(&role->acls.subscribe_literal);\n\trole__free_all_acls(&role->acls.subscribe_pattern);\n\trole__free_all_acls(&role->acls.unsubscribe_literal);\n\trole__free_all_acls(&role->acls.unsubscribe_pattern);\n\tmosquitto_free(role);\n}\n\nstruct dynsec__role *dynsec_roles__find(struct dynsec__data *data, const char *rolename)\n{\n\tstruct dynsec__role *role = NULL;\n\n\tif(rolename){\n\t\tHASH_FIND(hh, data->roles, rolename, strlen(rolename), role);\n\t}\n\treturn role;\n}\n\n\nvoid dynsec_roles__cleanup(struct dynsec__data *data)\n{\n\tstruct dynsec__role *role, *role_tmp = NULL;\n\n\tHASH_ITER(hh, data->roles, role, role_tmp){\n\t\trole__free_item(data, role, true);\n\t}\n}\n\n\nstatic void role__kick_all(struct dynsec__data *data, struct dynsec__role *role)\n{\n\tstruct dynsec__grouplist *grouplist, *grouplist_tmp = NULL;\n\n\tdynsec_clientlist__kick_all(data, role->clientlist);\n\n\tHASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){\n\t\tif(grouplist->group == data->anonymous_group){\n\t\t\tdynsec_kicklist__add(data, NULL);\n\t\t}\n\t\tdynsec_clientlist__kick_all(data, grouplist->group->clientlist);\n\t}\n}\n\n\n/* ################################################################\n * #\n * # Config file load and save\n * #\n * ################################################################ */\n\n\nstatic int add_single_acl_to_json(cJSON *j_array, const char *acl_type, struct dynsec__acl *acl)\n{\n\tstruct dynsec__acl *iter, *tmp = NULL;\n\tcJSON *j_acl;\n\n\tHASH_ITER(hh, acl, iter, tmp){\n\t\tj_acl = cJSON_CreateObject();\n\t\tif(j_acl == NULL){\n\t\t\treturn 1;\n\t\t}\n\t\tcJSON_AddItemToArray(j_array, j_acl);\n\n\t\tif(cJSON_AddStringToObject(j_acl, \"acltype\", acl_type) == NULL\n\t\t\t\t|| cJSON_AddStringToObject(j_acl, \"topic\", iter->topic) == NULL\n\t\t\t\t|| cJSON_AddIntToObject(j_acl, \"priority\", iter->priority) == NULL\n\t\t\t\t|| cJSON_AddBoolToObject(j_acl, \"allow\", iter->allow) == NULL\n\t\t\t\t){\n\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\n\treturn 0;\n}\n\n\nstatic int add_acls_to_json(cJSON *j_role, struct dynsec__role *role)\n{\n\tcJSON *j_acls;\n\n\tif((j_acls = cJSON_AddArrayToObject(j_role, \"acls\")) == NULL){\n\t\treturn 1;\n\t}\n\n\tif(add_single_acl_to_json(j_acls, ACL_TYPE_PUB_C_SEND, role->acls.publish_c_send) != MOSQ_ERR_SUCCESS\n\t\t\t|| add_single_acl_to_json(j_acls, ACL_TYPE_PUB_C_RECV, role->acls.publish_c_recv) != MOSQ_ERR_SUCCESS\n\t\t\t|| add_single_acl_to_json(j_acls, ACL_TYPE_SUB_LITERAL, role->acls.subscribe_literal) != MOSQ_ERR_SUCCESS\n\t\t\t|| add_single_acl_to_json(j_acls, ACL_TYPE_SUB_PATTERN, role->acls.subscribe_pattern) != MOSQ_ERR_SUCCESS\n\t\t\t|| add_single_acl_to_json(j_acls, ACL_TYPE_UNSUB_LITERAL, role->acls.unsubscribe_literal) != MOSQ_ERR_SUCCESS\n\t\t\t|| add_single_acl_to_json(j_acls, ACL_TYPE_UNSUB_PATTERN, role->acls.unsubscribe_pattern) != MOSQ_ERR_SUCCESS\n\t\t\t){\n\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\n\nint dynsec_roles__config_save(struct dynsec__data *data, cJSON *tree)\n{\n\tcJSON *j_roles, *j_role;\n\tstruct dynsec__role *role, *role_tmp = NULL;\n\n\tif((j_roles = cJSON_AddArrayToObject(tree, \"roles\")) == NULL){\n\t\treturn 1;\n\t}\n\n\tHASH_ITER(hh, data->roles, role, role_tmp){\n\t\tj_role = add_role_to_json(role, true);\n\t\tif(j_role == NULL){\n\t\t\treturn 1;\n\t\t}\n\t\tcJSON_AddItemToArray(j_roles, j_role);\n\t}\n\n\treturn 0;\n}\n\n\nstatic int insert_acl_cmp(struct dynsec__acl *a, struct dynsec__acl *b)\n{\n\treturn b->priority - a->priority;\n}\n\n\nstatic int dynsec_roles__acl_load(cJSON *j_acls, const char *key, struct dynsec__acl **acllist)\n{\n\tcJSON *j_acl;\n\tstruct dynsec__acl *acl;\n\n\tcJSON_ArrayForEach(j_acl, j_acls){\n\t\tconst char *acltype;\n\t\tconst char *topic;\n\t\tsize_t topic_len;\n\n\t\tif(json_get_string(j_acl, \"acltype\", &acltype, false) != MOSQ_ERR_SUCCESS){\n\t\t\tcontinue;\n\t\t}\n\t\tif(strcasecmp(acltype, key) != 0){\n\t\t\tcontinue;\n\t\t}\n\t\tif(json_get_string(j_acl, \"topic\", &topic, false) != MOSQ_ERR_SUCCESS){\n\t\t\tcontinue;\n\t\t}\n\n\t\ttopic_len = strlen(topic);\n\t\tif(topic_len == 0){\n\t\t\tcontinue;\n\t\t}\n\n\t\tHASH_FIND(hh, *acllist, topic, strlen(topic), acl);\n\t\tif(acl){\n\t\t\tcontinue;\n\t\t}\n\n\t\tacl = mosquitto_calloc(1, sizeof(struct dynsec__acl) + topic_len + 1);\n\t\tif(acl == NULL){\n\t\t\treturn 1;\n\t\t}\n\t\tstrncpy(acl->topic, topic, topic_len+1);\n\n\t\tjson_get_int(j_acl, \"priority\", &acl->priority, true, 0);\n\t\tif(acl->priority > PRIORITY_MAX){\n\t\t\tacl->priority = PRIORITY_MAX;\n\t\t}\n\t\tif(acl->priority < -PRIORITY_MAX){\n\t\t\tacl->priority = -PRIORITY_MAX;\n\t\t}\n\t\tjson_get_bool(j_acl, \"allow\", &acl->allow, true, false);\n\n\t\tbool allow;\n\t\tif(json_get_bool(j_acl, \"allow\", &allow, false, false) == MOSQ_ERR_SUCCESS){\n\t\t\tacl->allow = allow;\n\t\t}\n\n\t\tHASH_ADD_INORDER(hh, *acllist, topic, topic_len, acl, insert_acl_cmp);\n\t}\n\n\treturn 0;\n}\n\n\nint dynsec_roles__config_load(struct dynsec__data *data, cJSON *tree)\n{\n\tcJSON *j_roles, *j_role, *j_acls;\n\tstruct dynsec__role *role;\n\tsize_t rolename_len;\n\n\tj_roles = cJSON_GetObjectItem(tree, \"roles\");\n\tif(j_roles == NULL){\n\t\treturn 0;\n\t}\n\n\tif(cJSON_IsArray(j_roles) == false){\n\t\treturn 1;\n\t}\n\n\tcJSON_ArrayForEach(j_role, j_roles){\n\t\tif(cJSON_IsObject(j_role) == true){\n\t\t\t/* Role name */\n\t\t\tconst char *rolename;\n\t\t\tif(json_get_string(j_role, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\trolename_len = strlen(rolename);\n\t\t\tif(rolename_len == 0){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(dynsec_roles__find(data, rolename)){\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\trole = mosquitto_calloc(1, sizeof(struct dynsec__role) + rolename_len + 1);\n\t\t\tif(role == NULL){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tstrncpy(role->rolename, rolename, rolename_len+1);\n\n\t\t\t/* Text name */\n\t\t\tconst char *textname;\n\t\t\tif(json_get_string(j_role, \"textname\", &textname, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\trole->text_name = mosquitto_strdup(textname);\n\t\t\t\tif(role->text_name == NULL){\n\t\t\t\t\tmosquitto_free(role);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Text description */\n\t\t\tconst char *textdescription;\n\t\t\tif(json_get_string(j_role, \"textdescription\", &textdescription, false) == MOSQ_ERR_SUCCESS){\n\t\t\t\trole->text_description = mosquitto_strdup(textdescription);\n\t\t\t\tif(role->text_description == NULL){\n\t\t\t\t\tmosquitto_free(role->text_name);\n\t\t\t\t\tmosquitto_free(role);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Allow wildcard subs */\n\t\t\tjson_get_bool(j_role, \"allowwildcardsubs\", &role->allow_wildcard_subs, true, true);\n\n\t\t\t/* ACLs */\n\t\t\tj_acls = cJSON_GetObjectItem(j_role, \"acls\");\n\t\t\tif(j_acls && cJSON_IsArray(j_acls)){\n\t\t\t\tif(dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_SEND, &role->acls.publish_c_send) != 0\n\t\t\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_RECV, &role->acls.publish_c_recv) != 0\n\t\t\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_LITERAL, &role->acls.subscribe_literal) != 0\n\t\t\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_PATTERN, &role->acls.subscribe_pattern) != 0\n\t\t\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_LITERAL, &role->acls.unsubscribe_literal) != 0\n\t\t\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_PATTERN, &role->acls.unsubscribe_pattern) != 0\n\t\t\t\t\t\t){\n\n\t\t\t\t\tmosquitto_free(role->text_name);\n\t\t\t\t\tmosquitto_free(role->text_description);\n\t\t\t\t\tmosquitto_free(role);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tHASH_ADD(hh, data->roles, rolename, rolename_len, role);\n\t\t}\n\t}\n\tHASH_SORT(data->roles, role_cmp);\n\n\treturn 0;\n}\n\n\nint dynsec_roles__process_create(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *rolename;\n\tconst char *text_name, *text_description;\n\tbool allow_wildcard_subs;\n\tstruct dynsec__role *role;\n\tint rc = MOSQ_ERR_SUCCESS;\n\tcJSON *j_acls;\n\tconst char *admin_clientid, *admin_username;\n\tsize_t rolename_len;\n\n\tif(json_get_string(cmd->j_command, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\trolename_len = strlen(rolename);\n\tif(rolename_len == 0){\n\t\tmosquitto_control_command_reply(cmd, \"Empty rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(rolename, (int)rolename_len) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Role name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textname\", &text_name, true) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing textname\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textdescription\", &text_description, true) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing textdescription\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_bool(cmd->j_command, \"allowwildcardsubs\", &allow_wildcard_subs, true, true) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid allowwildcardsubs\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trole = dynsec_roles__find(data, rolename);\n\tif(role){\n\t\tmosquitto_control_command_reply(cmd, \"Role already exists\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\trole = mosquitto_calloc(1, sizeof(struct dynsec__role) + rolename_len + 1);\n\tif(role == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tstrncpy(role->rolename, rolename, rolename_len+1);\n\tif(text_name){\n\t\trole->text_name = mosquitto_strdup(text_name);\n\t\tif(role->text_name == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t}\n\tif(text_description){\n\t\trole->text_description = mosquitto_strdup(text_description);\n\t\tif(role->text_description == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t}\n\trole->allow_wildcard_subs = allow_wildcard_subs;\n\n\t/* ACLs */\n\tj_acls = cJSON_GetObjectItem(cmd->j_command, \"acls\");\n\tif(j_acls && cJSON_IsArray(j_acls)){\n\t\tif(dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_SEND, &role->acls.publish_c_send) != 0\n\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_RECV, &role->acls.publish_c_recv) != 0\n\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_LITERAL, &role->acls.subscribe_literal) != 0\n\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_PATTERN, &role->acls.subscribe_pattern) != 0\n\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_LITERAL, &role->acls.unsubscribe_literal) != 0\n\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_PATTERN, &role->acls.unsubscribe_pattern) != 0\n\t\t\t\t){\n\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\n\tHASH_ADD_INORDER(hh, data->roles, rolename, rolename_len, role, role_cmp);\n\n\tdynsec__config_batch_save(data);\n\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | createRole | rolename=%s\",\n\t\t\tadmin_clientid, admin_username, rolename);\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tif(role){\n\t\trole__free_item(data, role, false);\n\t}\n\treturn rc;\n}\n\n\nstatic void role__remove_all_clients(struct dynsec__data *data, struct dynsec__role *role)\n{\n\tstruct dynsec__clientlist *clientlist, *clientlist_tmp = NULL;\n\n\tHASH_ITER(hh, role->clientlist, clientlist, clientlist_tmp){\n\t\tdynsec_kicklist__add(data, clientlist->client->username);\n\n\t\tdynsec_rolelist__client_remove(clientlist->client, role);\n\t}\n}\n\n\nstatic void role__remove_all_groups(struct dynsec__data *data, struct dynsec__role *role)\n{\n\tstruct dynsec__grouplist *grouplist, *grouplist_tmp = NULL;\n\n\tHASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){\n\t\tif(grouplist->group == data->anonymous_group){\n\t\t\tdynsec_kicklist__add(data, NULL);\n\t\t}\n\t\tdynsec_clientlist__kick_all(data, grouplist->group->clientlist);\n\n\t\tdynsec_rolelist__group_remove(grouplist->group, role);\n\t}\n}\n\n\nint dynsec_roles__process_delete(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *rolename;\n\tstruct dynsec__role *role;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Role name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trole = dynsec_roles__find(data, rolename);\n\tif(role){\n\t\trole__remove_all_clients(data, role);\n\t\trole__remove_all_groups(data, role);\n\t\trole__free_item(data, role, true);\n\t\tdynsec__config_batch_save(data);\n\t\tmosquitto_control_command_reply(cmd, NULL);\n\n\t\tadmin_clientid = mosquitto_client_id(cmd->client);\n\t\tadmin_username = mosquitto_client_username(cmd->client);\n\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | deleteRole | rolename=%s\",\n\t\t\t\tadmin_clientid, admin_username, rolename);\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nstatic cJSON *add_role_to_json(struct dynsec__role *role, bool verbose)\n{\n\tcJSON *j_role = NULL;\n\n\tif(verbose){\n\t\tj_role = cJSON_CreateObject();\n\t\tif(j_role == NULL){\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif(cJSON_AddStringToObject(j_role, \"rolename\", role->rolename) == NULL\n\t\t\t\t|| (role->text_name && cJSON_AddStringToObject(j_role, \"textname\", role->text_name) == NULL)\n\t\t\t\t|| (role->text_description && cJSON_AddStringToObject(j_role, \"textdescription\", role->text_description) == NULL)\n\t\t\t\t|| cJSON_AddBoolToObject(j_role, \"allowwildcardsubs\", role->allow_wildcard_subs) == NULL\n\t\t\t\t){\n\n\t\t\tcJSON_Delete(j_role);\n\t\t\treturn NULL;\n\t\t}\n\t\tif(add_acls_to_json(j_role, role)){\n\t\t\tcJSON_Delete(j_role);\n\t\t\treturn NULL;\n\t\t}\n\t}else{\n\t\tj_role = cJSON_CreateString(role->rolename);\n\t\tif(j_role == NULL){\n\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn j_role;\n}\n\n\nint dynsec_roles__process_list(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tbool verbose;\n\tstruct dynsec__role *role, *role_tmp = NULL;\n\tcJSON *tree, *j_roles, *j_role, *j_data;\n\tint i, count, offset;\n\tconst char *admin_clientid, *admin_username;\n\n\tjson_get_bool(cmd->j_command, \"verbose\", &verbose, true, false);\n\tjson_get_int(cmd->j_command, \"count\", &count, true, -1);\n\tjson_get_int(cmd->j_command, \"offset\", &offset, true, 0);\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(cJSON_AddStringToObject(tree, \"command\", \"listRoles\") == NULL\n\t\t\t|| (j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL\n\t\t\t|| cJSON_AddIntToObject(j_data, \"totalCount\", (int)HASH_CNT(hh, data->roles)) == NULL\n\t\t\t|| (j_roles = cJSON_AddArrayToObject(j_data, \"roles\")) == NULL\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\ti = 0;\n\tHASH_ITER(hh, data->roles, role, role_tmp){\n\t\tif(i>=offset){\n\t\t\tj_role = add_role_to_json(role, verbose);\n\t\t\tif(j_role == NULL){\n\t\t\t\tcJSON_Delete(tree);\n\t\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tcJSON_AddItemToArray(j_roles, j_role);\n\n\t\t\tif(count >= 0){\n\t\t\t\tcount--;\n\t\t\t\tif(count <= 0){\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ti++;\n\t}\n\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | listRoles | verbose=%s | count=%d | offset=%d\",\n\t\t\tadmin_clientid, admin_username, verbose?\"true\":\"false\", count, offset);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_roles__process_add_acl(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *rolename;\n\tstruct dynsec__role *role;\n\tstruct dynsec__acl **acllist, *acl;\n\tint rc;\n\tconst char *admin_clientid, *admin_username;\n\tconst char *topic;\n\tsize_t topic_len;\n\tconst char *acltype;\n\n\tif(json_get_string(cmd->j_command, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Role name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trole = dynsec_roles__find(data, rolename);\n\tif(role == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"acltype\", &acltype, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing acltype\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tif(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){\n\t\tacllist = &role->acls.publish_c_send;\n\t}else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){\n\t\tacllist = &role->acls.publish_c_recv;\n\t}else if(!strcasecmp(acltype, ACL_TYPE_SUB_LITERAL)){\n\t\tacllist = &role->acls.subscribe_literal;\n\t}else if(!strcasecmp(acltype, ACL_TYPE_SUB_PATTERN)){\n\t\tacllist = &role->acls.subscribe_pattern;\n\t}else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_LITERAL)){\n\t\tacllist = &role->acls.unsubscribe_literal;\n\t}else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_PATTERN)){\n\t\tacllist = &role->acls.unsubscribe_pattern;\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Unknown acltype\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"topic\", &topic, false) == MOSQ_ERR_SUCCESS){\n\t\ttopic_len = strlen(topic);\n\t\tif(mosquitto_validate_utf8(topic, (int)topic_len) != MOSQ_ERR_SUCCESS){\n\t\t\tmosquitto_control_command_reply(cmd, \"Topic not valid UTF-8\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\trc = mosquitto_sub_topic_check(topic);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tmosquitto_control_command_reply(cmd, \"Invalid ACL topic\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing topic\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tHASH_FIND(hh, *acllist, topic, topic_len, acl);\n\tif(acl){\n\t\tmosquitto_control_command_reply(cmd, \"ACL with this topic already exists\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tacl = mosquitto_calloc(1, sizeof(struct dynsec__acl) + topic_len + 1);\n\tif(acl == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tstrncpy(acl->topic, topic, topic_len+1);\n\n\tjson_get_int(cmd->j_command, \"priority\", &acl->priority, true, 0);\n\tif(acl->priority > PRIORITY_MAX){\n\t\tacl->priority = PRIORITY_MAX;\n\t}\n\tif(acl->priority < -PRIORITY_MAX){\n\t\tacl->priority = -PRIORITY_MAX;\n\t}\n\tjson_get_bool(cmd->j_command, \"allow\", &acl->allow, true, false);\n\n\tHASH_ADD_INORDER(hh, *acllist, topic, topic_len, acl, insert_acl_cmp);\n\tdynsec__config_batch_save(data);\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\trole__kick_all(data, role);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | addRoleACL | rolename=%s | acltype=%s | topic=%s | priority=%d | allow=%s\",\n\t\t\tadmin_clientid, admin_username, rolename, acltype, topic, acl->priority, acl->allow?\"true\":\"false\");\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_roles__process_remove_acl(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *rolename;\n\tstruct dynsec__role *role;\n\tstruct dynsec__acl **acllist, *acl;\n\tconst char *topic;\n\tint rc;\n\tconst char *admin_clientid, *admin_username;\n\tconst char *acltype;\n\n\tif(json_get_string(cmd->j_command, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Role name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trole = dynsec_roles__find(data, rolename);\n\tif(role == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"acltype\", &acltype, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing acltype\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tif(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){\n\t\tacllist = &role->acls.publish_c_send;\n\t}else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){\n\t\tacllist = &role->acls.publish_c_recv;\n\t}else if(!strcasecmp(acltype, ACL_TYPE_SUB_LITERAL)){\n\t\tacllist = &role->acls.subscribe_literal;\n\t}else if(!strcasecmp(acltype, ACL_TYPE_SUB_PATTERN)){\n\t\tacllist = &role->acls.subscribe_pattern;\n\t}else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_LITERAL)){\n\t\tacllist = &role->acls.unsubscribe_literal;\n\t}else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_PATTERN)){\n\t\tacllist = &role->acls.unsubscribe_pattern;\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Unknown acltype\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"topic\", &topic, false)){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing topic\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tif(mosquitto_validate_utf8(topic, (int)strlen(topic)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Topic not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\trc = mosquitto_sub_topic_check(topic);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid ACL topic\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_FIND(hh, *acllist, topic, strlen(topic), acl);\n\tif(acl){\n\t\trole__free_acl(acllist, acl);\n\t\tdynsec__config_batch_save(data);\n\t\tmosquitto_control_command_reply(cmd, NULL);\n\n\t\trole__kick_all(data, role);\n\n\t\tadmin_clientid = mosquitto_client_id(cmd->client);\n\t\tadmin_username = mosquitto_client_username(cmd->client);\n\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | removeRoleACL | rolename=%s | acltype=%s | topic=%s\",\n\t\t\t\tadmin_clientid, admin_username, rolename, acltype, topic);\n\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"ACL not found\");\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_roles__process_get(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *rolename;\n\tstruct dynsec__role *role;\n\tcJSON *tree, *j_role, *j_data;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Role name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trole = dynsec_roles__find(data, rolename);\n\tif(role == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Role not found\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(cJSON_AddStringToObject(tree, \"command\", \"getRole\") == NULL\n\t\t\t|| (j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t){\n\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tj_role = add_role_to_json(role, true);\n\tif(j_role == NULL){\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToObject(j_data, \"role\", j_role);\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | getRole | rolename=%s\",\n\t\t\tadmin_clientid, admin_username, rolename);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint dynsec_roles__process_modify(struct dynsec__data *data, struct mosquitto_control_cmd *cmd)\n{\n\tconst char *rolename;\n\tconst char *text_name, *text_description;\n\tstruct dynsec__role *role;\n\tcJSON *j_acls;\n\tbool allow_wildcard_subs;\n\tbool do_kick = false;\n\tstruct dynsec__acl *tmp_publish_c_send = NULL, *tmp_publish_c_recv = NULL;\n\tstruct dynsec__acl *tmp_subscribe_literal = NULL, *tmp_subscribe_pattern = NULL;\n\tstruct dynsec__acl *tmp_unsubscribe_literal = NULL, *tmp_unsubscribe_pattern = NULL;\n\tconst char *admin_clientid, *admin_username;\n\n\tif(json_get_string(cmd->j_command, \"rolename\", &rolename, false) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Invalid/missing rolename\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_control_command_reply(cmd, \"Role name not valid UTF-8\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trole = dynsec_roles__find(data, rolename);\n\tif(role == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Role does not exist\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textname\", &text_name, false) == MOSQ_ERR_SUCCESS){\n\t\tchar *str = mosquitto_strdup(text_name);\n\t\tif(str == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tmosquitto_free(role->text_name);\n\t\trole->text_name = str;\n\t}\n\n\tif(json_get_string(cmd->j_command, \"textdescription\", &text_description, false) == MOSQ_ERR_SUCCESS){\n\t\tchar *str = mosquitto_strdup(text_description);\n\t\tif(str == NULL){\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tmosquitto_free(role->text_description);\n\t\trole->text_description = str;\n\t}\n\n\tif(json_get_bool(cmd->j_command, \"allowwildcardsubs\", &allow_wildcard_subs, false, true) == MOSQ_ERR_SUCCESS){\n\t\tif(role->allow_wildcard_subs != allow_wildcard_subs){\n\t\t\trole->allow_wildcard_subs = allow_wildcard_subs;\n\t\t\tdo_kick = true;\n\t\t}\n\t}\n\n\tj_acls = cJSON_GetObjectItem(cmd->j_command, \"acls\");\n\tif(j_acls && cJSON_IsArray(j_acls)){\n\t\tif(dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_SEND, &tmp_publish_c_send) != 0\n\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_RECV, &tmp_publish_c_recv) != 0\n\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_LITERAL, &tmp_subscribe_literal) != 0\n\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_PATTERN, &tmp_subscribe_pattern) != 0\n\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_LITERAL, &tmp_unsubscribe_literal) != 0\n\t\t\t\t|| dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_PATTERN, &tmp_unsubscribe_pattern) != 0\n\t\t\t\t){\n\n\t\t\t/* Free any that were successful */\n\t\t\trole__free_all_acls(&tmp_publish_c_send);\n\t\t\trole__free_all_acls(&tmp_publish_c_recv);\n\t\t\trole__free_all_acls(&tmp_subscribe_literal);\n\t\t\trole__free_all_acls(&tmp_subscribe_pattern);\n\t\t\trole__free_all_acls(&tmp_unsubscribe_literal);\n\t\t\trole__free_all_acls(&tmp_unsubscribe_pattern);\n\n\t\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\trole__free_all_acls(&role->acls.publish_c_send);\n\t\trole__free_all_acls(&role->acls.publish_c_recv);\n\t\trole__free_all_acls(&role->acls.subscribe_literal);\n\t\trole__free_all_acls(&role->acls.subscribe_pattern);\n\t\trole__free_all_acls(&role->acls.unsubscribe_literal);\n\t\trole__free_all_acls(&role->acls.unsubscribe_pattern);\n\n\t\trole->acls.publish_c_send = tmp_publish_c_send;\n\t\trole->acls.publish_c_recv = tmp_publish_c_recv;\n\t\trole->acls.subscribe_literal = tmp_subscribe_literal;\n\t\trole->acls.subscribe_pattern = tmp_subscribe_pattern;\n\t\trole->acls.unsubscribe_literal = tmp_unsubscribe_literal;\n\t\trole->acls.unsubscribe_pattern = tmp_unsubscribe_pattern;\n\t\tdo_kick = true;\n\t}\n\n\tif(do_kick){\n\t\trole__kick_all(data, role);\n\t}\n\tdynsec__config_batch_save(data);\n\n\tmosquitto_control_command_reply(cmd, NULL);\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"dynsec: %s/%s | modifyRole | rolename=%s\",\n\t\t\tadmin_clientid, admin_username, rolename);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/dynamic-security/test.conf",
    "content": "listener 1883\nplugin ./mosquitto_dynamic_security.so\nplugin_opt_config_file test.json\nenable_control_api true\n"
  },
  {
    "path": "plugins/dynamic-security/test.sh",
    "content": "rm test.json\nexport MOSQUITTO_DYNSEC_PASSWORD=passwordpass\nexport VG=\"valgrind --log-file=vglog\"\n${VG} ../../src/mosquitto -c test.conf\n"
  },
  {
    "path": "plugins/dynamic-security/tick.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"dynamic_security.h\"\n\n\nint dynsec__tick_callback(int event, void *event_data, void *userdata)\n{\n\tUNUSED(event);\n\tUNUSED(event_data);\n\n\tdynsec_kicklist__kick(userdata);\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/CMakeLists.txt",
    "content": "if(NOT WIN32)\n\tadd_subdirectory(add-properties)\n\tadd_subdirectory(client-lifetime-stats)\n\tadd_subdirectory(message-timestamp)\nendif()\nadd_subdirectory(auth-by-env)\nadd_subdirectory(auth-by-ip)\nadd_subdirectory(client-properties)\nadd_subdirectory(connection-state)\nadd_subdirectory(delayed-auth)\nadd_subdirectory(deny-protocol-version)\nadd_subdirectory(force-retain)\nadd_subdirectory(limit-subscription-qos)\nadd_subdirectory(payload-ban)\nadd_subdirectory(payload-modification)\nadd_subdirectory(payload-size-stats)\nadd_subdirectory(plugin-event-stats)\nadd_subdirectory(print-ip-on-publish)\nadd_subdirectory(tick-interval)\nadd_subdirectory(topic-hierarchy-flatten)\nadd_subdirectory(topic-modification)\nadd_subdirectory(topic-jail)\nadd_subdirectory(wildcard-temp)\n"
  },
  {
    "path": "plugins/examples/Makefile",
    "content": "DIRS= \\\n\t\tadd-properties \\\n\t\tauth-by-env \\\n\t\tauth-by-ip \\\n\t\tclient-lifetime-stats \\\n\t\tclient-properties \\\n\t\tconnection-state \\\n\t\tdelayed-auth \\\n\t\tdeny-protocol-version \\\n\t\tforce-retain \\\n\t\tlimit-subscription-qos \\\n\t\tmessage-timestamp \\\n\t\tpayload-ban \\\n\t\tpayload-modification \\\n\t\tpayload-size-stats \\\n\t\tplugin-event-stats \\\n\t\tprint-ip-on-publish \\\n\t\ttick-interval \\\n\t\ttopic-modification \\\n\t\ttopic-jail \\\n\t\twildcard-temp\n\n.PHONY : all binary check clean reallyclean test test-compile install uninstall\n\nall :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d}; done\n\nbinary :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\nclean :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\nreallyclean :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\ntest-compile:\n\ncheck : test\ntest: test-compile \n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\ninstall :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n\nuninstall :\n\tset -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done\n"
  },
  {
    "path": "plugins/examples/add-properties/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_add_properties)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/add-properties/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_add_properties\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/add-properties/mosquitto_add_properties.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n   Simon Christmann - use timestamp in unix epoch milliseconds; add client properties\n*/\n\n/*\n * Adds MQTT v5 user-properties to incoming messages:\n *  - $timestamp: unix epoch in milliseconds\n *  - $clientid: client id of the publishing client\n *  - $client_username: username that the publishing client used to authenticate\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared add_properties.c -o add_properties.so\n *\n * Use in config with:\n *\n *   plugin /path/to/add_properties.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include \"config.h\"\n\n#include <stdio.h>\n#include <time.h>\n\n#include \"mosquitto.h\"\n\n#define TS_BUF_LEN (14+1)  // 14 characters in unix epoch (ms) is ≈16 Nov 5138\n#define PLUGIN_NAME \"add-properties\"\n#define PLUGIN_VERSION \"1.0\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int callback_message_in(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\tint result;\n\tstruct timespec ts;\n\tchar ts_buf[TS_BUF_LEN];\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\t// Add timestamp in unix epoch (ms)\n\tclock_gettime(CLOCK_REALTIME, &ts);\n\tsnprintf(ts_buf, TS_BUF_LEN, \"%li%03lu\", ts.tv_sec, ts.tv_nsec / 1000 / 1000);\n\n\tresult = mosquitto_property_add_string_pair(\n\t\t\t&ed->properties,\n\t\t\tMQTT_PROP_USER_PROPERTY,\n\t\t\t\"$timestamp\",\n\t\t\tts_buf);\n\tif(result != MOSQ_ERR_SUCCESS){\n\t\treturn result;\n\t}\n\n\t// Add client id\n\tresult = mosquitto_property_add_string_pair(\n\t\t\t&ed->properties,\n\t\t\tMQTT_PROP_USER_PROPERTY,\n\t\t\t\"$clientid\",\n\t\t\tmosquitto_client_id(ed->client));\n\tif(result != MOSQ_ERR_SUCCESS){\n\t\treturn result;\n\t}\n\n\t// Add client username\n\tresult = mosquitto_property_add_string_pair(\n\t\t\t&ed->properties,\n\t\t\tMQTT_PROP_USER_PROPERTY,\n\t\t\t\"$client_username\",\n\t\t\tmosquitto_client_username(ed->client));\n\tif(result != MOSQ_ERR_SUCCESS){\n\t\treturn result;\n\t}\n\n\t// If no return occurred up to this point, we were successful\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, NULL);\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/add-properties/test.conf",
    "content": "plugin ./mosquitto_add_properties.so\n"
  },
  {
    "path": "plugins/examples/add-properties/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/auth-by-env/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_auth_by_env)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"libmosquitto_common\")\n"
  },
  {
    "path": "plugins/examples/auth-by-env/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_auth_by_env\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/auth-by-env/mosquitto_auth_by_env.c",
    "content": "/*\nCopyright (c) 2021 Frank Villaro-Dixon <frank@villaro-dixon.eu>\n\nThis plugin is under the WTFPL. Do what you want with it.\n\nSPDX-License-Identifier: WTFPL\n\nContributors:\n   Frank Villaro-Dixon - initial implementation and documentation.\n*/\n\n/*\n * This plugin allows users to authenticate with any username, as long as\n * the provided password matches the MOSQUITTO_PASSWORD environment variable.\n * If the MOSQUITTO_PASSWORD env variable is empty, then authentication is rejected.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_auth_by_env.c -o mosquitto_auth_by_env.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_auth_by_env.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include \"config.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define ENV_MOSQUITTO_PASSWORD \"MOSQUITTO_PASSWORD\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\nstatic char *environment_password = NULL;\n\n\nstatic int basic_auth_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tif(!environment_password || !ed->password){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}\n\tif(!strcmp(ed->password, environment_password)){\n\t\t/* Password matched MOSQUITTO_PASSWORD */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tchar *env_var_content;\n\n\tmosq_pid = identifier;\n\n\tenv_var_content = getenv(ENV_MOSQUITTO_PASSWORD);\n\tif(env_var_content && strlen(env_var_content) > 0){\n\t\tenvironment_password = mosquitto_strdup(env_var_content);\n\t\tif(!environment_password){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Out of memory.\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_BASIC_AUTH, basic_auth_callback, NULL, NULL);\n\t}\n\n\tmosquitto_log_printf(MOSQ_LOG_ERR, \"auth-by-env plugin called, but \" ENV_MOSQUITTO_PASSWORD \" environment variable is empty\");\n\treturn MOSQ_ERR_INVAL;\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/auth-by-ip/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_auth_by_ip)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"libmosquitto_common\")\n"
  },
  {
    "path": "plugins/examples/auth-by-ip/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_auth_by_ip\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/auth-by-ip/mosquitto_auth_by_ip.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an example plugin showing how to use the basic authentication\n * callback to allow/disallow client connections based on client IP addresses.\n *\n * This is an extremely basic type of access control, password based or similar\n * authentication is preferred.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_auth_by_ip.c -o mosquitto_auth_by_ip.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_auth_by_ip.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"auth-by-ip\"\n#define PLUGIN_VERSION \"1.0\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int basic_auth_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tconst char *ip_address;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tip_address = mosquitto_client_address(ed->client);\n\tif(!strcmp(ip_address, \"127.0.0.1\")){\n\t\t/* Only allow connections from localhost */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_BASIC_AUTH, basic_auth_callback, NULL, NULL);\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/auth-by-ip/test.conf",
    "content": "listener 1883\n\nplugin ./mosquitto_auth_by_ip.so\n"
  },
  {
    "path": "plugins/examples/auth-by-ip/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/client-lifetime-stats/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_client_lifetime_stats)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/client-lifetime-stats/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_client_lifetime_stats\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/client-lifetime-stats/mosquitto_client_lifetime_stats.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * Publish statistics on client session lifetimes.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_client_lifetime_stats.c -o mosquitto_client_lifetime_stats.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_client_lifetime_stats.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <time.h>\n#include <uthash.h>\n\n#include \"mosquitto.h\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\nstruct lifetime_s {\n\tUT_hash_handle hh;\n\tchar *id;\n\ttime_t connect;\n};\nstruct lifetime_s *local_lifetimes = NULL;\n\n#define LIFETIME_COUNT 28\nstatic const char *lifetime_strs[LIFETIME_COUNT] = {\n\t\"0\",\n\t\"1\", \"2\", \"5\",\n\t\"10\", \"20\", \"50\",\n\t\"100\", \"200\", \"500\",\n\t\"1k\", \"2k\", \"5k\",\n\t\"10k\", \"20k\", \"50k\",\n\t\"100k\", \"200k\", \"500k\",\n\t\"1M\", \"2M\", \"5M\",\n\t\"10M\", \"20M\", \"50M\",\n\t\"100M\", \"200M\", \"500M\"\n};\n\nstatic uint32_t lifetime_values[LIFETIME_COUNT] = {\n\t0,\n\t1, 2, 5,\n\t10, 20, 50,\n\t100, 200, 500,\n\t1000, 2000, 5000,\n\t10000, 20000, 50000,\n\t100000, 200000, 500000,\n\t1000000, 2000000, 5000000,\n\t10000000, 20000000, 50000000,\n\t100000000, 200000000, 500000000\n};\n\nstatic long lifetime_counts[LIFETIME_COUNT];\nstatic long last_lifetime_counts[LIFETIME_COUNT];\nstatic time_t last_report = 0;\n\n\nstatic int callback_tick(int event, void *event_data, void *userdata)\n{\n\tstruct timespec ts;\n\tchar topic[40];\n\tchar payload[40];\n\tint slen;\n\tint i;\n\n\tUNUSED(event);\n\tUNUSED(event_data);\n\tUNUSED(userdata);\n\n\tclock_gettime(CLOCK_REALTIME, &ts);\n\tif(last_report + 10 < ts.tv_sec){\n\t\tlast_report = ts.tv_sec;\n\n\t\tfor(i=0; i<LIFETIME_COUNT; i++){\n\t\t\tif(lifetime_counts[i] != last_lifetime_counts[i]){\n\t\t\t\tsnprintf(topic, sizeof(topic), \"$SYS/broker/client/lifetimes/%s\", lifetime_strs[i]);\n\t\t\t\tslen = snprintf(payload, sizeof(payload), \"%ld\", lifetime_counts[i]);\n\t\t\t\tmosquitto_broker_publish_copy(NULL, topic, slen, payload, 0, 1, NULL);\n\t\t\t\tlast_lifetime_counts[i] = lifetime_counts[i];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int callback_connect(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_connect *ed = event_data;\n\tconst char *id;\n\tstruct lifetime_s *client;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tid = mosquitto_client_id(ed->client);\n\tif(id){\n\t\tHASH_FIND(hh, local_lifetimes, id, strlen(id), client);\n\t\tif(!client){\n\t\t\tclient = malloc(sizeof(struct lifetime_s));\n\t\t\tif(client == NULL){\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t\tclient->id = strdup(id);\n\t\t\tif(client->id == NULL){\n\t\t\t\tfree(client);\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t\tclient->connect = time(NULL);\n\t\t\tHASH_ADD_KEYPTR(hh, local_lifetimes, client->id, strlen(client->id), client);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int callback_disconnect(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_disconnect *ed = event_data;\n\tint i;\n\tconst char *id;\n\tstruct lifetime_s *client;\n\ttime_t lifetime;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tid = mosquitto_client_id(ed->client);\n\tif(id){\n\t\tHASH_FIND(hh, local_lifetimes, id, strlen(id), client);\n\t\tif(client){\n\t\t\tHASH_DELETE(hh, local_lifetimes, client);\n\n\t\t\tlifetime = time(NULL) - client->connect;\n\t\t\tfree(client->id);\n\t\t\tfree(client);\n\n\t\t\tfor(i=0; i<LIFETIME_COUNT; i++){\n\t\t\t\tif(lifetime <= lifetime_values[i]){\n\t\t\t\t\tlifetime_counts[i]++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmemset(lifetime_counts, 0, sizeof(lifetime_counts));\n\tmemset(last_lifetime_counts, 0, sizeof(last_lifetime_counts));\n\n\tmosq_pid = identifier;\n\tmosquitto_callback_register(mosq_pid, MOSQ_EVT_CONNECT, callback_connect, NULL, NULL);\n\tmosquitto_callback_register(mosq_pid, MOSQ_EVT_DISCONNECT, callback_disconnect, NULL, NULL);\n\tmosquitto_callback_register(mosq_pid, MOSQ_EVT_TICK, callback_tick, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/client-lifetime-stats/test.conf",
    "content": "plugin ./mosquitto_client_lifetime_stats.so\n"
  },
  {
    "path": "plugins/examples/client-lifetime-stats/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/client-properties/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_client_properties)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/client-properties/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_client_properties\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/client-properties/mosquitto_client_properties.c",
    "content": "/*\n * This is an *example* plugin which prints information of a message after it is\n * received by the broker and before it is sent on to other clients.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared printf_example.c -o printf_example.so\n *\n * Use in config with:\n *\n *   plugin /path/to/printf_example.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"client-properties\"\n#define PLUGIN_VERSION \"1.0\"\n\n#define UNUSED(A) (void)(A)\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int callback_message_in(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tprintf(\"printf-example - client address: %s\\n\", mosquitto_client_address(ed->client));\n\tprintf(\"printf-example - client id: %s\\n\", mosquitto_client_id(ed->client));\n\tprintf(\"printf-example - client username: %s\\n\", mosquitto_client_username(ed->client));\n\tprintf(\"printf-example - payload: '%.*s'\\n\", ed->payloadlen, (char *)ed->payload);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, NULL);\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/client-properties/test.conf",
    "content": "plugin ./mosquitto_client_properties.so\n"
  },
  {
    "path": "plugins/examples/client-properties/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/connection-state/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_connection_state)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/connection-state/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_connection_state\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/connection-state/mosquitto_connection_state.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an example plugin showing how you could publish online/offline\n * state for all clients.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_connection_state.c -o mosquitto_connection_state.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_connection_state.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n\n\n#include <stdbool.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"connection-state\"\n#define PLUGIN_VERSION \"1.0\"\n\n#define UNUSED(A) (void)(A)\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int connect_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_connect *ed = event_data;\n\tconst char *clientid;\n\tchar topic[1024];\n\tint len;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tclientid = mosquitto_client_id(ed->client);\n\tlen = snprintf(topic, sizeof(topic), \"$SYS/broker/connection/client/%s/state\", clientid);\n\tif(len < (int)sizeof(topic)){\n\t\tmosquitto_broker_publish_copy(NULL, topic, 1, \"1\", 0, true, NULL);\n\t}else{\n\t\t/* client id too large */\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int disconnect_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_disconnect *ed = event_data;\n\tconst char *clientid;\n\tchar topic[1024];\n\tint len;\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tclientid = mosquitto_client_id(ed->client);\n\tlen = snprintf(topic, sizeof(topic), \"$SYS/broker/connection/client/%s/state\", clientid);\n\tif(len < (int)sizeof(topic)){\n\t\t/* Expire our \"disconnected\" message after a day. */\n\t\trc = mosquitto_property_add_int32(&proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 86400);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n\t\trc = mosquitto_broker_publish_copy(NULL, topic, 1, \"0\", 0, true, proplist);\n\t\tif(rc){\n\t\t\tmosquitto_property_free_all(&proplist);\n\t\t}\n\t}else{\n\t\t/* client id too large */\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tint rc;\n\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_CONNECT, connect_callback, NULL, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_DISCONNECT, disconnect_callback, NULL, NULL);\n\treturn rc;\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/connection-state/test.conf",
    "content": "plugin ./mosquitto_connection_state.so\n"
  },
  {
    "path": "plugins/examples/connection-state/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/delayed-auth/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_delayed_auth)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"libmosquitto_common\")\n"
  },
  {
    "path": "plugins/examples/delayed-auth/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_delayed_auth\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/delayed-auth/mosquitto_delayed_auth.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an example plugin showing how to carry out delayed authentication.\n * The \"authentication\" in this example makes no checks whatsoever, but delays\n * the response by 5 seconds, and randomly chooses whether it should succeed.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_delayed_auth.c -o mosquitto_delayed_auth.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_delayed_auth.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n\n\n#include <limits.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <string.h>\n#include <time.h>\n#include <uthash.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"delayed-auth\"\n#define PLUGIN_VERSION \"1.0\"\n\n#ifndef UNUSED\n#  define UNUSED(A) (void)(A)\n#endif\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstruct client_list {\n\tUT_hash_handle hh;\n\tchar *id;\n\ttime_t request_time;\n};\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\nstatic struct client_list *clients = NULL;\nstatic time_t last_check = 0;\n\n\nstatic bool authentication_check(struct client_list *client, time_t now)\n{\n\ttime_t secs;\n\n\tsecs = now - client->request_time;\n\n\treturn secs > 5 ? true : false;\n}\n\n\nstatic int basic_auth_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tstatic struct client_list *client;\n\tconst char *id;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tid = mosquitto_client_id(ed->client);\n\n\tHASH_FIND(hh, clients, id, strlen(id), client);\n\tif(client){\n\t\tclient->request_time = time(NULL);\n\t}else{\n\t\tclient = mosquitto_malloc(sizeof(struct client_list));\n\t\tif(client == NULL){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tclient->id = mosquitto_strdup(id);\n\t\tif(client->id == NULL){\n\t\t\tmosquitto_free(client);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tclient->request_time = time(NULL);\n\t\tHASH_ADD_KEYPTR(hh, clients, client->id, strlen(client->id), client);\n\n\t\tmosquitto_log_printf(MOSQ_LOG_DEBUG, \"Starting auth for %s at %ld\", client->id, time(NULL));\n\t}\n\n\treturn MOSQ_ERR_AUTH_DELAYED;\n}\n\n\nstatic int tick_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_tick *ed = event_data;\n\tstruct client_list *client, *client_tmp;\n\ttime_t now;\n\tlong r;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tnow = time(NULL);\n\tif(now >= last_check){\n\t\tHASH_ITER(hh, clients, client, client_tmp){\n\t\t\tif(authentication_check(client, now)){\n\t\t\t\t/* Deny access 1/4 of the time, yes it's biased number generation. */\n#ifdef WIN32\n\t\t\t\tr = rand() % 1000;\n#else\n\t\t\t\t/* coverity[dont_call] - we don't care about random() not being cryptographically secure here */\n\t\t\t\tr = random() % 1000;\n#endif\n\t\t\t\tif(r > 740){\n\t\t\t\t\tmosquitto_complete_basic_auth(client->id, MOSQ_ERR_AUTH);\n\t\t\t\t}else{\n\t\t\t\t\tmosquitto_complete_basic_auth(client->id, MOSQ_ERR_SUCCESS);\n\t\t\t\t}\n\t\t\t\tmosquitto_log_printf(MOSQ_LOG_DEBUG, \"Completing auth for %s at %ld\", client->id, now);\n\t\t\t\tHASH_DELETE(hh, clients, client);\n\t\t\t\tmosquitto_free(client->id);\n\t\t\t\tmosquitto_free(client);\n\t\t\t}\n\t\t}\n\t\tlast_check = now;\n\t}\n\t/* Declare that we want another call in 1 second at the earliest */\n\ted->next_s = 1;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tint rc;\n\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_BASIC_AUTH, basic_auth_callback, NULL, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_TICK, tick_callback, NULL, NULL);\n\treturn rc;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tstruct client_list *client, *client_tmp;\n\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tHASH_ITER(hh, clients, client, client_tmp){\n\t\tHASH_DELETE(hh, clients, client);\n\t\tmosquitto_free(client->id);\n\t\tmosquitto_free(client);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/delayed-auth/test.conf",
    "content": "listener 1883\n\nplugin ./mosquitto_delayed_auth.so\n\n\nsys_interval 1\nlog_timestamp_format %Y-%m-%dT%H:%M:%S\n"
  },
  {
    "path": "plugins/examples/delayed-auth/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/deny-protocol-version/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_deny_protocol_version)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/deny-protocol-version/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_deny_protocol_version\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/deny-protocol-version/mosquitto_deny_protocol_version.c",
    "content": "/*\nCopyright (c) 2022 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an example plugin showing how to deny access based on the version of\n * the protocol spec a client connects with. It does no other authentication\n * checks.\n *\n * It could be used with other authentication plugins by specifying it in the\n * config file before another plugin, for example:\n *\n * plugin /usr/lib/mosquitto_deny_protocol_version.so\n * plugin /usr/lib/mosquitto_dynamic_security.so\n *\n * or:\n *\n * plugin /usr/lib/mosquitto_deny_protocol_version.so\n * password_file pwfile\n *\n * It will *not* work on its own.\n *\n * In Mosquitto 2.1, this can be achieved with the `accept_protocol_version`\n * option instead.\n *\n *\n * To compile:\n *\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_deny_protocol_version.c -o mosquitto_deny_protocol_version.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nint mosquitto_plugin_version(int supported_version_count, const int *supported_versions)\n{\n\tint i;\n\n\tfor(i=0; i<supported_version_count; i++){\n\t\tif(supported_versions[i] == 5){\n\t\t\treturn 5;\n\t\t}\n\t}\n\treturn -1;\n}\n\n\nstatic int basic_auth_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tint protocol_version;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tprotocol_version = mosquitto_client_protocol_version(ed->client);\n\n\tif(protocol_version == 5 || protocol_version == 4){\n\t\t/* Allow access to MQTT v5.0 and v3.1.1 - this passes on responsibility\n\t\t * for the actual auth checks to the next plugin/password file in the\n\t\t * config list. If no other plugins/password file is defined, then\n\t\t * access will be denied. */\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}else{\n\t\t/* Deny access to all others */\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_BASIC_AUTH, basic_auth_callback, NULL, NULL);\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn mosquitto_callback_unregister(mosq_pid, MOSQ_EVT_BASIC_AUTH, basic_auth_callback, NULL);\n}\n"
  },
  {
    "path": "plugins/examples/deny-protocol-version/test.conf",
    "content": "listener 1883\n\nplugin ./mosquitto_deny_protocol_version.so\npassword_file pwfile\n"
  },
  {
    "path": "plugins/examples/deny-protocol-version/test.sh",
    "content": "#!/bin/sh\n\n../../apps/mosquitto_passwd/mosquitto_passwd -c -b pwfile username password\n../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/force-retain/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_force_retain)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/force-retain/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_force_retain\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/force-retain/mosquitto_force_retain.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an *example* plugin which forces all messages processed by the\n * broker to be treated as though they had the retained bit set.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_force_retain.c -o mosquitto_force_retain.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_force_retain.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"force-retain\"\n#define PLUGIN_VERSION \"1.0\"\n\n#define UNUSED(A) (void)(A)\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int callback_message_in(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\ted->retain = true;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, NULL);\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/force-retain/test.conf",
    "content": "plugin ./mosquitto_force_retain.so\n"
  },
  {
    "path": "plugins/examples/force-retain/test.sh",
    "content": "../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/limit-subscription-qos/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_limit_subscription_qos)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/limit-subscription-qos/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_limit_subscription_qos\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/limit-subscription-qos/mosquitto_limit_subscription_qos.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Abilio Marques - initial implementation and documentation.\n*/\n\n/*\n * This is an *example* plugin which limits all the subscriptions' QoS to 1.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_limit_subscription.c -o mosquitto_limit_subscription_qos.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_limit_subscription_qos.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"limit-subscription-qos\"\n#define PLUGIN_VERSION \"1.0\"\n\n#define UNUSED(A) (void)(A)\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int callback_subscribe(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_subscribe *ed = event_data;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tif(MQTT_SUB_OPT_GET_QOS(ed->data.options) > 1){\n\t\tMQTT_SUB_OPT_SET_QOS(ed->data.options, 1);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_SUBSCRIBE, callback_subscribe, NULL, NULL);\n}\n"
  },
  {
    "path": "plugins/examples/limit-subscription-qos/test.conf",
    "content": "plugin ./mosquitto_limit_subscription_qos.so\n"
  },
  {
    "path": "plugins/examples/limit-subscription-qos/test.sh",
    "content": "../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/message-timestamp/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_message_timestamp)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/message-timestamp/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_message_timestamp\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/message-timestamp/mosquitto_message_timestamp.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * Add an MQTT v5 user-property with key \"timestamp\" and value of timestamp in ISO-8601 format to all messages.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_timestamp.c -o mosquitto_timestamp.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_timestamp.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include \"config.h\"\n\n#include <stdio.h>\n#include <time.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"message-timestamp\"\n#define PLUGIN_VERSION \"1.0\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int callback_message_in(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\tstruct timespec ts;\n\tstruct tm *ti;\n\tchar time_buf[25];\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tclock_gettime(CLOCK_REALTIME, &ts);\n\tti = gmtime(&ts.tv_sec);\n\tstrftime(time_buf, sizeof(time_buf), \"%Y-%m-%dT%H:%M:%SZ\", ti);\n\n\treturn mosquitto_property_add_string_pair(&ed->properties, MQTT_PROP_USER_PROPERTY, \"timestamp\", time_buf);\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, NULL);\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/message-timestamp/test.conf",
    "content": "plugin ./mosquitto_message_timestamp.so\n"
  },
  {
    "path": "plugins/examples/message-timestamp/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/payload-ban/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_payload_ban)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/payload-ban/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_payload_ban\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/payload-ban/mosquitto_payload_ban.c",
    "content": "/*\nCopyright (c) 2022 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an example plugin that inspects payloads before they are published.\n * On matching payload, the publish is denied, the IP address of the client\n * added to a log file that can be used with fail2ban or similar to ban the\n * client connection, and the client kicked.\n *\n * This was developed in response to an obnoxious campaign by an MQTT client\n * that was republishing spam advertising messages to every retained message on\n * test.mosquitto.org\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_payload_ban.c -o mosquitto_payload_ban.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_payload_ban.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <uthash.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"payload-ban\"\n#define PLUGIN_VERSION \"1.0\"\n\nstruct banlist {\n\tUT_hash_handle hh_by_address;\n\tUT_hash_handle hh_by_id;\n\tchar ip_address[50];\n\tchar clientid[];\n};\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\nstatic struct banlist *banlist_by_address = NULL;\nstatic struct banlist *banlist_by_id = NULL;\n\n\nstatic int basic_auth_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tstruct banlist *entry;\n\tconst char *ip_address;\n\tconst char *clientid;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tip_address = mosquitto_client_address(ed->client);\n\tif(ip_address){\n\t\tHASH_FIND(hh_by_address, banlist_by_address, ip_address, strlen(ip_address), entry);\n\t\tif(entry){\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}\n\tclientid = mosquitto_client_id(ed->client);\n\tif(clientid){\n\t\tHASH_FIND(hh_by_id, banlist_by_id, clientid, strlen(clientid), entry);\n\t\tif(entry){\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int acl_check_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\tstruct banlist *new_entry = NULL, *entry;\n\tconst char *clientid, *ip_address;\n\tFILE *fptr;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tif(ed->payload && ed->payloadlen > sizeof(\"Tired of using an old outdated MQTT client\")){\n\t\tif(!strncmp(ed->payload, \"Tired of using an old outdated MQTT client\", strlen(\"Tired of using an old outdated MQTT client\"))){\n\t\t\tip_address = mosquitto_client_address(ed->client);\n\t\t\tclientid = mosquitto_client_id(ed->client);\n\n\t\t\tHASH_FIND(hh_by_address, banlist_by_address, ip_address, strlen(ip_address), entry);\n\t\t\tif(entry){\n\t\t\t\tmosquitto_kick_client_by_clientid(clientid, false);\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t\tnew_entry = calloc(1, sizeof(struct banlist)+strlen(clientid) + 1);\n\t\t\tif(!new_entry){\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t\tstrcpy(new_entry->clientid, clientid);\n\t\t\tstrncpy(new_entry->ip_address, ip_address, sizeof(new_entry->ip_address)-1);\n\t\t\tHASH_ADD(hh_by_id, banlist_by_id, clientid, strlen(clientid), new_entry);\n\t\t\tHASH_ADD(hh_by_address, banlist_by_address, ip_address, strlen(ip_address), new_entry);\n\t\t\tmosquitto_kick_client_by_clientid(clientid, false);\n\t\t\tfptr = fopen(\"/tmp/payload-banlist\", \"at\");\n\t\t\tif(fptr){\n\t\t\t\tfprintf(fptr, \"%s || %s\\n\", ip_address, clientid);\n\t\t\t\tfclose(fptr);\n\t\t\t}\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\tmosquitto_callback_register(mosq_pid, MOSQ_EVT_BASIC_AUTH, basic_auth_callback, NULL, NULL);\n\tmosquitto_callback_register(mosq_pid, MOSQ_EVT_ACL_CHECK, acl_check_callback, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tstruct banlist *entry, *entry_tmp;\n\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tHASH_ITER(hh_by_address, banlist_by_address, entry, entry_tmp){\n\t\tHASH_DELETE(hh_by_address, banlist_by_address, entry);\n\t\tHASH_DELETE(hh_by_id, banlist_by_id, entry);\n\t\tfree(entry);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/payload-ban/test.conf",
    "content": "listener 1883\n\nplugin ./mosquitto_payload_ban.so\n"
  },
  {
    "path": "plugins/examples/payload-ban/test.sh",
    "content": "#!/bin/sh\n\nvalgrind ../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/payload-modification/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_payload_modification)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/payload-modification/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_payload_modification\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/payload-modification/mosquitto_payload_modification.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an *example* plugin which demonstrates how to modify the payload of\n * a message after it is received by the broker and before it is sent on to\n * other clients.\n *\n * You should be very sure of what you are doing before making use of this feature.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_payload_modification.c -o mosquitto_payload_modification.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_payload_modification.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"payload-modification\"\n#define PLUGIN_VERSION \"1.0\"\n\n#define UNUSED(A) (void)(A)\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int callback_message_in(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\tchar *new_payload;\n\tuint32_t new_payloadlen;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\t/* This simply adds \"hello \" to the front of every payload. You can of\n\t * course do much more complicated message processing if needed. */\n\n\t/* Calculate the length of our new payload */\n\tnew_payloadlen = ed->payloadlen + (uint32_t)strlen(\"hello \")+1;\n\n\t/* Allocate some memory - use\n\t * mosquitto_calloc/mosquitto_malloc/mosquitto_strdup when allocating, to\n\t * allow the broker to track memory usage */\n\tnew_payload = mosquitto_calloc(1, new_payloadlen);\n\tif(new_payload == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t/* Print \"hello \" to the payload */\n\tsnprintf(new_payload, new_payloadlen, \"hello \");\n\tmemcpy(new_payload+(uint32_t)strlen(\"hello \"), ed->payload, ed->payloadlen);\n\n\t/* Assign the new payload and payloadlen to the event data structure. You\n\t * must *not* free the original payload, it will be handled by the\n\t * broker. */\n\ted->payload = new_payload;\n\ted->payloadlen = new_payloadlen;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, NULL);\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/payload-modification/test.conf",
    "content": "plugin ./mosquitto_payload_modification.so\n"
  },
  {
    "path": "plugins/examples/payload-modification/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/payload-size-stats/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_payload_size_stats)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/payload-size-stats/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_payload_size_stats\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/payload-size-stats/mosquitto_payload_size_stats.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * Publish statistics on message payload size.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_payload_size_stats.c -o mosquitto_payload_size_stats.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_payload_size_stats.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <time.h>\n\n#include \"mosquitto.h\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n#define SIZE_COUNT 28\nstatic const char *size_strs[SIZE_COUNT] = {\n\t\"0\",\n\t\"1\", \"2\", \"5\",\n\t\"10\", \"20\", \"50\",\n\t\"100\", \"200\", \"500\",\n\t\"1k\", \"2k\", \"5k\",\n\t\"10k\", \"20k\", \"50k\",\n\t\"100k\", \"200k\", \"500k\",\n\t\"1M\", \"2M\", \"5M\",\n\t\"10M\", \"20M\", \"50M\",\n\t\"100M\", \"200M\", \"500M\"\n};\n\nstatic uint32_t size_values[SIZE_COUNT] = {\n\t0,\n\t1, 2, 5,\n\t10, 20, 50,\n\t100, 200, 500,\n\t1000, 2000, 5000,\n\t10000, 20000, 50000,\n\t100000, 200000, 500000,\n\t1000000, 2000000, 5000000,\n\t10000000, 20000000, 50000000,\n\t100000000, 200000000, 500000000\n};\n\nstatic long size_counts[SIZE_COUNT];\nstatic long last_size_counts[SIZE_COUNT];\nstatic time_t last_report = 0;\n\n\nstatic int callback_tick(int event, void *event_data, void *userdata)\n{\n\ttime_t now_sec;\n\tchar topic[40];\n\tchar payload[40];\n\tint slen;\n\tint i;\n\n\tUNUSED(event);\n\tUNUSED(event_data);\n\tUNUSED(userdata);\n\n\tnow_sec = time(NULL);\n\tif(last_report + 10 < now_sec){\n\t\tlast_report = now_sec;\n\n\t\tfor(i=0; i<SIZE_COUNT; i++){\n\t\t\tif(size_counts[i] != last_size_counts[i]){\n\t\t\t\tsnprintf(topic, sizeof(topic), \"$SYS/broker/publish/sizes/%s\", size_strs[i]);\n\t\t\t\tslen = snprintf(payload, sizeof(payload), \"%ld\", size_counts[i]);\n\t\t\t\tmosquitto_broker_publish_copy(NULL, topic, slen, payload, 0, 1, NULL);\n\t\t\t\tlast_size_counts[i] = size_counts[i];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int callback_message_in(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\tint i;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tfor(i=0; i<SIZE_COUNT; i++){\n\t\tif(ed->payloadlen <= size_values[i]){\n\t\t\tsize_counts[i]++;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmemset(size_counts, 0, sizeof(size_counts));\n\tmemset(last_size_counts, 0, sizeof(last_size_counts));\n\n\tmosq_pid = identifier;\n\tmosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, NULL);\n\tmosquitto_callback_register(mosq_pid, MOSQ_EVT_TICK, callback_tick, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/payload-size-stats/test.conf",
    "content": "plugin ./mosquitto_payload_size_stats.so\n"
  },
  {
    "path": "plugins/examples/payload-size-stats/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/plugin-event-stats/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_plugin_event_stats)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/plugin-event-stats/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_plugin_event_stats\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/plugin-event-stats/mosquitto_plugin_event_stats.c",
    "content": "/*\nCopyright (c) 2022 Cedalo Gmbh\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * Publish statistics on plugin event counts\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_plugin_event_stats.c -o mosquitto_plugin_event_stats.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_plugin_event_stats.so\n *\n * Note that this only works on Mosquitto 2.1 or later.\n */\n#include <inttypes.h>\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"plugin-event-stats\"\n#define PLUGIN_VERSION \"1.0\"\n\n#define MAX_EVT MOSQ_EVT_MESSAGE_OUT\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\nstatic uint64_t evt_counts[MAX_EVT+1];\nstatic uint64_t last_evt_counts[MAX_EVT+1];\nstatic time_t last_report = 0;\n\n#define TOPIC_BASE \"$SYS/broker/plugin/events/\"\n\nconst char evt_topics[][60] = {\n\t\"\", /* No event */\n\tTOPIC_BASE \"reload\", /* MOSQ_EVT_RELOAD */\n\tTOPIC_BASE \"acl_check\", /* MOSQ_EVT_ACL_CHECK */\n\tTOPIC_BASE \"auth/basic\", /* MOSQ_EVT_BASIC_AUTH */\n\tTOPIC_BASE \"auth/ext/start\", /* MOSQ_EVT_EXT_AUTH_START */\n\tTOPIC_BASE \"auth/ext/continue\", /* MOSQ_EVT_EXT_AUTH_CONTINUE */\n\tTOPIC_BASE \"control\", /* MOSQ_EVT_CONTROL */\n\tTOPIC_BASE \"message/in\", /* MOSQ_EVT_MESSAGE_IN */\n\tTOPIC_BASE \"psk_key\", /* MOSQ_EVT_PSK_KEY */\n\tTOPIC_BASE \"tick\", /* MOSQ_EVT_TICK */\n\tTOPIC_BASE \"disconnect\", /* MOSQ_EVT_DISCONNECT */\n\tTOPIC_BASE \"connect\", /* MOSQ_EVT_CONNECT */\n\tTOPIC_BASE \"subscribe\", /* MOSQ_EVT_SUBSCRIBE */\n\tTOPIC_BASE \"unsubscribe\", /* MOSQ_EVT_UNSUBSCRIBE */\n\tTOPIC_BASE \"persist/restore\", /* MOSQ_EVT_PERSIST_RESTORE */\n\tTOPIC_BASE \"persist/message/base/add\", /* MOSQ_EVT_PERSIST_MSG_ADD */\n\tTOPIC_BASE \"persist/message/base/delete\", /* MOSQ_EVT_PERSIST_MSG_DELETE */\n\tTOPIC_BASE \"persist/message/retain/set\", /* MOSQ_EVT_PERSIST_RETAIN_SET */\n\tTOPIC_BASE \"persist/message/retain/delete\", /* MOSQ_EVT_PERSIST_RETAIN_DELETE */\n\tTOPIC_BASE \"persist/client/add\", /* MOSQ_EVT_PERSIST_CLIENT_ADD */\n\tTOPIC_BASE \"persist/client/delete\", /* MOSQ_EVT_PERSIST_CLIENT_DELETE */\n\tTOPIC_BASE \"persist/client/update\", /* MOSQ_EVT_PERSIST_CLIENT_UPDATE */\n\tTOPIC_BASE \"persist/subscription/add\", /* MOSQ_EVT_PERSIST_SUBSCRIPTION_ADD */\n\tTOPIC_BASE \"persist/subscription/delete\", /* MOSQ_EVT_PERSIST_SUBSCRIPTION_DELETE */\n\tTOPIC_BASE \"persist/message/client/add\", /* MOSQ_EVT_PERSIST_CLIENT_MSG_ADD */\n\tTOPIC_BASE \"persist/message/client/delete\", /* MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE */\n\tTOPIC_BASE \"persist/message/client/update\", /* MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE */\n\tTOPIC_BASE \"message/out\", /* MOSQ_EVT_MESSAGE_OUT */\n};\n\n\nstatic int callback_tick(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_tick *ed = event_data;\n\tchar payload[40];\n\tint slen;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tif(last_report + 10 < ed->now_s){\n\t\tlast_report = ed->now_s;\n\n\t\tfor(int i=1; i<MAX_EVT+1; i++){\n\t\t\tif(evt_counts[i] != last_evt_counts[i]){\n\t\t\t\tslen = snprintf(payload, sizeof(payload), \"%\" PRIu64, evt_counts[i]);\n\t\t\t\tmosquitto_broker_publish_copy(NULL, evt_topics[i], slen, payload, 0, 1, NULL);\n\t\t\t\tlast_evt_counts[i] = evt_counts[i];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int callback_counter(int event, void *event_data, void *userdata)\n{\n\tUNUSED(event_data);\n\tUNUSED(userdata);\n\n\tif(event < 0 || event > MAX_EVT){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tevt_counts[event]++;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmemset(evt_counts, 0, sizeof(evt_counts));\n\tmemset(last_evt_counts, 0, sizeof(last_evt_counts));\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\n\tmosquitto_callback_register(mosq_pid, MOSQ_EVT_TICK, callback_tick, NULL, NULL);\n\tfor(int i=1; i<MAX_EVT+1; i++){\n\t\tif(i != MOSQ_EVT_TICK){\n\t\t\tmosquitto_callback_register(mosq_pid, i, callback_counter, NULL, NULL);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/plugin-event-stats/test.conf",
    "content": "plugin ./mosquitto_plugin_event_stats.so\n"
  },
  {
    "path": "plugins/examples/plugin-event-stats/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/print-ip-on-publish/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_print_ip_on_publish)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/print-ip-on-publish/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_print_ip_on_publish\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/print-ip-on-publish/mosquitto_print_ip_on_publish.c",
    "content": "/* Example plugin that prints out the client id and IP address of any clients\n * that publish to a particular topic, defined in \"my_topic\". */\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"print-ip-on-publish\"\n#define PLUGIN_VERSION \"1.0\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\nstatic char my_topic[] = \"troublesome/topic\";\n\n\nstatic int callback_message_in(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tif(!strcmp(ed->topic, my_topic)){\n\t\tprintf(\"PUBLISH FROM %s on IP %s\\n\", mosquitto_client_id(ed->client), mosquitto_client_address(ed->client));\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, NULL);\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/print-ip-on-publish/test.conf",
    "content": "plugin ./mosquitto_print_ip_on_publish.so\n"
  },
  {
    "path": "plugins/examples/print-ip-on-publish/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/tick-interval/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_tick_interval)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"libmosquitto_common\")\n"
  },
  {
    "path": "plugins/examples/tick-interval/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_tick_interval\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/tick-interval/mosquitto_tick_interval.c",
    "content": "/*\nCopyright (c) 2025 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an example plugin showing how a plugin can choose how frequently it\n * receives a tick event. Note that this request is not a guarantee that the\n * tick will be called that frequently, only that it will not be called more\n * frequently.\n *\n * Setting to 0 means that a tick event will always be triggered for this\n * plugin when the broker is ready to do so.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_tick.c -o mosquitto_tick.so\n *\n * Use in config with the below, where the interval is in seconds:\n *\n *   plugin /path/to/mosquitto_tick.so\n *   plugin_opt_interval 1\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n\n\n#include <limits.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <string.h>\n#include <time.h>\n#include <uthash.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"tick-interval\"\n#define PLUGIN_VERSION NULL\n\n#ifndef UNUSED\n#  define UNUSED(A) (void)(A)\n#endif\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstruct plugin_data {\n\tmosquitto_plugin_id_t *pid;\n\tint interval;\n};\n\n\nstatic int tick_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_tick *ed = event_data;\n\tstruct plugin_data *data = userdata;\n\n\tUNUSED(event);\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"Tick event for plugin with interval %d.\", data->interval);\n\ted->next_s = data->interval; /* We want the next tick to occur at the earliest in \"interval\" seconds */\n\ted->next_ms = 0; /* And 0 milliseconds */\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **userdata, struct mosquitto_opt *opts, int opt_count)\n{\n\tstruct plugin_data *data;\n\n\tdata = mosquitto_calloc(1, sizeof(struct plugin_data));\n\tif(!data){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\t*userdata = data;\n\tdata->interval = -1;\n\tdata->pid = identifier;\n\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\n\tfor(int i=0; i<opt_count; i++){\n\t\tif(!strcmp(opts[i].key, \"interval\")){\n\t\t\tdata->interval = atoi(opts[i].value);\n\t\t}else{\n\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Unknown option '%s'.\", opts[i].key);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\tif(data->interval < 0){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: interval must be >= 0.\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn mosquitto_callback_register(data->pid, MOSQ_EVT_TICK, tick_callback, NULL, data);\n}\n\n\nint mosquitto_plugin_cleanup(void *userdata, struct mosquitto_opt *opts, int opt_count)\n{\n\tstruct plugin_data *data = userdata;\n\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosquitto_FREE(data);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/tick-interval/test.conf",
    "content": "listener 1883\n\nplugin ./mosquitto_tick_interval.so\nplugin_opt_interval 1\n\nplugin ./mosquitto_tick_interval.so\nplugin_opt_interval 4\n"
  },
  {
    "path": "plugins/examples/tick-interval/test.sh",
    "content": "#!/bin/sh\n\nvalgrind --log-file=vglog ../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/topic-hierarchy-flatten/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_topic_hierarchy_flatten)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/topic-hierarchy-flatten/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_topic_hierarchy_flatten\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/topic-hierarchy-flatten/mosquitto_topic_hierarchy_flatten.c",
    "content": "/*\nCopyright (c) 2020-2025 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an *example* plugin which looks at messages coming in and if their\n * topic matches a topic filter, makes the payload available on another topic,\n * flattening the hierarchy if the topic filter includes a wildcard.\n *\n * The input topic filter and output topic can be configured in the plugin\n * config. The \"plugin_opt_republish\" option can be set to true/false. If set\n * to true, then the original incoming messages are unaffected and a new\n * message is published for each matching message. If set to false, the\n * original message has its topic replaced with the output topic.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_topic_hierarchy_flatten.c -o mosquitto_topic_hierarchy_flatten.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_topic_hierarchy_flatten.so\n *   plugin_opt_input_topic_filter my/+/topics\n *   plugin_opt_output_topic the/single/output/topic\n *   plugin_opt_republish true\n *\n * Note that this only works on Mosquitto 2.1 or later.\n */\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"topic-hierarchy-flatten\"\n\n#define UNUSED(A) (void)(A)\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstruct plugin_data {\n\tmosquitto_plugin_id_t *pid;\n\tchar *input_topic_filter;\n\tchar *output_topic;\n\tbool republish;\n};\n\n\nstatic int callback_message_in(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\tstruct plugin_data *data = userdata;\n\tbool result;\n\tint rc;\n\n\tUNUSED(event);\n\n\tmosquitto_topic_matches_sub(data->input_topic_filter, ed->topic, &result);\n\n\tif(result){\n\t\tif(data->republish){\n\t\t\tmosquitto_property *props = NULL;\n\t\t\tif(ed->properties){\n\t\t\t\trc = mosquitto_property_copy_all(&props, ed->properties);\n\t\t\t\tif(rc){\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\t\t\trc = mosquitto_broker_publish_copy(NULL, data->output_topic, (int)ed->payloadlen,\n\t\t\t\t\ted->payload, ed->qos, ed->retain, props);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_property_free_all(&props);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}else{\n\t\t\ted->topic = mosquitto_strdup(data->output_topic);\n\t\t\tif(!ed->topic){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **userdata, struct mosquitto_opt *opts, int opt_count)\n{\n\tstruct plugin_data *data = mosquitto_calloc(1, sizeof(struct plugin_data));\n\tif(!data){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\t*userdata = data;\n\tdata->republish = true;\n\n\tfor(int i=0; i<opt_count; i++){\n\t\tif(!strcmp(opts[i].key, \"input_topic_filter\")){\n\t\t\tdata->input_topic_filter = mosquitto_strdup(opts[i].value);\n\t\t\tif(!data->input_topic_filter){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}else if(!strcmp(opts[i].key, \"output_topic\")){\n\t\t\tdata->output_topic = mosquitto_strdup(opts[i].value);\n\t\t\tif(!data->output_topic){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}else if(!strcmp(opts[i].key, \"republish\")){\n\t\t\tdata->republish = !strcmp(opts[i].value, \"true\");\n\t\t}\n\t}\n\n\tif(!data->input_topic_filter){\n\t\tdata->input_topic_filter = mosquitto_strdup(\"input/#\");\n\t\tif(!data->input_topic_filter){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\tif(!data->output_topic){\n\t\tdata->output_topic = mosquitto_strdup(\"output\");\n\t\tif(!data->output_topic){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, PLUGIN_NAME \": Input topic filter is '%s'\", data->input_topic_filter);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, PLUGIN_NAME \": Output topic is '%s'\", data->output_topic);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, PLUGIN_NAME \": Republish is %s\", data->republish?\"true\":\"false\");\n\n\tdata->pid = identifier;\n\treturn mosquitto_callback_register(data->pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, data);\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *userdata, struct mosquitto_opt *opts, int opt_count)\n{\n\tstruct plugin_data *data = userdata;\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tif(data){\n\t\tmosquitto_callback_unregister(data->pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL);\n\n\t\tmosquitto_FREE(data->input_topic_filter);\n\t\tmosquitto_FREE(data->output_topic);\n\t\tmosquitto_FREE(data);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/topic-hierarchy-flatten/test.conf",
    "content": "plugin ./mosquitto_topic_hierarchy_flatten.so\nplugin_opt_republish true\n"
  },
  {
    "path": "plugins/examples/topic-hierarchy-flatten/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/topic-jail/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_topic_jail)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/topic-jail/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_topic_jail\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/topic-jail/mosquitto_topic_jail.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Abilio Marques - initial implementation and documentation.\n*/\n\n/*\n * This is an *example* plugin which demonstrates how to jail a client.\n * It mounts such client topics in a subtree starting with the client id.\n * It modifies it's subscriptions and the topics of the messages destined to\n * that client. It also modifies the topics of the messages published\n * by the client.\n *\n *  client | event | destination | original topic       | modified topic\n *  -------|-------|-------------|----------------------|--------------------\n *  jailed |  sub  |     ---     | topic                | ${jailed_id}/topic\n *  normal |  pub  |   jailed    | ${jailed_id}/topic   | topic\n *  jailed |  pub  |   normal    | topic                | ${jailed_id}/topic\n *\n * For simplicity of this example, all clients with id starting with \"jailed\"\n * will be jailed. All other clients will work as normal.\n *\n * Two jailed clients cannot interact with each other. Normal clients can interact\n * with any jailed client by publishing or subscribing to the mounted topic.\n *\n * You should be very sure of what you are doing before making use of this feature.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_topic_jail.c -o mosquitto_topic_jail.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_topic_jail.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"topic-jail\"\n#define PLUGIN_VERSION \"1.0\"\n\n#define UNUSED(A) (void)(A)\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic bool is_jailed(const char *str)\n{\n\treturn strncmp(\"jailed\", str, 6) == 0;\n}\n\n\nstatic int callback_message_in(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\tchar *new_topic;\n\tsize_t new_topic_len;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tconst char *clientid = mosquitto_client_id(ed->client);\n\n\tif(!is_jailed(clientid)){\n\t\t/* will only modify the topic of jailed clients */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\t/* put the clientid on front of the topic */\n\n\t/* calculate the length of the new payload */\n\tnew_topic_len = strlen(clientid) + sizeof('/') + strlen(ed->topic) + 1;\n\n\t/* Allocate some memory - use\n\t * mosquitto_calloc/mosquitto_malloc/mosquitto_strdup when allocating, to\n\t * allow the broker to track memory usage */\n\tnew_topic = mosquitto_calloc(1, new_topic_len);\n\tif(new_topic == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t/* prepend the clientid to the topic */\n\tsnprintf(new_topic, new_topic_len, \"%s/%s\", clientid, ed->topic);\n\n\t/* Assign the new topic to the event data structure. You\n\t * must *not* free the original topic, it will be handled by the\n\t * broker. */\n\ted->topic = new_topic;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int callback_message_out(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\tsize_t clientid_len;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tconst char *clientid = mosquitto_client_id(ed->client);\n\n\tif(!is_jailed(clientid)){\n\t\t/* will only modify the topic of jailed clients */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\t/* remove the clientid from the front of the topic */\n\tclientid_len = strlen(clientid);\n\n\tif(strlen(ed->topic) <= clientid_len + 1){\n\t\t/* the topic is not long enough to contain the\n\t\t * clientid + '/' */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(!strncmp(clientid, ed->topic, clientid_len) && ed->topic[clientid_len] == '/'){\n\t\t/* Allocate some memory - use\n\t\t * mosquitto_calloc/mosquitto_malloc/mosquitto_strdup when allocating, to\n\t\t * allow the broker to track memory usage */\n\n\t\t/* skip the clientid + '/' */\n\t\tchar *new_topic = mosquitto_strdup(ed->topic + clientid_len + 1);\n\n\t\tif(new_topic == NULL){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\t/* Assign the new topic to the event data structure. You\n\t\t * must *not* free the original topic, it will be handled by the\n\t\t * broker. */\n\t\ted->topic = new_topic;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int callback_subscribe(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_subscribe *ed = event_data;\n\tchar *new_sub;\n\tsize_t new_sub_len;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tconst char *clientid = mosquitto_client_id(ed->client);\n\n\tif(!is_jailed(clientid)){\n\t\t/* will only modify the topic of jailed clients */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\t/* put the clientid on front of the topic */\n\n\t/* calculate the length of the new payload */\n\tnew_sub_len = strlen(clientid) + sizeof('/') + strlen(ed->data.topic_filter) + 1;\n\n\t/* Allocate some memory - use\n\t * mosquitto_calloc/mosquitto_malloc/mosquitto_strdup when allocating, to\n\t * allow the broker to track memory usage */\n\tnew_sub = mosquitto_calloc(1, new_sub_len);\n\tif(new_sub == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t/* prepend the clientid to the subscription */\n\tsnprintf(new_sub, new_sub_len, \"%s/%s\", clientid, ed->data.topic_filter);\n\n\t/* Assign the new topic to the event data structure. You\n\t * must *not* free the original topic, it will be handled by the\n\t * broker. */\n\ted->data.topic_filter = new_sub;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int callback_unsubscribe(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_unsubscribe *ed = event_data;\n\tchar *new_sub;\n\tsize_t new_sub_len;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tconst char *clientid = mosquitto_client_id(ed->client);\n\n\tif(!is_jailed(clientid)){\n\t\t/* will only modify the topic of jailed clients */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\t/* put the clientid on front of the topic */\n\n\t/* calculate the length of the new payload */\n\tnew_sub_len = strlen(clientid) + sizeof('/') + strlen(ed->data.topic_filter) + 1;\n\n\t/* Allocate some memory - use\n\t * mosquitto_calloc/mosquitto_malloc/mosquitto_strdup when allocating, to\n\t * allow the broker to track memory usage */\n\tnew_sub = mosquitto_calloc(1, new_sub_len);\n\tif(new_sub == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t/* prepend the clientid to the subscription */\n\tsnprintf(new_sub, new_sub_len, \"%s/%s\", clientid, ed->data.topic_filter);\n\n\t/* Assign the new topic to the event data structure. You\n\t * must *not* free the original topic, it will be handled by the\n\t * broker. */\n\ted->data.topic_filter = new_sub;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\n\tint rc;\n\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_OUT, callback_message_out, NULL, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_SUBSCRIBE, callback_subscribe, NULL, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_UNSUBSCRIBE, callback_unsubscribe, NULL, NULL);\n\treturn rc;\n}\n"
  },
  {
    "path": "plugins/examples/topic-jail/test.conf",
    "content": "plugin ./mosquitto_topic_jail.so\n"
  },
  {
    "path": "plugins/examples/topic-jail/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/topic-modification/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_topic_modification)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/topic-modification/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_topic_modification\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/topic-modification/mosquitto_topic_modification.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an *example* plugin which demonstrates how to modify the topic of\n * a message after it is received by the broker and before it is sent on to\n * other clients.\n *\n * You should be very sure of what you are doing before making use of this feature.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_topic_modification.c -o mosquitto_topic_modification.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_topic_modification.so\n *\n * Note that this only works on Mosquitto 2.0 or later.\n */\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"topic-modification\"\n#define PLUGIN_VERSION \"1.0\"\n\n#define UNUSED(A) (void)(A)\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int callback_message_in(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\tbool result;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\t/* This simply removes \"/uplink\" from the end of every matching topic. You\n\t * can of course do much more complicated message processing if needed. */\n\n\tmosquitto_topic_matches_sub(\"device/+/data/uplink\", ed->topic, &result);\n\n\tif(result){\n\t\ted->topic[strlen(ed->topic) - strlen(\"/uplink\")] = '\\0';\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\treturn mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, NULL);\n}\n\n\n/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/topic-modification/test.conf",
    "content": "plugin ./mosquitto_topic_modification.so\n"
  },
  {
    "path": "plugins/examples/topic-modification/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/examples/wildcard-temp/CMakeLists.txt",
    "content": "set (PLUGIN_NAME mosquitto_wildcard_temp)\n\nadd_mosquitto_plugin_no_install(\"${PLUGIN_NAME}\" \"${PLUGIN_NAME}.c\" \"\" \"\")\n"
  },
  {
    "path": "plugins/examples/wildcard-temp/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_wildcard_temp\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=\n\nall : binary\n\nOBJS:=${PLUGIN_NAME}.o\n\nPLUGIN_NOINST:=1\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/examples/wildcard-temp/mosquitto_wildcard_temp.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/*\n * This is an example plugin that denies subscriptions to the '#' topic filter,\n * but with special behaviour for clients that connect with the username\n * `wildcard`.\n *\n * The first time per session that clients with this username attempt to\n * subscribe to the '#' topic filter, it will succeed. They will then have 20\n * seconds of access to that subscription, subject to any other access controls\n * that are in place, after which the subscription will be automatically\n * removed.\n *\n * The intention is to allow temporary access to the '#' topic filter so that\n * topic discovery can be done on public servers such as test.mosquitto.org,\n * but also to limit bandwidth use.\n *\n * Compile with:\n *   gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_wildcard_temp.c -o mosquitto_wildcard_temp.so\n *\n * Use in config with:\n *\n *   plugin /path/to/mosquitto_wildcard_temp.so\n *\n * Note that this only works on Mosquitto 2.1 or later.\n */\n\n\n#include <limits.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <string.h>\n#include <time.h>\n#include <uthash.h>\n#include <utlist.h>\n\n#include \"mosquitto.h\"\n\n#define PLUGIN_NAME \"wildcard-temp\"\n#define PLUGIN_VERSION \"1.0\"\n\n/* How long the client has '#' access for, in seconds */\n#define ACCESS_PERIOD 20\n\n#ifndef UNUSED\n#  define UNUSED(A) (void)(A)\n#endif\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstruct client_list {\n\tUT_hash_handle hh;\n\tstruct client_list *next, *prev;\n\ttime_t sub_end;\n\tuint8_t sub_status;\n\tchar id[];\n};\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\nstatic struct client_list *clients = NULL;\nstatic struct client_list *active_subs = NULL;\n\n\nstatic int connect_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tstatic struct client_list *client;\n\tconst char *id, *username;\n\tsize_t idlen;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tusername = mosquitto_client_username(ed->client);\n\tif(!username || strcmp(username, \"wildcard\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tid = mosquitto_client_id(ed->client);\n\tidlen = strlen(id);\n\n\tHASH_FIND(hh, clients, id, idlen, client);\n\tif(client){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tclient = mosquitto_calloc(1, sizeof(struct client_list) + idlen+1);\n\t\tif(client == NULL){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tmemcpy(client->id, id, idlen);\n\t\tHASH_ADD_KEYPTR(hh, clients, client->id, idlen, client);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int disconnect_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tstatic struct client_list *client;\n\tconst char *id;\n\tsize_t idlen;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tid = mosquitto_client_id(ed->client);\n\tidlen = strlen(id);\n\n\tHASH_FIND(hh, clients, id, idlen, client);\n\tif(client){\n\t\tHASH_DELETE(hh, clients, client);\n\t\tif(active_subs){\n\t\t\tDL_DELETE(active_subs, client);\n\t\t}\n\t\tmosquitto_free(client);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int acl_check_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\tstatic struct client_list *client;\n\tconst char *id;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tif(ed->access == MOSQ_ACL_SUBSCRIBE && !strcmp(ed->topic, \"#\")){\n\t\tid = mosquitto_client_id(ed->client);\n\t\tHASH_FIND(hh, clients, id, strlen(id), client);\n\t\tif(client && client->sub_status == 0){\n\t\t\tclient->sub_status = 1;\n\t\t\tclient->sub_end = time(NULL) + ACCESS_PERIOD;\n\t\t\tDL_APPEND(active_subs, client);\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_PLUGIN_IGNORE;\n}\n\n\nstatic int tick_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_tick *ed = event_data;\n\tstruct client_list *client, *client_tmp;\n\ttime_t now;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tnow = time(NULL);\n\tDL_FOREACH_SAFE(active_subs, client, client_tmp){\n\t\tif(client->sub_end < now){\n\t\t\tmosquitto_subscription_delete(client->id, \"#\");\n\t\t\tDL_DELETE(active_subs, client);\n\t\t}else{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Declare that we want another call in 1 second at the earliest */\n\ted->next_s = 1;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tint rc;\n\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_CONNECT, connect_callback, NULL, NULL);\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_DISCONNECT, disconnect_callback, NULL, NULL);\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_ACL_CHECK, acl_check_callback, NULL, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_TICK, tick_callback, NULL, NULL);\n\treturn rc;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\tstruct client_list *client, *client_tmp;\n\n\tUNUSED(user_data);\n\tUNUSED(opts);\n\tUNUSED(opt_count);\n\n\tHASH_ITER(hh, clients, client, client_tmp){\n\t\tHASH_DELETE(hh, clients, client);\n\t\tmosquitto_free(client);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/examples/wildcard-temp/test.conf",
    "content": "listener 1883\n\nplugin ./mosquitto_wildcard_temp.so\nallow_anonymous true\n\n\nsys_interval 1\nlog_timestamp_format %Y-%m-%dT%H:%M:%S\n"
  },
  {
    "path": "plugins/examples/wildcard-temp/test.sh",
    "content": "#!/bin/sh\n\n../../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/password-file/CMakeLists.txt",
    "content": "set(PLUGIN_NAME mosquitto_password_file)\n\nset(SRCLIST\n\tpassword_check.c\n\tpassword_parse.c\n\tplugin.c\n)\n\nset(INCLIST ${mosquitto_SOURCE_DIR}/src)\nset(LINKLIST libmosquitto_common)\n\nadd_mosquitto_plugin(\"${PLUGIN_NAME}\" \"${SRCLIST}\" \"${INCLIST}\" \"${LINKLIST}\")\n"
  },
  {
    "path": "plugins/password-file/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_password_file\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=-I${R}/src\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=${LIBMOSQ_COMMON}\n\n# Objects for this plugin only, built from source in this directory\nOBJS = \\\n\tpassword_check.o \\\n\tpassword_parse.o \\\n\tplugin.o\n\n# Objects from e.g. the common directory that are not in this directory\nOBJS_EXTERNAL =\n\nall : binary\n\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/password-file/password_check.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <uthash.h>\n\n#include \"mosquitto.h\"\n#include \"password_file.h\"\n\n\nint password_file__check(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tstruct password_file_data *data = userdata;\n\tstruct mosquitto__unpwd *u;\n\n\tUNUSED(event);\n\n\tif(ed->username == NULL){\n\t\treturn MOSQ_ERR_PLUGIN_IGNORE;\n\t}\n\n\t// FIXME if(ed->client->bridge) return MOSQ_ERR_SUCCESS;\n\n\tHASH_FIND(hh, data->unpwd, ed->username, strlen(ed->username), u);\n\tif(u){\n\t\tif(u->pw){\n\t\t\tif(ed->password){\n\t\t\t\treturn mosquitto_pw_verify(u->pw, ed->password);\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_AUTH;\n\t\t\t}\n\t\t}else{\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_AUTH;\n}\n"
  },
  {
    "path": "plugins/password-file/password_parse.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <ctype.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n#include \"password_file.h\"\n\n\nint password_file__parse(struct password_file_data *data)\n{\n\tFILE *pwfile;\n\tstruct mosquitto__unpwd *unpwd = NULL;\n\tchar *username, *password;\n\tchar *saveptr = NULL;\n\tchar *buf;\n\tint buflen = 256;\n\tint rc = MOSQ_ERR_INVAL;\n\n\tbuf = mosquitto_malloc((size_t)buflen);\n\tif(buf == NULL){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tpwfile = mosquitto_fopen(data->password_file, \"rt\", true);\n\tif(!pwfile){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"password-file: Error: Unable to open pwfile \\\"%s\\\".\", data->password_file);\n\t\tmosquitto_FREE(buf);\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\twhile(!feof(pwfile)){\n\t\tif(mosquitto_fgets(&buf, &buflen, pwfile)){\n\t\t\tunpwd = NULL;\n\n\t\t\tif(buf[0] == '#'){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(!strchr(buf, ':')){\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tusername = strtok_r(buf, \":\", &saveptr);\n\t\t\tif(username){\n\t\t\t\tusername = mosquitto_trimblanks(username);\n\t\t\t\tif(strlen(username) > 65535){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"password-file: Error: Invalid line in password file '%s', username too long.\", data->password_file);\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\tif(strlen(username) <= 0){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"password-file: Error: Empty username in password file '%s'.\", data->password_file);\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\n\t\t\t\tHASH_FIND(hh, data->unpwd, username, strlen(username), unpwd);\n\t\t\t\tif(unpwd){\n\t\t\t\t\tunpwd = NULL;\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"password-file: Error: Duplicate user '%s' in password file '%s'.\", username, data->password_file);\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\n\t\t\t\tunpwd = mosquitto_calloc(1, sizeof(struct mosquitto__unpwd));\n\t\t\t\tif(!unpwd){\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\n\t\t\t\tunpwd->username = mosquitto_strdup(username);\n\t\t\t\tif(!unpwd->username){\n\t\t\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\tpassword = strtok_r(NULL, \":\", &saveptr);\n\t\t\t\tif(password){\n\t\t\t\t\tpassword = mosquitto_trimblanks(password);\n\n\t\t\t\t\tif(strlen(password) > 65535){\n\t\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"password-file: Error: Invalid line in password file '%s', password too long.\", data->password_file);\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\n\t\t\t\t\tif(mosquitto_pw_new(&unpwd->pw, MOSQ_PW_DEFAULT)\n\t\t\t\t\t\t\t|| mosquitto_pw_decode(unpwd->pw, password)){\n\n\t\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"password-file: Error: Unable to decode line in password file '%s'.\", data->password_file);\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\n\t\t\t\t\tHASH_ADD_KEYPTR(hh, data->unpwd, unpwd->username, strlen(unpwd->username), unpwd);\n\t\t\t\t}else{\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"password-file: Error: Invalid line in password file '%s': %s\", data->password_file, buf);\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfclose(pwfile);\n\tmosquitto_FREE(buf);\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tif(unpwd){\n\t\tmosquitto_pw_cleanup(unpwd->pw);\n\t\tmosquitto_FREE(unpwd->username);\n\t\tmosquitto_FREE(unpwd);\n\t}\n\tmosquitto_FREE(buf);\n\tfclose(pwfile);\n\treturn rc;\n}\n\n\nvoid password_file__cleanup(struct password_file_data *data)\n{\n\tstruct mosquitto__unpwd *u, *tmp = NULL;\n\n\tif(!data){\n\t\treturn;\n\t}\n\n\tHASH_ITER(hh, data->unpwd, u, tmp){\n\t\tHASH_DEL(data->unpwd, u);\n\t\tmosquitto_pw_cleanup(u->pw);\n\t\tmosquitto_FREE(u->username);\n\t\tmosquitto_FREE(u);\n\t}\n}\n\n\nint password_file__reload(int event, void *event_data, void *userdata)\n{\n\tstruct password_file_data *data = userdata;\n\n\tUNUSED(event);\n\tUNUSED(event_data);\n\n\tpassword_file__cleanup(data);\n\treturn password_file__parse(data);\n}\n"
  },
  {
    "path": "plugins/password-file/plugin.c",
    "content": "/*\nCopyright (c) 2025 Cedalo Gmbh\n*/\n\n#include \"config.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n#include \"password_file.h\"\n\n#define PLUGIN_NAME \"password-file\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nstatic int handle_options(struct password_file_data *data, struct mosquitto_opt *options, int option_count)\n{\n\tfor(int i=0; i<option_count; i++){\n\t\tif(!strcmp(options[i].key, \"password_file\")){\n\t\t\tmosquitto_FREE(data->password_file);\n\t\t\tdata->password_file = mosquitto_strdup(options[i].value);\n\t\t\tif(!data->password_file){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}else{\n\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, PLUGIN_NAME \": Error: Unknown option '%s'.\", options[i].key);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *options, int option_count)\n{\n\tstruct password_file_data *data;\n\tint rc;\n\n\tUNUSED(options);\n\tUNUSED(option_count);\n\n\tdata = mosquitto_calloc(1, sizeof(struct password_file_data));\n\tif(!data){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\t*user_data = data;\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, NULL);\n\n\trc = handle_options(data, options, option_count);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = password_file__parse(data);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_BASIC_AUTH, password_file__check, NULL, data);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_RELOAD, password_file__reload, NULL, data);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *options, int option_count)\n{\n\tstruct password_file_data *data = user_data;\n\n\tUNUSED(options);\n\tUNUSED(option_count);\n\n\tmosquitto_callback_unregister(mosq_pid, MOSQ_EVT_BASIC_AUTH, password_file__check, NULL);\n\tmosquitto_callback_unregister(mosq_pid, MOSQ_EVT_RELOAD, password_file__reload, NULL);\n\tpassword_file__cleanup(data);\n\n\tmosquitto_FREE(data->password_file);\n\tmosquitto_FREE(data);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/password-file/test.conf",
    "content": "listener 1883\nallow_anonymous true\nplugin ./mosquitto_password_file.so\nplugin_opt_password_file ./test.pwfile\n"
  },
  {
    "path": "plugins/password-file/test.pwfile",
    "content": "user:$7$1000$h0tqVxBwkB9rKAXukTtffzdbBQtNy1q5FBTDwSW4hucfjpqunBbxW10NVnRk7Cfh0lQndnOv2+k4wJavgz1JNw==$02ujkUXlKkJGFzlQHNjUgXwG3XRB1mr3vs8NX5teGCJGbN4hdgSpHNHuj47j8r5SHXsO7GeHpmkpPNhLraVVcQ==\n"
  },
  {
    "path": "plugins/password-file/test.sh",
    "content": "VG=\"valgrind --log-file=vglog\"\n${VG} ../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/persist-sqlite/CMakeLists.txt",
    "content": "if(SQLITE3_FOUND)\n\tset(PLUGIN_NAME \"mosquitto_persist_sqlite\")\n\n\tset(SRCLIST\n\t\tpersist_sqlite.h\n\t\tutil.h\n\t\t../../common/json_help.h\n\t\tbase_msgs.c\n\t\tclients.c\n\t\tclient_msgs.c\n\t\tcommon.c\n\t\tinit.c\n\t\t../../common/json_help.c\n\t\tplugin.c\n\t\trestore.c\n\t\tretain_msgs.c\n\t\tsubscriptions.c\n\t\ttick.c\n\t\twill.c\n\t)\n\n\tset(INCLIST\n\t\t\"${CJSON_INCLUDE_DIRS}\"\n\t)\n\n\tset(LINKLIST\n\t\tlibmosquitto_common\n\t\tcJSON\n\t\tSQLite::SQLite3\n\t)\n\n\tadd_mosquitto_plugin(\"${PLUGIN_NAME}\" \"${SRCLIST}\" \"${INCLIST}\" \"${LINKLIST}\")\nendif()\n"
  },
  {
    "path": "plugins/persist-sqlite/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_persist_sqlite\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=-I${R}/src/ -I${R}/plugins/common\nLOCAL_LIBADD+=-lsqlite3 ${LIBMOSQ_COMMON}\nLOCAL_LDFLAGS+=\n\nOBJS = \\\n\tbase_msgs.o \\\n\tclients.o \\\n\tclient_msgs.o \\\n\tcommon.o \\\n\tinit.o \\\n\tplugin.o \\\n\trestore.o \\\n\tretain_msgs.o \\\n\tsubscriptions.o \\\n\ttick.o \\\n\twill.o\n\nOBJS_EXTERNAL = \\\n\tjson_help.o\n\nALL_DEPS:= binary\n\nall : ${ALL_DEPS}\n\njson_help.o : ${R}/common/json_help.c ${R}/common/json_help.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nEXTRA_DEPS:=persist_sqlite.h\n\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/persist-sqlite/base_msgs.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <string.h>\n#include <sqlite3.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <cjson/cJSON.h>\n\n#include \"mosquitto.h\"\n#include \"persist_sqlite.h\"\n#include \"util.h\"\n\n\nint persist_sqlite__base_msg_add_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_base_msg *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = MOSQ_ERR_UNKNOWN;\n\tchar *str = NULL;\n\tUNUSED(event);\n\n\n\trc = 0;\n\trc += sqlite3_bind_int64(ms->base_msg_add_stmt, 1, (int64_t)ed->data.store_id);\n\trc += sqlite3_bind_int64(ms->base_msg_add_stmt, 2, ed->data.expiry_time);\n\trc += sqlite3_bind_text(ms->base_msg_add_stmt, 3, ed->data.topic, (int)strlen(ed->data.topic), SQLITE_STATIC);\n\tif(ed->data.payload){\n\t\trc += sqlite3_bind_blob(ms->base_msg_add_stmt, 4, ed->data.payload, (int)ed->data.payloadlen, SQLITE_STATIC);\n\t}else{\n\t\trc += sqlite3_bind_null(ms->base_msg_add_stmt, 4);\n\t}\n\tif(ed->data.source_id){\n\t\trc += sqlite3_bind_text(ms->base_msg_add_stmt, 5, ed->data.source_id, (int)strlen(ed->data.source_id), SQLITE_STATIC);\n\t}else{\n\t\trc += sqlite3_bind_null(ms->base_msg_add_stmt, 5);\n\t}\n\tif(ed->data.source_username){\n\t\trc += sqlite3_bind_text(ms->base_msg_add_stmt, 6, ed->data.source_username, (int)strlen(ed->data.source_username), SQLITE_STATIC);\n\t}else{\n\t\trc += sqlite3_bind_null(ms->base_msg_add_stmt, 6);\n\t}\n\trc += sqlite3_bind_int(ms->base_msg_add_stmt, 7, (int)ed->data.payloadlen);\n\trc += sqlite3_bind_int(ms->base_msg_add_stmt, 8, ed->data.source_mid);\n\trc += sqlite3_bind_int(ms->base_msg_add_stmt, 9, ed->data.source_port);\n\trc += sqlite3_bind_int(ms->base_msg_add_stmt, 10, ed->data.qos);\n\trc += sqlite3_bind_int(ms->base_msg_add_stmt, 11, ed->data.retain);\n\tif(ed->data.properties){\n\t\tstr = properties_to_json_str(ed->data.properties);\n\t}\n\tif(str){\n\t\trc += sqlite3_bind_text(ms->base_msg_add_stmt, 12, str, (int)strlen(str), SQLITE_STATIC);\n\t}else{\n\t\trc += sqlite3_bind_null(ms->base_msg_add_stmt, 12);\n\t}\n\n\trc = sqlite3_single_step_stmt(rc, ms, ms->base_msg_add_stmt);\n\tsqlite3_reset(ms->base_msg_add_stmt);\n\tfree(str);\n\n\treturn rc;\n}\n\n\nint persist_sqlite__base_msg_remove_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_base_msg *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = 1;\n\n\tUNUSED(event);\n\n\tif(sqlite3_bind_int64(ms->base_msg_remove_stmt, 1, (int64_t)ed->data.store_id) == SQLITE_OK){\n\t\trc = sqlite3_single_step_stmt(0, ms, ms->base_msg_remove_stmt);\n\t}\n\tsqlite3_reset(ms->base_msg_remove_stmt);\n\n\treturn rc;\n}\n\n\nint persist_sqlite__base_msg_clear(struct mosquitto_sqlite *ms, const char *clientid)\n{\n\tint rc = MOSQ_ERR_UNKNOWN;\n\n\tif(sqlite3_bind_text(ms->base_msg_remove_for_clientid_stmt, 1, clientid, (int)strlen(clientid), SQLITE_STATIC) == SQLITE_OK){\n\t\trc = sqlite3_single_step_stmt(0, ms, ms->base_msg_remove_for_clientid_stmt);\n\t}\n\tsqlite3_reset(ms->base_msg_remove_for_clientid_stmt);\n\n\treturn rc;\n}\n"
  },
  {
    "path": "plugins/persist-sqlite/client_msgs.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <string.h>\n#include <sqlite3.h>\n\n#include \"mosquitto.h\"\n#include \"mosquitto/broker.h\"\n#include \"persist_sqlite.h\"\n\n\nint persist_sqlite__client_msg_add_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_client_msg *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = MOSQ_ERR_UNKNOWN;\n\n\tUNUSED(event);\n\n\tif(sqlite3_bind_text(ms->client_msg_add_stmt, 1, ed->data.clientid, (int)strlen(ed->data.clientid), SQLITE_STATIC) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int64(ms->client_msg_add_stmt, 2, (int64_t)ed->data.cmsg_id) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int64(ms->client_msg_add_stmt, 3, (int64_t)ed->data.store_id) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int(ms->client_msg_add_stmt, 4, ed->data.dup) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int(ms->client_msg_add_stmt, 5, ed->data.direction) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int(ms->client_msg_add_stmt, 6, ed->data.mid) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int(ms->client_msg_add_stmt, 7, ed->data.qos) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int(ms->client_msg_add_stmt, 8, ed->data.retain) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int(ms->client_msg_add_stmt, 9, ed->data.state) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int(ms->client_msg_add_stmt, 10, (int)ed->data.subscription_identifier) == SQLITE_OK\n\n\t\t\t){\n\n\t\tms->event_count++;\n\t\trc = sqlite3_step(ms->client_msg_add_stmt);\n\t\tif(rc == SQLITE_DONE){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\tsqlite3_reset(ms->client_msg_add_stmt);\n\n\treturn rc;\n}\n\n\nint persist_sqlite__client_msg_remove_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_client_msg *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\n\tUNUSED(event);\n\tms->event_count++;\n\treturn persist_sqlite__client_msg_remove(ms, ed->data.clientid, (int64_t)ed->data.store_id, ed->data.direction);\n}\n\n\nint persist_sqlite__client_msg_update_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_client_msg *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = MOSQ_ERR_UNKNOWN;\n\n\tUNUSED(event);\n\n\tif(sqlite3_bind_int(ms->client_msg_update_stmt, 1, ed->data.state) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int(ms->client_msg_update_stmt, 2, ed->data.dup) == SQLITE_OK\n\t\t\t&& sqlite3_bind_text(ms->client_msg_update_stmt, 3, ed->data.clientid, (int)strlen(ed->data.clientid), SQLITE_STATIC) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int64(ms->client_msg_update_stmt, 4, (int64_t)ed->data.store_id) == SQLITE_OK\n\t\t\t){\n\n\t\tms->event_count++;\n\t\trc = sqlite3_step(ms->client_msg_update_stmt);\n\t\tif(rc == SQLITE_DONE){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\tsqlite3_reset(ms->client_msg_update_stmt);\n\n\treturn rc;\n}\n\n\nint persist_sqlite__client_msg_clear(struct mosquitto_sqlite *ms, const char *clientid)\n{\n\tint rc = MOSQ_ERR_UNKNOWN;\n\n\tif(sqlite3_bind_text(ms->client_msg_clear_all_stmt, 1, clientid, (int)strlen(clientid), SQLITE_STATIC) == SQLITE_OK){\n\t\tms->event_count++;\n\t\trc = sqlite3_step(ms->client_msg_clear_all_stmt);\n\t\tif(rc == SQLITE_DONE){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\tsqlite3_reset(ms->client_msg_clear_all_stmt);\n\n\treturn rc;\n}\n"
  },
  {
    "path": "plugins/persist-sqlite/clients.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <string.h>\n#include <sqlite3.h>\n\n#include \"mosquitto.h\"\n#include \"mosquitto/broker.h\"\n#include \"persist_sqlite.h\"\n#include \"util.h\"\n\n\nint persist_sqlite__client_add_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_client *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = MOSQ_ERR_UNKNOWN;\n\ttime_t now;\n\n\tUNUSED(event);\n\n\tif(sqlite3_bind_text(ms->client_add_stmt, 1,\n\t\t\ted->data.clientid, (int)strlen(ed->data.clientid), SQLITE_STATIC) == SQLITE_OK){\n\n\t\tif(ed->data.username){\n\t\t\tsqlite3_bind_text(ms->client_add_stmt, 2,\n\t\t\t\t\ted->data.username, (int)strlen(ed->data.username),\n\t\t\t\t\tSQLITE_STATIC);\n\t\t}else{\n\t\t\tsqlite3_bind_null(ms->client_add_stmt, 2);\n\t\t}\n\n\t\tnow = time(NULL);\n\t\tif(sqlite3_bind_int64(ms->client_add_stmt, 3, now) == SQLITE_OK\n\t\t\t\t&& sqlite3_bind_int64(ms->client_add_stmt, 4, ed->data.will_delay_time) == SQLITE_OK\n\t\t\t\t&& sqlite3_bind_int64(ms->client_add_stmt, 5, ed->data.session_expiry_time) == SQLITE_OK\n\t\t\t\t&& sqlite3_bind_int(ms->client_add_stmt, 6, ed->data.listener_port) == SQLITE_OK\n\t\t\t\t&& sqlite3_bind_int(ms->client_add_stmt, 7, (int)ed->data.max_packet_size) == SQLITE_OK\n\t\t\t\t&& sqlite3_bind_int(ms->client_add_stmt, 8, ed->data.max_qos) == SQLITE_OK\n\t\t\t\t&& sqlite3_bind_int(ms->client_add_stmt, 9, ed->data.retain_available) == SQLITE_OK\n\t\t\t\t&& sqlite3_bind_int(ms->client_add_stmt, 10, (int)ed->data.session_expiry_interval) == SQLITE_OK\n\t\t\t\t&& sqlite3_bind_int(ms->client_add_stmt, 11, (int)ed->data.will_delay_interval) == SQLITE_OK\n\t\t\t\t){\n\n\t\t\tms->event_count++;\n\t\t\trc = sqlite3_step(ms->client_add_stmt);\n\t\t\tif(rc == SQLITE_DONE){\n\t\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t\t}else{\n\t\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t\t}\n\t\t}\n\t}\n\tsqlite3_reset(ms->client_add_stmt);\n\n\treturn rc;\n}\n\n\nint persist_sqlite__client_remove_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_client *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = 1;\n\n\tUNUSED(event);\n\n\tif(sqlite3_bind_text(ms->subscription_clear_stmt, 1,\n\t\t\ted->data.clientid, (int)strlen(ed->data.clientid), SQLITE_STATIC) == SQLITE_OK){\n\n\t\tms->event_count++;\n\t\trc = sqlite3_step(ms->subscription_clear_stmt);\n\t\tsqlite3_reset(ms->subscription_clear_stmt);\n\t\tif(rc == SQLITE_DONE){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\n\t/* Delete base msgs before deletion of client_msgs as the query will iterate over the client_msgs table */\n\tpersist_sqlite__base_msg_clear(ms, ed->data.clientid);\n\tpersist_sqlite__client_msg_clear(ms, ed->data.clientid);\n\n\tif(sqlite3_bind_text(ms->client_remove_stmt, 1,\n\t\t\ted->data.clientid, (int)strlen(ed->data.clientid), SQLITE_STATIC) == SQLITE_OK){\n\n\t\tms->event_count++;\n\t\trc = sqlite3_step(ms->client_remove_stmt);\n\t\tsqlite3_reset(ms->client_remove_stmt);\n\t\tif(rc == SQLITE_DONE){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\nint persist_sqlite__client_update_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_client *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = 1;\n\n\tUNUSED(event);\n\n\tif(sqlite3_bind_int64(ms->client_update_stmt, 1, ed->data.session_expiry_time) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int64(ms->client_update_stmt, 2, ed->data.will_delay_time) == SQLITE_OK\n\t\t\t&& sqlite3_bind_text(ms->client_update_stmt, 3, ed->data.clientid,\n\t\t\t(int)strlen(ed->data.clientid), SQLITE_STATIC) == SQLITE_OK\n\t\t\t){\n\n\t\tms->event_count++;\n\t\trc = sqlite3_step(ms->client_update_stmt);\n\t\tif(rc == SQLITE_DONE){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\tsqlite3_reset(ms->client_update_stmt);\n\n\treturn rc;\n}\n\n"
  },
  {
    "path": "plugins/persist-sqlite/common.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <string.h>\n#include <sqlite3.h>\n\n#include \"mosquitto.h\"\n#include \"persist_sqlite.h\"\n\n\nint persist_sqlite__client_msg_remove(struct mosquitto_sqlite *ms, const char *clientid, int64_t store_id, int direction)\n{\n\tint rc = 1;\n\n\tmosquitto_log_printf(MOSQ_LOG_DEBUG, \"Drop message clientid %s store_id %ld direction %d\", clientid, store_id, direction);\n\n\tif(sqlite3_bind_text(ms->client_msg_remove_stmt, 1, clientid, (int)strlen(clientid), SQLITE_STATIC) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int64(ms->client_msg_remove_stmt, 2, store_id) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int(ms->client_msg_remove_stmt, 3, direction) == SQLITE_OK\n\t\t\t){\n\t\trc = sqlite3_step(ms->client_msg_remove_stmt);\n\t\tif(rc == SQLITE_DONE){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\tsqlite3_reset(ms->client_msg_remove_stmt);\n\n\treturn rc;\n}\n\n"
  },
  {
    "path": "plugins/persist-sqlite/init.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <sqlite3.h>\n#include <string.h>\n\n#include \"persist_sqlite.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto/broker.h\"\n\n\nstatic int extract_version_numbers(void *data_ptr, int num_columns, char **values, char **column_names)\n{\n\tunsigned int found = 0;\n\tint *version_array = (int *)data_ptr;\n\n\tfor(int i = 0; i < num_columns; ++i){\n\t\tif(!sqlite3_stricmp(column_names[i], \"MAJOR\")){\n\t\t\tversion_array[0] = values[i] ? atoi(values[i]) : 0;\n\t\t\tfound |= 0x4;\n\t\t}else if(!sqlite3_stricmp(column_names[i], \"MINOR\")){\n\t\t\tversion_array[1] = values[i] ? atoi(values[i]) : 0;\n\t\t\tfound |= 0x2;\n\t\t}else if(!sqlite3_stricmp(column_names[i], \"PATCH\")){\n\t\t\tversion_array[2] = values[i] ? atoi(values[i]) : 0;\n\t\t\tfound |= 0x1;\n\t\t}\n\t}\n\tif(found != 0x7){\n\t\treturn SQLITE_MISMATCH;\n\t}\n\treturn SQLITE_OK;\n}\n\n\nstatic int create_tables_1_1(struct mosquitto_sqlite *ms)\n{\n\tint rc;\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"CREATE TABLE IF NOT EXISTS wills \"\n\t\t\t\"(\"\n\t\t\t\"client_id TEXT PRIMARY KEY,\"\n\t\t\t\"payload BLOB,\"\n\t\t\t\"topic STRING NOT NULL,\"\n\t\t\t\"payloadlen INTEGER,\"\n\t\t\t\"qos INTEGER,\"\n\t\t\t\"retain INTEGER,\"\n\t\t\t\"properties STRING\"\n\t\t\t\");\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = sqlite3_exec((*ms).db,\n\t\t\t\"UPDATE version_info\"\n\t\t\t\" SET major = 1, minor = 1, patch = 0\"\n\t\t\t\" WHERE component = 'database_schema';\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\treturn 0;\n}\n\n\nstatic int create_tables(struct mosquitto_sqlite *ms)\n{\n\tint rc;\n\tint db_schema_version[3] = { 0, 0, 0 };\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"CREATE TABLE IF NOT EXISTS base_msgs \"\n\t\t\t\"(\"\n\t\t\t\"store_id INT64 PRIMARY KEY,\"\n\t\t\t\"expiry_time INT64,\"\n\t\t\t\"topic STRING NOT NULL,\"\n\t\t\t\"payload BLOB,\"\n\t\t\t\"source_id STRING,\"\n\t\t\t\"source_username STRING,\"\n\t\t\t\"payloadlen INTEGER,\"\n\t\t\t\"source_mid INTEGER,\"\n\t\t\t\"source_port INTEGER,\"\n\t\t\t\"qos INTEGER,\"\n\t\t\t\"retain INTEGER,\"\n\t\t\t\"properties STRING\"\n\t\t\t\");\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"CREATE TABLE IF NOT EXISTS retains \"\n\t\t\t\"(\"\n\t\t\t\"topic STRING PRIMARY KEY,\"\n\t\t\t\"store_id INT64\"\n\t\t\t//\"FOREIGN KEY (store_id) REFERENCES msg_store(store_id) \"\n\t\t\t//\"ON DELETE CASCADE\"\n\t\t\t\");\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"CREATE TABLE IF NOT EXISTS clients \"\n\t\t\t\"(\"\n\t\t\t\"client_id TEXT PRIMARY KEY,\"\n\t\t\t\"username TEXT,\"\n\t\t\t\"connection_time INT64,\"\n\t\t\t\"will_delay_time INT64,\"\n\t\t\t\"session_expiry_time INT64,\"\n\t\t\t\"listener_port INT,\"\n\t\t\t\"max_packet_size INT,\"\n\t\t\t\"max_qos INT,\"\n\t\t\t\"retain_available INT,\"\n\t\t\t\"session_expiry_interval INT,\"\n\t\t\t\"will_delay_interval INT\"\n\t\t\t\");\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"CREATE TABLE IF NOT EXISTS subscriptions \"\n\t\t\t\"(\"\n\t\t\t\"client_id TEXT NOT NULL,\"\n\t\t\t\"topic TEXT NOT NULL,\"\n\t\t\t\"subscription_options INTEGER,\"\n\t\t\t\"subscription_identifier INTEGER,\"\n\t\t\t\"PRIMARY KEY (client_id, topic) \"\n\t\t\t\");\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"CREATE TABLE IF NOT EXISTS client_msgs \"\n\t\t\t\"(\"\n\t\t\t\"client_id TEXT NOT NULL,\"\n\t\t\t\"cmsg_id INT64,\"\n\t\t\t\"store_id INT64,\"\n\t\t\t\"dup INTEGER,\"\n\t\t\t\"direction INTEGER,\"\n\t\t\t\"mid INTEGER,\"\n\t\t\t\"qos INTEGER,\"\n\t\t\t\"retain INTEGER,\"\n\t\t\t\"state INTEGER,\"\n\t\t\t\"subscription_identifier INTEGER\"\n\t\t\t//\"state INTEGER,\"\n\t\t\t//\"FOREIGN KEY (client_id) REFERENCES clients(client_id) \"\n\t\t\t//\"ON DELETE CASCADE,\"\n\t\t\t//\"FOREIGN KEY (store_id) REFERENCES msg_store(store_id) \"\n\t\t\t//\"ON DELETE CASCADE\"\n\t\t\t\");\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"CREATE INDEX IF NOT EXISTS client_msgs_client_id ON client_msgs(client_id);\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"DROP INDEX IF EXISTS client_msgs_store_id;\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"CREATE INDEX IF NOT EXISTS client_msgs_store_id ON client_msgs(store_id,client_id);\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"CREATE INDEX IF NOT EXISTS retains_storeid ON retains(store_id);\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\tsqlite3_exec(ms->db, \"ALTER TABLE client_msgs ADD COLUMN cmsg_id INT64\", NULL, NULL, NULL);\n\tsqlite3_exec(ms->db, \"ALTER TABLE client_msgs ADD COLUMN subscription_identifier INT\", NULL, NULL, NULL);\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"CREATE TABLE IF NOT EXISTS version_info \"\n\t\t\t\"(\"\n\t\t\t\"component TEXT NOT NULL,\"\n\t\t\t\"major INTEGER NOT NULL,\"\n\t\t\t\"minor INTEGER NOT NULL,\"\n\t\t\t\"patch INTEGER NOT NULL\"\n\t\t\t\");\",\n\t\t\tNULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_exec(ms->db,\n\t\t\t\"SELECT major,minor,patch\"\n\t\t\t\"  FROM version_info \"\n\t\t\t\"  WHERE component = 'database_schema';\",\n\t\t\t&extract_version_numbers, db_schema_version, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\tif(db_schema_version[0] == 0){\n\t\trc = sqlite3_exec((*ms).db,\n\t\t\t\t\"INSERT INTO version_info(component,major,minor,patch) \"\n\t\t\t\t\"VALUES ('database_schema','1','0','0');\",\n\t\t\t\tNULL, NULL, NULL);\n\t\tif(rc){\n\t\t\tgoto fail;\n\t\t}\n\t\tdb_schema_version[0] = 1;\n\t\tdb_schema_version[1] = 0;\n\t\tdb_schema_version[2] = 0;\n\t}\n\tif(db_schema_version[0] == 1){\n\t\t/* 1.0.x needs to be upgraded to 1.1 */\n\t\tif(db_schema_version[1] == 0){\n\t\t\trc = create_tables_1_1(ms);\n\t\t\tif(rc){\n\t\t\t\tgoto fail;\n\t\t\t}\n\t\t\tdb_schema_version[0] = 1;\n\t\t\tdb_schema_version[1] = 1;\n\t\t\tdb_schema_version[2] = 0;\n\t\t}\n\t\t/* 1.1.x  is the current DB-Schema version */\n\t\tif(db_schema_version[1] == 1){\n\t\t\treturn 0;\n\t\t}\n\t}\n\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Sqlite persistence: Unknown database_schema version %d.%d.%d\",\n\t\t\tdb_schema_version[0], db_schema_version[1], db_schema_version[2]);\n\trc = MOSQ_ERR_INVAL;\n\tgoto close_db;\n\nfail:\n\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Sqlite persistence: Error creating tables: %s %s\", sqlite3_errstr(rc), ms->db ? sqlite3_errmsg(ms->db) : \"\");\nclose_db:\n\tsqlite3_close(ms->db);\n\tms->db = NULL;\n\treturn rc;\n}\n\n\nstatic int prepare_statements(struct mosquitto_sqlite *ms)\n{\n\tint rc;\n\n\t/* Subscriptions */\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"INSERT OR REPLACE INTO subscriptions \"\n\t\t\t\"(client_id, topic, subscription_options, subscription_identifier) \"\n\t\t\t\"VALUES (?,?,?,?)\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->subscription_add_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"DELETE FROM subscriptions WHERE client_id=? and topic=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->subscription_remove_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"DELETE FROM subscriptions WHERE client_id=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->subscription_clear_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\n\t/* Clients */\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"INSERT OR REPLACE INTO clients \"\n\t\t\t\"(client_id, username, connection_time, will_delay_time, session_expiry_time, \"\n\t\t\t\"listener_port, max_packet_size, max_qos, retain_available, \"\n\t\t\t\"session_expiry_interval, will_delay_interval) \"\n\t\t\t\"VALUES(?,?,?,?,?,?,?,?,?,?,?)\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->client_add_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"DELETE FROM clients WHERE client_id=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->client_remove_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"UPDATE clients SET session_expiry_time=?, will_delay_time=? \"\n\t\t\t\"WHERE client_id=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->client_update_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\t/* Client messages */\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"INSERT INTO client_msgs \"\n\t\t\t\"(client_id,cmsg_id,store_id,dup,direction,mid,qos,retain,state,subscription_identifier) \"\n\t\t\t\"VALUES(?,?,?,?,?,?,?,?,?,?)\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->client_msg_add_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"DELETE FROM client_msgs WHERE client_id=? AND store_id=? AND direction=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->client_msg_remove_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"UPDATE client_msgs SET state=?,dup=? WHERE client_id=? AND store_id=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->client_msg_update_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"DELETE FROM client_msgs WHERE client_id=? AND direction=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->client_msg_clear_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"DELETE FROM client_msgs WHERE client_id=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->client_msg_clear_all_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\t/* Message store */\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"INSERT INTO base_msgs \"\n\t\t\t\"(store_id, expiry_time, topic, payload, source_id, source_username, \"\n\t\t\t\"payloadlen, source_mid, source_port, qos, retain, properties) \"\n\t\t\t\"VALUES(?,?,?,?,?,?,?,?,?,?,?,?)\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->base_msg_add_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"DELETE FROM base_msgs WHERE store_id=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->base_msg_remove_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"DELETE FROM base_msgs AS bm \"\n\t\t\t\"WHERE bm.store_id IN \"\n\t\t\t\"( SELECT cm.store_id FROM client_msgs AS cm\"\n\t\t\t\"  LEFT OUTER JOIN client_msgs AS oc ON oc.store_id = cm.store_id AND oc.client_id != cm.client_id\"\n\t\t\t\"  LEFT OUTER JOIN retains AS rm ON rm.store_id = cm.store_id\"\n\t\t\t\"  WHERE cm.client_id = ? AND oc.store_id IS NULL AND rm.store_id IS NULL)\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->base_msg_remove_for_clientid_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"SELECT store_id, expiry_time, topic, payload, source_id, source_username, \"\n\t\t\t\"payloadlen, source_mid, source_port, qos, retain, properties \"\n\t\t\t\"FROM base_msgs WHERE store_id=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->base_msg_load_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\t/* Retains */\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"INSERT OR REPLACE INTO retains \"\n\t\t\t\"(topic, store_id)\"\n\t\t\t\"VALUES(?,?)\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->retain_msg_set_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"DELETE FROM retains WHERE topic=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->retain_msg_remove_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\t/* Will messages */\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"INSERT OR REPLACE INTO wills \"\n\t\t\t\"(client_id, payload, topic, payloadlen, qos, retain, properties)\"\n\t\t\t\"VALUES(?,?,?,?,?,?,?)\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->will_add_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = sqlite3_prepare_v3(ms->db,\n\t\t\t\"DELETE FROM wills WHERE client_id=?\",\n\t\t\t-1, SQLITE_PREPARE_PERSISTENT,\n\t\t\t&ms->will_remove_stmt, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\treturn 0;\nfail:\n\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Sqlite persistence: Error preparing statements: %s\", sqlite3_errstr(rc));\n\tsqlite3_close(ms->db);\n\tms->db = NULL;\n\treturn 1;\n}\n\n\nint persist_sqlite__init(struct mosquitto_sqlite *ms)\n{\n\tint rc;\n\tchar buf[50];\n\n\trc = sqlite3_open_v2(ms->db_file, &ms->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL);\n\tif(rc != SQLITE_OK){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Sqlite persistence: Error opening %s: %s\",\n\t\t\t\tms->db_file, sqlite3_errstr(rc));\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tsnprintf(buf, sizeof(buf), \"PRAGMA page_size=%u;\", ms->page_size);\n\trc = sqlite3_exec(ms->db, buf, NULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = sqlite3_exec(ms->db, \"PRAGMA journal_mode=WAL;\", NULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = sqlite3_exec(ms->db, \"PRAGMA foreign_keys = ON;\", NULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\tsnprintf(buf, sizeof(buf), \"PRAGMA synchronous=%d;\", ms->synchronous);\n\trc = sqlite3_exec(ms->db, buf, NULL, NULL, NULL);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\trc = create_tables(ms);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = prepare_statements(ms);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tsqlite3_exec(ms->db, \"BEGIN;\", NULL, NULL, NULL);\n\treturn MOSQ_ERR_SUCCESS;\nfail:\n\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Sqlite persistence: Error opening database: %s\", sqlite3_errstr(rc));\n\treturn MOSQ_ERR_UNKNOWN;\n}\n\n\nvoid persist_sqlite__cleanup(struct mosquitto_sqlite *ms)\n{\n\tif(ms->db){\n\t\tint rc = sqlite3_exec(ms->db, \"END;\", NULL, NULL, NULL);\n\t\tif(rc !=  SQLITE_OK){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Sqlite persistence: Closing final transaction %s\", sqlite3_errstr(rc));\n\t\t}\n\t}\n\n\tsqlite3_finalize(ms->client_add_stmt);\n\tsqlite3_finalize(ms->client_remove_stmt);\n\tsqlite3_finalize(ms->client_update_stmt);\n\tsqlite3_finalize(ms->subscription_add_stmt);\n\tsqlite3_finalize(ms->subscription_remove_stmt);\n\tsqlite3_finalize(ms->subscription_clear_stmt);\n\tsqlite3_finalize(ms->client_msg_add_stmt);\n\tsqlite3_finalize(ms->client_msg_remove_stmt);\n\tsqlite3_finalize(ms->client_msg_update_stmt);\n\tsqlite3_finalize(ms->client_msg_clear_stmt);\n\tsqlite3_finalize(ms->client_msg_clear_all_stmt);\n\tsqlite3_finalize(ms->base_msg_add_stmt);\n\tsqlite3_finalize(ms->base_msg_remove_stmt);\n\tsqlite3_finalize(ms->base_msg_remove_for_clientid_stmt);\n\tsqlite3_finalize(ms->base_msg_load_stmt);\n\tsqlite3_finalize(ms->retain_msg_set_stmt);\n\tsqlite3_finalize(ms->retain_msg_remove_stmt);\n\tsqlite3_finalize(ms->will_add_stmt);\n\tsqlite3_finalize(ms->will_remove_stmt);\n\n\tif(ms->db){\n\t\tint rc = sqlite3_wal_checkpoint_v2(ms->db, NULL, SQLITE_CHECKPOINT_TRUNCATE, NULL, NULL);\n\t\tif(rc !=  SQLITE_OK){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Warning: Sqlite persistence: Final  wal_checkpoint  %s\", sqlite3_errstr(rc));\n\t\t}\n\t\trc = sqlite3_close(ms->db);\n\t\tif(rc !=  SQLITE_OK){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Warning: Sqlite persistence: Error closing database: %s\", sqlite3_errstr(rc));\n\t\t}\n\t\tms->db = NULL;\n\t}\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"Sqlite persistence: Closed DB\");\n\n}\n"
  },
  {
    "path": "plugins/persist-sqlite/migrate_to_persist_sqlite.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nMigration script to migrate from Snapshot persistence to Persist SQLite Plugin.\n\"\"\"\n\nimport argparse\nimport base64\nimport json\nimport shutil\nimport sqlite3\nimport subprocess\nimport sys\nfrom pathlib import Path  # use Path to be able to handle Windows as well\nfrom typing import Self\n\n\n# Snapshot persistence\n\n\nclass SnapshotPersistence:\n    def __init__(self, json_dump: str):\n        j_snapshot_persistence = json.loads(json_dump)\n\n        self.base_messages: list[dict] = j_snapshot_persistence[\"base-messages\"]\n        self.clients: list[dict] = j_snapshot_persistence[\"clients\"]\n        self.client_messages: list[dict] = j_snapshot_persistence[\"client-messages\"]\n        self.retained_messages: list[dict] = j_snapshot_persistence[\"retained-messages\"]\n        self.subscriptions: list[dict] = j_snapshot_persistence[\"subscriptions\"]\n\n\n# SQlite3 DB\n\n\nclass SQLite3Persistence:\n    def __init__(self):\n        self.__store_id_topic_map: dict[int, str] = {}\n        self.__db_path: Path = Path(__file__).parent.resolve() / \"mosquitto.sqlite3\"\n        self.__conn: sqlite3.Connection = None\n        self.__cursor: sqlite3.Cursor = None\n\n        self.__init_db()\n\n    def __del__(self):\n        self.__close_db()\n\n    def __close_db(self):\n        if self.__cursor is not None:\n            self.__cursor.close()\n\n        if self.__conn is not None:\n            self.__conn.commit()\n            self.__conn.close()\n\n    def __on_error(self):\n        self.__close_db()\n        self.__db_path.unlink(missing_ok=True)\n        sys.exit(1)\n\n    def __init_db(self):\n        try:\n            self.__conn = sqlite3.connect(self.__db_path)\n            self.__cursor = self.__conn.cursor()\n\n            self.__create_tables()\n            self.__create_indices()\n        except sqlite3.Error as err:\n            print(f\"Error during SQLite3 DB initialization. Reason: {str(err)}\")\n            self.__on_error()\n\n    def __create_clients_table(self):\n        self.__cursor.execute(\n            \"CREATE TABLE IF NOT EXISTS clients \"\n            \"(\"\n            \"client_id TEXT PRIMARY KEY,\"\n            \"username TEXT,\"\n            \"connection_time INT64,\"\n            \"will_delay_time INT64,\"\n            \"session_expiry_time INT64,\"\n            \"listener_port INT,\"\n            \"max_packet_size INT,\"\n            \"max_qos INT,\"\n            \"retain_available INT,\"\n            \"session_expiry_interval INT,\"\n            \"will_delay_interval INT\"\n            \");\",\n        )\n\n    def __create_client_msgs_table(self):\n        self.__cursor.execute(\n            \"CREATE TABLE IF NOT EXISTS client_msgs \"\n            \"(\"\n            \"client_id TEXT NOT NULL,\"\n            \"cmsg_id INT64,\"\n            \"store_id INT64,\"\n            \"dup INTEGER,\"\n            \"direction INTEGER,\"\n            \"mid INTEGER,\"\n            \"qos INTEGER,\"\n            \"retain INTEGER,\"\n            \"state INTEGER,\"\n            \"subscription_identifier INT\"\n            # \"state INTEGER,\"\n            # \"FOREIGN KEY (client_id) REFERENCES clients(client_id) \"\n            # \"ON DELETE CASCADE,\"\n            # \"FOREIGN KEY (store_id) REFERENCES msg_store(store_id) \"\n            # \"ON DELETE CASCADE\"\n            \");\"\n        )\n\n    def __create_base_msgs_table(self):\n        self.__cursor.execute(\n            \"CREATE TABLE IF NOT EXISTS base_msgs \"\n            \"(\"\n            \"store_id INT64 PRIMARY KEY,\"\n            \"expiry_time INT64,\"\n            \"topic STRING NOT NULL,\"\n            \"payload BLOB,\"\n            \"source_id STRING,\"\n            \"source_username STRING,\"\n            \"payloadlen INTEGER,\"\n            \"source_mid INTEGER,\"\n            \"source_port INTEGER,\"\n            \"qos INTEGER,\"\n            \"retain INTEGER,\"\n            \"properties STRING\"\n            \");\"\n        )\n\n    def __create_retains_table(self):\n        self.__cursor.execute(\n            \"CREATE TABLE IF NOT EXISTS retains \"\n            \"(\"\n            \"topic STRING PRIMARY KEY,\"\n            \"store_id INT64\"\n            # \"FOREIGN KEY (store_id) REFERENCES msg_store(store_id) \"\n            # \"ON DELETE CASCADE\"\n            \");\"\n        )\n\n    def __create_subscriptions_table(self):\n        self.__cursor.execute(\n            \"CREATE TABLE IF NOT EXISTS subscriptions \"\n            \"(\"\n            \"client_id TEXT NOT NULL,\"\n            \"topic TEXT NOT NULL,\"\n            \"subscription_options INTEGER,\"\n            \"subscription_identifier INTEGER,\"\n            \"PRIMARY KEY (client_id, topic)\"\n            \");\",\n        )\n\n    def __create_version_info_table(self):\n        self.__cursor.execute(\n            \"CREATE TABLE IF NOT EXISTS version_info \"\n            \"(\"\n            \"component TEXT NOT NULL,\"\n            \"major INTEGER NOT NULL,\"\n            \"minor INTEGER NOT NULL,\"\n            \"patch INTEGER NOT NULL\"\n            \");\"\n        )\n\n    def __create_tables(self):\n        self.__create_clients_table()\n        self.__create_client_msgs_table()\n        self.__create_base_msgs_table()\n        self.__create_retains_table()\n        self.__create_subscriptions_table()\n        self.__create_version_info_table()\n\n    def __create_indices(self):\n        self.__cursor.execute(\n            \"CREATE INDEX IF NOT EXISTS client_msgs_client_id ON client_msgs(client_id);\"\n        )\n        self.__cursor.execute(\"DROP INDEX IF EXISTS client_msgs_store_id;\")\n        self.__cursor.execute(\n            \"CREATE INDEX IF NOT EXISTS client_msgs_store_id ON client_msgs(store_id,client_id);\"\n        )\n        self.__cursor.execute(\n            \"CREATE INDEX IF NOT EXISTS retains_storeid ON retains(store_id);\"\n        )\n\n    def __add_clients(self, clients: list[dict]):\n        self.__cursor.executemany(\n            \"INSERT OR REPLACE INTO clients \"\n            \"(client_id, username, connection_time, will_delay_time, session_expiry_time, \"\n            \"listener_port, max_packet_size, max_qos, retain_available, \"\n            \"session_expiry_interval, will_delay_interval) \"\n            \"VALUES(?,?,?,?,?,?,?,?,?,?,?)\",\n            [\n                (\n                    client[\"clientid\"],\n                    client[\"username\"],\n                    0,  # connection_time\n                    0,  # will_delay_time\n                    client[\"session-expiry-time\"],\n                    client[\"listener-port\"],\n                    0,  # max_packet_size\n                    2,  # max_qos\n                    True,  # retain_available\n                    client[\"session-expiry-interval\"],\n                    0,  # will_delay_interval\n                )\n                for client in clients\n            ],\n        )\n\n    def __add_client_msgs(self, client_msgs: list[dict]):\n        for cmsg_counter, client_msg in enumerate(client_msgs, start=1):\n            self.__cursor.execute(\n                \"INSERT INTO client_msgs \"\n                \"(client_id,cmsg_id,store_id,dup,direction,mid,qos,\"\n                \"retain,state,subscription_identifier) \"\n                \"VALUES(?,?,?,?,?,?,?,?,?,?)\",\n                (\n                    client_msg[\"clientid\"],\n                    cmsg_counter,  # cmsg_id\n                    client_msg[\"storeid\"],\n                    0,  # dup\n                    client_msg[\"direction\"],\n                    client_msg[\"mid\"],\n                    client_msg[\"qos\"],\n                    False,  # retain\n                    client_msg[\"state\"],\n                    client_msg[\"subscription-identifier\"],\n                ),\n            )\n\n    def __add_base_msgs(self, base_msgs: list[dict]):\n        for base_msg in base_msgs:\n            self.__store_id_topic_map.update({base_msg[\"storeid\"]: base_msg[\"topic\"]})\n\n            payload = base64.b64decode(base_msg[\"payload\"]) if \"payload\" in base_msg else None\n\n            self.__cursor.execute(\n                \"INSERT INTO base_msgs \"\n                \"(store_id, expiry_time, topic, payload, source_id, source_username, \"\n                \"payloadlen, source_mid, source_port, qos, retain, properties) \"\n                \"VALUES(?,?,?,?,?,?,?,?,?,?,?,?)\",\n                (\n                    base_msg[\"storeid\"],\n                    base_msg[\"expiry-time\"],\n                    base_msg[\"topic\"],\n                    payload if \"payload\" in base_msg else None,\n                    base_msg[\"clientid\"] if \"clientid\" in base_msg else None,\n                    base_msg[\"username\"] if \"username\" in base_msg else None,\n                    len(payload) if \"username\" in base_msg else 0,\n                    base_msg[\"source-mid\"],\n                    base_msg[\"source-port\"],\n                    base_msg[\"qos\"],\n                    base_msg[\"retain\"],\n                    base_msg[\"properties\"] if \"properties\" in base_msg else None,\n                ),\n            )\n\n    def __add_subscriptions(self, subscriptions: list[dict]):\n        self.__cursor.executemany(\n            \"INSERT OR REPLACE INTO subscriptions \"\n            \"(client_id, topic, subscription_options, subscription_identifier) \"\n            \"VALUES (?,?,?,?)\",\n            [\n                (\n                    subscription[\"clientid\"],\n                    subscription[\"topic\"],\n                    subscription[\"options\"],\n                    subscription[\"identifier\"],\n                )\n                for subscription in subscriptions\n            ],\n        )\n\n    def __add_retained_messages(self, retained_messages: list[dict]):\n        self.__cursor.executemany(\n            \"INSERT OR REPLACE INTO retains (topic, store_id) VALUES(?,?)\",\n            [\n                (\n                    self.__store_id_topic_map[retained_message[\"storeid\"]],\n                    retained_message[\"storeid\"],\n                )\n                for retained_message in retained_messages\n            ],\n        )\n\n    def migrate_to_persist_sqlite(\n        self, snapshot_persistence: SnapshotPersistence\n    ) -> Self:\n        try:\n            # self.__add_base_msgs must be executed before self.__add_retained_messages gets invoked\n            self.__add_base_msgs(snapshot_persistence.base_messages)\n            self.__add_retained_messages(snapshot_persistence.retained_messages)\n            self.__add_subscriptions(snapshot_persistence.subscriptions)\n            self.__add_clients(snapshot_persistence.clients)\n            self.__add_client_msgs(snapshot_persistence.client_messages)\n        except (sqlite3.Error, TypeError) as err:\n            print(f\"Error during SQLite3 DB creation. Reason: {str(err)}\")\n            self.__on_error()\n\n\n# Migration\n\n\ndef find_mosquitto_db_dump() -> str:\n    mosquitto_db_dump = shutil.which(\"mosquitto_db_dump\")\n    if mosquitto_db_dump is None:\n        raise RuntimeError(\n            'Could not find mosquitto_db_dump. Provide the path via the \"--dump-tool\" argument '\n            \"or make sure the executable is contained in your system's path.\"\n        )\n    return mosquitto_db_dump\n\n\ndef dump_mosquitto_db_to_json(\n    mosquitto_db_dump: str, persistence_db_path: Path\n) -> str:\n    return subprocess.check_output(\n        [\n            mosquitto_db_dump,\n            \"--json\",\n            str(persistence_db_path),\n        ]\n    ).decode(encoding=\"utf-8\")\n\n\ndef migrate_mosquitto_conf(mosquitto_conf: str, persist_sqlite_lib_path: Path) -> str:\n    migrated_mosquitto_conf: list[str] = []\n    for line in mosquitto_conf.splitlines():\n        if line.startswith(\"persistence true\"):\n            migrated_mosquitto_conf.append(f\"plugin {str(persist_sqlite_lib_path)}\")\n            continue\n\n        migrated_mosquitto_conf.append(line)\n    return \"\\n\".join(migrated_mosquitto_conf)\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        \"--persistence-db\", required=True, type=str, help=\"Path to mosquitto.db file\"\n    )\n    parser.add_argument(\n        \"--conf\", required=False, type=str, help=\"Path to mosquitto.conf file\"\n    )\n    parser.add_argument(\n        \"--persist-sqlite-lib\",\n        required=False,\n        type=str,\n        help=\"Path to mosquitto_persist_sqlite.so/.dll\",\n    )\n    parser.add_argument(\n        \"--dump-tool\",\n        required=False,\n        type=str,\n        help=\"Path to mosquitto_db_dump executable\",\n    )  # support local builds\n\n    args = parser.parse_args()\n    persistence_db_path = Path(args.persistence_db)\n    mosquitto_db_dump = (\n        args.dump_tool if args.dump_tool else find_mosquitto_db_dump()\n    )\n\n    if args.conf is not None:\n        if args.persist_sqlite_lib is None:\n            print(\n                \"Error: Cannot migrate mosquitto.conf file. Reason: --persist-sqlite-lib argument is missing\"\n            )\n            sys.exit(1)\n\n        # Migrate mosquitto.conf\n        mosquitto_conf_path = Path(args.conf)\n        mosquitto_conf = mosquitto_conf_path.read_text(encoding=\"utf-8\")\n        migrated_mosquitto_conf = migrate_mosquitto_conf(\n            mosquitto_conf, Path(args.persist_sqlite_lib)\n        )\n\n        # Backup old mosquitto.conf and afterwards write migrated mosquitto.conf file\n        mosquitto_conf_path.with_suffix(\".conf.old.persistence\").write_text(\n            mosquitto_conf, encoding=\"utf-8\"\n        )\n        mosquitto_conf_path.write_text(migrated_mosquitto_conf, encoding=\"utf-8\")\n\n    # Dump Snapshot persistence and parse JSON\n    snapshot_persistence = SnapshotPersistence(\n        dump_mosquitto_db_to_json(mosquitto_db_dump, persistence_db_path)\n    )\n\n    # Migrate Snapshot persistence and write SQLite3 file\n    SQLite3Persistence().migrate_to_persist_sqlite(snapshot_persistence)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "plugins/persist-sqlite/persist_sqlite.h",
    "content": "/*\nCopyright (c) 2021,2022 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef PERSIST_SQLITE_H\n#define PERSIST_SQLITE_H\n\n#include <sqlite3.h>\n#include <time.h>\n#include <stdint.h>\n\n#ifndef UNUSED\n#  define UNUSED(A) (void)(A)\n#endif\n\nstruct mosquitto_sqlite {\n\tchar *db_file;\n\tsqlite3 *db;\n\tsqlite3_stmt *client_add_stmt;\n\tsqlite3_stmt *client_remove_stmt;\n\tsqlite3_stmt *client_update_stmt;\n\tsqlite3_stmt *subscription_add_stmt;\n\tsqlite3_stmt *subscription_remove_stmt;\n\tsqlite3_stmt *subscription_clear_stmt;\n\tsqlite3_stmt *client_msg_add_stmt;\n\tsqlite3_stmt *client_msg_remove_stmt;\n\tsqlite3_stmt *client_msg_update_stmt;\n\tsqlite3_stmt *client_msg_clear_stmt;\n\tsqlite3_stmt *client_msg_clear_all_stmt;\n\tsqlite3_stmt *base_msg_add_stmt;\n\tsqlite3_stmt *base_msg_remove_stmt;\n\tsqlite3_stmt *base_msg_remove_for_clientid_stmt;\n\tsqlite3_stmt *base_msg_load_stmt;\n\tsqlite3_stmt *retain_msg_set_stmt;\n\tsqlite3_stmt *retain_msg_remove_stmt;\n\tsqlite3_stmt *will_add_stmt;\n\tsqlite3_stmt *will_remove_stmt;\n\tint synchronous;\n\tunsigned int event_count;\n\tunsigned int flush_period;\n\tunsigned int page_size;\n};\n\nint persist_sqlite__init(struct mosquitto_sqlite *ms);\nvoid persist_sqlite__cleanup(struct mosquitto_sqlite *ms);\n\nint persist_sqlite__restore_cb(int event, void *event_data, void *userdata);\n\nint persist_sqlite__client_msg_remove(struct mosquitto_sqlite *ms, const char *clientid, int64_t store_id, int direction);\n\nint persist_sqlite__client_add_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__client_update_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__client_remove_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__client_msg_add_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__client_msg_clear(struct mosquitto_sqlite *ms, const char *clientid);\nint persist_sqlite__client_msg_remove_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__client_msg_update_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__base_msg_add_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__base_msg_load_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__base_msg_remove_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__base_msg_clear(struct mosquitto_sqlite *ms, const char *clientid);\nint persist_sqlite__retain_msg_set_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__retain_msg_remove_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__subscription_add_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__subscription_remove_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__will_add_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__will_remove_cb(int event, void *event_data, void *userdata);\nint persist_sqlite__tick_cb(int event, void *event_data, void *userdata);\n#endif\n"
  },
  {
    "path": "plugins/persist-sqlite/plugin.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#ifdef WIN32\n#  include <direct.h>\n#else\n#  include <strings.h>\n#endif\n\n#include \"mosquitto.h\"\n#include \"mosquitto/broker.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n\n#include \"persist_sqlite.h\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *plg_id = NULL;\nstatic struct mosquitto_sqlite plg_data;\n\n\nstatic int conf_parse_uint(const char *in, const char *name, unsigned int *value, int min_value)\n{\n\tint v = atoi(in);\n\tif(v < min_value){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Error: Invalid '%s' value %d in configuration.\", name, v);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t*value = (unsigned int)v;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void set_defaults(void)\n{\n\t/* \"normal\" synchronous mode. */\n\tplg_data.synchronous = 1;\n\n\t/* 5 seconds */\n\tplg_data.flush_period = 5;\n\n\tplg_data.page_size = 4 * 1024;\n}\n\n\nstatic int get_db_file(struct mosquitto_opt *options, int option_count)\n{\n\tconst char *persistence_location;\n\tint i;\n\n\tpersistence_location = mosquitto_persistence_location();\n\tif(persistence_location){\n#ifdef WIN32\n\t\t(void)mkdir(persistence_location);\n#else\n\t\t(void)mkdir(persistence_location, 0770);\n#endif\n\t\tplg_data.db_file = malloc(strlen(persistence_location) + 1 + strlen(\"/mosquitto.sqlite3\"));\n\t\tif(!plg_data.db_file){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"Sqlite persistence: Out of memory.\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tsprintf(plg_data.db_file, \"%s/mosquitto.sqlite3\", persistence_location);\n\t}else{\n\t\tfor(i=0; i<option_count; i++){\n\t\t\tif(!strcasecmp(options[i].key, \"db_file\")){\n\t\t\t\tplg_data.db_file = mosquitto_strdup(options[i].value);\n\t\t\t\tif(plg_data.db_file == NULL){\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *options, int option_count)\n{\n\tint i;\n\tint rc;\n\n\tUNUSED(user_data);\n\n\tmemset(&plg_data, 0, sizeof(struct mosquitto_sqlite));\n\tset_defaults();\n\n\tif(get_db_file(options, option_count)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tfor(i=0; i<option_count; i++){\n\t\tif(!strcasecmp(options[i].key, \"sync\")){\n\t\t\tif(!strcasecmp(options[i].value, \"extra\")){\n\t\t\t\tplg_data.synchronous = 3;\n\t\t\t}else if(!strcasecmp(options[i].value, \"full\")){\n\t\t\t\tplg_data.synchronous = 2;\n\t\t\t}else if(!strcasecmp(options[i].value, \"normal\")){\n\t\t\t\tplg_data.synchronous = 1;\n\t\t\t}else if(!strcasecmp(options[i].value, \"off\")){\n\t\t\t\tplg_data.synchronous = 0;\n\t\t\t}else{\n\t\t\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Sqlite persistence: Invalid plugin_opt_sync value '%s'.\", options[i].value);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}else if(!strcasecmp(options[i].key, \"flush_period\")){\n\t\t\trc = conf_parse_uint(options[i].value, \"flush_period\", &plg_data.flush_period, 0);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}else if(!strcasecmp(options[i].key, \"page_size\")){\n\t\t\trc = conf_parse_uint(options[i].value, \"page_size\", &plg_data.page_size, 1);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}\n\tif(plg_data.db_file == NULL){\n\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Warning: Sqlite persistence plugin has no plugin_opt_db_file defined. The plugin will not be activated.\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\trc = persist_sqlite__init(&plg_data);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tplg_id = identifier;\n\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_RESTORE, persist_sqlite__restore_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_BASE_MSG_ADD, persist_sqlite__base_msg_add_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_BASE_MSG_DELETE, persist_sqlite__base_msg_remove_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_RETAIN_MSG_SET, persist_sqlite__retain_msg_set_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_RETAIN_MSG_DELETE, persist_sqlite__retain_msg_remove_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_CLIENT_ADD, persist_sqlite__client_add_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_CLIENT_DELETE, persist_sqlite__client_remove_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_CLIENT_UPDATE, persist_sqlite__client_update_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_SUBSCRIPTION_ADD, persist_sqlite__subscription_add_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_SUBSCRIPTION_DELETE, persist_sqlite__subscription_remove_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_CLIENT_MSG_ADD, persist_sqlite__client_msg_add_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE, persist_sqlite__client_msg_remove_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE, persist_sqlite__client_msg_update_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_WILL_ADD, persist_sqlite__will_add_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_WILL_DELETE, persist_sqlite__will_remove_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\trc = mosquitto_callback_register(plg_id, MOSQ_EVT_TICK, persist_sqlite__tick_cb, NULL, &plg_data);\n\tif(rc){\n\t\tgoto fail;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nfail:\n\tif(rc == MOSQ_ERR_NOT_SUPPORTED){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Sqlite persistence: Unable to register plugin: broker doesn't support persistence plugins, please upgrade to 2.1 or higher\");\n\t}else if(rc == MOSQ_ERR_NOMEM){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Sqlite persistence: Unable to register plugin: out of memory\");\n\t}else{\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"Sqlite persistence: Unable to register plugin (%d)\", rc);\n\t}\n\tmosquitto_plugin_cleanup(NULL, NULL, 0);\n\treturn rc;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *options, int option_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(options);\n\tUNUSED(option_count);\n\n\tif(plg_id){\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_RESTORE, persist_sqlite__restore_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_BASE_MSG_ADD, persist_sqlite__base_msg_add_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_BASE_MSG_DELETE, persist_sqlite__base_msg_remove_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_RETAIN_MSG_DELETE, persist_sqlite__retain_msg_remove_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_CLIENT_ADD, persist_sqlite__client_add_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_CLIENT_DELETE, persist_sqlite__client_remove_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_SUBSCRIPTION_ADD, persist_sqlite__subscription_add_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_SUBSCRIPTION_DELETE, persist_sqlite__subscription_remove_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_CLIENT_MSG_ADD, persist_sqlite__client_msg_add_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE, persist_sqlite__client_msg_remove_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE, persist_sqlite__client_msg_update_cb, NULL);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_TICK, persist_sqlite__tick_cb, NULL);\n\t}\n\n\tmosquitto_free(plg_data.db_file);\n\tpersist_sqlite__cleanup(&plg_data);\n\tmemset(&plg_data, 0, sizeof(struct mosquitto_sqlite));\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/persist-sqlite/restore.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <stdlib.h>\n#include <string.h>\n#include <sqlite3.h>\n#include <cjson/cJSON.h>\n\n#include \"json_help.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto/broker.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"persist_sqlite.h\"\n\n\nstatic uint8_t hex2nibble(char c)\n{\n\tswitch(c){\n\t\tcase '0': return 0;\n\t\tcase '1': return 1;\n\t\tcase '2': return 2;\n\t\tcase '3': return 3;\n\t\tcase '4': return 4;\n\t\tcase '5': return 5;\n\t\tcase '6': return 6;\n\t\tcase '7': return 7;\n\t\tcase '8': return 8;\n\t\tcase '9': return 9;\n\t\tcase 'A': return 10;\n\t\tcase 'a': return 10;\n\t\tcase 'B': return 11;\n\t\tcase 'b': return 11;\n\t\tcase 'C': return 12;\n\t\tcase 'c': return 12;\n\t\tcase 'D': return 13;\n\t\tcase 'd': return 13;\n\t\tcase 'E': return 14;\n\t\tcase 'e': return 14;\n\t\tcase 'F': return 15;\n\t\tcase 'f': return 15;\n\t\tdefault: return 0;\n\t}\n}\n\n\nstatic mosquitto_property *json_to_properties(const char *json)\n{\n\tmosquitto_property *properties = NULL;\n\tcJSON *array, *obj, *j_value;\n\tint propid, proptype;\n\tsize_t slen;\n\n\tif(!json){\n\t\treturn NULL;\n\t}\n\n\tarray = cJSON_Parse(json);\n\tif(!array){\n\t\treturn NULL;\n\t}\n\tif(!cJSON_IsArray(array)){\n\t\tcJSON_Delete(array);\n\t\treturn NULL;\n\t}\n\n\tcJSON_ArrayForEach(obj, array){\n\t\tconst char *identifier;\n\n\t\tj_value = cJSON_GetObjectItem(obj, \"value\");\n\n\t\tif(json_get_string(obj, \"identifier\", &identifier, false) != MOSQ_ERR_SUCCESS\n\t\t\t\t|| !j_value){\n\n\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring property whilst restoring, invalid identifier / value\");\n\t\t\tcontinue;\n\t\t}\n\t\tif(mosquitto_string_to_property_info(identifier, &propid, &proptype)){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring property whilst restoring, unknown identifier\");\n\t\t\tcontinue;\n\t\t}\n\t\tswitch(proptype){\n\t\t\tcase MQTT_PROP_TYPE_BYTE:\n\t\t\t\tif(!cJSON_IsNumber(j_value)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring %s property whilst restoring, value is incorrect type\", \"byte\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(mosquitto_property_add_byte(&properties, propid, (uint8_t)j_value->valueint)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Out of memory whilst restoring %s property\", \"byte\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase MQTT_PROP_TYPE_INT16:\n\t\t\t\tif(!cJSON_IsNumber(j_value)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring %s property whilst restoring, value is incorrect type\", \"int16\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(mosquitto_property_add_int16(&properties, propid, (uint16_t)j_value->valueint)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Out of memory whilst restoring %s property\", \"int16\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase MQTT_PROP_TYPE_INT32:\n\t\t\t\tif(!cJSON_IsNumber(j_value)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring %s property whilst restoring, value is incorrect type\", \"int32\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(mosquitto_property_add_int32(&properties, propid, (uint32_t)j_value->valueint)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Out of memory whilst restoring %s property\", \"int32\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase MQTT_PROP_TYPE_VARINT:\n\t\t\t\tif(!cJSON_IsNumber(j_value)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring %s property whilst restoring, value is incorrect type\", \"varint\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(mosquitto_property_add_varint(&properties, propid, (uint32_t)j_value->valueint)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Out of memory whilst restoring %s property\", \"varint\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase MQTT_PROP_TYPE_BINARY:\n\t\t\t\tif(!cJSON_IsString(j_value)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring %s property whilst restoring, value is incorrect type\", \"binary\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tuint8_t *binstr = NULL;\n\t\t\t\tuint16_t len = 0;\n\n\t\t\t\tif(j_value->valuestring){\n\t\t\t\t\tslen = strlen(j_value->valuestring);\n\t\t\t\t\tif(slen/2 > UINT16_MAX){\n\t\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring %s property whilst restoring, value is too large\", \"binary\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor(size_t i=0; i<slen; i+=2){\n\t\t\t\t\t\t((uint8_t *)j_value->valuestring)[i/2] = (uint8_t)(hex2nibble(j_value->valuestring[i])<<4) + hex2nibble(j_value->valuestring[i+1]);\n\t\t\t\t\t}\n\t\t\t\t\tbinstr = (uint8_t *)j_value->valuestring;\n\t\t\t\t\tlen = (uint16_t)slen/2;\n\t\t\t\t}\n\t\t\t\tif(mosquitto_property_add_binary(&properties, propid, binstr, len)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Out of memory whilst restoring %s property\", \"binary\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase MQTT_PROP_TYPE_STRING:\n\t\t\t\tif(!cJSON_IsString(j_value)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring %s property whilst restoring, value is incorrect type\", \"string\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(mosquitto_property_add_string(&properties, propid, j_value->valuestring)){\n\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Out of memory whilst restoring %s property\", \"string\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase MQTT_PROP_TYPE_STRING_PAIR:\n\t\t\t\t{\n\t\t\t\t\tconst char *prop_name;\n\n\t\t\t\t\tif(!cJSON_IsString(j_value)){\n\t\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring %s property whilst restoring, value is incorrect type\", \"string pair\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif(json_get_string(obj, \"name\", &prop_name, false) != MOSQ_ERR_SUCCESS){\n\t\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Ignoring string pair property whilst restoring, name is missing or incorrect type\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif(mosquitto_property_add_string_pair(&properties, propid, prop_name, j_value->valuestring)){\n\t\t\t\t\t\tmosquitto_log_printf(MOSQ_LOG_WARNING, \"Sqlite persistence: Out of memory whilst restoring %s property\", \"string pair\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tcJSON_Delete(array);\n\n\treturn properties;\n}\n\n\nstatic int client_restore(struct mosquitto_sqlite *ms)\n{\n\tsqlite3_stmt *stmt;\n\tint rc;\n\tstruct mosquitto_client client;\n\tlong count = 0, failed = 0;\n\tconst char *str;\n\n\tmemset(&client, 0, sizeof(client));\n\n\trc = sqlite3_prepare_v2(ms->db,\n\t\t\t\"SELECT client_id,username,will_delay_time,session_expiry_time,\"\n\t\t\t\"listener_port,max_packet_size,max_qos,\"\n\t\t\t\"retain_available,session_expiry_interval,will_delay_interval \"\n\t\t\t\"FROM clients\",\n\t\t\t-1, &stmt, NULL);\n\n\tif(rc != SQLITE_OK){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"sqlite: Error restoring clients: %s\", sqlite3_errstr(rc));\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\n\twhile(sqlite3_step(stmt) == SQLITE_ROW){\n\t\tstr = (const char *)sqlite3_column_text(stmt, 0);\n\t\tif(str){\n\t\t\tclient.clientid = mosquitto_strdup(str);\n\t\t}\n\t\tstr = (const char *)sqlite3_column_text(stmt, 1);\n\t\tif(str){\n\t\t\tclient.username = mosquitto_strdup(str);\n\t\t}\n\t\tclient.will_delay_time = (time_t)sqlite3_column_int64(stmt, 2);\n\t\tclient.session_expiry_time = (time_t)sqlite3_column_int64(stmt, 3);\n\t\tclient.listener_port = (uint16_t)sqlite3_column_int(stmt, 4);\n\t\tclient.max_packet_size = (uint32_t)sqlite3_column_int(stmt, 5);\n\t\tclient.max_qos = (uint8_t)sqlite3_column_int(stmt, 6);\n\t\tclient.retain_available = (bool)sqlite3_column_int(stmt, 7);\n\t\tclient.session_expiry_interval = (uint32_t)sqlite3_column_int(stmt, 8);\n\t\tclient.will_delay_interval = (uint32_t)sqlite3_column_int(stmt, 9);\n\n\t\trc = mosquitto_persist_client_add(&client);\n\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\tcount++;\n\t\t}else{\n\t\t\tfailed++;\n\t\t}\n\t}\n\tsqlite3_finalize(stmt);\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"sqlite: Restored %ld clients (%ld failed)\", count, failed);\n\n\treturn rc;\n}\n\n\nstatic int subscription_restore(struct mosquitto_sqlite *ms)\n{\n\tsqlite3_stmt *stmt;\n\tstruct mosquitto_subscription sub;\n\tint rc;\n\tlong count = 0, failed = 0;\n\n\trc = sqlite3_prepare_v2(ms->db,\n\t\t\t\"SELECT client_id,topic,subscription_options,subscription_identifier \"\n\t\t\t\"FROM subscriptions\",\n\t\t\t-1, &stmt, NULL);\n\n\tif(rc != SQLITE_OK){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"sqlite: Error restoring subscriptions: %s\", sqlite3_errstr(rc));\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\twhile(sqlite3_step(stmt) == SQLITE_ROW){\n\t\tmemset(&sub, 0, sizeof(sub));\n\t\tsub.clientid = (char *)sqlite3_column_text(stmt, 0);\n\t\tsub.topic_filter = (char *)sqlite3_column_text(stmt, 1);\n\t\tsub.options = (uint8_t)sqlite3_column_int(stmt, 2);\n\t\tsub.identifier = (uint32_t)sqlite3_column_int(stmt, 3);\n\n\t\trc = mosquitto_subscription_add(&sub);\n\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\tcount++;\n\t\t}else{\n\t\t\tfailed++;\n\t\t}\n\t}\n\tsqlite3_finalize(stmt);\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"sqlite: Restored %ld subscriptions (%ld failed)\", count, failed);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int base_msg_restore(struct mosquitto_sqlite *ms)\n{\n\tsqlite3_stmt *stmt;\n\tstruct mosquitto_base_msg base_msg;\n\tint rc;\n\tlong count = 0, failed = 0;\n\tconst char *str;\n\tconst void *payload;\n\n\trc = sqlite3_prepare_v2(ms->db,\n\t\t\t\"SELECT store_id, expiry_time, topic, payload, source_id, source_username, payloadlen, source_mid, source_port, qos, retain, properties \"\n\t\t\t\"FROM base_msgs\",\n\t\t\t-1, &stmt, NULL);\n\n\tif(rc != SQLITE_OK){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"sqlite: Error restoring messages: %s\", sqlite3_errstr(rc));\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\twhile(sqlite3_step(stmt) == SQLITE_ROW){\n\t\tmemset(&base_msg, 0, sizeof(base_msg));\n\t\tbase_msg.store_id = (uint64_t)sqlite3_column_int64(stmt, 0);\n\t\tbase_msg.expiry_time = (time_t)sqlite3_column_int64(stmt, 1);\n\t\tstr = (const char *)sqlite3_column_text(stmt, 2);\n\t\tif(str){\n\t\t\tbase_msg.topic = mosquitto_strdup(str);\n\t\t\tif(!base_msg.topic){\n\t\t\t\tfailed++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tbase_msg.source_id = (char *)sqlite3_column_text(stmt, 4);\n\t\tbase_msg.source_username = (char *)sqlite3_column_text(stmt, 5);\n\t\tpayload = (const void *)sqlite3_column_blob(stmt, 3);\n\t\tbase_msg.payloadlen = (uint32_t)sqlite3_column_int(stmt, 6);\n\t\tif(payload && base_msg.payloadlen){\n\t\t\tbase_msg.payload = mosquitto_malloc(base_msg.payloadlen+1);\n\t\t\tif(!base_msg.payload){\n\t\t\t\tmosquitto_free(base_msg.topic);\n\t\t\t\tfailed++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmemcpy(base_msg.payload, payload, base_msg.payloadlen);\n\t\t\t((uint8_t *)base_msg.payload)[base_msg.payloadlen] = 0;\n\t\t}\n\n\t\tbase_msg.source_mid = (uint16_t)sqlite3_column_int(stmt, 7);\n\t\tbase_msg.source_port = (uint16_t)sqlite3_column_int(stmt, 8);\n\t\tbase_msg.qos = (uint8_t)sqlite3_column_int(stmt, 9);\n\t\tbase_msg.retain = sqlite3_column_int(stmt, 10);\n\t\tbase_msg.properties = json_to_properties((const char *)sqlite3_column_text(stmt, 11));\n\n\t\trc = mosquitto_persist_base_msg_add(&base_msg);\n\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\tcount++;\n\t\t}else{\n\t\t\tfailed++;\n\t\t}\n\t}\n\tsqlite3_finalize(stmt);\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"sqlite: Restored %ld base messages (%ld failed)\", count, failed);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int client_msg_restore(struct mosquitto_sqlite *ms)\n{\n\tsqlite3_stmt *stmt;\n\tstruct mosquitto_client_msg client_msg;\n\tint rc;\n\tlong count = 0, failed = 0;\n\n\trc = sqlite3_prepare_v2(ms->db,\n\t\t\t\"SELECT client_id, cmsg_id, store_id, dup, direction, mid, qos, retain, state, subscription_identifier \"\n\t\t\t\"FROM client_msgs ORDER BY rowid\",\n\t\t\t-1, &stmt, NULL);\n\n\tif(rc != SQLITE_OK){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"sqlite: Error restoring client messages: %s\", sqlite3_errstr(rc));\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tmemset(&client_msg, 0, sizeof(client_msg));\n\twhile(sqlite3_step(stmt) == SQLITE_ROW){\n\t\tclient_msg.clientid = (const char *)sqlite3_column_text(stmt, 0);\n\t\tclient_msg.cmsg_id = (uint64_t)sqlite3_column_int64(stmt, 1);\n\t\tclient_msg.store_id = (uint64_t)sqlite3_column_int64(stmt, 2);\n\t\tclient_msg.dup = (uint8_t)sqlite3_column_int(stmt, 3);\n\t\tclient_msg.direction = (uint8_t)sqlite3_column_int(stmt, 4);\n\t\tclient_msg.mid = (uint16_t)sqlite3_column_int(stmt, 5);\n\t\tclient_msg.qos = (uint8_t)sqlite3_column_int(stmt, 6);\n\t\tclient_msg.retain = sqlite3_column_int(stmt, 7);\n\t\tclient_msg.state = (uint8_t)sqlite3_column_int(stmt, 8);\n\t\tclient_msg.subscription_identifier = (uint32_t)sqlite3_column_int(stmt, 9);\n\n\t\trc = mosquitto_persist_client_msg_add(&client_msg);\n\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\tcount++;\n\t\t}else{\n\t\t\tfailed++;\n\t\t}\n\t}\n\tsqlite3_finalize(stmt);\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"sqlite: Restored %ld client messages (%ld failed)\", count, failed);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int retain_restore(struct mosquitto_sqlite *ms)\n{\n\tsqlite3_stmt *stmt;\n\tint rc;\n\tlong count = 0, failed = 0;\n\tconst char *topic;\n\tuint64_t store_id;\n\n\trc = sqlite3_prepare_v2(ms->db,\n\t\t\t\"SELECT topic, store_id \"\n\t\t\t\"FROM retains ORDER BY topic\",\n\t\t\t-1, &stmt, NULL);\n\n\tif(rc != SQLITE_OK){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"sqlite: Error restoring retained messages: %s\", sqlite3_errstr(rc));\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\twhile(sqlite3_step(stmt) == SQLITE_ROW){\n\t\ttopic = (const char *)sqlite3_column_text(stmt, 0);\n\t\tif(!topic){\n\t\t\tfailed++;\n\t\t\tcontinue;\n\t\t}\n\t\tstore_id = (uint64_t)sqlite3_column_int64(stmt, 1);\n\n\t\trc = mosquitto_persist_retain_msg_set(topic, store_id);\n\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\tcount++;\n\t\t}else{\n\t\t\tfailed++;\n\t\t}\n\t}\n\tsqlite3_finalize(stmt);\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"sqlite: Restored %ld retained messages (%ld failed)\", count, failed);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int publish_will_msg(const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties)\n{\n\tvoid *payload_mosq = NULL;\n\tint rc;\n\n\tif(payloadlen){\n\t\tpayload_mosq = mosquitto_malloc((size_t)payloadlen);\n\t\tif(!payload_mosq){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tmemcpy(payload_mosq, payload, (size_t)payloadlen);\n\t}\n\n\trc = mosquitto_broker_publish(NULL, topic, payloadlen, payload_mosq, qos, retain, properties);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_free(payload_mosq);\n\t}\n\treturn rc;\n}\n\n\nstatic int will_restore(struct mosquitto_sqlite *ms)\n{\n\tsqlite3_stmt *stmt;\n\tint rc;\n\tlong count = 0, failed = 0;\n\tconst char *clientid, *topic;\n\tconst void *payload;\n\tmosquitto_property *properties;\n\tint payloadlen, qos, retain;\n\n\trc = sqlite3_prepare_v2(ms->db,\n\t\t\t\"SELECT w.client_id,w.topic,w.payload,w.payloadlen,w.qos,w.retain,w.properties,\"\n\t\t\t\" c.session_expiry_time,c.will_delay_interval\"\n\t\t\t\" FROM wills w\"\n\t\t\t\" LEFT OUTER JOIN clients c ON c.client_id = w.client_id\",\n\t\t\t-1, &stmt, NULL);\n\n\tif(rc != SQLITE_OK){\n\t\tmosquitto_log_printf(MOSQ_LOG_ERR, \"sqlite: Error restoring will messages: %s\", sqlite3_errstr(rc));\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\twhile(sqlite3_step(stmt) == SQLITE_ROW){\n\t\tclientid = (const char *)sqlite3_column_text(stmt, 0);\n\t\ttopic = (const char *)sqlite3_column_text(stmt, 1);\n\t\tpayload = (const void *)sqlite3_column_blob(stmt, 2);\n\t\tpayloadlen = (int)sqlite3_column_int64(stmt, 3);\n\t\tqos = sqlite3_column_int(stmt, 4);\n\t\tretain = (bool)sqlite3_column_int(stmt, 5);\n\t\tproperties = json_to_properties((const char *)sqlite3_column_text(stmt, 6));\n\n\t\trc = mosquitto_client_will_set(clientid, topic, payloadlen, payload, qos, retain, properties);\n\t\tif(rc == MOSQ_ERR_NOT_FOUND){\n\t\t\t/* If the client does not exist this is the will message of a non-persistent client. */\n\t\t\trc = publish_will_msg(topic, payloadlen, payload, qos, retain, properties);\n\t\t}else if(rc == MOSQ_ERR_SUCCESS && (sqlite3_column_int64(stmt, 7) == 0 && sqlite3_column_int64(stmt, 8) == 0)){\n\t\t\t/* If the client is a persistent client and was connected at the moment of a crash\n\t\t\t\t and has no will delay we publish it's will message now, but need a new copy of the properties. */\n\t\t\tproperties = json_to_properties((const char *)sqlite3_column_text(stmt, 6));\n\t\t\trc = publish_will_msg(topic, payloadlen, payload, qos, retain, properties);\n\t\t}\n\n\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\tcount++;\n\t\t}else{\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\tfailed++;\n\t\t}\n\t}\n\tsqlite3_finalize(stmt);\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"sqlite: Restored %ld will messages (%ld failed)\", count, failed);\n\n\treturn rc;\n}\n\n\nint persist_sqlite__restore_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_sqlite *ms = userdata;\n\tUNUSED(event);\n\tUNUSED(event_data);\n\n\tif(base_msg_restore(ms)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tif(retain_restore(ms)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tif(client_restore(ms)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tif(subscription_restore(ms)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tif(client_msg_restore(ms)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tif(will_restore(ms)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "plugins/persist-sqlite/retain_msgs.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <string.h>\n#include <sqlite3.h>\n\n#include \"mosquitto.h\"\n#include \"mosquitto/broker.h\"\n#include \"persist_sqlite.h\"\n\n\nint persist_sqlite__retain_msg_set_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_retain_msg *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = MOSQ_ERR_UNKNOWN;\n\n\tUNUSED(event);\n\n\tif(sqlite3_bind_text(ms->retain_msg_set_stmt, 1, ed->topic, (int)strlen(ed->topic), SQLITE_STATIC) == SQLITE_OK\n\t\t\t&& sqlite3_bind_int64(ms->retain_msg_set_stmt, 2, (int64_t)ed->store_id) == SQLITE_OK\n\t\t\t){\n\n\t\tms->event_count++;\n\t\trc = sqlite3_step(ms->retain_msg_set_stmt);\n\t\tif(rc == SQLITE_DONE){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\tsqlite3_reset(ms->retain_msg_set_stmt);\n\n\treturn rc;\n}\n\n\nint persist_sqlite__retain_msg_remove_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_retain_msg *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = 1;\n\n\tUNUSED(event);\n\n\tif(sqlite3_bind_text(ms->retain_msg_remove_stmt, 1,\n\t\t\ted->topic, (int)strlen(ed->topic), SQLITE_STATIC) == SQLITE_OK){\n\n\t\tms->event_count++;\n\t\trc = sqlite3_step(ms->retain_msg_remove_stmt);\n\t\tif(rc == SQLITE_DONE){\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\tsqlite3_reset(ms->retain_msg_remove_stmt);\n\n\treturn rc;\n}\n"
  },
  {
    "path": "plugins/persist-sqlite/subscriptions.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <string.h>\n#include <sqlite3.h>\n\n#include \"mosquitto.h\"\n#include \"mosquitto/broker.h\"\n#include \"persist_sqlite.h\"\n\n\nint persist_sqlite__subscription_add_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_subscription *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = MOSQ_ERR_UNKNOWN;\n\n\tUNUSED(event);\n\n\tif(sqlite3_bind_text(ms->subscription_add_stmt, 1,\n\t\t\ted->data.clientid, (int)strlen(ed->data.clientid), SQLITE_STATIC) == SQLITE_OK){\n\n\t\tif(sqlite3_bind_text(ms->subscription_add_stmt, 2,\n\t\t\t\ted->data.topic_filter, (int)strlen(ed->data.topic_filter), SQLITE_STATIC) == SQLITE_OK){\n\n\t\t\tif(sqlite3_bind_int(ms->subscription_add_stmt, 3,\n\t\t\t\t\ted->data.options) == SQLITE_OK){\n\n\t\t\t\tif(sqlite3_bind_int(ms->subscription_add_stmt, 4,\n\t\t\t\t\t\t(int)ed->data.identifier) == SQLITE_OK){\n\n\t\t\t\t\tms->event_count++;\n\t\t\t\t\trc = sqlite3_step(ms->subscription_add_stmt);\n\t\t\t\t\tif(rc == SQLITE_DONE){\n\t\t\t\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t\t\t\t}else{\n\t\t\t\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tsqlite3_reset(ms->subscription_add_stmt);\n\n\treturn rc;\n}\n\n\nint persist_sqlite__subscription_remove_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_subscription *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = 1;\n\n\tUNUSED(event);\n\n\tif(sqlite3_bind_text(ms->subscription_remove_stmt, 1,\n\t\t\ted->data.clientid, (int)strlen(ed->data.clientid), SQLITE_STATIC) == SQLITE_OK){\n\n\t\tif(sqlite3_bind_text(ms->subscription_remove_stmt, 2,\n\t\t\t\ted->data.topic_filter, (int)strlen(ed->data.topic_filter), SQLITE_STATIC) == SQLITE_OK){\n\n\t\t\tms->event_count++;\n\t\t\trc = sqlite3_step(ms->subscription_remove_stmt);\n\t\t\tif(rc == SQLITE_DONE){\n\t\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t\t}else{\n\t\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t\t}\n\t\t}\n\t}\n\tsqlite3_reset(ms->subscription_remove_stmt);\n\n\treturn rc;\n}\n"
  },
  {
    "path": "plugins/persist-sqlite/test.conf",
    "content": "persistence_location .\nplugin ./mosquitto_persist_sqlite.so\nplugin_opt_page_size 4096\nplugin_opt_flush_period 5\n"
  },
  {
    "path": "plugins/persist-sqlite/test.sh",
    "content": "../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "plugins/persist-sqlite/tick.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include <string.h>\n#include <sqlite3.h>\n#include <stdlib.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto/broker.h\"\n#include \"persist_sqlite.h\"\n\n\nint persist_sqlite__tick_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_tick *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\n\tUNUSED(event);\n\n\tif(ms->event_count > 0){\n\t\tms->event_count = 0;\n\t\tsqlite3_exec(ms->db, \"END;\", NULL, NULL, NULL);\n\t\tsqlite3_exec(ms->db, \"BEGIN;\", NULL, NULL, NULL);\n\t}\n\n\ted->next_s = ms->flush_period;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/persist-sqlite/util.h",
    "content": "/*\nCopyright (c) 2025 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n*/\n\n#include <string.h>\n#include <sqlite3.h>\n\n\nstatic inline int sqlite3_bind_text_from_c_str(sqlite3_stmt *stmt, int col_index, const char *str)\n{\n\treturn sqlite3_bind_text(stmt, col_index, str, (int)strlen(str), SQLITE_STATIC);\n}\n\n\nstatic inline int sqlite3_bind_text_from_optional_c_str(sqlite3_stmt *stmt, int col_index, const char *str)\n{\n\treturn str\n\t\t? sqlite3_bind_text_from_c_str(stmt, col_index, str)\n\t\t: sqlite3_bind_null(stmt, col_index);\n}\n\n\nstatic inline int sqlite3_bind_blob_optional(sqlite3_stmt *stmt, int col_index, const void *ptr, int blob_len)\n{\n\treturn ptr\n\t\t?  sqlite3_bind_blob(stmt, col_index, ptr, blob_len, SQLITE_STATIC)\n\t\t: sqlite3_bind_null(stmt, col_index);\n}\n\n\nstatic inline int sqlite3_single_step_stmt(int rc, struct mosquitto_sqlite *ms, sqlite3_stmt *stmt)\n{\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\tms->event_count++;\n\treturn sqlite3_step(stmt) == SQLITE_DONE ? MOSQ_ERR_SUCCESS : MOSQ_ERR_UNKNOWN;\n}\n\n\nstatic inline char *properties_to_json_str(const mosquitto_property *properties)\n{\n\tcJSON *array;\n\tchar *json_str;\n\n\tarray = mosquitto_properties_to_json(properties);\n\tif(!array){\n\t\treturn NULL;\n\t}\n\n\tjson_str = cJSON_PrintUnformatted(array);\n\tcJSON_Delete(array);\n\treturn json_str;\n}\n\n\n"
  },
  {
    "path": "plugins/persist-sqlite/will.c",
    "content": "/*\nCopyright (c) 2025 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n*/\n\n#include <string.h>\n#include <sqlite3.h>\n\n#include \"mosquitto.h\"\n#include \"mosquitto/broker.h\"\n#include \"persist_sqlite.h\"\n#include \"util.h\"\n\n\nint persist_sqlite__will_add_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_will_msg *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = MOSQ_ERR_SUCCESS;\n\tchar *propties_json_str = NULL;\n\tUNUSED(event);\n\n\tif(!ed->data.clientid || !ed->data.topic){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(ed->data.properties){\n\t\tpropties_json_str = properties_to_json_str(ed->data.properties);\n\t\tif(!propties_json_str){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\tif(sqlite3_bind_text_from_c_str(ms->will_add_stmt, 1, ed->data.clientid) != SQLITE_OK\n\t\t\t|| sqlite3_bind_blob_optional(ms->will_add_stmt, 2, ed->data.payload, (int)ed->data.payloadlen) != SQLITE_OK\n\t\t\t|| sqlite3_bind_text_from_c_str(ms->will_add_stmt, 3, ed->data.topic) != SQLITE_OK\n\t\t\t|| sqlite3_bind_int64(ms->will_add_stmt, 4, (int64_t)ed->data.payloadlen) != SQLITE_OK\n\t\t\t|| sqlite3_bind_int(ms->will_add_stmt, 5, ed->data.qos) != SQLITE_OK\n\t\t\t|| sqlite3_bind_int(ms->will_add_stmt, 6, ed->data.retain) != SQLITE_OK\n\t\t\t|| sqlite3_bind_text_from_optional_c_str(ms->will_add_stmt, 7, propties_json_str) != SQLITE_OK){\n\t\trc = MOSQ_ERR_UNKNOWN;\n\t}\n\trc = sqlite3_single_step_stmt(rc, ms, ms->will_add_stmt);\n\tsqlite3_reset(ms->will_add_stmt);\n\tmosquitto_free(propties_json_str);\n\n\treturn rc;\n}\n\n\nint persist_sqlite__will_remove_cb(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_persist_will_msg *ed = event_data;\n\tstruct mosquitto_sqlite *ms = userdata;\n\tint rc = MOSQ_ERR_SUCCESS;\n\tUNUSED(event);\n\n\tif(sqlite3_bind_text_from_c_str(ms->will_remove_stmt, 1, ed->data.clientid) != SQLITE_OK){\n\t\trc = MOSQ_ERR_UNKNOWN;\n\t}\n\trc = sqlite3_single_step_stmt(rc, ms, ms->will_remove_stmt);\n\tsqlite3_reset(ms->will_remove_stmt);\n\n\treturn rc;\n}\n"
  },
  {
    "path": "plugins/plugin.mk",
    "content": ".PHONY : all binary check clean reallyclean test test-compile install uninstall\n\n\nLOCAL_CFLAGS+=-fPIC\nLOCAL_CPPFLAGS+=\nLOCAL_LIBADD+=\nLOCAL_LDFLAGS+=-fPIC -shared\n\nifeq ($(UNAME),AIX)\n\tLOCAL_LDFLAGS+=-Wl,-G\nendif\n\nbinary : ${PLUGIN_NAME}.so ${PLUGIN_NAME}.a\n\n${PLUGIN_NAME}.a : ${OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}$(AR) cr $@ $^\n\n${PLUGIN_NAME}.so : ${OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}${CC} $(LOCAL_LDFLAGS) $^ -o $@ ${LOCAL_LIBADD}\n\n${OBJS} : %.o: %.c ${EXTRA_DEPS}\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nreallyclean : clean\nclean:\n\t-rm -f *.o ${PLUGIN_NAME}.a ${PLUGIN_NAME}.so *.gcda *.gcno\n\ntest-compile:\n\ncheck: test\ntest: test-compile\n\nifeq ($(PLUGIN_NOINST),)\ninstall: all\n\t$(INSTALL) -d \"${DESTDIR}$(libdir)\"\n\t$(INSTALL) ${STRIP_OPTS} ${PLUGIN_NAME}.so \"${DESTDIR}${libdir}/${PLUGIN_NAME}.so\"\n\nuninstall :\n\t-rm -f \"${DESTDIR}${libdir}/${PLUGIN_NAME}.so\"\nendif\n"
  },
  {
    "path": "plugins/sparkplug-aware/CMakeLists.txt",
    "content": "set(PLUGIN_NAME mosquitto_sparkplug_aware)\n\nset(SRCLIST\n\ton_message.c\n\tplugin.c\n)\n\nset(INCLIST )\nset(LINKLIST )\n\nadd_mosquitto_plugin(\"${PLUGIN_NAME}\" \"${SRCLIST}\" \"${INCLIST}\" \"${LINKLIST}\")\n"
  },
  {
    "path": "plugins/sparkplug-aware/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\nPLUGIN_NAME=mosquitto_sparkplug_aware\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=\nLOCAL_LDFLAGS+=\nLOCAL_LDADD+=\n\n# Objects for this plugin only, built from source in this directory\nOBJS = \\\n\ton_message.o \\\n\tplugin.o\n\n# Objects from e.g. the common directory that are not in this directory\nOBJS_EXTERNAL =\n\nall : binary\n\ninclude ${R}/plugins/plugin.mk\n"
  },
  {
    "path": "plugins/sparkplug-aware/README.md",
    "content": "# Mosquitto Sparkplug Aware\n\nThis plugin implements the requirements of the Sparkplug 3.0 specification\n\"awareness\".\n\nIt is not tested with the official TCK, because that is only capable of testing\none brand of broker, and hence it is not possible to validate any other broker.\nSee https://github.com/eclipse-sparkplug/sparkplug/issues/478\n"
  },
  {
    "path": "plugins/sparkplug-aware/on_message.c",
    "content": "/*\nCopyright (c) 2023 Cedalo Gmbh\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto.h\"\n#include \"plugin_global.h\"\n\n\nint plugin__message_in_callback(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\tbool match;\n\n\tUNUSED(event);\n\tUNUSED(user_data);\n\n\tmosquitto_topic_matches_sub(\"spBv1.0/+/NBIRTH/+\", ed->topic, &match);\n\tif(!match){\n\t\tmosquitto_topic_matches_sub(\"spBv1.0/+/DBIRTH/+/+\", ed->topic, &match);\n\t}\n\tif(match){\n\t\tsize_t len = strlen(\"$sparkplug/certificates/\") + strlen(ed->topic) + 1;\n\t\tchar *topic = mosquitto_malloc(len);\n\t\tif(topic){\n\t\t\tsnprintf(topic, len, \"$sparkplug/certificates/%s\", ed->topic);\n\t\t\tmosquitto_broker_publish_copy(NULL, topic, (int)ed->payloadlen, ed->payload, ed->qos, true, ed->properties);\n\t\t\tmosquitto_free(topic);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/sparkplug-aware/plugin.c",
    "content": "/*\nCopyright (c) 2023 Cedalo Gmbh\n*/\n\n#include \"config.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"mosquitto/broker.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"plugin_global.h\"\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *mosq_pid = NULL;\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *options, int option_count)\n{\n\tint rc;\n\n\tUNUSED(user_data);\n\tUNUSED(options);\n\tUNUSED(option_count);\n\n\tmosq_pid = identifier;\n\tmosquitto_plugin_set_info(identifier, PLUGIN_NAME, PLUGIN_VERSION);\n\n\trc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE_IN, plugin__message_in_callback, NULL, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/sparkplug-aware/plugin_global.h",
    "content": "/*\nCopyright (c) 2023 Cedalo Gmbh\n*/\n#ifndef PLUGIN_GLOBAL_H\n#define PLUGIN_GLOBAL_H\n\n#include \"config.h\"\n\n/* PLUGIN_NAME and PLUGIN_VERSION reported to the broker */\n#define PLUGIN_NAME \"sparkplug-aware\"\n#define PLUGIN_VERSION \"1.0\"\n\nint plugin__message_in_callback(int event, void *event_data, void *user_data);\n\n#endif\n"
  },
  {
    "path": "plugins/sparkplug-aware/test.conf",
    "content": "listener 1883\nallow_anonymous true\nplugin ./mosquitto_sparkplug_aware.so\n"
  },
  {
    "path": "plugins/sparkplug-aware/test.sh",
    "content": "#VG=\"valgrind --log-file=vglog\"\n${VG} ../../src/mosquitto -c test.conf -v\n"
  },
  {
    "path": "pskfile.example",
    "content": "id:deadbeef\neasy:12345\n"
  },
  {
    "path": "pwfile.example",
    "content": "roger:$6$clQ4Ocu312S0qWgl$Cv2wUxgEN73c6C6jlBkswqR4AkHsvDLWvtEXZZ8NpsBLgP1WAo/qA+WXcmEN/mjDNgdUwcxRAveqNMs2xUVQYA==\nsub_client:$6$U+qg0/32F0g2Fh+n$fBPSkq/rfNyEQ/TkEjRgwGTTVBpvNhKSyGShovH9KHewsvJ731tD5Zx26IHhR5RYCICt0L9qBW0/KK31UkCliw==\npub_client:$6$vxQ89y+7WrsnL2yn$fSPMmEZn9TSrC8s/jaPmxJ9NijWpkP2e7bMJLz78JXR1vW2x8+T3FZ23byJA6xs5Mt+LeOybAHwcUv0OCl40rA==\n"
  },
  {
    "path": "run_tests.py",
    "content": "#!/usr/bin/env python3\n\nimport inspect\nimport os\nimport sys\n\nsys.path.insert(0, \"test\")\n\nimport ptest\nimport test.apps.ctrl.test as p_ctrl\nimport test.apps.db_dump.test as p_db_dump\nimport test.apps.passwd.test as p_passwd\nimport test.apps.signal.test as p_signal\nimport test.broker.test as p_broker\nimport test.client.test as p_client\nimport test.lib.test as p_lib\n\ntest = ptest.PTest()\ntest.add_tests(p_client.tests, \"test/client\")\ntest.add_tests(p_lib.tests, \"test/lib\")\ntest.add_tests(p_ctrl.tests, \"test/apps/ctrl\")\ntest.add_tests(p_db_dump.tests, \"test/apps/db_dump\")\ntest.add_tests(p_passwd.tests, \"test/apps/ctrl\")\ntest.add_tests(p_signal.tests, \"test/apps/signal\")\ntest.add_tests(p_broker.tests, \"test/broker\")\n\ntest.run()\n"
  },
  {
    "path": "security/mosquitto.apparmor",
    "content": "/usr/sbin/mosquitto {\n\t#include <abstractions/base>\n\t#include <abstractions/nameservice>\n\n\t/usr/sbin/mosquitto r,\n\t/etc/mosquitto/mosquitto.conf r,\n\t/etc/mosquitto/ca_certificates/* r,\n\t/etc/mosquitto/certs/* r,\n\t/etc/mosquitto/conf.d/* r,\n\t/var/lib/mosquitto/ r,\n\t/var/lib/mosquitto/mosquitto.db rwk,\n\t/var/lib/mosquitto/mosquitto.db.new rwk,\n\t/var/run/mosquitto.pid rw,\n\n\tnetwork inet stream,\n\tnetwork inet6 stream,\n\tnetwork inet dgram,\n\tnetwork inet6 dgram,\n\n\t# For drop privileges\n\tcapability setgid,\n\tcapability setuid,\n\n\t# For tcp-wrappers\n\t/lib{,32,64}/libwrap.so*  rm,\n\t/etc/hosts.allow r,\n\t/etc/hosts.deny r,\n}\n"
  },
  {
    "path": "service/monit/mosquitto.monit",
    "content": "check process mosquitto with pidfile /run/mosquitto.pid\n\tstart = \"/etc/init.d/mosquitto start\"\n\tstop = \"/etc/init.d/mosquitto stop\"\n\n"
  },
  {
    "path": "service/svscan/run",
    "content": "#!/bin/sh\n\n/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf\n"
  },
  {
    "path": "service/systemd/README",
    "content": "Select appropriate systemd service based on your compile settings. If you\nenabled WITH_SYSTEMD, use mosquitto.service.notify, otherwise use\nmosquitto.service.simple. The service must be renamed to mosquitto.service\nbefore usage. Don't forget to change default paths in service file if you\nchanged the default build settings.\n\nWith WITH_SYSTEMD mosquitto will notify a complete startup after\ninitialization. This means that follow-up units can be started after full\ninitialization of mosquitto (i.e. sockets are opened).\n"
  },
  {
    "path": "service/systemd/mosquitto.service.notify",
    "content": "[Unit]\nDescription=Mosquitto MQTT Broker\nDocumentation=man:mosquitto.conf(5) man:mosquitto(8)\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nUser=mosquitto\nType=notify\nWatchdogSec=3min\nNotifyAccess=main\nExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf\nExecReload=/bin/kill -HUP $MAINPID\nRestart=on-failure\nRuntimeDirectory=mosquitto\nLogsDirectory=mosquitto\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "service/systemd/mosquitto.service.simple",
    "content": "[Unit]\nDescription=Mosquitto MQTT Broker\nDocumentation=man:mosquitto.conf(5) man:mosquitto(8)\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nUser=mosquitto\nExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf\nExecReload=/bin/kill -HUP $MAINPID\nRestart=on-failure\nRuntimeDirectory=mosquitto\nLogsDirectory=mosquitto\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "service/upstart/mosquitto.conf",
    "content": "description \"Mosquitto MQTTv3.1 broker\"\nauthor \"Roger Light <roger@atchoo.org>\"\n\nstart on net-device-up\n\nrespawn\n\nexec /usr/local/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf\n"
  },
  {
    "path": "set-version.sh",
    "content": "#!/bin/sh\n\nMAJOR=2\nMINOR=1\nREVISION=2\n\nsed -i \"s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/\" config.mk\n\nsed -i \"s/^#define LIBMOSQUITTO_MAJOR .*/#define LIBMOSQUITTO_MAJOR ${MAJOR}/\" include/mosquitto.h\nsed -i \"s/^#define LIBMOSQUITTO_MINOR .*/#define LIBMOSQUITTO_MINOR ${MINOR}/\" include/mosquitto.h\nsed -i \"s/^#define LIBMOSQUITTO_REVISION .*/#define LIBMOSQUITTO_REVISION ${REVISION}/\" include/mosquitto.h\n\nsed -i \"s/^set (VERSION .*)/set (VERSION ${MAJOR}.${MINOR}.${REVISION})/\" CMakeLists.txt\n\nsed -i \"s/^!define VERSION .*/!define VERSION ${MAJOR}.${MINOR}.${REVISION}/\" installer/*.nsi\n\nsed -i \"s/^version: .*/version: ${MAJOR}.${MINOR}.${REVISION}/\" snap/snapcraft.yaml\n\nsed -i \"s/\\\"version-string\\\": \\\".*\\\",/\\\"version-string\\\": \\\"${MAJOR}.${MINOR}.${REVISION}\\\",/\" vcpkg.json\n"
  },
  {
    "path": "snap/local/default_config.conf",
    "content": "persistence false\nuser root\n"
  },
  {
    "path": "snap/local/launcher.sh",
    "content": "#!/bin/sh\n# Wrapper to check for custom config in $SNAP_USER_COMMON or $SNAP_COMMON and\n# use it otherwise fall back to the included basic config which will at least\n# allow mosquitto to run and do something.\n# This script will also copy the full example config in to SNAP_USER_COMMON or\n# SNAP_COMMON so that people can refer to it.\n#\n# The decision about whether to use SNAP_USER_COMMON or SNAP_COMMON is taken\n# based on the user that runs the command. If the user is root, it is assumed\n# that mosquitto is being run as a system daemon, and SNAP_COMMON will be used.\n# If a non-root user runs the command, then SNAP_USER_COMMON will be used.\n\ncase \"$SNAP_USER_COMMON\" in\n\t*/root/snap/mosquitto/common*) COMMON=$SNAP_COMMON ;;\n\t*)                             COMMON=$SNAP_USER_COMMON ;;\nesac\n\nCONFIG_FILE=\"$SNAP/default_config.conf\"\nCUSTOM_CONFIG=\"$COMMON/mosquitto.conf\"\n\n\n# Copy the example config if it doesn't exist\nif [ ! -e \"$COMMON/mosquitto_example.conf\" ]\nthen\n  echo \"Copying example config to $COMMON/mosquitto_example.conf\"\n  echo \"You can create a custom config by creating a file called $CUSTOM_CONFIG\"\n  cp $SNAP/mosquitto.conf $COMMON/mosquitto_example.conf\nfi\n\n\n# Does the custom config exist?  If so use it.\nif [ -e \"$CUSTOM_CONFIG\" ]\nthen\n  echo \"Found config in $CUSTOM_CONFIG\"\n  CONFIG_FILE=$CUSTOM_CONFIG\nelse\n  echo \"Using default config from $CONFIG_FILE\"\nfi\n\n# Launch the snap\n$SNAP/usr/sbin/mosquitto -c $CONFIG_FILE $@\n"
  },
  {
    "path": "snap/snapcraft.yaml",
    "content": "name: mosquitto\nversion: 2.1.2\nsummary: Eclipse Mosquitto MQTT broker\ndescription: This is a message broker that supports version 5.0, 3.1.1, and 3.1 of the MQTT protocol.\n    MQTT provides a method of carrying out messaging using a publish/subscribe\n    model. It is lightweight, both in terms of bandwidth usage and ease of\n    implementation. This makes it particularly useful at the edge of the network\n    where a sensor or other simple device may be implemented using a microcontroller for\n    example.\nconfinement: strict\ngrade: stable\nbase: core24\n\napps:\n  mosquitto:\n    command: launcher.sh\n    daemon: simple\n    restart-condition: always\n    plugs: [home, network, network-bind]\n\n  ctrl:\n    command: usr/bin/mosquitto_ctrl\n    plugs: [home, network]\n\n  pub:\n    command: usr/bin/mosquitto_pub\n    plugs: [home, network]\n\n  rr:\n    command: usr/bin/mosquitto_rr\n    plugs: [home, network]\n\n  signal:\n    command: usr/bin/mosquitto_signal\n    plugs: [home, network]\n\n  sub:\n    command: usr/bin/mosquitto_sub\n    plugs: [home, network]\n\n  passwd:\n    command: usr/bin/mosquitto_passwd\n    plugs: [home]\n\n\nparts:\n  script:\n    plugin: dump\n    source: snap/local/\n    prime:\n      - default_config.conf\n      - launcher.sh\n  config:\n    plugin: dump\n    source: .\n    prime:\n      - mosquitto.conf\n\n  dashboard:\n    plugin: dump\n    source: dashboard/src\n    organize:\n      '*': dashboard/\n\n  mosquitto:\n    plugin: cmake\n    cmake-parameters:\n      - -DCMAKE_INSTALL_PREFIX=/usr\n      - -DHTTP_API_DIR=\"/snap/mosquitto/current/dashboard/\"\n      - -DWITH_TESTS=OFF\n    source: https://github.com/eclipse-mosquitto/mosquitto\n    source-type: git\n\n    build-packages:\n      - docbook-xsl\n      - g++\n      - gcc\n      - libcjson-dev\n      - libedit-dev\n      - libmicrohttpd-dev\n      - libsqlite3-dev\n      - libssl-dev\n      - xsltproc\n    stage-packages:\n      - ca-certificates\n      - libcjson1\n      - libmicrohttpd12t64\n      - libssl3t64\n    prime:\n      - usr/sbin/mosquitto\n      - usr/bin/mosquitto_ctrl\n      - usr/bin/mosquitto_db_dump\n      - usr/bin/mosquitto_pub\n      - usr/bin/mosquitto_rr\n      - usr/bin/mosquitto_signal\n      - usr/bin/mosquitto_sub\n      - usr/bin/mosquitto_passwd\n      - usr/include/mosquitto.h\n      - usr/include/mosquitto/*.h\n      - usr/include/mosquitto_broker.h\n      - usr/include/mosquitto_plugin.h\n      - usr/include/mosquittopp.h\n      - usr/include/mqtt_protocol.h\n      - usr/lib/*-linux-gnu/libcjson.so*\n      - usr/lib/*-linux-gnu/libcrypto.so*\n      - usr/lib/*-linux-gnu/libmicrohttpd.so*\n      - usr/lib/*-linux-gnu/libmosquitto.so*\n      - usr/lib/*-linux-gnu/libmosquitto_common.so*\n      - usr/lib/*-linux-gnu/libmosquittopp.so*\n      - usr/lib/*-linux-gnu/libssl.so*\n      - usr/lib/*-linux-gnu/mosquitto_acl_file.so*\n      - usr/lib/*-linux-gnu/mosquitto_dynamic_security.so*\n      - usr/lib/*-linux-gnu/mosquitto_password_file.so*\n      - usr/lib/*-linux-gnu/mosquitto_persist_sqlite.so*\n      - usr/lib/*-linux-gnu/mosquitto_sparkplug_aware.so*\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "add_executable(mosquitto\n\tacl_file.c acl_file.h\n\t../plugins/acl-file/acl_check.c\n\t../plugins/acl-file/acl_parse.c\n\t../lib/alias_mosq.c ../lib/alias_mosq.h\n\tbridge.c bridge_topic.c\n\tbroker_control.c\n\tconf.c\n\tconf_includedir.c\n\tcontext.c\n\tcontrol.c\n\tcontrol_common.c\n\tdatabase.c\n\thandle_auth.c\n\thandle_connack.c\n\thandle_connect.c\n\thandle_disconnect.c\n\t../lib/handle_ping.c\n\t../lib/handle_pubackcomp.c\n\thandle_publish.c\n\t../lib/handle_pubrec.c\n\t../lib/handle_pubrel.c\n\t../lib/handle_suback.c\n\thandle_subscribe.c\n\t../lib/handle_unsuback.c\n\thandle_unsubscribe.c\n\thttp_serv.c\n\t../common/json_help.c ../common/json_help.h\n\tkeepalive.c\n\t../common/lib_load.h\n\tlisteners.c\n\tlogging.c\n\tloop.c\n\tmosquitto.c\n\t../include/mosquitto_broker.h mosquitto_broker_internal.h\n\tmux.c mux.h mux_epoll.c mux_kqueue.c mux_poll.c\n\tnet.c\n\t../lib/net_mosq_ocsp.c ../lib/net_mosq.c ../lib/net_mosq.h\n\t../lib/net_ws.c\n\t../lib/packet_datatypes.c\n\t../lib/packet_mosq.c ../lib/packet_mosq.h\n\tpassword_file.c password_file.h\n\t../plugins/password-file/password_check.c\n\t../plugins/password-file/password_parse.c\n\tpersist_read_v234.c persist_read_v5.c persist_read.c\n\tpersist_write_v5.c persist_write.c\n\tpersist.h\n\tplugin_callbacks.c plugin_v5.c plugin_v4.c plugin_v3.c plugin_v2.c\n\tplugin_init.c plugin_cleanup.c plugin_persist.c\n\tplugin_acl_check.c plugin_basic_auth.c plugin_connect.c plugin_disconnect.c\n\tplugin_client_offline.c\n\tplugin_extended_auth.c plugin_message.c plugin_psk_key.c plugin_public.c\n\tplugin_reload.c\n\tplugin_subscribe.c\n\tplugin_tick.c\n\tplugin_unsubscribe.c\n\tproperty_broker.c\n\tproxy_v1.c\n\tproxy_v2.c\n\t../lib/property_mosq.c ../lib/property_mosq.h\n\tpsk_file.c\n\tread_handle.c\n\t../lib/read_handle.h\n\tretain.c\n\tsecurity_default.c\n\t../lib/send_mosq.c ../lib/send_mosq.h\n\tsend_auth.c\n\tsend_connack.c\n\t../lib/send_connect.c\n\t../lib/send_disconnect.c\n\t../lib/send_publish.c\n\tsend_suback.c\n\tsignals.c\n\t../lib/send_subscribe.c\n\tsend_unsuback.c\n\t../lib/send_unsubscribe.c\n\tsession_expiry.c\n\tsubs.c\n\tsys_tree.c sys_tree.h\n\t../lib/tls_mosq.c\n\ttopic_tok.c\n\t../lib/util_mosq.c ../lib/util_mosq.h\n\twatchdog.c\n\twebsockets.c\n\twill_delay.c\n\t../lib/will_mosq.c ../lib/will_mosq.h\n)\n\nCHECK_INCLUDE_FILES(sys/event.h HAVE_SYS_EVENT_H)\nif(HAVE_SYS_EVENT_H)\n\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_KQUEUE\")\nendif()\n\nfind_path(HAVE_SYS_EPOLL_H sys/epoll.h)\nif(HAVE_SYS_EPOLL_H)\n\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_EPOLL\")\nendif()\n\noption(INC_BRIDGE_SUPPORT\n\t\"Include bridge support for connecting to other brokers?\" ON)\nif(INC_BRIDGE_SUPPORT)\n\ttarget_sources(mosquitto PRIVATE bridge.c)\n\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_BRIDGE\")\nendif()\n\noption(WITH_HTTP_API\n\t\"Include http_api support for listeners?\" ON)\nset(HTTP_API_DIR \"Default http_api directory\" CACHE STRING \"\")\nif(WITH_HTTP_API)\n\tif(WIN32)\n\t\tfind_library(MICROHTTPD_LIBRARY NAMES microhttpd-dll)\n\telse()\n\t\tfind_library(MICROHTTPD_LIBRARY NAMES microhttpd)\n\tendif()\n\tif(MICROHTTPD_LIBRARY)\n\t\ttarget_sources(mosquitto PRIVATE http_api.c)\n\t\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_HTTP_API\")\n\t\ttarget_link_libraries(mosquitto PRIVATE ${MICROHTTPD_LIBRARY})\n\t\tif(HTTP_API_DIR)\n\t\t\ttarget_compile_definitions(mosquitto PRIVATE \"HTTP_API_DIR=\\\"${HTTP_API_DIR}\\\"\")\n\t\tendif()\n\telse()\n\t\tmessage(WARNING \"microhttpd not found, disabling WITH_HTTP_API\")\n\tendif()\nendif()\n\n\noption(USE_LIBWRAP \"Include tcp-wrappers support?\" OFF)\n\nif(USE_LIBWRAP)\n\ttarget_link_libraries(mosquitto PRIVATE wrap)\n\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_WRAP\")\nendif()\n\noption(INC_DB_UPGRADE \"Include database upgrade support? (recommended)\" ON)\n\nif(INC_MEMTRACK)\n\ttarget_compile_definitions(mosquitto PUBLIC \"WITH_MEMORY_TRACKING\")\nendif()\n\noption(WITH_PERSISTENCE \"Include persistence support?\" ON)\nif(WITH_PERSISTENCE)\n\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_PERSISTENCE\")\nendif()\n\noption(WITH_SYS_TREE \"Include $SYS tree support?\" ON)\nif(WITH_SYS_TREE)\n\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_SYS_TREE\")\nendif()\n\noption(WITH_OLD_KEEPALIVE \"Use legacy keepalive check mechanism?\" OFF)\nif (WITH_OLD_KEEPALIVE)\n\tadd_definitions(\"-DWITH_OLD_KEEPALIVE\")\nendif (WITH_OLD_KEEPALIVE)\n\noption(WITH_ADNS \"Include ADNS support?\" OFF)\n\nif(CMAKE_SYSTEM_NAME STREQUAL Linux)\n\toption(WITH_SYSTEMD \"Include systemd support?\" OFF)\n\tif(WITH_SYSTEMD)\n\t\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_SYSTEMD\")\n\t\tfind_library(SYSTEMD_LIBRARY systemd)\n\t\ttarget_link_libraries(mosquitto PRIVATE ${SYSTEMD_LIBRARY})\n\tendif()\nendif()\n\noption(STATIC_WEBSOCKETS \"Use the static libwebsockets library?\" OFF)\n\noption(WITH_CONTROL \"Include $CONTROL topic support?\" ON)\nif(WITH_CONTROL)\n\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_CONTROL\")\nendif()\n\nif(WIN32 OR CYGWIN)\n\ttarget_sources(mosquitto PRIVATE service.c)\nendif()\n\ntarget_compile_definitions(mosquitto PRIVATE \"WITH_BROKER\")\n\nif(WITH_TLS)\n\ttarget_link_libraries(mosquitto PRIVATE OpenSSL::SSL)\nendif()\n\n# Check for getaddrinfo_a\n# Prior to glibc 2.34 this required linking with anl\nif(WITH_ADNS)\n\tcmake_push_check_state()\n\tset(ANL_CODE \"\n\t\t#define _GNU_SOURCE\n\t\t#include <stdlib.h>\n\t\t#include <netdb.h>\n\t\tint main(){\n\t\t\treturn getaddrinfo_a(0, NULL, 0, NULL);\n\t\t}\n\t\")\n\tcheck_source_compiles(C \"${ANL_CODE}\" HAVE_GETADDRINFO_A)\n\tif(HAVE_GETADDRINFO_A)\n\t\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_ADNS\")\n\telse()\n\t\tset(CMAKE_REQUIRED_LIBRARIES \"anl\")\n\t\tcheck_source_compiles(C \"${ANL_CODE}\" HAVE_GETADDRINFO_A_LIBANL)\n\n\t\tif(HAVE_GETADDRINFO_A_LIBANL)\n\t\t\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_ADNS\")\n\t\t\ttarget_link_libraries(mosquitto PRIVATE anl)\n\t\telse()\n\t\t\tmessage(FATAL_ERROR \"WITH_ADNS is set, but getaddrinfo_a() is not available.\")\n\t\tendif()\n\tendif()\n\tcmake_pop_check_state()\nendif()\n\n\nif(UNIX)\n\tif(APPLE)\n\t\ttarget_link_libraries(mosquitto PRIVATE dl m)\n\telseif (${CMAKE_SYSTEM_NAME} MATCHES \"OpenBSD\")\n\t\ttarget_link_libraries(mosquitto PRIVATE m)\n\telseif (${CMAKE_SYSTEM_NAME} MATCHES \"NetBSD\")\n\t\ttarget_link_libraries(mosquitto PRIVATE m)\n\telseif (${CMAKE_SYSTEM_NAME} MATCHES \"Haiku\")\n\t\ttarget_link_libraries(mosquitto PRIVATE m network)\n\telseif(QNX)\n\t\ttarget_link_libraries(mosquitto PRIVATE m socket)\n\telse()\n\t\ttarget_link_libraries(mosquitto PRIVATE dl m)\n\tendif()\nendif()\n\nif(WIN32)\n\ttarget_link_libraries(mosquitto PRIVATE ws2_32)\nendif()\n\nif(WITH_WEBSOCKETS)\n\tif(WITH_WEBSOCKETS_BUILTIN)\n\t\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_WEBSOCKETS=WS_IS_BUILTIN\")\n\t\ttarget_sources(mosquitto PRIVATE ${mosquitto_SOURCE_DIR}/deps/picohttpparser/picohttpparser.c)\n\telse()\n\t\tfind_package(libwebsockets)\n\t\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_WEBSOCKETS=WS_IS_LWS\")\n\tendif()\nendif()\n\nif (ANDROID)\n\ttarget_link_libraries(mosquitto PRIVATE log)\nendif (ANDROID)\n\nif(WITH_WEBSOCKETS)\n\tif(WITH_WEBSOCKETS_BUILTIN)\n\t\ttarget_include_directories(mosquitto PRIVATE\n\t\t\t\"${mosquitto_SOURCE_DIR}/deps/picohttpparser\")\n\telse()\n\t\tif(STATIC_WEBSOCKETS)\n\t\t\ttarget_link_libraries(mosquitto PRIVATE websockets)\n\t\t\tif(WIN32)\n\t\t\t\ttarget_link_libraries(mosquitto PRIVATE iphlpapi)\n\t\t\tendif()\n\t\telse(STATIC_WEBSOCKETS)\n\t\t\ttarget_link_libraries(mosquitto PRIVATE websockets_shared)\n\t\tendif()\n\tendif()\nendif()\n\nif(WITH_DLT)\n\tmessage(STATUS \"DLT_LIBDIR = ${DLT_LIBDIR}\")\n\ttarget_link_directories(mosquitto PRIVATE ${DLT_LIBDIR})\n\ttarget_link_libraries(mosquitto PRIVATE ${DLT_LIBRARIES})\n\ttarget_compile_definitions(mosquitto PRIVATE \"WITH_DLT\")\nendif()\n\ntarget_link_libraries(mosquitto PRIVATE cJSON)\n\ntarget_include_directories(mosquitto\n\tPUBLIC\n\t\t\"${mosquitto_SOURCE_DIR}/include\"\n\tPRIVATE\n\t\t\"${mosquitto_SOURCE_DIR}/common\"\n\t\t\"${mosquitto_SOURCE_DIR}/lib\"\n\t\t\"${mosquitto_SOURCE_DIR}/libcommon\"\n\t\t\"${mosquitto_SOURCE_DIR}/src\"\n\t\t\"${CJSON_INCLUDE_DIRS}\"\n)\n\ntarget_link_libraries(mosquitto\n\tPUBLIC\n\t\tlibmosquitto_common\n\tPRIVATE\n\t\tcommon-options\n\t\t${MOSQ_LIBS}\n)\n\nif (WITH_THREADING AND NOT WIN32)\n\tset(THREADS_PREFER_PTHREAD_FLAG ON)\n\tfind_package(Threads REQUIRED)\n\n\ttarget_link_libraries(mosquitto PRIVATE Threads::Threads)\nendif()\n\nset_target_properties(mosquitto PROPERTIES\n\tENABLE_EXPORTS 1\n\tARCHIVE_OUTPUT_NAME \"mosquitto_broker\"\n)\n\nif(UNIX)\n\tif(APPLE)\n\t\tset_target_properties(mosquitto PROPERTIES\n\t\t\tLINK_FLAGS \"-Wl,-exported_symbols_list -Wl,${mosquitto_SOURCE_DIR}/src/linker-macosx.syms\"\n\t\t)\n\telseif (AIX)\n\telse()\n\t\tset_target_properties(mosquitto PROPERTIES\n\t\t\tLINK_FLAGS \"-Wl,-dynamic-list=${mosquitto_SOURCE_DIR}/src/linker.syms\"\n\t\t)\n\tendif()\nendif()\n\ninstall(TARGETS mosquitto\n\tRUNTIME DESTINATION \"${CMAKE_INSTALL_SBINDIR}\"\n)\n"
  },
  {
    "path": "src/Makefile",
    "content": "R=..\ninclude ${R}/config.mk\n\n.PHONY: all install uninstall clean reallyclean\n\nLOCAL_CFLAGS+=\nLOCAL_CPPFLAGS+=-DWITH_BROKER -I${R}/lib -I${R}/libcommon\nLOCAL_LDFLAGS+=\nLOCAL_LDADD+=-lcjson -lm ${LIBMOSQ_COMMON}\n\n# ------------------------------------------\n#  Platform specific\n# ------------------------------------------\nifneq ($(or $(findstring $(UNAME),AIX), $(findstring $(UNAME),FreeBSD), $(findstring $(UNAME),OpenBSD), $(findstring $(UNAME),NetBSD)),)\n\tLOCAL_LDFLAGS+=-Wl,--dynamic-list=linker.syms\nelse\n\tLOCAL_LDADD+=-ldl\nendif\n\nifeq ($(UNAME),AIX)\n\tLOCAL_LDFLAGS+=-Wl,-bE:linker-aix.syms\nendif\n\nifeq ($(UNAME),Linux)\n\tLOCAL_LDADD+=\n\tLOCAL_LDFLAGS+=-Wl,--dynamic-list=linker.syms\nendif\n\nifeq ($(UNAME),QNX)\n\tLOCAL_LDADD+= -lsocket\nendif\n\n# ------------------------------------------\n#  Compile time options\n# ------------------------------------------\ninclude ${R}/make/broker.mk\n\n# ------------------------------------------\n#  Targets\n# ------------------------------------------\nifeq ($(WITH_FUZZING),yes)\nall : mosquitto_broker.a\nelse\nall : mosquitto\nendif\n\nOBJS=\tmosquitto.o \\\n\t\tacl_file.o \\\n\t\tbridge.o \\\n\t\tbridge_topic.o \\\n\t\tbroker_control.o \\\n\t\tconf.o \\\n\t\tconf_includedir.o \\\n\t\tcontext.o \\\n\t\tcontrol.o \\\n\t\tcontrol_common.o \\\n\t\tdatabase.o \\\n\t\thandle_auth.o \\\n\t\thandle_connack.o \\\n\t\thandle_connect.o \\\n\t\thandle_disconnect.o \\\n\t\thandle_publish.o \\\n\t\thandle_subscribe.o \\\n\t\thandle_unsubscribe.o \\\n\t\thttp_api.o \\\n\t\thttp_serv.o \\\n\t\tkeepalive.o \\\n\t\tlisteners.o \\\n\t\tlogging.o \\\n\t\tloop.o \\\n\t\tmux.o \\\n\t\tmux_epoll.o \\\n\t\tmux_kqueue.o \\\n\t\tmux_poll.o \\\n\t\tnet.o \\\n\t\tpassword_file.o \\\n\t\tproperty_broker.o \\\n\t\tpersist_read.o \\\n\t\tpersist_read_v234.o \\\n\t\tpersist_read_v5.o \\\n\t\tpersist_write.o \\\n\t\tpersist_write_v5.o \\\n\t\tplugin_callbacks.o \\\n\t\tplugin_v2.o \\\n\t\tplugin_v3.o \\\n\t\tplugin_v4.o \\\n\t\tplugin_v5.o \\\n\t\tplugin_acl_check.o \\\n\t\tplugin_basic_auth.o \\\n\t\tplugin_cleanup.o \\\n\t\tplugin_client_offline.o \\\n\t\tplugin_connect.o \\\n\t\tplugin_disconnect.o \\\n\t\tplugin_extended_auth.o \\\n\t\tplugin_init.o \\\n\t\tplugin_message.o \\\n\t\tplugin_persist.o \\\n\t\tplugin_psk_key.o \\\n\t\tplugin_public.o \\\n\t\tplugin_reload.o \\\n\t\tplugin_subscribe.o \\\n\t\tplugin_tick.o \\\n\t\tplugin_unsubscribe.o \\\n\t\tproxy_v1.o \\\n\t\tproxy_v2.o \\\n\t\tpsk_file.o \\\n\t\tread_handle.o \\\n\t\tretain.o \\\n\t\tsecurity_default.o \\\n\t\tsend_auth.o \\\n\t\tsend_connack.o \\\n\t\tsend_suback.o \\\n\t\tsend_unsuback.o \\\n\t\tservice.o \\\n\t\tsession_expiry.o \\\n\t\tsignals.o \\\n\t\tsubs.o \\\n\t\tsys_tree.o \\\n\t\ttopic_tok.o \\\n\t\twatchdog.o \\\n\t\twebsockets.o \\\n\t\twill_delay.o \\\n\t\txtreport.o\n\nOBJS_EXTERNAL= \\\n\t\tacl_check.o \\\n\t\tacl_parse.o \\\n\t\talias_mosq.o \\\n\t\thandle_ping.o \\\n\t\thandle_pubackcomp.o \\\n\t\thandle_pubrec.o \\\n\t\thandle_pubrel.o \\\n\t\thandle_suback.o \\\n\t\thandle_unsuback.o \\\n\t\tjson_help.o \\\n\t\tnet_mosq.o \\\n\t\tnet_mosq_ocsp.o \\\n\t\tnet_ws.o \\\n\t\tpacket_datatypes.o \\\n\t\tpacket_mosq.o \\\n\t\tpassword_check.o \\\n\t\tpassword_parse.o \\\n\t\tproperty_mosq.o \\\n\t\tsend_connect.o \\\n\t\tsend_disconnect.o \\\n\t\tsend_mosq.o \\\n\t\tsend_publish.o \\\n\t\tsend_subscribe.o \\\n\t\tsend_unsubscribe.o \\\n\t\ttls_mosq.o \\\n\t\tutil_mosq.o \\\n\t\twill_mosq.o\n\nifeq ($(WITH_WEBSOCKETS),yes)\n\tOBJS_EXTERNAL+=${R}/deps/picohttpparser/picohttpparser.o\nendif\n\nmosquitto : ${OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}${CC} ${LOCAL_LDFLAGS} $^ -o $@ $(LOCAL_LDADD)\n\nmosquitto_broker.a : ${OBJS} ${OBJS_EXTERNAL}\n\t${CROSS_COMPILE}$(AR) cr $@ $^\n\n${OBJS} : %.o: %.c mosquitto_broker_internal.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nacl_check.o : ${R}/plugins/acl-file/acl_check.c acl_file.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nacl_parse.o : ${R}/plugins/acl-file/acl_parse.c acl_file.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nalias_mosq.o : ${R}/lib/alias_mosq.c ${R}/lib/alias_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nhandle_ping.o : ${R}/lib/handle_ping.c ${R}/lib/read_handle.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nhandle_pubackcomp.o : ${R}/lib/handle_pubackcomp.c ${R}/lib/read_handle.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nhandle_pubrec.o : ${R}/lib/handle_pubrec.c ${R}/lib/read_handle.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nhandle_pubrel.o : ${R}/lib/handle_pubrel.c ${R}/lib/read_handle.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nhandle_suback.o : ${R}/lib/handle_suback.c ${R}/lib/read_handle.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nhandle_unsuback.o : ${R}/lib/handle_unsuback.c ${R}/lib/read_handle.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\njson_help.o : ${R}/common/json_help.c ${R}/common/json_help.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nnet_mosq_ocsp.o : ${R}/lib/net_mosq_ocsp.c ${R}/lib/net_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nnet_mosq.o : ${R}/lib/net_mosq.c ${R}/lib/net_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nnet_ws.o : ${R}/lib/net_ws.c ${R}/lib/net_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\npacket_datatypes.o : ${R}/lib/packet_datatypes.c ${R}/lib/packet_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\npacket_mosq.o : ${R}/lib/packet_mosq.c ${R}/lib/packet_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\npassword_check.o : ${R}/plugins/password-file/password_check.c password_file.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\npassword_parse.o : ${R}/plugins/password-file/password_parse.c password_file.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n${R}/deps/picohttpparser/picohttpparser.o : ${R}/deps/picohttpparser/picohttpparser.c ${R}/deps/picohttpparser/picohttpparser.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nproperty_mosq.o : ${R}/lib/property_mosq.c ${R}/lib/property_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nsend_connect.o : ${R}/lib/send_connect.c ${R}/lib/send_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nsend_disconnect.o : ${R}/lib/send_disconnect.c ${R}/lib/send_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nsend_mosq.o : ${R}/lib/send_mosq.c ${R}/lib/send_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nsend_publish.o : ${R}/lib/send_publish.c ${R}/lib/send_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nsend_subscribe.o : ${R}/lib/send_subscribe.c ${R}/lib/send_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nsend_unsubscribe.o : ${R}/lib/send_unsubscribe.c ${R}/lib/send_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\ntls_mosq.o : ${R}/lib/tls_mosq.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nutil_mosq.o : ${R}/lib/util_mosq.c ${R}/lib/util_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nwill_mosq.o : ${R}/lib/will_mosq.c ${R}/lib/will_mosq.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\ninstall : all\n\t$(INSTALL) -d \"${DESTDIR}$(prefix)/sbin\"\n\t$(INSTALL) ${STRIP_OPTS} mosquitto \"${DESTDIR}${prefix}/sbin/mosquitto\"\n\nuninstall :\n\t-rm -f \"${DESTDIR}${prefix}/sbin/mosquitto\"\n\t-rm -f \"${DESTDIR}${prefix}/include/mosquitto_broker.h\"\n\t-rm -f \"${DESTDIR}${prefix}/include/mosquitto_plugin.h\"\n\nclean :\n\t-rm -f ${OBJS} ${OBJS_EXTERNAL} mosquitto mosquitto_broker.a *.gcda *.gcno\n\nreallyclean : clean\n\t-rm -rf *.orig *.db\n"
  },
  {
    "path": "src/acl_file.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <ctype.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"acl_file.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint broker_acl_file__init(void)\n{\n\tint rc;\n\n\t/* Load acl data if required. */\n\tif(db.config->per_listener_settings){\n\t\tfor(int i=0; i<db.config->listener_count; i++){\n\t\t\tif(db.config->listeners[i].security_options->acl_data.acl_file){\n\t\t\t\trc = acl_file__parse(&db.config->listeners[i].security_options->acl_data);\n\t\t\t\tif(rc){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error opening acl file \\\"%s\\\".\", db.config->listeners[i].security_options->acl_data.acl_file);\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t\tif(db.config->listeners[i].security_options->plugin_count == 0){\n\t\t\t\t\tconfig__plugin_add_secopt(db.config->listeners[i].security_options->pid, db.config->listeners[i].security_options);\n\t\t\t\t}\n\n\t\t\t\tmosquitto_callback_register(db.config->listeners[i].security_options->pid,\n\t\t\t\t\t\tMOSQ_EVT_ACL_CHECK, acl_file__check, NULL, &db.config->listeners[i].security_options->acl_data);\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(db.config->security_options.acl_data.acl_file){\n\t\t\trc = acl_file__parse(&db.config->security_options.acl_data);\n\t\t\tif(rc){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error opening acl file \\\"%s\\\".\", db.config->security_options.acl_data.acl_file);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tif(db.config->security_options.plugin_count == 0){\n\t\t\t\tconfig__plugin_add_secopt(db.config->security_options.pid, &db.config->security_options);\n\t\t\t}\n\n\t\t\tmosquitto_callback_register(db.config->security_options.pid,\n\t\t\t\t\tMOSQ_EVT_ACL_CHECK, acl_file__check, NULL, &db.config->security_options.acl_data);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid broker_acl_file__cleanup(void)\n{\n\tif(db.config->per_listener_settings){\n\t\tfor(int i=0; i<db.config->listener_count; i++){\n\t\t\tif(db.config->listeners[i].security_options->pid){\n\t\t\t\tmosquitto_callback_unregister(db.config->listeners[i].security_options->pid,\n\t\t\t\t\t\tMOSQ_EVT_ACL_CHECK, acl_file__check, NULL);\n\n\t\t\t\tacl_file__cleanup(&db.config->listeners[i].security_options->acl_data);\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(db.config->security_options.pid){\n\t\t\tmosquitto_callback_unregister(db.config->security_options.pid,\n\t\t\t\t\tMOSQ_EVT_ACL_CHECK, acl_file__check, NULL);\n\n\t\t\tacl_file__cleanup(&db.config->security_options.acl_data);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/acl_file.h",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef ACL_FILE_H\n#define ACL_FILE_H\n\n#include <uthash.h>\n\nstruct acl__entry {\n\tstruct acl__entry *next, *prev;\n\tchar *topic;\n\tint access;\n\tint ucount;\n\tint ccount;\n};\n\n\nstruct acl__user {\n\tUT_hash_handle hh;\n\tchar *username;\n\tstruct acl__entry *acl;\n};\n\n\nstruct acl_file_data {\n\tchar *acl_file;\n\tstruct acl__user *acl_users;\n\tstruct acl__user acl_anon;\n\tstruct acl__entry *acl_patterns;\n};\n\n\nint acl_file__parse(struct acl_file_data *data);\nint acl_file__check(int event, void *event_data, void *userdata);\nint acl_file__reload(int event, void *event_data, void *userdata);\nvoid acl_file__cleanup(struct acl_file_data *data);\n\n#endif\n"
  },
  {
    "path": "src/bridge.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifndef WIN32\n#include <netdb.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#else\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#endif\n\n#ifndef WIN32\n#include <unistd.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#endif\n\n#include \"mosquitto.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto_internal.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_common.h\"\n#include \"send_mosq.h\"\n#include \"sys_tree.h\"\n#include \"tls_mosq.h\"\n#include \"util_mosq.h\"\n#include \"will_mosq.h\"\n#include \"utlist.h\"\n\n#ifdef WITH_BRIDGE\n\nstatic void bridge__update_backoff(struct mosquitto__bridge *bridge);\n#if defined(__GLIBC__) && defined(WITH_ADNS)\nstatic int bridge__connect_step1(struct mosquitto *context);\nstatic int bridge__connect_step2(struct mosquitto *context);\n#endif\nstatic void bridge__packet_cleanup(struct mosquitto *context);\n\nstatic struct mosquitto *bridge__new(struct mosquitto__bridge *bridge)\n{\n\tstruct mosquitto *new_context = NULL;\n\tstruct mosquitto **bridges;\n\tchar *local_id;\n\n\tassert(bridge);\n\n\tlocal_id = mosquitto_strdup(bridge->local_clientid);\n\tif(!local_id){\n\t\treturn NULL;\n\t}\n\n\tHASH_FIND(hh_id, db.contexts_by_id, local_id, strlen(local_id), new_context);\n\tif(new_context){\n\t\t/* (possible from persistent db) */\n\t\tmosquitto_FREE(local_id);\n\t}else{\n\t\t/* id wasn't found, so generate a new context */\n\t\tnew_context = context__init();\n\t\tif(!new_context){\n\t\t\tmosquitto_FREE(local_id);\n\t\t\treturn NULL;\n\t\t}\n\t\tnew_context->id = local_id;\n\t\tcontext__add_to_by_id(new_context);\n\t}\n\tnew_context->transport = mosq_t_tcp;\n\tnew_context->bridge = bridge;\n\tnew_context->is_bridge = true;\n\n\tnew_context->username = bridge->remote_username;\n\tnew_context->password = bridge->remote_password;\n\n#ifdef WITH_TLS\n\tnew_context->tls_cafile = bridge->tls_cafile;\n\tnew_context->tls_capath = bridge->tls_capath;\n\tnew_context->tls_certfile = bridge->tls_certfile;\n\tnew_context->tls_keyfile = bridge->tls_keyfile;\n\tnew_context->tls_cert_reqs = SSL_VERIFY_PEER;\n\tnew_context->tls_ocsp_required = bridge->tls_ocsp_required;\n\tnew_context->tls_version = bridge->tls_version;\n\tnew_context->tls_insecure = bridge->tls_insecure;\n\tnew_context->tls_alpn = bridge->tls_alpn;\n\tnew_context->tls_ciphers = bridge->tls_ciphers;\n\tnew_context->tls_13_ciphers = bridge->tls_13_ciphers;\n\tnew_context->tls_engine = NULL;\n\tnew_context->tls_keyform = mosq_k_pem;\n\tnew_context->tls_use_os_certs = bridge->tls_use_os_certs;\n\tnew_context->ssl_ctx_defaults = true;\n#ifdef FINAL_WITH_TLS_PSK\n\tnew_context->tls_psk_identity = bridge->tls_psk_identity;\n\tnew_context->tls_psk = bridge->tls_psk;\n#endif\n#endif\n\n\tbridge->try_private_accepted = true;\n\tif(bridge->clean_start_local == -1){\n\t\t/* default to \"regular\" clean start setting */\n\t\tbridge->clean_start_local = bridge->clean_start;\n\t}\n\tnew_context->retain_available = bridge->outgoing_retain;\n\tnew_context->protocol = bridge->protocol_version;\n\tif(!bridge->clean_start_local){\n\t\tnew_context->session_expiry_interval = MQTT_SESSION_EXPIRY_NEVER;\n\t\tplugin_persist__handle_client_add(new_context);\n\t\tif(new_context->expiry_list_item){\n\t\t\t/* We've restored from persistence and been added to the session\n\t\t\t * expiry list, even though we should never be expired */\n\t\t\tsession_expiry__remove(new_context);\n\t\t}\n\t}\n\n\tbridges = mosquitto_realloc(db.bridges, (size_t)(db.bridge_count+1)*sizeof(struct mosquitto *));\n\tif(bridges){\n\t\tdb.bridges = bridges;\n\t\tdb.bridge_count++;\n\t\tdb.bridges[db.bridge_count-1] = new_context;\n\t}else{\n\t\treturn NULL;\n\t}\n\n\treturn new_context;\n}\n\n\nstatic void bridge__destroy(struct mosquitto *context)\n{\n\tsend__disconnect(context, MQTT_RC_SUCCESS, NULL);\n\tcontext__cleanup(context, true);\n}\n\n\nvoid bridge__start_all(void)\n{\n\tfor(int i=0; i<db.config->bridge_count; i++){\n\t\tstruct mosquitto *context;\n\t\tint ret;\n\n\t\tcontext = bridge__new(db.config->bridges[i]);\n\t\tif(!context){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\treturn;\n\t\t}\n\t\tassert(context);\n\n#if defined(__GLIBC__) && defined(WITH_ADNS)\n\t\tcontext->bridge->restart_t = 1; /* force quick restart of bridge */\n\t\tloop__update_next_event(1000);\n\t\tret = bridge__connect_step1(context);\n#else\n\t\tret = bridge__connect(context);\n#endif\n\n\t\tif(ret && ret != MOSQ_ERR_CONN_PENDING){\n\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Unable to connect bridge %s.\",\n\t\t\t\t\tcontext->bridge->name);\n\t\t}\n\n\t\tdb.config->bridges[i] = NULL;\n\t}\n}\n\n\nstatic int bridge__set_tcp_keepalive(struct mosquitto *context)\n{\n\tunsigned int enabled = 1;\n\tbool ret;\n\n\tif(context->bridge->tcp_keepalive_idle == 0\n\t\t\t|| context->bridge->tcp_keepalive_interval == 0\n\t\t\t|| context->bridge->tcp_keepalive_counter == 0){\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n#ifdef WIN32\n#  define SETSOCKOPT_TYPE char *\n#else\n#  define SETSOCKOPT_TYPE const void *\n#endif\n\n\tret = setsockopt(context->sock, SOL_SOCKET, SO_KEEPALIVE, (SETSOCKOPT_TYPE)&enabled, sizeof(enabled));\n\n#ifdef TCP_KEEPIDLE\n\tret = ret || setsockopt(context->sock, IPPROTO_TCP, TCP_KEEPIDLE,\n\t\t\t(SETSOCKOPT_TYPE)&context->bridge->tcp_keepalive_idle, sizeof(context->bridge->tcp_keepalive_idle));\n#endif\n#ifdef TCP_KEEPINTVL\n\tret = ret || setsockopt(context->sock, IPPROTO_TCP, TCP_KEEPINTVL,\n\t\t\t(SETSOCKOPT_TYPE)&context->bridge->tcp_keepalive_interval, sizeof(context->bridge->tcp_keepalive_interval));\n#endif\n#ifdef TCP_KEEPCNT\n\tret = ret || setsockopt(context->sock, IPPROTO_TCP, TCP_KEEPCNT,\n\t\t\t(SETSOCKOPT_TYPE)&context->bridge->tcp_keepalive_counter, sizeof(context->bridge->tcp_keepalive_counter));\n#endif\n\n#undef SETSOCKOPT_TYPE\n\n\tif(ret){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n#ifdef WITH_TCP_USER_TIMEOUT\n\n\nstatic int bridge__set_tcp_user_timeout(struct mosquitto *context)\n{\n\tint timeout = context->bridge->tcp_user_timeout;\n\tif(timeout >= 0){\n\t\tif(setsockopt(context->sock, IPPROTO_TCP, TCP_USER_TIMEOUT, (char *)&timeout, sizeof(timeout))){\n\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n#if defined(__GLIBC__) && defined(WITH_ADNS)\n\n\nstatic int bridge__connect_step1(struct mosquitto *context)\n{\n\tint rc;\n\tchar *notification_topic;\n\tsize_t notification_topic_len;\n\tuint8_t notification_payload;\n\tstruct mosquitto__bridge_topic *cur_topic;\n\tuint8_t qos;\n\n\tif(!context || !context->bridge){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmosquitto__set_state(context, mosq_cs_new);\n\tcontext->sock = INVALID_SOCKET;\n\tcontext->last_msg_in = db.now_s;\n\tcontext->next_msg_out = db.now_s + context->bridge->keepalive;\n\tcontext->keepalive = context->bridge->keepalive;\n\tcontext->clean_start = context->bridge->clean_start;\n\tcontext->in_packet.payload = NULL;\n\tcontext->ping_t = 0;\n\tcontext->bridge->lazy_reconnect = false;\n\tcontext->maximum_packet_size = context->bridge->maximum_packet_size;\n\tbridge__packet_cleanup(context);\n\tdb__message_reconnect_reset(context);\n\n\tdb__messages_delete(context, false);\n\n\t/* Delete all local subscriptions even for clean_start==false. We don't\n\t * remove any messages and the next loop carries out the resubscription\n\t * anyway. This means any unwanted subs will be removed.\n\t */\n\tsub__clean_session(context);\n\n\tLL_FOREACH(context->bridge->topics, cur_topic){\n\t\tif(cur_topic->direction == bd_out || cur_topic->direction == bd_both){\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Bridge %s doing local SUBSCRIBE on topic %s\", context->id, cur_topic->local_topic);\n\t\t\tif(cur_topic->qos > context->max_qos){\n\t\t\t\tqos = context->max_qos;\n\t\t\t}else{\n\t\t\t\tqos = cur_topic->qos;\n\t\t\t}\n\t\t\tstruct mosquitto_subscription sub;\n\t\t\tsub.topic_filter = cur_topic->local_topic;\n\t\t\tsub.identifier = 0;\n\t\t\tsub.options = MQTT_SUB_OPT_NO_LOCAL | MQTT_SUB_OPT_RETAIN_AS_PUBLISHED | qos;\n\t\t\tif(sub__add(context, &sub) > 0){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tretain__queue(context, &sub);\n\t\t}\n\t}\n\n\tif(context->bridge->notifications){\n\t\tif(context->max_qos == 0){\n\t\t\tqos = 0;\n\t\t}else{\n\t\t\tqos = 1;\n\t\t}\n\t\tif(context->bridge->notification_topic){\n\t\t\tif(!context->bridge->initial_notification_done){\n\t\t\t\tnotification_payload = '0';\n\t\t\t\tdb__messages_easy_queue(context, context->bridge->notification_topic, qos, 1, &notification_payload, 1, MSG_EXPIRY_INFINITE, NULL);\n\t\t\t\tcontext->bridge->initial_notification_done = true;\n\t\t\t}\n\t\t\tnotification_payload = '0';\n\t\t\trc = will__set(context, context->bridge->notification_topic, 1, &notification_payload, qos, true, NULL);\n\t\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}else{\n\t\t\tnotification_topic_len = strlen(context->bridge->remote_clientid)+strlen(\"$SYS/broker/connection//state\");\n\t\t\tnotification_topic = mosquitto_malloc(sizeof(char)*(notification_topic_len+1));\n\t\t\tif(!notification_topic){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\n\t\t\tsnprintf(notification_topic, notification_topic_len+1, \"$SYS/broker/connection/%s/state\", context->bridge->remote_clientid);\n\n\t\t\tif(!context->bridge->initial_notification_done){\n\t\t\t\tnotification_payload = '0';\n\t\t\t\tdb__messages_easy_queue(context, notification_topic, qos, 1, &notification_payload, 1, MSG_EXPIRY_INFINITE, NULL);\n\t\t\t\tcontext->bridge->initial_notification_done = true;\n\t\t\t}\n\n\t\t\tnotification_payload = '0';\n\t\t\trc = will__set(context, notification_topic, 1, &notification_payload, qos, true, NULL);\n\t\t\tmosquitto_FREE(notification_topic);\n\t\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}\n\n\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connecting bridge (step 1) %s (%s:%d)\", context->bridge->name, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port);\n\trc = net__try_connect_step1(context, context->bridge->addresses[context->bridge->cur_address].address);\n\tif(rc > 0){\n\t\tif(rc == MOSQ_ERR_TLS){\n\t\t\tmux__delete(context);\n\t\t\tnet__socket_close(context);\n\t\t\treturn rc; /* Error already printed */\n\t\t}else if(rc == MOSQ_ERR_ERRNO){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", strerror(errno));\n\t\t}else if(rc == MOSQ_ERR_EAI){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", gai_strerror(errno));\n\t\t}\n\n\t\treturn rc;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int bridge__connect_step2(struct mosquitto *context)\n{\n\tint rc;\n\n\tif(!context || !context->bridge){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connecting bridge (step 2) %s (%s:%d)\", context->bridge->name, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port);\n\trc = net__try_connect_step2(context, context->bridge->addresses[context->bridge->cur_address].port, &context->sock);\n\tif(rc > 0){\n\t\tif(rc == MOSQ_ERR_TLS){\n\t\t\tmux__delete(context);\n\t\t\tnet__socket_close(context);\n\t\t\treturn rc; /* Error already printed */\n\t\t}else if(rc == MOSQ_ERR_ERRNO){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", strerror(errno));\n\t\t}else if(rc == MOSQ_ERR_EAI){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", gai_strerror(errno));\n\t\t}\n\n\t\treturn rc;\n\t}\n\n\tHASH_ADD(hh_sock, db.contexts_by_sock, sock, sizeof(context->sock), context);\n\n\tif(rc == MOSQ_ERR_CONN_PENDING){\n\t\tmosquitto__set_state(context, mosq_cs_connect_pending);\n\t\tmux__add_out(context);\n\t}\n\treturn rc;\n}\n\n\nint bridge__connect_step3(struct mosquitto *context)\n{\n\tint rc;\n\tmosquitto_property receive_maximum;\n\tmosquitto_property session_expiry_interval;\n\tmosquitto_property topic_alias_max;\n\tmosquitto_property *properties = NULL;\n\n\trc = net__socket_connect_step3(context, context->bridge->addresses[context->bridge->cur_address].address);\n\tif(rc > 0){\n\t\tif(rc == MOSQ_ERR_TLS){\n\t\t\tmux__delete(context);\n\t\t\tnet__socket_close(context);\n\t\t\treturn rc; /* Error already printed */\n\t\t}else if(rc == MOSQ_ERR_ERRNO){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", strerror(errno));\n\t\t}else if(rc == MOSQ_ERR_EAI){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", gai_strerror(errno));\n\t\t}\n\n\t\treturn rc;\n\t}\n\n\tif(context->bridge->round_robin == false && context->bridge->cur_address != 0){\n\t\tcontext->bridge->primary_retry = db.now_s + 5;\n\t\tloop__update_next_event(5000);\n\t}\n\n\tif(bridge__set_tcp_keepalive(context) != MOSQ_ERR_SUCCESS){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n#ifdef WITH_TCP_USER_TIMEOUT\n\tif(bridge__set_tcp_user_timeout(context)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n#endif\n\n\tif(context->bridge->receive_maximum != 0){\n\t\treceive_maximum.value.i16 = context->bridge->receive_maximum;\n\t\treceive_maximum.identifier = MQTT_PROP_RECEIVE_MAXIMUM;\n\t\treceive_maximum.property_type = MQTT_PROP_TYPE_INT16;\n\t\treceive_maximum.client_generated = false;\n\t\treceive_maximum.next = properties;\n\t\tproperties = &receive_maximum;\n\t}\n\tif(context->bridge->session_expiry_interval != MQTT_SESSION_EXPIRY_IMMEDIATE){\n\t\tsession_expiry_interval.value.i32 = context->bridge->session_expiry_interval;\n\t\tsession_expiry_interval.identifier = MQTT_PROP_SESSION_EXPIRY_INTERVAL;\n\t\tsession_expiry_interval.property_type = MQTT_PROP_TYPE_INT32;\n\t\tsession_expiry_interval.client_generated = false;\n\t\tsession_expiry_interval.next = properties;\n\t\tproperties = &session_expiry_interval;\n\t}\n\tif(context->bridge->max_topic_alias != 0){\n\t\ttopic_alias_max.value.i16 = context->bridge->max_topic_alias;\n\t\ttopic_alias_max.identifier = MQTT_PROP_TOPIC_ALIAS_MAXIMUM;\n\t\ttopic_alias_max.property_type = MQTT_PROP_TYPE_INT16;\n\t\ttopic_alias_max.client_generated = false;\n\t\ttopic_alias_max.next = properties;\n\t\tproperties = &topic_alias_max;\n\t}\n\n\trc = send__connect(context, context->keepalive, context->clean_start, properties);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(rc == MOSQ_ERR_ERRNO && errno == ENOTCONN){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tif(rc == MOSQ_ERR_TLS){\n\t\t\treturn rc; /* Error already printed */\n\t\t}else if(rc == MOSQ_ERR_ERRNO){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", strerror(errno));\n\t\t}else if(rc == MOSQ_ERR_EAI){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", gai_strerror(errno));\n\t\t}\n\t\tmux__delete(context);\n\t\tnet__socket_close(context);\n\t\treturn rc;\n\t}\n}\n#else\n\n\nint bridge__connect(struct mosquitto *context)\n{\n\tint rc, rc2;\n\tchar *notification_topic = NULL;\n\tsize_t notification_topic_len;\n\tuint8_t notification_payload;\n\tstruct mosquitto__bridge_topic *cur_topic;\n\tuint8_t qos;\n\n\tmosquitto_property receive_maximum;\n\tmosquitto_property session_expiry_interval;\n\tmosquitto_property topic_alias_max;\n\tmosquitto_property *properties = NULL;\n\n\tif(!context || !context->bridge){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmosquitto__set_state(context, mosq_cs_new);\n\tcontext->sock = INVALID_SOCKET;\n\t/* coverity[missing_lock] - broker is single threaded, so no lock required */\n\tcontext->last_msg_in = db.now_s;\n\t/* coverity[missing_lock] - broker is single threaded, so no lock required */\n\tcontext->next_msg_out = db.now_s + context->bridge->keepalive;\n\tcontext->keepalive = context->bridge->keepalive;\n\tcontext->clean_start = context->bridge->clean_start;\n\tcontext->in_packet.payload = NULL;\n\tcontext->ping_t = 0;\n\tcontext->bridge->lazy_reconnect = false;\n\tcontext->maximum_packet_size = context->bridge->maximum_packet_size;\n\tbridge__packet_cleanup(context);\n\tdb__message_reconnect_reset(context);\n\n\tdb__messages_delete(context, false);\n\n\t/* Delete all local subscriptions even for clean_start==false. We don't\n\t * remove any messages and the next loop carries out the resubscription\n\t * anyway. This means any unwanted subs will be removed.\n\t */\n\tsub__clean_session(context);\n\n\tLL_FOREACH(context->bridge->topics, cur_topic){\n\t\tif(cur_topic->direction == bd_out || cur_topic->direction == bd_both){\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Bridge %s doing local SUBSCRIBE on topic %s\", context->id, cur_topic->local_topic);\n\t\t\tif(cur_topic->qos > context->max_qos){\n\t\t\t\tqos = context->max_qos;\n\t\t\t}else{\n\t\t\t\tqos = cur_topic->qos;\n\t\t\t}\n\t\t\tstruct mosquitto_subscription sub;\n\t\t\tsub.topic_filter = cur_topic->local_topic;\n\t\t\tsub.identifier = 0;\n\t\t\tsub.options = MQTT_SUB_OPT_NO_LOCAL | MQTT_SUB_OPT_RETAIN_AS_PUBLISHED | qos;\n\t\t\tif(sub__add(context, &sub) > 0){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(context->bridge->notifications){\n\t\tif(context->max_qos == 0){\n\t\t\tqos = 0;\n\t\t}else{\n\t\t\tqos = 1;\n\t\t}\n\t\tif(context->bridge->notification_topic){\n\t\t\tif(!context->bridge->initial_notification_done){\n\t\t\t\tnotification_payload = '0';\n\t\t\t\tdb__messages_easy_queue(context, context->bridge->notification_topic, qos, 1, &notification_payload, 1, MSG_EXPIRY_INFINITE, NULL);\n\t\t\t\tcontext->bridge->initial_notification_done = true;\n\t\t\t}\n\n\t\t\tnotification_payload = '0';\n\t\t\trc = will__set(context, context->bridge->notification_topic, 1, &notification_payload, qos, true, NULL);\n\t\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}else{\n\t\t\tnotification_topic_len = strlen(context->bridge->remote_clientid)+strlen(\"$SYS/broker/connection//state\");\n\t\t\tnotification_topic = mosquitto_malloc(sizeof(char)*(notification_topic_len+1));\n\t\t\tif(!notification_topic){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\n\t\t\tsnprintf(notification_topic, notification_topic_len+1, \"$SYS/broker/connection/%s/state\", context->bridge->remote_clientid);\n\n\t\t\tif(!context->bridge->initial_notification_done){\n\t\t\t\tnotification_payload = '0';\n\t\t\t\tdb__messages_easy_queue(context, notification_topic, qos, 1, &notification_payload, 1, MSG_EXPIRY_INFINITE, NULL);\n\t\t\t\tcontext->bridge->initial_notification_done = true;\n\t\t\t}\n\n\t\t\tnotification_payload = '0';\n\t\t\trc = will__set(context, notification_topic, 1, &notification_payload, qos, true, NULL);\n\t\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\t\tmosquitto_FREE(notification_topic);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tmosquitto_FREE(notification_topic);\n\t\t}\n\t}\n\n\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connecting bridge %s (%s:%d)\", context->bridge->name, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port);\n\trc = net__socket_connect(context,\n\t\t\tcontext->bridge->addresses[context->bridge->cur_address].address,\n\t\t\tcontext->bridge->addresses[context->bridge->cur_address].port,\n\t\t\tcontext->bridge->bind_address,\n\t\t\tfalse);\n\n\tif(rc > 0){\n\t\tif(rc == MOSQ_ERR_TLS){\n\t\t\tmux__delete(context);\n\t\t\tnet__socket_close(context);\n\t\t\treturn rc; /* Error already printed */\n\t\t}else if(rc == MOSQ_ERR_ERRNO){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", strerror(errno));\n\t\t}else if(rc == MOSQ_ERR_EAI){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", gai_strerror(errno));\n\t\t}\n\n\t\treturn rc;\n\t}else if(rc == MOSQ_ERR_CONN_PENDING){\n\t\tmosquitto__set_state(context, mosq_cs_connect_pending);\n\t\tmux__add_out(context);\n\t}\n\n\tHASH_ADD(hh_sock, db.contexts_by_sock, sock, sizeof(context->sock), context);\n\n\tif(bridge__set_tcp_keepalive(context) != MOSQ_ERR_SUCCESS){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n#ifdef WITH_TCP_USER_TIMEOUT\n\tif(bridge__set_tcp_user_timeout(context)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n#endif\n\n\tif(context->bridge->receive_maximum != 0){\n\t\treceive_maximum.value.i16 = context->bridge->receive_maximum;\n\t\treceive_maximum.identifier = MQTT_PROP_RECEIVE_MAXIMUM;\n\t\treceive_maximum.property_type = MQTT_PROP_TYPE_INT16;\n\t\treceive_maximum.client_generated = false;\n\t\treceive_maximum.next = properties;\n\t\tproperties = &receive_maximum;\n\t}\n\tif(context->bridge->session_expiry_interval != MQTT_SESSION_EXPIRY_IMMEDIATE){\n\t\tsession_expiry_interval.value.i32 = context->bridge->session_expiry_interval;\n\t\tsession_expiry_interval.identifier = MQTT_PROP_SESSION_EXPIRY_INTERVAL;\n\t\tsession_expiry_interval.property_type = MQTT_PROP_TYPE_INT32;\n\t\tsession_expiry_interval.client_generated = false;\n\t\tsession_expiry_interval.next = properties;\n\t\tproperties = &session_expiry_interval;\n\t}\n\tif(context->bridge->max_topic_alias != 0){\n\t\ttopic_alias_max.value.i16 = context->bridge->max_topic_alias;\n\t\ttopic_alias_max.identifier = MQTT_PROP_TOPIC_ALIAS_MAXIMUM;\n\t\ttopic_alias_max.property_type = MQTT_PROP_TYPE_INT16;\n\t\ttopic_alias_max.client_generated = false;\n\t\ttopic_alias_max.next = properties;\n\t\tproperties = &topic_alias_max;\n\t}\n\n\trc2 = send__connect(context, context->keepalive, context->clean_start, properties);\n\tif(rc2 == MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}else if(rc2 == MOSQ_ERR_ERRNO && errno == ENOTCONN){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tif(rc2 == MOSQ_ERR_TLS){\n\t\t\treturn rc2; /* Error already printed */\n\t\t}else if(rc2 == MOSQ_ERR_ERRNO){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", strerror(errno));\n\t\t}else if(rc2 == MOSQ_ERR_EAI){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating bridge: %s.\", gai_strerror(errno));\n\t\t}\n\t\tmux__delete(context);\n\t\tnet__socket_close(context);\n\t\treturn rc2;\n\t}\n}\n#endif\n\n\nint bridge__on_connect(struct mosquitto *context)\n{\n\tchar *notification_topic;\n\tsize_t notification_topic_len;\n\tstruct mosquitto__bridge_topic *cur_topic;\n\tint sub_opts;\n\tbool retain = true;\n\tuint8_t qos;\n\n\tif(context->bridge->notifications){\n\t\tif(context->max_qos == 0){\n\t\t\tqos = 0;\n\t\t}else{\n\t\t\tqos = 1;\n\t\t}\n\t\tif(!context->retain_available){\n\t\t\tretain = false;\n\t\t}\n\t\tchar notification_payload = '1';\n\t\tif(context->bridge->notification_topic){\n\t\t\tif(!context->bridge->notifications_local_only){\n\t\t\t\tif(send__real_publish(context, mosquitto__mid_generate(context),\n\t\t\t\t\t\tcontext->bridge->notification_topic, 1, &notification_payload, qos, retain, 0, 0, NULL, 0)){\n\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tdb__messages_easy_queue(context, context->bridge->notification_topic, qos, 1, &notification_payload, 1, MSG_EXPIRY_INFINITE, NULL);\n\t\t}else{\n\t\t\tnotification_topic_len = strlen(context->bridge->remote_clientid)+strlen(\"$SYS/broker/connection//state\");\n\t\t\tnotification_topic = mosquitto_malloc(sizeof(char)*(notification_topic_len+1));\n\t\t\tif(!notification_topic){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\n\t\t\tsnprintf(notification_topic, notification_topic_len+1, \"$SYS/broker/connection/%s/state\", context->bridge->remote_clientid);\n\t\t\tnotification_payload = '1';\n\t\t\tif(!context->bridge->notifications_local_only){\n\t\t\t\tif(send__real_publish(context, mosquitto__mid_generate(context),\n\t\t\t\t\t\tnotification_topic, 1, &notification_payload, qos, retain, 0, 0, NULL, 0)){\n\n\t\t\t\t\tmosquitto_FREE(notification_topic);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tdb__messages_easy_queue(context, notification_topic, qos, 1, &notification_payload, 1, MSG_EXPIRY_INFINITE, NULL);\n\t\t\tmosquitto_FREE(notification_topic);\n\t\t}\n\t}\n\n\tLL_FOREACH(context->bridge->topics, cur_topic){\n\t\tif(cur_topic->direction == bd_in || cur_topic->direction == bd_both){\n\t\t\tif(cur_topic->qos > context->max_qos){\n\t\t\t\tsub_opts = context->max_qos;\n\t\t\t}else{\n\t\t\t\tsub_opts = cur_topic->qos;\n\t\t\t}\n\t\t\tif(context->bridge->protocol_version == mosq_p_mqtt5){\n\t\t\t\tsub_opts = sub_opts\n\t\t\t\t\t\t| MQTT_SUB_OPT_NO_LOCAL\n\t\t\t\t\t\t| MQTT_SUB_OPT_RETAIN_AS_PUBLISHED\n\t\t\t\t\t\t| MQTT_SUB_OPT_SEND_RETAIN_ALWAYS;\n\t\t\t}\n\t\t\tif(send__subscribe(context, NULL, 1, &cur_topic->remote_topic, sub_opts, NULL)){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}else{\n\t\t\tif(context->bridge->attempt_unsubscribe){\n\t\t\t\tif(send__unsubscribe(context, NULL, 1, &cur_topic->remote_topic, NULL)){\n\t\t\t\t\t/* direction = inwards only. This means we should not be subscribed\n\t\t\t\t\t* to the topic. It is possible that we used to be subscribed to\n\t\t\t\t\t* this topic so unsubscribe. */\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tstruct mosquitto_subscription sub;\n\tmemset(&sub, 0, sizeof(sub));\n\tLL_FOREACH(context->bridge->topics, cur_topic){\n\t\tsub.topic_filter = cur_topic->local_topic;\n\t\tif(cur_topic->direction == bd_out || cur_topic->direction == bd_both){\n\t\t\tif(cur_topic->qos > context->max_qos){\n\t\t\t\tsub.options = context->max_qos;\n\t\t\t}else{\n\t\t\t\tsub.options = cur_topic->qos;\n\t\t\t}\n\t\t\tretain__queue(context, &sub);\n\t\t}\n\t}\n\n\tcontext->bridge->connected_at = db.now_s;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint bridge__register_local_connections(void)\n{\n\tstruct mosquitto *context, *ctxt_tmp = NULL;\n\n\tHASH_ITER(hh_sock, db.contexts_by_sock, context, ctxt_tmp){\n\t\tif(context->bridge){\n\t\t\tif(mux__new(context)){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in initial bridge registration: %s\", strerror(errno));\n\t\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t\t}\n\t\t\tmux__add_out(context);\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid bridge__reload(void)\n{\n\tint i;\n\tint j;\n\n\t// destroy old bridges that dissappeared\n\tfor(i=0; i<db.bridge_count; i++){\n\t\tfor(j=0; j<db.config->bridge_count; j++){\n\t\t\tif(!strcmp(db.bridges[i]->bridge->name, db.config->bridges[j]->name)){\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif(j==db.config->bridge_count){\n\t\t\tbridge__destroy(db.bridges[i]);\n\t\t}\n\t}\n\n\tfor(i=0; i<db.config->bridge_count; i++){\n\t\tfor(j=0; j<db.bridge_count; j++){\n\t\t\tif(!strcmp(db.config->bridges[i]->name, db.bridges[j]->bridge->name)){\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif(j==db.bridge_count){\n\t\t\t// a new bridge was found, create it\n\t\t\tbridge__new(db.config->bridges[i]);\n\t\t\tdb.config->bridges[i] = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(db.config->bridges[i]->reload_type == brt_immediate){\n\t\t\t// in this case, an existing bridge should match\n\t\t\tfor(j=0; j<db.bridge_count; j++){\n\t\t\t\tif(!strcmp(db.config->bridges[i]->name, db.bridges[j]->bridge->name)){\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tassert(j<db.bridge_count);\n\t\t\tdb.bridges[j]->will_delay_interval = 0;\n\t\t\tbridge__destroy(db.bridges[j]);\n\t\t\tbridge__new(db.config->bridges[i]);\n\t\t\tdb.config->bridges[i] = NULL;\n\t\t}\n\t}\n}\n\n\nvoid bridge__db_cleanup(void)\n{\n\tint i;\n\n\tfor(i=0; i<db.bridge_count; i++){\n\t\tif(db.bridges[i]){\n\t\t\tcontext__cleanup(db.bridges[i], true);\n\t\t}\n\t}\n\tmosquitto_FREE(db.bridges);\n}\n\n\nvoid bridge__cleanup(struct mosquitto *context)\n{\n\tint i;\n\n\tassert(db.bridge_count > 0);\n\n\tfor(i=0; i<db.bridge_count; i++){\n\t\tif(db.bridges[i] == context){\n\t\t\tdb.bridges[i] = db.bridges[db.bridge_count-1];\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tdb.bridge_count--;\n\tif(db.bridge_count == 0){\n\t\tmosquitto_FREE(db.bridges);\n\t}else{\n\t\tdb.bridges = mosquitto_realloc(db.bridges, (unsigned)db.bridge_count * sizeof(db.bridges[0]));\n\t}\n\n\tmosquitto_FREE(context->bridge->name);\n\tmosquitto_FREE(context->bridge->local_clientid);\n\tmosquitto_FREE(context->bridge->local_username);\n\tmosquitto_FREE(context->bridge->local_password);\n#ifdef WITH_TLS\n\tmosquitto_FREE(context->bridge->tls_certfile);\n\tmosquitto_FREE(context->bridge->tls_keyfile);\n#endif\n\n\tif(context->bridge->remote_clientid != context->id){\n\t\tmosquitto_FREE(context->bridge->remote_clientid);\n\t}\n\tcontext->bridge->remote_clientid = NULL;\n\n\tif(context->bridge->remote_username != context->username){\n\t\tmosquitto_FREE(context->bridge->remote_username);\n\t}\n\tcontext->bridge->remote_username = NULL;\n\n\tif(context->bridge->remote_password != context->password){\n\t\tmosquitto_FREE(context->bridge->remote_password);\n\t}\n\tcontext->bridge->remote_password = NULL;\n#ifdef WITH_TLS\n\tif(context->ssl_ctx){\n\t\tSSL_CTX_free(context->ssl_ctx);\n\t\tcontext->ssl_ctx = NULL;\n\t}\n#endif\n\n\tfor(i=0; i<context->bridge->address_count; i++){\n\t\tmosquitto_FREE(context->bridge->addresses[i].address);\n\t}\n\n\tmosquitto_FREE(context->bridge->addresses);\n\n\tconfig__bridge_cleanup(context->bridge);\n\tcontext->bridge = NULL;\n}\n\n\nstatic void bridge__packet_cleanup(struct mosquitto *context)\n{\n\tstruct mosquitto__packet *packet;\n\tif(!context){\n\t\treturn;\n\t}\n\n\twhile(context->out_packet){\n\t\tpacket = context->out_packet;\n\t\tcontext->out_packet = context->out_packet->next;\n\t\tmosquitto_FREE(packet);\n\t}\n\tcontext->out_packet = NULL;\n\tcontext->out_packet_last = NULL;\n\tmetrics__int_dec(mosq_gauge_out_packets, context->out_packet_count);\n\tmetrics__int_dec(mosq_gauge_out_packet_bytes, context->out_packet_bytes);\n\tcontext->out_packet_count = 0;\n\tcontext->out_packet_bytes = 0;\n\n\tpacket__cleanup(&(context->in_packet));\n}\n\n\nstatic int rand_between(int low, int high)\n{\n\tint r;\n\tmosquitto_getrandom(&r, sizeof(int));\n\treturn (abs(r) % (high - low)) + low;\n}\n\n\nstatic void bridge__backoff_step(struct mosquitto__bridge *bridge)\n{\n\t/*\n\t\t“Decorrelated Jitter” calculation, according to:\n\t\t\thttps://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/\n\t*/\n\n\tbridge->restart_timeout = rand_between(bridge->backoff_base, bridge->restart_timeout * 3);\n\tif(bridge->restart_timeout > bridge->backoff_cap){\n\t\tbridge->restart_timeout = bridge->backoff_cap;\n\t}\n}\n\n\nstatic void bridge__backoff_reset(struct mosquitto__bridge *bridge)\n{\n\tbridge->restart_timeout = bridge->backoff_base;\n}\n\n\nstatic void bridge__update_backoff(struct mosquitto__bridge *bridge)\n{\n\tif(!bridge){\n\t\treturn;\n\t}\n\tif(!bridge->backoff_cap){\n\t\t/* skip if not using jitter */\n\t\treturn;\n\n\t}\n\tif(bridge->connected_at && db.now_s - bridge->connected_at >= bridge->stable_connection_period){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Bridge %s connection was stable enough, resetting backoff\", bridge->name);\n\t\tbridge__backoff_reset(bridge);\n\t}else{\n\t\tbridge__backoff_step(bridge);\n\t}\n\n\tbridge->connected_at = 0;\n\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Bridge %s next backoff will be %d ms\", bridge->name, bridge->restart_timeout);\n}\n\n\nstatic void bridge_check_pending(struct mosquitto *context)\n{\n\tint err;\n\tsocklen_t len;\n\n\tif(context->state == mosq_cs_connect_pending){\n\t\tlen = sizeof(int);\n\t\tif(!getsockopt(context->sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len)){\n\t\t\tif(err == 0){\n\t\t\t\tmosquitto__set_state(context, mosq_cs_new);\n#if defined(WITH_ADNS) && defined(WITH_BRIDGE)\n\t\t\t\tif(context->bridge){\n\t\t\t\t\tbridge__connect_step3(context);\n\t\t\t\t}\n#endif\n\t\t\t}else if(err == ECONNREFUSED){\n\t\t\t\tdo_disconnect(context, MOSQ_ERR_CONN_LOST);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}else{\n\t\t\tdo_disconnect(context, MOSQ_ERR_CONN_LOST);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n\nstatic bool reload_if_needed(struct mosquitto *context)\n{\n\tint i;\n\n\tfor(i=0; i<db.config->bridge_count; i++){\n\t\tif(db.config->bridges[i] && !strcmp(context->bridge->name, db.config->bridges[i]->name)){\n\t\t\tbridge__destroy(context);\n\t\t\tbridge__new(db.config->bridges[i]);\n\t\t\tdb.config->bridges[i] = NULL;\n\t\t\tloop__update_next_event(100);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\nvoid bridge_check(void)\n{\n\tstatic time_t last_check = 0;\n\tstruct mosquitto *context = NULL;\n\tsocklen_t len;\n\tint i;\n\tint rc;\n\tint err;\n\n\tif(db.now_s <= last_check){\n\t\treturn;\n\t}\n\n\tfor(i=0; i<db.bridge_count; i++){\n\t\tif(!db.bridges[i]){\n\t\t\tcontinue;\n\t\t}\n\n\t\tcontext = db.bridges[i];\n\n\t\tif(net__is_connected(context)){\n\t\t\tmosquitto__check_keepalive(context);\n\t\t\tbridge_check_pending(context);\n\n\t\t\t/* Check for bridges that are not round robin and not currently\n\t\t\t * connected to their primary broker. */\n\t\t\tif(context->bridge->round_robin == false\n\t\t\t\t\t&& context->bridge->cur_address != 0\n\t\t\t\t\t&& context->bridge->primary_retry\n\t\t\t\t\t&& db.now_s >= context->bridge->primary_retry){\n\n\t\t\t\tif(context->bridge->primary_retry_sock == INVALID_SOCKET){\n\t\t\t\t\trc = net__try_connect(context->bridge->addresses[0].address,\n\t\t\t\t\t\t\tcontext->bridge->addresses[0].port,\n\t\t\t\t\t\t\t&context->bridge->primary_retry_sock,\n\t\t\t\t\t\t\tcontext->bridge->bind_address, false);\n\n\t\t\t\t\tif(rc == 0){\n\t\t\t\t\t\tCOMPAT_CLOSE(context->bridge->primary_retry_sock);\n\t\t\t\t\t\tcontext->bridge->primary_retry_sock = INVALID_SOCKET;\n\t\t\t\t\t\tcontext->bridge->primary_retry = 0;\n\t\t\t\t\t\tmux__delete(context);\n\t\t\t\t\t\tnet__socket_close(context);\n\t\t\t\t\t\tcontext->bridge->cur_address = 0;\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tlen = sizeof(int);\n\t\t\t\t\tif(!getsockopt(context->bridge->primary_retry_sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len)){\n\t\t\t\t\t\tif(err == 0){\n\t\t\t\t\t\t\tCOMPAT_CLOSE(context->bridge->primary_retry_sock);\n\t\t\t\t\t\t\tcontext->bridge->primary_retry_sock = INVALID_SOCKET;\n\t\t\t\t\t\t\tcontext->bridge->primary_retry = 0;\n\t\t\t\t\t\t\tmux__delete(context);\n\t\t\t\t\t\t\tnet__socket_close(context);\n\t\t\t\t\t\t\tcontext->bridge->cur_address = context->bridge->address_count-1;\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\tCOMPAT_CLOSE(context->bridge->primary_retry_sock);\n\t\t\t\t\t\t\tcontext->bridge->primary_retry_sock = INVALID_SOCKET;\n\t\t\t\t\t\t\tcontext->bridge->primary_retry = db.now_s+5;\n\t\t\t\t\t\t\tloop__update_next_event(5000);\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\tCOMPAT_CLOSE(context->bridge->primary_retry_sock);\n\t\t\t\t\t\tcontext->bridge->primary_retry_sock = INVALID_SOCKET;\n\t\t\t\t\t\tcontext->bridge->primary_retry = db.now_s+5;\n\t\t\t\t\t\tloop__update_next_event(5000);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}else{\n\t\t\tif(reload_if_needed(context)){\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* Want to try to restart the bridge connection */\n\t\t\tif(!context->bridge->restart_t){\n\t\t\t\tbridge__update_backoff(context->bridge);\n\t\t\t\tcontext->bridge->restart_t = 1000*db.now_s+context->bridge->restart_timeout;\n\t\t\t\tcontext->bridge->cur_address++;\n\t\t\t\tif(context->bridge->cur_address == context->bridge->address_count){\n\t\t\t\t\tcontext->bridge->cur_address = 0;\n\t\t\t\t}\n\t\t\t\tloop__update_next_event(context->bridge->restart_timeout);\n\t\t\t}else{\n\t\t\t\tif((context->bridge->start_type == bst_lazy && context->bridge->lazy_reconnect)\n\t\t\t\t\t\t|| (context->bridge->start_type == bst_automatic && 1000*db.now_s >= context->bridge->restart_t)){\n\n#if defined(__GLIBC__) && defined(WITH_ADNS)\n\t\t\t\t\tif(context->adns){\n\t\t\t\t\t\t/* Connection attempted, waiting on DNS lookup */\n\t\t\t\t\t\trc = gai_error(context->adns);\n\t\t\t\t\t\tif(rc == EAI_INPROGRESS){\n\t\t\t\t\t\t\t/* Just keep on waiting */\n\t\t\t\t\t\t}else if(rc == 0){\n\t\t\t\t\t\t\trc = bridge__connect_step2(context);\n\t\t\t\t\t\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\t\t\t\t\t\tmux__new(context);\n\t\t\t\t\t\t\t\tif(context->out_packet){\n\t\t\t\t\t\t\t\t\tmux__add_out(context);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}else if(rc == MOSQ_ERR_CONN_PENDING){\n\t\t\t\t\t\t\t\tmux__new(context);\n\t\t\t\t\t\t\t\tmux__add_out(context);\n\t\t\t\t\t\t\t\tcontext->bridge->restart_t = 0;\n\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\tcontext->bridge->cur_address++;\n\t\t\t\t\t\t\t\tif(context->bridge->cur_address == context->bridge->address_count){\n\t\t\t\t\t\t\t\t\tcontext->bridge->cur_address = 0;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcontext->bridge->restart_t = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t/* Need to retry */\n\t\t\t\t\t\t\tif(context->adns->ar_result){\n\t\t\t\t\t\t\t\tfreeaddrinfo(context->adns->ar_result);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmosquitto_FREE(context->adns);\n\t\t\t\t\t\t\tcontext->bridge->restart_t = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\trc = bridge__connect_step1(context);\n\t\t\t\t\t\tif(rc){\n\t\t\t\t\t\t\tcontext->bridge->cur_address++;\n\t\t\t\t\t\t\tif(context->bridge->cur_address == context->bridge->address_count){\n\t\t\t\t\t\t\t\tcontext->bridge->cur_address = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t/* Short wait for ADNS lookup */\n\t\t\t\t\t\t\tcontext->bridge->restart_t = 1;\n\t\t\t\t\t\t\tloop__update_next_event(1000);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#else\n\t\t\t\t\t{\n\t\t\t\t\t\trc = bridge__connect(context);\n\t\t\t\t\t\tcontext->bridge->restart_t = 0;\n\t\t\t\t\t\tif(rc == MOSQ_ERR_SUCCESS || rc == MOSQ_ERR_CONN_PENDING){\n\t\t\t\t\t\t\tif(context->bridge->round_robin == false && context->bridge->cur_address != 0){\n\t\t\t\t\t\t\t\tcontext->bridge->primary_retry = db.now_s + 5;\n\t\t\t\t\t\t\t\tloop__update_next_event(5000);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmux__new(context);\n\t\t\t\t\t\t\tif(context->out_packet){\n\t\t\t\t\t\t\t\tmux__add_out(context);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\tcontext->bridge->cur_address++;\n\t\t\t\t\t\t\tif(context->bridge->cur_address == context->bridge->address_count){\n\t\t\t\t\t\t\t\tcontext->bridge->cur_address = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n#endif\n"
  },
  {
    "path": "src/bridge_topic.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"utlist.h\"\n\n#ifdef WITH_BRIDGE\n\n\nstatic int bridge__create_remap_topic(const char *prefix, const char *topic, char **remap_topic)\n{\n\tif(prefix){\n\t\tif(topic){\n\t\t\tsize_t len = strlen(topic) + strlen(prefix)+1;\n\t\t\t*remap_topic = mosquitto_malloc(len+1);\n\t\t\tif(!(*remap_topic)){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tsnprintf(*remap_topic, len+1, \"%s%s\", prefix, topic);\n\t\t\t(*remap_topic)[len] = '\\0';\n\t\t}else{\n\t\t\t*remap_topic = mosquitto_strdup(prefix);\n\t\t\tif(!(*remap_topic)){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}else{\n\t\t*remap_topic = mosquitto_strdup(topic);\n\t\tif(!(*remap_topic)){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int bridge__create_prefix(char **full_prefix, const char *topic, const char *prefix, const char *direction)\n{\n\tsize_t len;\n\n\tif(!prefix || strlen(prefix) != 0){\n\t\tif(mosquitto_pub_topic_check(prefix) != MOSQ_ERR_SUCCESS){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge topic local prefix '%s'.\", prefix);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\n\tif(topic){\n\t\tlen = strlen(topic) + strlen(prefix) + 1;\n\t}else{\n\t\tlen = strlen(prefix) + 1;\n\t}\n\t*full_prefix = mosquitto_malloc(len);\n\tif(*full_prefix == NULL){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(topic){\n\t\t/* Print full_prefix+pattern to check for validity */\n\t\tsnprintf(*full_prefix, len, \"%s%s\", prefix, topic);\n\t}else{\n\t\tsnprintf(*full_prefix, len, \"%s\", prefix);\n\t}\n\n\tif(mosquitto_sub_topic_check(*full_prefix) != MOSQ_ERR_SUCCESS){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Invalid bridge topic %s prefix and pattern combination '%s'.\",\n\t\t\t\tdirection, *full_prefix);\n\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* Print just the prefix for storage */\n\tsnprintf(*full_prefix, len, \"%s\", prefix);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic struct mosquitto__bridge_topic *bridge__find_topic(struct mosquitto__bridge *bridge, const char *topic, enum mosquitto__bridge_direction direction, uint8_t qos, const char *local_prefix, const char *remote_prefix)\n{\n\tstruct mosquitto__bridge_topic *cur_topic = NULL;\n\tbool found = false;\n\n\tLL_FOREACH(bridge->topics, cur_topic){\n\t\tif(cur_topic->direction != direction){\n\t\t\tcontinue;\n\t\t}\n\t\tif(cur_topic->qos != qos){\n\t\t\tcontinue;\n\t\t}\n\t\tif(cur_topic->topic != NULL && topic != NULL){\n\t\t\tif(strcmp(cur_topic->topic, topic)){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif(cur_topic->local_prefix != NULL && local_prefix != NULL){\n\t\t\tif(strcmp(cur_topic->local_prefix, local_prefix)){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif(cur_topic->remote_prefix != NULL && remote_prefix != NULL){\n\t\t\tif(strcmp(cur_topic->remote_prefix, remote_prefix)){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tfound = true;\n\t\tbreak;\n\t}\n\tif(!found){\n\t\tcur_topic = NULL;\n\t}\n\n\treturn cur_topic;\n}\n\n\nvoid bridge__cleanup_topics(struct mosquitto__bridge *bridge)\n{\n\tstruct mosquitto__bridge_topic *topic, *topic_tmp;\n\n\tif(!bridge){\n\t\treturn;\n\t}\n\n\tLL_FOREACH_SAFE(bridge->topics, topic, topic_tmp){\n\t\tLL_DELETE(bridge->topics, topic);\n\t\tmosquitto_free(topic->local_prefix);\n\t\tmosquitto_free(topic->remote_prefix);\n\t\tmosquitto_free(topic->local_topic);\n\t\tmosquitto_free(topic->remote_topic);\n\t\tmosquitto_free(topic->topic);\n\t\tmosquitto_free(topic);\n\t}\n}\n\n\n/* topic <topic> [[[out | in | both] qos-level] local-prefix remote-prefix] */\nint bridge__add_topic(struct mosquitto__bridge *bridge, const char *topic, enum mosquitto__bridge_direction direction, uint8_t qos, const char *local_prefix, const char *remote_prefix)\n{\n\tstruct mosquitto__bridge_topic *cur_topic;\n\n\tif(bridge == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(direction != bd_out && direction != bd_in && direction != bd_both){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(qos > 2){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(local_prefix && mosquitto_pub_topic_check(local_prefix)){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge topic local prefix '%s'.\", local_prefix);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(remote_prefix && mosquitto_pub_topic_check(remote_prefix)){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge topic remote prefix '%s'.\", remote_prefix);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif((topic == NULL || !strcmp(topic, \"\\\"\\\"\")) &&\n\t\t\t(local_prefix == NULL || remote_prefix == NULL)){\n\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge remapping.\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(bridge__find_topic(bridge, topic, direction, qos, local_prefix, remote_prefix) != NULL){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Duplicate bridge topic '%s', skipping\", topic);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tbridge->topic_count++;\n\tcur_topic = mosquitto_calloc(1, sizeof(struct mosquitto__bridge_topic));\n\tif(cur_topic == NULL){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcur_topic->next = NULL;\n\n\tcur_topic->direction = direction;\n\tcur_topic->qos = qos;\n\tcur_topic->local_prefix = NULL;\n\tcur_topic->remote_prefix = NULL;\n\n\tif(topic == NULL || !strcmp(topic, \"\\\"\\\"\")){\n\t\tcur_topic->topic = NULL;\n\t}else{\n\t\tcur_topic->topic = mosquitto_strdup(topic);\n\t\tif(cur_topic->topic == NULL){\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\tif(local_prefix || remote_prefix){\n\t\tbridge->topic_remapping = true;\n\t\tif(local_prefix){\n\t\t\tif(bridge__create_prefix(&cur_topic->local_prefix, cur_topic->topic, local_prefix, \"local\")){\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}\n\t\tif(remote_prefix){\n\t\t\tif(bridge__create_prefix(&cur_topic->remote_prefix, cur_topic->topic, remote_prefix, \"local\")){\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(bridge__create_remap_topic(cur_topic->local_prefix,\n\t\t\tcur_topic->topic, &cur_topic->local_topic)){\n\n\t\tgoto error;\n\t}\n\n\tif(bridge__create_remap_topic(cur_topic->remote_prefix,\n\t\t\tcur_topic->topic, &cur_topic->remote_topic)){\n\n\t\tgoto error;\n\t}\n\n\tLL_APPEND(bridge->topics, cur_topic);\n\n\treturn MOSQ_ERR_SUCCESS;\n\nerror:\n\tmosquitto_FREE(cur_topic->local_prefix);\n\tmosquitto_FREE(cur_topic->remote_prefix);\n\tmosquitto_FREE(cur_topic->local_topic);\n\tmosquitto_FREE(cur_topic->remote_topic);\n\tmosquitto_FREE(cur_topic->topic);\n\tmosquitto_FREE(cur_topic);\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\treturn MOSQ_ERR_NOMEM;\n}\n\n\nint bridge__remap_topic_in(struct mosquitto *context, char **topic)\n{\n\tstruct mosquitto__bridge_topic *cur_topic;\n\tbool match;\n\n\tif(context->bridge && context->bridge->topics && context->bridge->topic_remapping){\n\t\tLL_FOREACH(context->bridge->topics, cur_topic){\n\t\t\tif((cur_topic->direction == bd_both || cur_topic->direction == bd_in)\n\t\t\t\t\t&& (cur_topic->remote_prefix || cur_topic->local_prefix)){\n\n\t\t\t\t/* Topic mapping required on this topic if the message matches */\n\n\t\t\t\tint rc = mosquitto_topic_matches_sub(cur_topic->remote_topic, *topic, &match);\n\t\t\t\tif(rc){\n\t\t\t\t\tmosquitto_FREE(*topic);\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t\tif(match){\n\t\t\t\t\tchar *topic_temp;\n\n\t\t\t\t\tif(cur_topic->remote_prefix){\n\t\t\t\t\t\t/* This prefix needs removing. */\n\t\t\t\t\t\tif(!strncmp(cur_topic->remote_prefix, *topic, strlen(cur_topic->remote_prefix))){\n\t\t\t\t\t\t\ttopic_temp = mosquitto_strdup((*topic)+strlen(cur_topic->remote_prefix));\n\t\t\t\t\t\t\tif(!topic_temp){\n\t\t\t\t\t\t\t\tmosquitto_FREE(*topic);\n\t\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmosquitto_FREE(*topic);\n\t\t\t\t\t\t\t*topic = topic_temp;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif(cur_topic->local_prefix){\n\t\t\t\t\t\t/* This prefix needs adding. */\n\t\t\t\t\t\tsize_t len = strlen(*topic) + strlen(cur_topic->local_prefix)+1;\n\t\t\t\t\t\ttopic_temp = mosquitto_malloc(len+1);\n\t\t\t\t\t\tif(!topic_temp){\n\t\t\t\t\t\t\tmosquitto_FREE(*topic);\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsnprintf(topic_temp, len, \"%s%s\", cur_topic->local_prefix, *topic);\n\t\t\t\t\t\ttopic_temp[len] = '\\0';\n\n\t\t\t\t\t\tmosquitto_FREE(*topic);\n\t\t\t\t\t\t*topic = topic_temp;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n#endif\n"
  },
  {
    "path": "src/broker_control.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <cjson/cJSON.h>\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <utlist.h>\n\n#include \"json_help.h\"\n#include \"mosquitto.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker.h\"\n#include \"mosquitto/broker_control.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n\nstatic mosquitto_plugin_id_t plg_id;\nstatic int broker__handle_control(struct mosquitto_control_cmd *cmd, void *userdata);\n\n\nstatic int add_plugin_info(cJSON *j_plugins, mosquitto_plugin_id_t *pid)\n{\n\tcJSON *j_plugin, *j_eps, *j_ep;\n\tstruct control_endpoint *ep;\n\n\tif(pid->plugin_name == NULL){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tj_plugin = cJSON_CreateObject();\n\tif(j_plugin == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(cJSON_AddStringToObject(j_plugin, \"name\", pid->plugin_name) == NULL\n\t\t\t|| (pid->plugin_version && cJSON_AddStringToObject(j_plugin, \"version\", pid->plugin_version) == NULL)\n\t\t\t|| (pid->listener && cJSON_AddNumberToObject(j_plugin, \"port\", pid->listener->port) == NULL)\n\t\t\t|| (j_eps = cJSON_AddArrayToObject(j_plugin, \"control-endpoints\")) == NULL\n\t\t\t){\n\n\t\tcJSON_Delete(j_plugin);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tDL_FOREACH(pid->control_endpoints, ep){\n\t\tj_ep = cJSON_CreateString(ep->topic);\n\t\tif(j_ep == NULL){\n\t\t\tcJSON_Delete(j_plugin);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tcJSON_AddItemToArray(j_eps, j_ep);\n\t}\n\n\tcJSON_AddItemToArray(j_plugins, j_plugin);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int broker__process_list_plugins(struct mosquitto_control_cmd *cmd)\n{\n\tcJSON *tree, *j_data, *j_plugins;\n\tconst char *admin_clientid, *admin_username;\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"Broker: %s/%s | listPlugins\",\n\t\t\tadmin_clientid, admin_username);\n\n\tif(cJSON_AddStringToObject(tree, \"command\", \"listPlugins\") == NULL\n\t\t\t|| ((j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL)\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t){\n\n\t\tgoto internal_error;\n\t}\n\n\tj_plugins = cJSON_AddArrayToObject(j_data, \"plugins\");\n\tif(j_plugins == NULL){\n\t\tgoto internal_error;\n\t}\n\n\tfor(int i=0; i<db.plugin_count; i++){\n\t\tif(add_plugin_info(j_plugins, db.plugins[i])){\n\t\t\tgoto internal_error;\n\t\t}\n\t}\n\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\treturn MOSQ_ERR_SUCCESS;\n\ninternal_error:\n\tcJSON_Delete(tree);\n\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\treturn MOSQ_ERR_NOMEM;\n}\n\n\nstatic int add_listener(cJSON *j_listeners, struct mosquitto__listener *listener)\n{\n\tcJSON *j_listener;\n\tconst char *protocol = NULL;\n\n\tj_listener = cJSON_CreateObject();\n\tif(j_listener == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcJSON_AddItemToArray(j_listeners, j_listener);\n\n\tif(listener->protocol == mp_mqtt){\n\t\tprotocol = \"mqtt\";\n\t}else if(listener->protocol == mp_websockets){\n\t\tprotocol = \"mqtt+websockets\";\n\t}\n\n\tif(cJSON_AddNumberToObject(j_listener, \"port\", listener->port) == NULL\n\t\t\t|| (protocol && cJSON_AddStringToObject(j_listener, \"protocol\", protocol) == NULL)\n\t\t\t|| (listener->host && cJSON_AddStringToObject(j_listener, \"bind-address\", listener->host) == NULL)\n#ifdef WITH_UNIX_SOCKETS\n\t\t\t|| (listener->unix_socket_path && cJSON_AddStringToObject(j_listener, \"socket-path\", listener->unix_socket_path) == NULL)\n#endif\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n#ifdef WITH_TLS\n\tif(cJSON_AddBoolToObject(j_listener, \"tls\", listener->ssl_ctx != NULL) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n#endif\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int broker__process_list_listeners(struct mosquitto_control_cmd *cmd)\n{\n\tcJSON *tree, *j_data, *j_listeners;\n\tconst char *admin_clientid, *admin_username;\n\n\ttree = cJSON_CreateObject();\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tadmin_clientid = mosquitto_client_id(cmd->client);\n\tadmin_username = mosquitto_client_username(cmd->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"Broker: %s/%s | listListeners\",\n\t\t\tadmin_clientid, admin_username);\n\n\tif(cJSON_AddStringToObject(tree, \"command\", \"listListeners\") == NULL\n\t\t\t|| ((j_data = cJSON_AddObjectToObject(tree, \"data\")) == NULL)\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(tree, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t){\n\n\t\tgoto internal_error;\n\t}\n\n\tj_listeners = cJSON_AddArrayToObject(j_data, \"listeners\");\n\tif(j_listeners == NULL){\n\t\tgoto internal_error;\n\t}\n\n\tfor(int i=0; i<db.config->listener_count; i++){\n\t\tif(add_listener(j_listeners, &db.config->listeners[i])){\n\t\t\tgoto internal_error;\n\t\t}\n\t}\n\n\tcJSON_AddItemToArray(cmd->j_responses, tree);\n\n\treturn MOSQ_ERR_SUCCESS;\n\ninternal_error:\n\tcJSON_Delete(tree);\n\tmosquitto_control_command_reply(cmd, \"Internal error\");\n\treturn MOSQ_ERR_NOMEM;\n}\n\n\nstatic int broker_control_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_control *ed = event_data;\n\n\tUNUSED(event);\n\n\treturn mosquitto_control_generic_callback(ed, \"$CONTROL/broker/v1/response\", userdata, broker__handle_control);\n}\n\n\nvoid broker_control__init(void)\n{\n\tmemset(&plg_id, 0, sizeof(plg_id));\n\n\tif(db.config->enable_control_api){\n\t\tmosquitto_callback_register(&plg_id, MOSQ_EVT_CONTROL, broker_control_callback, \"$CONTROL/broker/v1\", NULL);\n\t}\n}\n\n\nvoid broker_control__cleanup(void)\n{\n\tmosquitto_callback_unregister(&plg_id, MOSQ_EVT_CONTROL, broker_control_callback, \"$CONTROL/broker/v1\");\n}\n\n\nvoid broker_control__reload(void)\n{\n\tbroker_control__cleanup();\n\tbroker_control__init();\n}\n\n\n/* ################################################################\n * #\n * # $CONTROL/broker/v1 handler\n * #\n * ################################################################ */\n\n\nstatic int broker__handle_control(struct mosquitto_control_cmd *cmd, void *userdata)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tUNUSED(userdata);\n\n\tif(!strcasecmp(cmd->command_name, \"listPlugins\")){\n\t\trc = broker__process_list_plugins(cmd);\n\t}else if(!strcasecmp(cmd->command_name, \"listListeners\")){\n\t\trc = broker__process_list_listeners(cmd);\n\n\t\t/* Unknown */\n\t}else{\n\t\tmosquitto_control_command_reply(cmd, \"Unknown command\");\n\t\trc = MOSQ_ERR_INVAL;\n\t}\n\treturn rc;\n}\n"
  },
  {
    "path": "src/conf.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n\n#ifdef WIN32\n#else\n#  include <dirent.h>\n#  include <strings.h>\n#endif\n\n#ifndef WIN32\n#  include <netdb.h>\n#  include <sys/socket.h>\n#else\n#  include <winsock2.h>\n#  include <ws2tcpip.h>\n#endif\n\n#if !defined(WIN32) && !defined(__CYGWIN__)\n#  include <syslog.h>\n#endif\n\n#include \"mosquitto_broker_internal.h\"\n#include \"tls_mosq.h\"\n#include \"util_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n\n#include \"utlist.h\"\n\nstruct config_recurse {\n\tunsigned int log_dest;\n\tint log_dest_set;\n\tunsigned int log_type;\n\tint log_type_set;\n};\n\n#if defined(WIN32) || defined(__CYGWIN__)\n#include <windows.h>\nextern SERVICE_STATUS_HANDLE service_handle;\n#endif\n\n\n#define REQUIRE_LISTENER(A) \\\n\t\tdo{ \\\n\t\t\tif(cur_listener == NULL){ \\\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: The '%s' option requires a listener to be defined first.\", (A)); \\\n\t\t\t\treturn MOSQ_ERR_INVAL; \\\n\t\t\t} \\\n\t\t}while(0)\n\n#define REQUIRE_LISTENER_OR_DEFAULT_LISTENER(A) \\\n\t\tdo{ \\\n\t\t\tif(cur_listener == NULL){ \\\n\t\t\t\tif(config__create_default_listener(config, (A))){ \\\n\t\t\t\t\treturn MOSQ_ERR_NOMEM; \\\n\t\t\t\t} \\\n\t\t\t\tcur_listener = config->default_listener; \\\n\t\t\t} \\\n\t\t}while(0)\n\n#define REQUIRE_LISTENER_IF_PER_LISTENER(A) \\\n\t\tdo{ \\\n\t\t\tif(config->per_listener_settings == true && cur_listener == NULL){ \\\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: The '%s' option requires a listener to be defined first.\", (A)); \\\n\t\t\t\treturn MOSQ_ERR_INVAL; \\\n\t\t\t} \\\n\t\t}while(0)\n\n#define REQUIRE_NON_DEFAULT_LISTENER(A) \\\n\t\tdo{ \\\n\t\t\tif(cur_listener == config->default_listener || cur_listener == NULL){ \\\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: The '%s' option requires a listener to be defined first.\", (A)); \\\n\t\t\t\treturn MOSQ_ERR_INVAL; \\\n\t\t\t} \\\n\t\t}while(0)\n\n#define REQUIRE_BRIDGE(A) \\\n\t\tdo{ \\\n\t\t\tif(cur_bridge == NULL){ \\\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: The '%s' option requires a bridge to be defined first.\", (A)); \\\n\t\t\t\treturn MOSQ_ERR_INVAL; \\\n\t\t\t} \\\n\t\t}while(0)\n\n#define REQUIRE_PLUGIN(A) \\\n\t\tdo{ \\\n\t\t\tif(cur_plugin == NULL){ \\\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: The '%s' option requires plugin/global_plugin/plugin_load to be defined first.\", (A)); \\\n\t\t\t\treturn MOSQ_ERR_INVAL; \\\n\t\t\t} \\\n\t\t}while(0)\n\n#define OPTION_DEPRECATED(A, B) \\\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"The '%s' option is now deprecated and will be removed in version 3.0. %s\", (A), (B))\n\n#define OPTION_UNAVAILABLE(A) \\\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: The '%s' option is no longer available.\", (A));\n\n#define REQUIRE_NON_EMPTY_OPTION(A, B) \\\n\t\tdo{ \\\n\t\t\tif(!(A)){ \\\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty '%s' value in configuration.\", (B)); \\\n\t\t\t\treturn MOSQ_ERR_INVAL; \\\n\t\t\t} \\\n\t\t}while(0)\n\n#define PER_LISTENER_ALTERNATIVE(A, B) \\\n\t\tif(config->per_listener_settings){ \\\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"You are using the '%s' option with 'per_listener_settings true'. Please replace this with '%s'.\", A, B); \\\n\t\t} \\\n\n\n#ifdef FINAL_WITH_TLS_PSK\n#  define REQUIRE_BRIDGE_NO_TLS_PSK(A) \\\n\t\tdo{ \\\n\t\t\tif(cur_bridge->tls_psk_identity || cur_bridge->tls_psk){ \\\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: '%s': Cannot use both certificate and psk encryption in a single bridge.\", (A)); \\\n\t\t\t\treturn MOSQ_ERR_INVAL; \\\n\t\t\t} \\\n\t\t}while(0)\n\n#  define REQUIRE_BRIDGE_NO_X509(A) \\\n\t\tdo{ \\\n\t\t\tif(cur_bridge->tls_cafile || cur_bridge->tls_capath || cur_bridge->tls_certfile || cur_bridge->tls_keyfile){ \\\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: '%s': Cannot use both certificate and identity encryption in a single bridge.\", (A)); \\\n\t\t\t\treturn MOSQ_ERR_INVAL; \\\n\t\t\t} \\\n\t\t}while(0)\n\n#else\n#  define REQUIRE_BRIDGE_NO_TLS_PSK(A)\n#  define REQUIRE_BRIDGE_NO_X509(A)\n#endif\n\nstatic struct mosquitto__security_options *cur_security_options = NULL;\n\nstatic int conf__parse_bool(char **token, const char *name, bool *value, char **saveptr);\nstatic int conf__parse_int(char **token, const char *name, int *value, char **saveptr);\nstatic int conf__parse_ssize_t(char **token, const char *name, ssize_t *value, char **saveptr);\nstatic int conf__parse_string(char **token, const char *name, char **value, char **saveptr);\nstatic int config__read_file(struct mosquitto__config *config, bool reload, const char *file, struct config_recurse *config_tmp, int level, int *lineno);\nstatic int config__check(struct mosquitto__config *config);\nstatic void config__cleanup_plugins(void);\n#ifdef WITH_BRIDGE\nstatic int config__check_bridges(struct mosquitto__config *config);\n#endif\n\n\nstatic int config__add_listener(struct mosquitto__config *config)\n{\n\tstruct mosquitto__listener *listener;\n\tstruct mosquitto__listener *new_listeners;\n\tint def_listener = -1;\n\n\tif(config->default_listener){\n\t\tfor(int i=0; i<config->listener_count; i++){\n\t\t\tif(&config->listeners[i] == config->default_listener){\n\t\t\t\tdef_listener = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tnew_listeners = mosquitto_realloc(config->listeners, sizeof(struct mosquitto__listener)*(size_t)(config->listener_count+1));\n\tif(!new_listeners){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tconfig->listeners = new_listeners;\n\tlistener = &config->listeners[config->listener_count];\n\tmemset(listener, 0, sizeof(struct mosquitto__listener));\n\tlistener->security_options = mosquitto_calloc(1, sizeof(struct mosquitto__security_options));\n\tif(!listener->security_options){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tif(def_listener != -1){\n\t\tconfig->default_listener = &config->listeners[def_listener];\n\t}\n\tconfig->listener_count++;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int config__create_default_listener(struct mosquitto__config *config, const char *option_name)\n{\n\tif(config->default_listener){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Creating default listener due to '%s' option.\", option_name);\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"It is best practice to define a 'listener' first. Using the '%s' option without a listener will be disabled in the future.\", option_name);\n\tif(config__add_listener(config)){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tconfig->default_listener = &config->listeners[config->listener_count-1];\n\tlistener__set_defaults(config->default_listener);\n\tconfig->default_listener->port = 1883;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void conf__set_cur_security_options(struct mosquitto__config *config, struct mosquitto__listener **cur_listener, struct mosquitto__security_options **security_options, const char *option_name)\n{\n\tif(config->per_listener_settings){\n\t\tif(*cur_listener == NULL){\n\t\t\tif(config__create_default_listener(config, option_name)){\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t*cur_listener = config->default_listener;\n\t\t}\n\t\t(*security_options) = (*cur_listener)->security_options;\n\t}else{\n\t\t(*security_options) = &config->security_options;\n\t}\n}\n\n\nstatic int conf__attempt_resolve(const char *host, const char *text, unsigned int log, const char *msg)\n{\n\tstruct addrinfo gai_hints;\n\tstruct addrinfo *gai_res;\n\tint rc;\n\n\tmemset(&gai_hints, 0, sizeof(struct addrinfo));\n\tgai_hints.ai_family = AF_UNSPEC;\n\tgai_hints.ai_socktype = SOCK_STREAM;\n\tgai_res = NULL;\n\trc = getaddrinfo(host, NULL, &gai_hints, &gai_res);\n\tif(gai_res){\n\t\tfreeaddrinfo(gai_res);\n\t}\n\tif(rc != 0){\n#ifndef WIN32\n\t\tif(rc == EAI_SYSTEM){\n\t\t\tif(errno == ENOENT){\n\t\t\t\tlog__printf(NULL, log, \"%s: Unable to resolve %s %s.\", msg, text, host);\n\t\t\t}else{\n\t\t\t\tlog__printf(NULL, log, \"%s: Error resolving %s: %s.\", msg, text, strerror(errno));\n\t\t\t}\n\t\t}else{\n\t\t\tlog__printf(NULL, log, \"%s: Error resolving %s: %s.\", msg, text, gai_strerror(rc));\n\t\t}\n#else\n\t\tif(rc == WSAHOST_NOT_FOUND){\n\t\t\tlog__printf(NULL, log, \"%s: Error resolving %s.\", msg, text);\n\t\t}\n#endif\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void config__init_reload(struct mosquitto__config *config)\n{\n\t/* Set defaults */\n\tfor(int i=0; i<config->listener_count; i++){\n\t\tlistener__set_defaults(&config->listeners[i]);\n\t}\n\n\tconfig->local_only = true;\n\tconfig->allow_duplicate_messages = true;\n\n\tmosquitto_FREE(config->security_options.acl_data.acl_file);\n\tmosquitto_FREE(config->security_options.password_data.password_file);\n\tmosquitto_FREE(config->security_options.psk_file);\n\n\tconfig->security_options.allow_anonymous = -1;\n\tconfig->security_options.allow_zero_length_clientid = true;\n\tconfig->security_options.auto_id_prefix = NULL;\n\tconfig->security_options.auto_id_prefix_len = 0;\n\n\tconfig->autosave_interval = 1800;\n\tconfig->autosave_on_changes = false;\n\n\tmosquitto_FREE(config->clientid_prefixes);\n\n\tconfig->connection_messages = true;\n\tconfig->clientid_prefixes = NULL;\n\tconfig->per_listener_settings = false;\n\tif(config->log_fptr){\n\t\tfclose(config->log_fptr);\n\t\tconfig->log_fptr = NULL;\n\t}\n\tmosquitto_FREE(config->log_file);\n\n#if defined(WIN32) || defined(__CYGWIN__)\n\tif(service_handle){\n\t\t/* This is running as a Windows service. Default to no logging. Using\n\t\t * stdout/stderr is forbidden because the first clients to connect will\n\t\t * get log information sent to them for some reason. */\n\t\tconfig->log_dest = MQTT3_LOG_NONE;\n\t}else{\n\t\tconfig->log_dest = MQTT3_LOG_STDERR;\n\t}\n#else\n\tconfig->log_facility = LOG_DAEMON;\n\tconfig->log_dest = MQTT3_LOG_STDERR | MQTT3_LOG_DLT;\n\tif(db.quiet){\n\t\tconfig->log_type = 0;\n\t}else if(db.verbose){\n\t\tconfig->log_type = UINT_MAX;\n\t}else{\n\t\tconfig->log_type = MOSQ_LOG_ERR | MOSQ_LOG_WARNING | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO;\n\t}\n#endif\n\tconfig->log_timestamp = true;\n\tmosquitto_FREE(config->log_timestamp_format);\n\tconfig->global_max_clients = -1;\n\tconfig->global_max_connections = -1;\n\tconfig->log_timestamp_format = NULL;\n\tconfig->max_keepalive = 0;\n\tconfig->max_packet_size = 2000000;\n\tconfig->max_inflight_messages = 20;\n\tconfig->max_queued_messages = 1000;\n\tconfig->max_inflight_bytes = 0;\n\tconfig->max_queued_bytes = 0;\n\tconfig->persistence = false;\n\tmosquitto_FREE(config->persistence_location);\n\tmosquitto_FREE(config->persistence_file);\n\tconfig->persistent_client_expiration = 0;\n\tconfig->queue_qos0_messages = false;\n\tconfig->retain_available = true;\n\tconfig->retain_expiry_interval = 0;\n\tconfig->set_tcp_nodelay = false;\n\tconfig->sys_interval = 10;\n\tconfig->upgrade_outgoing_qos = false;\n\tconfig->packet_buffer_size = 4096;\n\n\tconfig->packet_max_auth = 100000;\n\tconfig->packet_max_connect = 100000;\n\tconfig->packet_max_sub = 100000;\n\tconfig->packet_max_simple = 10000;\n}\n\n\nstatic void config__cleanup_plugin_config(mosquitto_plugin_id_t *plugin)\n{\n\tmosquitto_FREE(plugin->config.path);\n\tmosquitto_FREE(plugin->config.name);\n\n\tif(plugin->config.options){\n\t\tfor(int j=0; j<plugin->config.option_count; j++){\n\t\t\tmosquitto_FREE(plugin->config.options[j].key);\n\t\t\tmosquitto_FREE(plugin->config.options[j].value);\n\t\t}\n\t\tmosquitto_FREE(plugin->config.options);\n\t\tplugin->config.option_count = 0;\n\t}\n\tmosquitto_FREE(plugin->config.security_options);\n\tmosquitto_FREE(plugin);\n}\n\n\nstatic void config__cleanup_plugins(void)\n{\n\tfor(int i=0; i<db.plugin_count; i++){\n\t\tconfig__cleanup_plugin_config(db.plugins[i]);\n\t}\n\tmosquitto_FREE(db.plugins);\n}\n\n\nvoid config__init(struct mosquitto__config *config)\n{\n\tmemset(config, 0, sizeof(struct mosquitto__config));\n\tconfig__init_reload(config);\n\n\tconfig->daemon = false;\n}\n\n\nvoid config__cleanup(struct mosquitto__config *config)\n{\n\tmosquitto_FREE(config->clientid_prefixes);\n\tmosquitto_FREE(config->persistence_location);\n\tmosquitto_FREE(config->persistence_file);\n\tmosquitto_FREE(config->persistence_filepath);\n\tmosquitto_FREE(config->security_options.auto_id_prefix);\n\tmosquitto_FREE(config->security_options.acl_data.acl_file);\n\tmosquitto_FREE(config->security_options.password_data.password_file);\n\tmosquitto_FREE(config->security_options.psk_file);\n\tmosquitto_FREE(config->security_options.plugins);\n\tmosquitto_FREE(config->pid_file);\n\tmosquitto_FREE(config->user);\n\tmosquitto_FREE(config->log_timestamp_format);\n\tif(config->listeners){\n\t\tfor(int i=0; i<config->listener_count; i++){\n\t\t\tmosquitto_FREE(config->listeners[i].host);\n\t\t\tmosquitto_FREE(config->listeners[i].bind_interface);\n\t\t\tmosquitto_FREE(config->listeners[i].mount_point);\n\t\t\tmosquitto_FREE(config->listeners[i].socks);\n\t\t\tif(config->listeners[i].security_options){\n\t\t\t\tmosquitto_FREE(config->listeners[i].security_options->auto_id_prefix);\n\t\t\t\tmosquitto_FREE(config->listeners[i].security_options->acl_data.acl_file);\n\t\t\t\tmosquitto_FREE(config->listeners[i].security_options->password_data.password_file);\n\t\t\t\tmosquitto_FREE(config->listeners[i].security_options->psk_file);\n\t\t\t\tmosquitto_FREE(config->listeners[i].security_options->plugins);\n\t\t\t\tmosquitto_FREE(config->listeners[i].security_options);\n\t\t\t}\n#ifdef WITH_TLS\n\t\t\tmosquitto_FREE(config->listeners[i].cafile);\n\t\t\tmosquitto_FREE(config->listeners[i].capath);\n\t\t\tmosquitto_FREE(config->listeners[i].certfile);\n\t\t\tmosquitto_FREE(config->listeners[i].keyfile);\n\t\t\tmosquitto_FREE(config->listeners[i].ciphers);\n\t\t\tmosquitto_FREE(config->listeners[i].ciphers_tls13);\n\t\t\tmosquitto_FREE(config->listeners[i].psk_hint);\n\t\t\tmosquitto_FREE(config->listeners[i].crlfile);\n\t\t\tmosquitto_FREE(config->listeners[i].tls_version);\n\t\t\tmosquitto_FREE(config->listeners[i].tls_engine);\n\t\t\tmosquitto_FREE(config->listeners[i].tls_engine_kpass_sha1);\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\t\tif(!config->listeners[i].ws_context) /* libwebsockets frees its own SSL_CTX */\n#endif\n\t\t\t{\n\t\t\t\tSSL_CTX_free(config->listeners[i].ssl_ctx);\n\t\t\t\tconfig->listeners[i].ssl_ctx = NULL;\n\t\t\t}\n#endif\n#if defined(WITH_WEBSOCKETS) || defined(WITH_HTTP_API)\n\t\t\tmosquitto_FREE(config->listeners[i].http_dir);\n#endif\n#ifdef WITH_WEBSOCKETS\n\t\t\tfor(int j=0; j<config->listeners[i].ws_origin_count; j++){\n\t\t\t\tmosquitto_FREE(config->listeners[i].ws_origins[j]);\n\t\t\t}\n\t\t\tmosquitto_FREE(config->listeners[i].ws_origins);\n#endif\n#ifdef WITH_UNIX_SOCKETS\n\t\t\tmosquitto_FREE(config->listeners[i].unix_socket_path);\n#endif\n\t\t}\n\t\tmosquitto_FREE(config->listeners);\n\t}\n#ifdef WITH_BRIDGE\n\tif(config->bridges){\n\t\tfor(int i=0; i<config->bridge_count; i++){\n\t\t\tconfig__bridge_cleanup(config->bridges[i]);\n\t\t}\n\t\tmosquitto_FREE(config->bridges);\n\t}\n#endif\n\tconfig__cleanup_plugins();\n\n\tif(config->log_fptr){\n\t\tfclose(config->log_fptr);\n\t\tconfig->log_fptr = NULL;\n\t}\n\tif(config->log_file){\n\t\tmosquitto_FREE(config->log_file);\n\t\tconfig->log_file = NULL;\n\t}\n}\n\n#ifdef WITH_BRIDGE\n\n\nvoid config__bridge_cleanup(struct mosquitto__bridge *bridge)\n{\n\tif(bridge == NULL){\n\t\treturn;\n\t}\n\n\tmosquitto_FREE(bridge->name);\n\tif(bridge->addresses){\n\t\tfor(int i=0; i<bridge->address_count; i++){\n\t\t\tmosquitto_FREE(bridge->addresses[i].address);\n\t\t}\n\t\tmosquitto_FREE(bridge->addresses);\n\t}\n\tmosquitto_FREE(bridge->bind_address);\n\tmosquitto_FREE(bridge->remote_clientid);\n\tmosquitto_FREE(bridge->remote_username);\n\tmosquitto_FREE(bridge->remote_password);\n\tmosquitto_FREE(bridge->local_clientid);\n\tmosquitto_FREE(bridge->local_username);\n\tmosquitto_FREE(bridge->local_password);\n\tif(bridge->topics){\n\t\tstruct mosquitto__bridge_topic *cur_topic, *topic_tmp;\n\n\t\tLL_FOREACH_SAFE(bridge->topics, cur_topic, topic_tmp){\n\t\t\tmosquitto_FREE(cur_topic->topic);\n\t\t\tmosquitto_FREE(cur_topic->local_prefix);\n\t\t\tmosquitto_FREE(cur_topic->remote_prefix);\n\t\t\tmosquitto_FREE(cur_topic->local_topic);\n\t\t\tmosquitto_FREE(cur_topic->remote_topic);\n\t\t\tLL_DELETE(bridge->topics, cur_topic);\n\t\t\tmosquitto_FREE(cur_topic);\n\t\t}\n\t\tmosquitto_FREE(bridge->topics);\n\t}\n\tmosquitto_FREE(bridge->notification_topic);\n#ifdef WITH_TLS\n\tmosquitto_FREE(bridge->tls_certfile);\n\tmosquitto_FREE(bridge->tls_keyfile);\n\tmosquitto_FREE(bridge->tls_version);\n\tmosquitto_FREE(bridge->tls_cafile);\n\tmosquitto_FREE(bridge->tls_capath);\n\tmosquitto_FREE(bridge->tls_alpn);\n\tmosquitto_FREE(bridge->tls_ciphers);\n\tmosquitto_FREE(bridge->tls_13_ciphers);\n#ifdef FINAL_WITH_TLS_PSK\n\tmosquitto_FREE(bridge->tls_psk_identity);\n\tmosquitto_FREE(bridge->tls_psk);\n#endif\n#endif\n\tmosquitto_FREE(bridge);\n}\n#endif\n\n\nstatic void print_version(void)\n{\n\tprintf(\"mosquitto %s\\n\", VERSION);\n\tprintf(\"Copyright © 2025 Roger Light.\\n\");\n\tprintf(\"License EPL-2.0 OR BSD-3-Clause.\\n\");\n}\n\n\nstatic void print_usage(void)\n{\n\tprintf(\"mosquitto version %s\\n\\n\", VERSION);\n\tprintf(\"mosquitto is an MQTT v5.0/v3.1.1/v3.1 broker.\\n\\n\");\n\tprintf(\"Usage: mosquitto [-c config_file] [-d] [-h] [-p port] [-v]\\n\");\n\tprintf(\"                 [--tls-keylog file]\\n\\n\");\n\tprintf(\" -c : specify the broker config file.\\n\");\n\tprintf(\" -d : put the broker into the background after starting.\\n\");\n\tprintf(\" -h : display this help.\\n\");\n\tprintf(\" -p : start the broker listening on the specified port.\\n\");\n\tprintf(\"      Not recommended in conjunction with the -c option.\\n\");\n\tprintf(\" -q : quiet mode - disable all logging types. This overrides\\n\");\n\tprintf(\"      any logging options given in the config file, and -v.\\n\");\n\tprintf(\" -v : verbose mode - enable all logging types. This overrides\\n\");\n\tprintf(\"      any logging options given in the config file.\\n\");\n\tprintf(\" --test-config : test config file and exit\\n\");\n\tprintf(\" --tls-keylog : log TLS connection information to a file, to allow\\n\");\n\tprintf(\"      debugging with e.g. wireshark. Do not use on a production\\n\");\n\tprintf(\"      server.\\n\");\n\tprintf(\"\\nSee https://mosquitto.org/ for more information.\\n\\n\");\n}\n\n\nint config__parse_args(struct mosquitto__config *config, int argc, char *argv[])\n{\n\tint i;\n\tint port_tmp;\n\n\tfor(i=1; i<argc; i++){\n\t\tif(!strcmp(argv[i], \"-c\") || !strcmp(argv[i], \"--config-file\")){\n\t\t\tif(i<argc-1){\n\t\t\t\tdb.config_file = argv[i+1];\n\n\t\t\t\tif(config__read(config, false)){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: -c argument given, but no config file specified.\");\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"-d\") || !strcmp(argv[i], \"--daemon\")){\n\t\t\tconfig->daemon = true;\n\t\t}else if(!strcmp(argv[i], \"-h\") || !strcmp(argv[i], \"--help\")){\n\t\t\tprint_usage();\n\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t}else if(!strcmp(argv[i], \"--version\")){\n\t\t\tprint_version();\n\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t}else if(!strcmp(argv[i], \"-p\") || !strcmp(argv[i], \"--port\")){\n\t\t\tif(i<argc-1){\n\t\t\t\tport_tmp = atoi(argv[i+1]);\n\t\t\t\tif(port_tmp<1 || port_tmp>UINT16_MAX){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid port specified (%d).\", port_tmp);\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}else{\n\t\t\t\t\tif(config->cmd_port_count == CMD_PORT_LIMIT){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Only %d ports can be specified on the command line.\", CMD_PORT_LIMIT);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->cmd_port[config->cmd_port_count] = (uint16_t)port_tmp;\n\t\t\t\t\tconfig->cmd_port_count++;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: -p argument given, but no port specified.\");\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\ti++;\n\t\t}else if(!strcmp(argv[i], \"--tls-keylog\")){\n#ifdef WITH_TLS\n\t\t\tif(i<argc-1){\n\t\t\t\tdb.tls_keylog = mosquitto_strdup(argv[i+1]);\n\t\t\t\tif(db.tls_keylog == NULL){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: --tls-keylog argument given, but no file specified.\");\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t\ti++;\n#else\n\t\t\tfprintf(stderr, \"Error: TLS support not available so --tls-keylog is not available.\\n\");\n\t\t\treturn MOSQ_ERR_INVAL;\n#endif\n\t\t}else if(!strcmp(argv[i], \"-q\") || !strcmp(argv[i], \"--quiet\")){\n\t\t\tdb.quiet = true;\n\t\t}else if(!strcmp(argv[i], \"-v\") || !strcmp(argv[i], \"--verbose\")){\n\t\t\tdb.verbose = true;\n\t\t}else if(!strcmp(argv[i], \"--test-config\")){\n\t\t\tconfig->test_configuration = true;\n\t\t}else{\n\t\t\tfprintf(stderr, \"Error: Unknown option '%s'.\\n\", argv[i]);\n\t\t\tprint_usage();\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\n\t/* Default to drop to mosquitto user if we are privileged and no user specified. */\n\tif(!config->user){\n\t\tconfig->user = mosquitto_strdup(\"mosquitto\");\n\t\tif(config->user == NULL){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\tif(db.quiet){\n\t\tconfig->log_type = 0;\n\t}else if(db.verbose){\n\t\tconfig->log_type = UINT_MAX;\n\t}\n\n\tif(getenv(\"MOSQUITTO_PERSISTENCE_LOCATION\")){\n\t\tmosquitto_FREE(config->persistence_location);\n\t\tconfig->persistence_location = mosquitto_strdup(getenv(\"MOSQUITTO_PERSISTENCE_LOCATION\"));\n\t\tif(!config->persistence_location){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\treturn config__check(config);\n}\n\n\nstatic void config__copy(struct mosquitto__config *src, struct mosquitto__config *dest)\n{\n\tmosquitto_FREE(dest->security_options.acl_data.acl_file);\n\tdest->security_options.acl_data.acl_file = src->security_options.acl_data.acl_file;\n\n\tacl_file__cleanup(&dest->security_options.acl_data);\n\tdest->security_options.acl_data.acl_users = src->security_options.acl_data.acl_users;\n\tdest->security_options.acl_data.acl_patterns = src->security_options.acl_data.acl_patterns;\n\tdest->security_options.acl_data.acl_anon.username = src->security_options.acl_data.acl_anon.username;\n\tdest->security_options.acl_data.acl_anon.acl = src->security_options.acl_data.acl_anon.acl;\n\n\tdest->security_options.allow_anonymous = src->security_options.allow_anonymous;\n\tdest->security_options.allow_zero_length_clientid = src->security_options.allow_zero_length_clientid;\n\n\tmosquitto_FREE(dest->security_options.auto_id_prefix);\n\tdest->security_options.auto_id_prefix = src->security_options.auto_id_prefix;\n\tdest->security_options.auto_id_prefix_len = src->security_options.auto_id_prefix_len;\n\n\tmosquitto_FREE(dest->security_options.password_data.password_file);\n\tdest->security_options.password_data.password_file = src->security_options.password_data.password_file;\n\n\tpassword_file__cleanup(&dest->security_options.password_data);\n\tdest->security_options.password_data.unpwd = src->security_options.password_data.unpwd;\n\n\tmosquitto_FREE(dest->security_options.psk_file);\n\tdest->security_options.psk_file = src->security_options.psk_file;\n\n\tmosquitto_FREE(dest->security_options.plugins);\n\tdest->security_options.plugin_count = src->security_options.plugin_count;\n\tdest->security_options.plugins = src->security_options.plugins;\n\n\tdest->allow_duplicate_messages = src->allow_duplicate_messages;\n\n\n\tdest->autosave_interval = src->autosave_interval;\n\tdest->autosave_on_changes = src->autosave_on_changes;\n\n\tmosquitto_FREE(dest->clientid_prefixes);\n\tdest->clientid_prefixes = src->clientid_prefixes;\n\n\tdest->connection_messages = src->connection_messages;\n\tdest->log_dest = src->log_dest;\n\tdest->log_facility = src->log_facility;\n\tdest->log_type = src->log_type;\n\tdest->log_timestamp = src->log_timestamp;\n\n\tmosquitto_FREE(dest->log_timestamp_format);\n\tdest->log_timestamp_format = src->log_timestamp_format;\n\n\tmosquitto_FREE(dest->log_file);\n\tdest->log_file = src->log_file;\n\n\tdest->message_size_limit = src->message_size_limit;\n\n\tdest->persistence = src->persistence;\n\n\tmosquitto_FREE(dest->persistence_location);\n\tdest->persistence_location = src->persistence_location;\n\n\tmosquitto_FREE(dest->persistence_file);\n\tdest->persistence_file = src->persistence_file;\n\n\tmosquitto_FREE(dest->persistence_filepath);\n\tdest->persistence_filepath = src->persistence_filepath;\n\n\tdest->persistent_client_expiration = src->persistent_client_expiration;\n\n\n\tdest->queue_qos0_messages = src->queue_qos0_messages;\n\tdest->sys_interval = src->sys_interval;\n\tdest->upgrade_outgoing_qos = src->upgrade_outgoing_qos;\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tdest->websockets_log_level = src->websockets_log_level;\n#endif\n\n#ifdef WITH_BRIDGE\n\tfor(int i=0; i<dest->bridge_count; i++){\n\t\tif(dest->bridges[i]){\n\t\t\tconfig__bridge_cleanup(dest->bridges[i]);\n\t\t}\n\t}\n\tmosquitto_FREE(dest->bridges);\n\tdest->bridges = src->bridges;\n\tdest->bridge_count = src->bridge_count;\n#endif\n}\n\n\nint config__read(struct mosquitto__config *config, bool reload)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\tstruct config_recurse cr;\n\tint lineno = 0;\n#ifdef WITH_PERSISTENCE\n\tsize_t len;\n#endif\n\tstruct mosquitto__config config_reload;\n\tint i;\n\n\tif(reload){\n\t\tmemset(&config_reload, 0, sizeof(struct mosquitto__config));\n\t}\n\n\tcr.log_dest = MQTT3_LOG_NONE;\n\tcr.log_dest_set = 0;\n\tcr.log_type = MOSQ_LOG_NONE;\n\tcr.log_type_set = 0;\n\n\tif(!db.config_file){\n\t\treturn 0;\n\t}\n\n\tif(reload){\n\t\t/* Re-initialise appropriate config vars to default for reload. */\n\t\tconfig__init_reload(&config_reload);\n\t\tconfig_reload.listeners = config->listeners;\n\t\tconfig_reload.listener_count = config->listener_count;\n\t\tcur_security_options = NULL;\n\t\trc = config__read_file(&config_reload, reload, db.config_file, &cr, 0, &lineno);\n\t}else{\n\t\trc = config__read_file(config, reload, db.config_file, &cr, 0, &lineno);\n\t}\n\tif(rc){\n\t\tif(lineno > 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error found at %s:%d.\", db.config_file, lineno);\n\t\t}\n\t\treturn rc;\n\t}\n\n\tif(reload){\n\t\tconfig__copy(&config_reload, config);\n\t}\n\n\t/* If auth/access options are set and allow_anonymous not explicitly set, disallow anon. */\n\tif(config->local_only == false){\n\t\tif(config->per_listener_settings){\n\t\t\tfor(i=0; i<config->listener_count; i++){\n\t\t\t\t/* Default option if no security options set */\n\t\t\t\tif(config->listeners[i].security_options->allow_anonymous == -1){\n\t\t\t\t\tconfig->listeners[i].security_options->allow_anonymous = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}else{\n\t\t\tif(config->security_options.allow_anonymous == -1){\n\t\t\t\tconfig->security_options.allow_anonymous = false;\n\t\t\t}\n\t\t}\n\t}\n#ifdef WITH_PERSISTENCE\n\tif(config->persistence){\n\t\tif(!config->persistence_file){\n\t\t\tconfig->persistence_file = mosquitto_strdup(\"mosquitto.db\");\n\t\t\tif(!config->persistence_file){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t\tmosquitto_FREE(config->persistence_filepath);\n\t\tif(config->persistence_location && strlen(config->persistence_location)){\n\t\t\tlen = strlen(config->persistence_location) + strlen(config->persistence_file) + 2;\n\t\t\tconfig->persistence_filepath = mosquitto_malloc(len);\n\t\t\tif(!config->persistence_filepath){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n#ifdef WIN32\n\t\t\tsnprintf(config->persistence_filepath, len, \"%s\\\\%s\", config->persistence_location, config->persistence_file);\n#else\n\t\t\tsnprintf(config->persistence_filepath, len, \"%s/%s\", config->persistence_location, config->persistence_file);\n#endif\n\t\t}else{\n\t\t\tconfig->persistence_filepath = mosquitto_strdup(config->persistence_file);\n\t\t\tif(!config->persistence_filepath){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\t/* Default to drop to mosquitto user if no other user specified. This must\n\t * remain here even though it is covered in config__parse_args() because this\n\t * function may be called on its own. */\n\tif(!config->user){\n\t\tconfig->user = mosquitto_strdup(\"mosquitto\");\n\t}\n\n#ifdef WITH_BRIDGE\n\tfor(i=0; i<config->bridge_count; i++){\n\t\tif(!config->bridges[i]->name){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge configuration: bridge name not defined.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(config->bridges[i]->addresses  == 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge configuration: no remote addresses defined.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(config->bridges[i]->topic_count == 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge configuration: no topics defined.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n#ifdef FINAL_WITH_TLS_PSK\n\t\tif(config->bridges[i]->tls_psk && !config->bridges[i]->tls_psk_identity){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge configuration: missing bridge_identity.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(config->bridges[i]->tls_psk_identity && !config->bridges[i]->tls_psk){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge configuration: missing bridge_psk.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n#endif\n\t}\n#endif\n\n\tif(cr.log_dest_set){\n\t\tconfig->log_dest = cr.log_dest;\n\t}\n\tif(db.quiet){\n\t\tconfig->log_type = 0;\n\t}else if(db.verbose){\n\t\tconfig->log_type = UINT_MAX;\n\t}else if(cr.log_type_set){\n\t\tconfig->log_type = cr.log_type;\n\t}\n#ifdef WITH_BRIDGE\n\treturn config__check_bridges(config);\n#else\n\treturn MOSQ_ERR_SUCCESS;\n#endif\n}\n\n\nstatic mosquitto_plugin_id_t *config__plugin_find(const char *name)\n{\n\tif(db.plugins && name){\n\t\tfor(int i=0; i<db.plugin_count; i++){\n\t\t\tif(db.plugins[i]->config.name && !strcmp(db.plugins[i]->config.name, name)){\n\t\t\t\treturn db.plugins[i];\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\nstatic mosquitto_plugin_id_t *config__plugin_load(const char *name, const char *path)\n{\n\tmosquitto_plugin_id_t *plugin = NULL;\n\tmosquitto_plugin_id_t **plugins = NULL;\n\n\tif(name && config__plugin_find(name)){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Duplicate plugin name '%s'.\", name);\n\t\treturn NULL;\n\t}\n\n\tif(!path || !strcmp(path, \"\")){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Missing plugin path for plugin name '%s'.\", name);\n\t\treturn NULL;\n\t}\n\n\tplugin = mosquitto_calloc(1, sizeof(mosquitto_plugin_id_t));\n\tif(!plugin){\n\t\tgoto error;\n\t}\n\n\tif(name){\n\t\tplugin->config.name = mosquitto_strdup(name);\n\t}\n\tplugin->config.path = mosquitto_strdup(path);\n\tif((name && !plugin->config.name) || !plugin->config.path){\n\t\tgoto error;\n\t}\n\tplugin->config.options = NULL;\n\tplugin->config.option_count = 0;\n\tplugin->config.deny_special_chars = true;\n\n\t/* Add to db list */\n\tplugins = mosquitto_realloc(db.plugins, (size_t)(db.plugin_count+1)*sizeof(struct mosquitto__plugin_config *));\n\tif(!plugins){\n\t\tgoto error;\n\t}\n\n\tplugins[db.plugin_count] = plugin;\n\tdb.plugins = plugins;\n\tdb.plugin_count++;\n\n\treturn plugin;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\tif(plugin){\n\t\tmosquitto_FREE(plugin->config.name);\n\t\tmosquitto_FREE(plugin->config.path);\n\t}\n\tmosquitto_FREE(plugin);\n\treturn NULL;\n}\n\n\nint config__plugin_add_secopt(mosquitto_plugin_id_t *plugin, struct mosquitto__security_options *security_options)\n{\n\tstruct mosquitto__security_options **new_options;\n\tmosquitto_plugin_id_t **new_plugins;\n\n\tnew_options = mosquitto_realloc(plugin->config.security_options, (size_t)(plugin->config.security_option_count+1)*sizeof(struct mosquitto__security_options *));\n\tnew_plugins = mosquitto_realloc(security_options->plugins, (size_t)(security_options->plugin_count+1)*sizeof(mosquitto_plugin_id_t *));\n\n\tif(!new_options || !new_plugins){\n\t\tmosquitto_FREE(new_options);\n\t\tmosquitto_FREE(new_plugins);\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tnew_options[plugin->config.security_option_count] = security_options;\n\tplugin->config.security_options = new_options;\n\tplugin->config.security_option_count++;\n\n\tnew_plugins[security_options->plugin_count] = plugin;\n\tsecurity_options->plugins = new_plugins;\n\tsecurity_options->plugin_count++;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int config__read_file_core(struct mosquitto__config *config, bool reload, struct config_recurse *cr, int level, int *lineno, FILE *fptr, char **buf, int *buflen)\n{\n\tint rc;\n\tchar *token;\n\tint tmp_int;\n\tchar *saveptr = NULL;\n#ifdef WITH_BRIDGE\n\tchar *tmp_char;\n\tstruct mosquitto__bridge *cur_bridge = NULL;\n#endif\n\tmosquitto_plugin_id_t *cur_plugin = NULL;\n\n\tchar *key;\n\tstruct mosquitto__listener *cur_listener = NULL;\n\tint i;\n\tint lineno_ext = 0;\n\tsize_t prefix_len;\n\tsize_t slen;\n#ifdef WITH_WEBSOCKETS\n\tchar **ws_origins = NULL;\n#endif\n\n\t*lineno = 0;\n\n\twhile(mosquitto_fgets(buf, buflen, fptr)){\n\t\t(*lineno)++;\n\t\tif((*buf)[0] != '#' && (*buf)[0] != 10 && (*buf)[0] != 13){\n\t\t\tslen = strlen(*buf);\n\t\t\tif(slen == 0){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twhile((*buf)[slen-1] == 10 || (*buf)[slen-1] == 13){\n\t\t\t\t(*buf)[slen-1] = 0;\n\t\t\t\tslen = strlen(*buf);\n\t\t\t\tif(slen == 0){\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\ttoken = strtok_r((*buf), \" \", &saveptr);\n\t\t\tif(token){\n\t\t\t\tif(!strcmp(token, \"accept_protocol_versions\")){\n\t\t\t\t\tREQUIRE_NON_DEFAULT_LISTENER(token);\n\t\t\t\t\tcur_listener->disable_protocol_v3 = true;\n\t\t\t\t\tcur_listener->disable_protocol_v4 = true;\n\t\t\t\t\tcur_listener->disable_protocol_v5 = true;\n\t\t\t\t\tif(saveptr == NULL){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty '%s' value in configuration.\", \"accept_protocol_versions\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\ttoken = strtok_r(saveptr, \", \\t\", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"accept_protocol_versions\");\n\n\t\t\t\t\twhile(token){\n\t\t\t\t\t\tif(!strcmp(token, \"3\")){\n\t\t\t\t\t\t\tcur_listener->disable_protocol_v3 = false;\n\t\t\t\t\t\t}else if(!strcmp(token, \"4\")){\n\t\t\t\t\t\t\tcur_listener->disable_protocol_v4 = false;\n\t\t\t\t\t\t}else if(!strcmp(token, \"5\")){\n\t\t\t\t\t\t\tcur_listener->disable_protocol_v5 = false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttoken = strtok_r(NULL, \", \\t\", &saveptr);\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"acl_file\")){\n\t\t\t\t\tREQUIRE_LISTENER_IF_PER_LISTENER(token);\n\n\t\t\t\t\tconf__set_cur_security_options(config, &cur_listener, &cur_security_options, token);\n\t\t\t\t\tmosquitto_FREE(cur_security_options->acl_data.acl_file);\n\t\t\t\t\tif(conf__parse_string(&token, \"acl_file\", &cur_security_options->acl_data.acl_file, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"address\") || !strcmp(token, \"addresses\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(cur_bridge->addresses){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge configuration, 'address' only allowed once.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\twhile((token = strtok_r(NULL, \" \", &saveptr))){\n\t\t\t\t\t\tif(token[0] == '#'){\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstruct bridge_address *new_addresses = mosquitto_realloc(cur_bridge->addresses, sizeof(struct bridge_address)*(size_t)(cur_bridge->address_count+1));\n\t\t\t\t\t\tif(!new_addresses){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcur_bridge->address_count++;\n\t\t\t\t\t\tcur_bridge->addresses = new_addresses;\n\t\t\t\t\t\tmemset(&cur_bridge->addresses[cur_bridge->address_count-1], 0, sizeof(struct bridge_address));\n\t\t\t\t\t\tcur_bridge->addresses[cur_bridge->address_count-1].address = mosquitto_strdup(token);\n\t\t\t\t\t\tif(!cur_bridge->addresses[cur_bridge->address_count-1].address){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor(i=0; i<cur_bridge->address_count; i++){\n\t\t\t\t\t\t/* cur_bridge->addresses[i].address is now\n\t\t\t\t\t\t * \"address[:port]\". If address is an IPv6 address,\n\t\t\t\t\t\t * then port is required. We must check for the :\n\t\t\t\t\t\t * backwards. */\n\t\t\t\t\t\ttmp_char = strrchr(cur_bridge->addresses[i].address, ':');\n\t\t\t\t\t\tif(tmp_char){\n\t\t\t\t\t\t\t/* Remove ':', so cur_bridge->addresses[i].address\n\t\t\t\t\t\t\t * now just looks like the address. */\n\t\t\t\t\t\t\ttmp_char[0] = '\\0';\n\n\t\t\t\t\t\t\t/* The remainder of the string */\n\t\t\t\t\t\t\ttmp_int = atoi(&tmp_char[1]);\n\t\t\t\t\t\t\tif(tmp_int < 1 || tmp_int > UINT16_MAX){\n\t\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge port value (%d).\", tmp_int);\n\t\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcur_bridge->addresses[i].port = (uint16_t)tmp_int;\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\tcur_bridge->addresses[i].port = 1883;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconf__attempt_resolve(cur_bridge->addresses[i].address, \"bridge address\", MOSQ_LOG_WARNING, \"Warning\");\n\t\t\t\t\t}\n\t\t\t\t\tif(cur_bridge->address_count == 0){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty address value in configuration.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"allow_anonymous\")){\n\t\t\t\t\tREQUIRE_LISTENER_IF_PER_LISTENER(token);\n\t\t\t\t\tPER_LISTENER_ALTERNATIVE(token, \"listener_allow_anonymous\");\n\t\t\t\t\tconf__set_cur_security_options(config, &cur_listener, &cur_security_options, token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"allow_anonymous\", (bool *)&cur_security_options->allow_anonymous, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"allow_duplicate_messages\")){\n\t\t\t\t\tOPTION_DEPRECATED(token, \"The behaviour will default to true.\");\n\t\t\t\t\tif(conf__parse_bool(&token, \"allow_duplicate_messages\", &config->allow_duplicate_messages, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"allow_zero_length_clientid\")){\n\t\t\t\t\tREQUIRE_LISTENER_IF_PER_LISTENER(token);\n\t\t\t\t\tconf__set_cur_security_options(config, &cur_listener, &cur_security_options, token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"allow_zero_length_clientid\", &cur_security_options->allow_zero_length_clientid, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strncmp(token, \"auth_opt_\", strlen(\"auth_opt_\")) || !strncmp(token, \"plugin_opt_\", strlen(\"plugin_opt_\"))){\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* Auth plugin not currently valid for reloading. */\n\n\t\t\t\t\t}\n\t\t\t\t\tREQUIRE_PLUGIN(token);\n\n\t\t\t\t\tif(!strncmp(token, \"auth_opt_\", strlen(\"auth_opt_\"))){\n\t\t\t\t\t\tprefix_len = strlen(\"auth_opt_\");\n\t\t\t\t\t}else{\n\t\t\t\t\t\tprefix_len = strlen(\"plugin_opt_\");\n\t\t\t\t\t}\n\t\t\t\t\tif(strlen(token) < prefix_len + 3){\n\t\t\t\t\t\t/* auth_opt_ == 9, + one digit key == 10, + one space == 11, + one value == 12 */\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'plugin_opt_' config option.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tkey = mosquitto_strdup(&token[prefix_len]);\n\t\t\t\t\tif(!key){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}else if(STREMPTY(key)){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty 'plugin_opt_' config option.\");\n\t\t\t\t\t\tmosquitto_FREE(key);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\ttoken = saveptr;\n\t\t\t\t\tif(token && token[0]){\n\t\t\t\t\t\twhile(token[0] == ' ' || token[0] == '\\t'){\n\t\t\t\t\t\t\ttoken++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstruct mosquitto_opt *new_options = mosquitto_realloc(cur_plugin->config.options, (size_t)(cur_plugin->config.option_count+1)*sizeof(struct mosquitto_opt));\n\t\t\t\t\t\tif(!new_options){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\t\tmosquitto_FREE(key);\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcur_plugin->config.option_count++;\n\t\t\t\t\t\tcur_plugin->config.options = new_options;\n\t\t\t\t\t\tcur_plugin->config.options[cur_plugin->config.option_count-1].key = key;\n\t\t\t\t\t\tcur_plugin->config.options[cur_plugin->config.option_count-1].value = mosquitto_strdup(token);\n\t\t\t\t\t\tif(!cur_plugin->config.options[cur_plugin->config.option_count-1].value){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty '%s' value in configuration.\", key);\n\t\t\t\t\t\tmosquitto_FREE(key);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"auth_plugin\") || !strcmp(token, \"plugin\") || !strcmp(token, \"global_plugin\")){\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* plugin not currently valid for reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tif(!strcmp(token, \"global_plugin\")){\n\t\t\t\t\t\tcur_security_options = &db.config->security_options;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tREQUIRE_LISTENER_IF_PER_LISTENER(token);\n\t\t\t\t\t\tconf__set_cur_security_options(config, &cur_listener, &cur_security_options, token);\n\t\t\t\t\t}\n\n\t\t\t\t\tcur_plugin = config__plugin_load(NULL, saveptr);\n\t\t\t\t\tif(cur_plugin == NULL){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(config__plugin_add_secopt(cur_plugin, cur_security_options)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"auth_plugin_deny_special_chars\")){\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* Auth plugin not currently valid for reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tif(!cur_plugin){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: An auth_plugin_deny_special_chars option exists in the config file without a plugin.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(conf__parse_bool(&token, \"auth_plugin_deny_special_chars\", &cur_plugin->config.deny_special_chars, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"plugin_load\")){\n\t\t\t\t\tchar *name = NULL;\n\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* plugin not currently valid for reloading. */\n\n\t\t\t\t\t}\n\t\t\t\t\tname = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(name, \"plugin_load\");\n\n\t\t\t\t\tcur_plugin = config__plugin_load(name, saveptr);\n\t\t\t\t\tif(cur_plugin == NULL){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"plugin_use\")){\n\t\t\t\t\tchar *name = NULL;\n\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* plugin not currently valid for reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tREQUIRE_NON_DEFAULT_LISTENER(token);\n\n\t\t\t\t\tif(conf__parse_string(&token, \"plugin_use\", &name, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_plugin = config__plugin_find(name);\n\t\t\t\t\tif(!cur_plugin){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Plugin '%s' not previously loaded.\", name);\n\t\t\t\t\t\tmosquitto_FREE(name);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_FREE(name);\n\t\t\t\t\tif(config__plugin_add_secopt(cur_plugin, cur_listener->security_options)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"auto_id_prefix\")){\n\t\t\t\t\tREQUIRE_LISTENER_IF_PER_LISTENER(token);\n\t\t\t\t\tOPTION_DEPRECATED(token, \"Please use 'listener_auto_id_prefix' instead.\");\n\t\t\t\t\tconf__set_cur_security_options(config, &cur_listener, &cur_security_options, token);\n\t\t\t\t\tif(conf__parse_string(&token, \"auto_id_prefix\", &cur_security_options->auto_id_prefix, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(cur_security_options->auto_id_prefix){\n\t\t\t\t\t\tif(strlen(cur_security_options->auto_id_prefix) > 50){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: auto_id_prefix length must be <= 50.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcur_security_options->auto_id_prefix_len = (uint16_t)strlen(cur_security_options->auto_id_prefix);\n\t\t\t\t\t}else{\n\t\t\t\t\t\tcur_security_options->auto_id_prefix_len = 0;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"autosave_interval\")){\n\t\t\t\t\tif(conf__parse_int(&token, \"autosave_interval\", &config->autosave_interval, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(config->autosave_interval < 0){\n\t\t\t\t\t\tconfig->autosave_interval = 0;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"autosave_on_changes\")){\n\t\t\t\t\tif(conf__parse_bool(&token, \"autosave_on_changes\", &config->autosave_on_changes, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"bind_address\")){\n\t\t\t\t\tOPTION_DEPRECATED(token, \"\");\n\t\t\t\t\tconfig->local_only = false;\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* Rebinding listeners not valid during reloading. */\n\n\t\t\t\t\t}\n\t\t\t\t\tif(config__create_default_listener(config, token)){\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tcur_listener = config->default_listener;\n\n\t\t\t\t\tif(conf__parse_string(&token, \"default listener bind_address\", &config->default_listener->host, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(conf__attempt_resolve(config->default_listener->host, \"bind_address\", MOSQ_LOG_ERR, \"Error\")){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"bind_interface\")){\n#ifdef SO_BINDTODEVICE\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* Rebinding listeners not valid during reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bind_interface\", &cur_listener->bind_interface, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: bind_interface specified but socket option not available.\");\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_attempt_unsubscribe\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"bridge_attempt_unsubscribe\", &cur_bridge->attempt_unsubscribe, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_cafile\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tREQUIRE_BRIDGE_NO_TLS_PSK(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_cafile\", &cur_bridge->tls_cafile, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_alpn\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_alpn\", &cur_bridge->tls_alpn, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_ciphers\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_ciphers\", &cur_bridge->tls_ciphers, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_ciphers_tls1.3\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_ciphers_tls1.3\", &cur_bridge->tls_13_ciphers, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_bind_address\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_bind_address\", &cur_bridge->bind_address, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_capath\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tREQUIRE_BRIDGE_NO_TLS_PSK(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_capath\", &cur_bridge->tls_capath, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_certfile\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tREQUIRE_BRIDGE_NO_TLS_PSK(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_certfile\", &cur_bridge->tls_certfile, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_identity\")){\n#if defined(WITH_BRIDGE) && defined(FINAL_WITH_TLS_PSK)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tREQUIRE_BRIDGE_NO_X509(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_identity\", &cur_bridge->tls_psk_identity, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS-PSK support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_insecure\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"bridge_insecure\", &cur_bridge->tls_insecure, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(cur_bridge->tls_insecure){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge '%s' using insecure mode.\", cur_bridge->name);\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS-PSK support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_require_ocsp\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"bridge_require_ocsp\", &cur_bridge->tls_ocsp_required, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_max_packet_size\")){\n#if defined(WITH_BRIDGE)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_int(&token, \"bridge_max_packet_size\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0){\n\t\t\t\t\t\ttmp_int = 0;\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->maximum_packet_size = (uint32_t)tmp_int;\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_max_topic_alias\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_int(&token, \"bridge_max_topic_alias\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\n\t\t\t\t\tif(tmp_int < 0 || tmp_int > UINT16_MAX){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: bridge_max_topic_alias must be > 0 and <= 65535.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->max_topic_alias = (uint16_t)tmp_int;\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_outgoing_retain\")){\n#if defined(WITH_BRIDGE)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"bridge_outgoing_retain\", &cur_bridge->outgoing_retain, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_keyfile\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tREQUIRE_BRIDGE_NO_TLS_PSK(token);\n\t\t\t\t\tmosquitto_FREE(cur_bridge->tls_keyfile);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_keyfile\", &cur_bridge->tls_keyfile, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_protocol_version\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\ttoken = strtok_r(NULL, \"\", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"bridge_protocol_version\");\n\n\t\t\t\t\tif(!strcmp(token, \"mqttv31\")){\n\t\t\t\t\t\tcur_bridge->protocol_version = mosq_p_mqtt31;\n\t\t\t\t\t}else if(!strcmp(token, \"mqttv311\")){\n\t\t\t\t\t\tcur_bridge->protocol_version = mosq_p_mqtt311;\n\t\t\t\t\t}else if(!strcmp(token, \"mqttv50\")){\n\t\t\t\t\t\tcur_bridge->protocol_version = mosq_p_mqtt5;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'bridge_protocol_version' value (%s).\", token);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_psk\")){\n#if defined(WITH_BRIDGE) && defined(FINAL_WITH_TLS_PSK)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tREQUIRE_BRIDGE_NO_X509(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_psk\", &cur_bridge->tls_psk, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS-PSK support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_receive_maximum\")){\n#if defined(WITH_BRIDGE)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_int(&token, \"bridge_receive_maximum\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int <= 0){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: bridge_receive_maximum must be greater than 0.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}else if((uint64_t)tmp_int > (uint64_t)UINT16_MAX){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: bridge_receive_maximum must be lower than %u.\", UINT16_MAX);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->receive_maximum = (uint16_t)tmp_int;\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_reload_type\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"bridge_reload_type\");\n\n\t\t\t\t\tif(!strcmp(token, \"lazy\")){\n\t\t\t\t\t\tcur_bridge->reload_type = brt_lazy;\n\t\t\t\t\t}else if(!strcmp(token, \"immediate\")){\n\t\t\t\t\t\tcur_bridge->reload_type = brt_immediate;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'bridge_reload_type' value in configuration (%s).\", token);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_session_expiry_interval\")){\n#if defined(WITH_BRIDGE)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_int(&token, \"bridge_session_expiry_interval\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: bridge_session_expiry_interval must not be negative.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}else if((uint64_t)tmp_int > (uint64_t)MQTT_SESSION_EXPIRY_NEVER){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: bridge_session_expiry_interval must be lower than %u.\", MQTT_SESSION_EXPIRY_NEVER);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->session_expiry_interval = (uint32_t)tmp_int;\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_tcp_keepalive\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\n\t\t\t\t\tif(conf__parse_int(&token, \"bridge_tcp_keepalive_idle\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int <= 0){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: invalid TCP keepalive idle value.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->tcp_keepalive_idle = (unsigned int)tmp_int;\n\n\t\t\t\t\tif(conf__parse_int(&token, \"bridge_tcp_keepalive_interval\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int <= 0){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: invalid TCP keepalive interval value.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->tcp_keepalive_interval = (unsigned int)tmp_int;\n\n\t\t\t\t\tif(conf__parse_int(&token, \"bridge_tcp_keepalive_counter\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int <= 0){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: invalid TCP keepalive counter value.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->tcp_keepalive_counter = (unsigned int)tmp_int;\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_tcp_user_timeout\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n#ifdef WITH_TCP_USER_TIMEOUT\n\t\t\t\t\tif(conf__parse_int(&token, \"bridge_tcp_user_timeout\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: invalid TCP user timeout value.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->tcp_user_timeout = tmp_int;\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge TCP user timeout support not available.\");\n#endif\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_tls_use_os_certs\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"bridge_tls_use_os_certs\", &cur_bridge->tls_use_os_certs, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"bridge_tls_version\")){\n#if defined(WITH_BRIDGE) && defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge_tls_version\", &cur_bridge->tls_version, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge and/or TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"cafile\")){\n#if defined(WITH_TLS)\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tif(cur_listener->psk_hint){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Cannot use both certificate and psk encryption in a single listener.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_FREE(cur_listener->cafile);\n\t\t\t\t\tif(conf__parse_string(&token, \"cafile\", &cur_listener->cafile, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"capath\")){\n#ifdef WITH_TLS\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tmosquitto_FREE(cur_listener->capath);\n\t\t\t\t\tif(conf__parse_string(&token, \"capath\", &cur_listener->capath, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"certfile\")){\n#ifdef WITH_TLS\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(cur_listener->psk_hint){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Cannot use both certificate and psk encryption in a single listener.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_FREE(cur_listener->certfile);\n\t\t\t\t\tif(conf__parse_string(&token, \"certfile\", &cur_listener->certfile, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"check_retain_source\")){\n\t\t\t\t\tif(conf__parse_bool(&token, \"check_retain_source\", &config->check_retain_source, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"ciphers\")){\n#ifdef WITH_TLS\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tmosquitto_FREE(cur_listener->ciphers);\n\t\t\t\t\tif(conf__parse_string(&token, \"ciphers\", &cur_listener->ciphers, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"ciphers_tls1.3\")){\n#if defined(WITH_TLS) && (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER > 0x3040000FL)\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tmosquitto_FREE(cur_listener->ciphers_tls13);\n\t\t\t\t\tif(conf__parse_string(&token, \"ciphers_tls1.3\", &cur_listener->ciphers_tls13, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: ciphers_tls1.3 support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"clientid\") || !strcmp(token, \"remote_clientid\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge remote clientid\", &cur_bridge->remote_clientid, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"cleansession\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"cleansession\", &cur_bridge->clean_start, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"local_cleansession\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"local_cleansession\", (bool *)&cur_bridge->clean_start_local, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"clientid_prefixes\")){\n\t\t\t\t\tOPTION_DEPRECATED(token, \"\");\n\t\t\t\t\tmosquitto_FREE(config->clientid_prefixes);\n\t\t\t\t\tif(conf__parse_string(&token, \"clientid_prefixes\", &config->clientid_prefixes, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"connection\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"connection\");\n\n\t\t\t\t\t/* Check for existing bridge name. */\n\t\t\t\t\tfor(i=0; i<config->bridge_count; i++){\n\t\t\t\t\t\tif(!strcmp(config->bridges[i]->name, token)){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Duplicate bridge name '%s'.\", token);\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tstruct mosquitto__bridge **bridges_new = mosquitto_realloc(config->bridges, (size_t)(config->bridge_count+1)*sizeof(struct mosquitto__bridge *));\n\t\t\t\t\tif(!bridges_new){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->bridges = bridges_new;\n\t\t\t\t\tcur_bridge = mosquitto_malloc(sizeof(struct mosquitto__bridge));\n\t\t\t\t\tif(!cur_bridge){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\n\t\t\t\t\tconfig->bridge_count++;\n\t\t\t\t\tconfig->bridges[config->bridge_count-1] = cur_bridge;\n\n\t\t\t\t\tmemset(cur_bridge, 0, sizeof(struct mosquitto__bridge));\n\t\t\t\t\tcur_bridge->name = mosquitto_strdup(token);\n\t\t\t\t\tif(!cur_bridge->name){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->keepalive = 60;\n\t\t\t\t\tcur_bridge->notifications = true;\n\t\t\t\t\tcur_bridge->notifications_local_only = false;\n\t\t\t\t\tcur_bridge->start_type = bst_automatic;\n\t\t\t\t\tcur_bridge->idle_timeout = 60;\n\t\t\t\t\tcur_bridge->restart_timeout = 0;\n\t\t\t\t\tcur_bridge->backoff_base = 5 * 1000;\n\t\t\t\t\tcur_bridge->backoff_cap = 30 * 1000;\n\t\t\t\t\tcur_bridge->stable_connection_period = 0;\n\t\t\t\t\tcur_bridge->threshold = 10;\n\t\t\t\t\tcur_bridge->try_private = true;\n\t\t\t\t\tcur_bridge->attempt_unsubscribe = true;\n\t\t\t\t\tcur_bridge->protocol_version = mosq_p_mqtt311;\n\t\t\t\t\tcur_bridge->primary_retry_sock = INVALID_SOCKET;\n\t\t\t\t\tcur_bridge->outgoing_retain = true;\n\t\t\t\t\tcur_bridge->clean_start_local = -1;\n\t\t\t\t\tcur_bridge->reload_type = brt_lazy;\n\t\t\t\t\tcur_bridge->max_topic_alias = 10;\n#ifdef WITH_TCP_USER_TIMEOUT\n\t\t\t\t\tcur_bridge->tcp_user_timeout = -1;\n#endif\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"connection_messages\")){\n\t\t\t\t\tif(conf__parse_bool(&token, token, &config->connection_messages, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"crlfile\")){\n#ifdef WITH_TLS\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tmosquitto_FREE(cur_listener->crlfile);\n\t\t\t\t\tif(conf__parse_string(&token, \"crlfile\", &cur_listener->crlfile, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"dhparamfile\")){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: dhparamfile is no longer required.\");\n\t\t\t\t}else if(!strcmp(token, \"disable_client_cert_date_checks\")){\n#ifdef WITH_TLS\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"disable_client_cert_date_checks\", &cur_listener->disable_client_cert_date_checks, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"enable_control_api\")){\n#ifdef WITH_CONTROL\n\t\t\t\t\tif(conf__parse_bool(&token, \"enable_control_api\", &config->enable_control_api, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: $CONTROL support not available (enable_control_api).\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"enable_proxy_protocol\")){\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: PROXY support not available with libwebsockets.\");\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n#endif\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tif(conf__parse_int(&token, \"enable_proxy_protocol\", &cur_listener->enable_proxy_protocol, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(cur_listener->enable_proxy_protocol < 1 || cur_listener->enable_proxy_protocol > 2){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: enable_proxy_protocol must be 1 or 2.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"global_max_clients\")){\n\t\t\t\t\tif(conf__parse_int(&token, \"global_max_clients\", &config->global_max_clients, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"global_max_connections\")){\n\t\t\t\t\tif(conf__parse_int(&token, \"global_max_connections\", &config->global_max_connections, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"http_dir\")){\n#if defined(WITH_WEBSOCKETS) || defined(WITH_HTTP_API)\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* Not valid for reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"http_dir\", &cur_listener->http_dir, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#ifdef WIN32\n\t\t\t\t\tchar *http_dir_canonical = _fullpath(NULL, cur_listener->http_dir, 0);\n\t\t\t\t\tconst char sep = '\\\\';\n#else\n\t\t\t\t\tchar *http_dir_canonical = realpath(cur_listener->http_dir, NULL);\n\t\t\t\t\tconst char sep = '/';\n#endif\n\t\t\t\t\tif(!http_dir_canonical){\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tsize_t http_dir_len = strlen(http_dir_canonical) + sizeof(sep) + 1;\n\t\t\t\t\tchar *http_dir_canonical_sep = mosquitto_calloc(http_dir_len, sizeof(char));\n\t\t\t\t\tif(!http_dir_canonical_sep){\n\t\t\t\t\t\tfree(http_dir_canonical);\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tsnprintf(http_dir_canonical_sep, http_dir_len, \"%s%c\", http_dir_canonical, sep);\n\t\t\t\t\tfree(http_dir_canonical);\n\t\t\t\t\tmosquitto_FREE(cur_listener->http_dir);\n\t\t\t\t\tcur_listener->http_dir = http_dir_canonical_sep;\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: http_dir support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"idle_timeout\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_int(&token, \"idle_timeout\", &cur_bridge->idle_timeout, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(cur_bridge->idle_timeout < 1){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"idle_timeout interval too low, using 1 second.\");\n\t\t\t\t\t\tcur_bridge->idle_timeout = 1;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"include_dir\")){\n\t\t\t\t\tif(level == 0){\n\t\t\t\t\t\tchar **files;\n\t\t\t\t\t\tint file_count;\n\n\t\t\t\t\t\t/* Only process include_dir from the main config file. */\n\t\t\t\t\t\ttoken = strtok_r(NULL, \"\", &saveptr);\n\t\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"include_dir\");\n\n\t\t\t\t\t\trc = config__get_dir_files(token, &files, &file_count);\n\t\t\t\t\t\tif(rc){\n\t\t\t\t\t\t\treturn rc;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor(i=0; i<file_count; i++){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Loading config file '%s'\", files[i]);\n\n\t\t\t\t\t\t\trc = config__read_file(config, reload, files[i], cr, level+1, &lineno_ext);\n\t\t\t\t\t\t\tif(rc){\n\t\t\t\t\t\t\t\tif(lineno_ext > 0){\n\t\t\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error found at '%s:%d'.\", files[i], lineno_ext);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t/* Free happens below */\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor(i=0; i<file_count; i++){\n\t\t\t\t\t\t\tmosquitto_FREE(files[i]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmosquitto_FREE(files);\n\t\t\t\t\t\tif(rc){\n\t\t\t\t\t\t\treturn rc;    /* This returns if config__read_file() fails above */\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: The include_dir option is only valid in the main configuration file.\");\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"keepalive_interval\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_int(&token, \"keepalive_interval\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int > UINT16_MAX){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Bridge keepalive value too high.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 5){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"keepalive interval too low, using 5 seconds.\");\n\t\t\t\t\t\ttmp_int = 5;\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->keepalive = (uint16_t)tmp_int;\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"keyfile\")){\n#ifdef WITH_TLS\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tmosquitto_FREE(cur_listener->keyfile);\n\t\t\t\t\tif(conf__parse_string(&token, \"keyfile\", &cur_listener->keyfile, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"listener\")){\n\t\t\t\t\tconfig->local_only = false;\n\n\t\t\t\t\tif(conf__parse_int(&token, \"listener port\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#ifdef WITH_UNIX_SOCKETS\n\t\t\t\t\tif(tmp_int < 0 || tmp_int > UINT16_MAX){\n#else\n\t\t\t\t\tif(tmp_int < 1 || tmp_int > UINT16_MAX){\n#endif\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'port' value (%d).\", tmp_int);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\n\t\t\t\t\t/* Look for bind address / unix socket path */\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tif(token != NULL && token[0] == '#'){\n\t\t\t\t\t\ttoken = NULL;\n\t\t\t\t\t}\n\n\t\t\t\t\tif(tmp_int == 0 && token == NULL){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: A listener with port 0 must provide a Unix socket path.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\t/* We reload listeners settings based on port number/unix socket path.\n\t\t\t\t\t\t * If the port number/unix path doesn't already exist, exit with a complaint. */\n\t\t\t\t\t\tcur_listener = NULL;\n#ifdef WITH_UNIX_SOCKETS\n\t\t\t\t\t\tif(tmp_int == 0){\n\t\t\t\t\t\t\tfor(i=0; i<config->listener_count; i++){\n\t\t\t\t\t\t\t\tif(config->listeners[i].unix_socket_path != NULL\n\t\t\t\t\t\t\t\t\t\t&& strcmp(config->listeners[i].unix_socket_path, token) == 0){\n\n\t\t\t\t\t\t\t\t\tcur_listener = &config->listeners[i];\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}else\n#endif\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor(i=0; i<config->listener_count; i++){\n\t\t\t\t\t\t\t\tif(config->listeners[i].port == tmp_int){\n\t\t\t\t\t\t\t\t\t/* Now check we have a matching bind address, if defined */\n\t\t\t\t\t\t\t\t\tif(config->listeners[i].host){\n\t\t\t\t\t\t\t\t\t\tif(token && !strcmp(config->listeners[i].host, token)){\n\t\t\t\t\t\t\t\t\t\t\t/* They both have a bind address, and they match */\n\t\t\t\t\t\t\t\t\t\t\tcur_listener = &config->listeners[i];\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\t\t\tif(token == NULL){\n\t\t\t\t\t\t\t\t\t\t\t/* Neither this config nor the new config have a bind address,\n\t\t\t\t\t\t\t\t\t\t\t * so they match. */\n\t\t\t\t\t\t\t\t\t\t\tcur_listener = &config->listeners[i];\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(!cur_listener){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: It is not currently possible to add/remove listeners when reloading the config file.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\tif(config__add_listener(config)){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcur_listener = &config->listeners[config->listener_count-1];\n\t\t\t\t\t}\n\n\t\t\t\t\tlistener__set_defaults(cur_listener);\n\t\t\t\t\tcur_listener->port = (uint16_t)tmp_int;\n\n\t\t\t\t\tmosquitto_FREE(cur_listener->host);\n\n#ifdef WITH_UNIX_SOCKETS\n\t\t\t\t\tmosquitto_FREE(cur_listener->unix_socket_path);\n#endif\n\n\t\t\t\t\tif(token){\n#ifdef WITH_UNIX_SOCKETS\n\t\t\t\t\t\tif(cur_listener->port == 0){\n\t\t\t\t\t\t\tcur_listener->unix_socket_path = mosquitto_strdup(token);\n\t\t\t\t\t\t}else\n#endif\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcur_listener->host = mosquitto_strdup(token);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"listener_allow_anonymous\")){\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"listener_allow_anonymous\", (bool *)&cur_listener->security_options->allow_anonymous, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"listener_auto_id_prefix\")){\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"listener_auto_id_prefix\", &cur_listener->security_options->auto_id_prefix, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(cur_listener->security_options->auto_id_prefix){\n\t\t\t\t\t\tif(strlen(cur_listener->security_options->auto_id_prefix) > 50){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: 'listener_auto_id_prefix' length must be <= 50.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcur_listener->security_options->auto_id_prefix_len = (uint16_t)strlen(cur_listener->security_options->auto_id_prefix);\n\t\t\t\t\t}else{\n\t\t\t\t\t\tcur_listener->security_options->auto_id_prefix_len = 0;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"local_clientid\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge local clientd\", &cur_bridge->local_clientid, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"local_password\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge local_password\", &cur_bridge->local_password, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"local_username\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge local_username\", &cur_bridge->local_username, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"log_dest\")){\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"log_dest\");\n\n\t\t\t\t\tcr->log_dest_set = 1;\n\t\t\t\t\tif(!strcmp(token, \"none\")){\n\t\t\t\t\t\tcr->log_dest = MQTT3_LOG_NONE;\n\t\t\t\t\t}else if(!strcmp(token, \"syslog\")){\n\t\t\t\t\t\tcr->log_dest |= MQTT3_LOG_SYSLOG;\n\t\t\t\t\t}else if(!strcmp(token, \"stdout\")){\n\t\t\t\t\t\tcr->log_dest |= MQTT3_LOG_STDOUT;\n\t\t\t\t\t}else if(!strcmp(token, \"stderr\")){\n\t\t\t\t\t\tcr->log_dest |= MQTT3_LOG_STDERR;\n\t\t\t\t\t}else if(!strcmp(token, \"topic\")){\n\t\t\t\t\t\tcr->log_dest |= MQTT3_LOG_TOPIC;\n\t\t\t\t\t}else if(!strcmp(token, \"dlt\")){\n\t\t\t\t\t\tcr->log_dest |= MQTT3_LOG_DLT;\n#ifdef ANDROID\n\t\t\t\t\t}else if(!strcmp(token, \"android\")){\n\t\t\t\t\t\tcr->log_dest |= MQTT3_LOG_ANDROID;\n#endif\n\t\t\t\t\t}else if(!strcmp(token, \"file\")){\n\t\t\t\t\t\tif(config->log_fptr || config->log_file){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Duplicate \\\"log_dest file\\\" value.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* Get remaining string. */\n\t\t\t\t\t\ttoken = saveptr;\n\t\t\t\t\t\tif(token && token[0]){\n\t\t\t\t\t\t\twhile(token[0] == ' ' || token[0] == '\\t'){\n\t\t\t\t\t\t\t\ttoken++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* Duplicate \"token\" check here saves a log__printf() */\n\t\t\t\t\t\tif(token && token[0]){\n\t\t\t\t\t\t\tconfig->log_file = mosquitto_strdup(token);\n\t\t\t\t\t\t\tif(!config->log_file){\n\t\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcr->log_dest |= MQTT3_LOG_FILE;\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty \\\"log_dest file\\\" value in configuration.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcr->log_dest |= MQTT3_LOG_FILE;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'log_dest' value (%s).\", token);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#if defined(WIN32) || defined(__CYGWIN__)\n\t\t\t\t\tif(service_handle){\n\t\t\t\t\t\tif(cr->log_dest == MQTT3_LOG_STDOUT || cr->log_dest == MQTT3_LOG_STDERR){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Cannot log to stdout/stderr when running as a Windows service.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#endif\n\t\t\t\t}else if(!strcmp(token, \"log_facility\")){\n#if defined(WIN32) || defined(__CYGWIN__)\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: log_facility not supported on Windows.\");\n#else\n\t\t\t\t\tif(conf__parse_int(&token, \"log_facility\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tswitch(tmp_int){\n\t\t\t\t\t\tcase 0:\n\t\t\t\t\t\t\tconfig->log_facility = LOG_LOCAL0;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 1:\n\t\t\t\t\t\t\tconfig->log_facility = LOG_LOCAL1;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 2:\n\t\t\t\t\t\t\tconfig->log_facility = LOG_LOCAL2;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 3:\n\t\t\t\t\t\t\tconfig->log_facility = LOG_LOCAL3;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 4:\n\t\t\t\t\t\t\tconfig->log_facility = LOG_LOCAL4;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 5:\n\t\t\t\t\t\t\tconfig->log_facility = LOG_LOCAL5;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 6:\n\t\t\t\t\t\t\tconfig->log_facility = LOG_LOCAL6;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 7:\n\t\t\t\t\t\t\tconfig->log_facility = LOG_LOCAL7;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'log_facility' value (%d).\", tmp_int);\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#endif\n\t\t\t\t}else if(!strcmp(token, \"log_timestamp\")){\n\t\t\t\t\tif(conf__parse_bool(&token, token, &config->log_timestamp, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"log_timestamp_format\")){\n\t\t\t\t\tif(conf__parse_string(&token, token, &config->log_timestamp_format, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"log_type\")){\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"log_type\");\n\n\t\t\t\t\tcr->log_type_set = 1;\n\t\t\t\t\tif(!strcmp(token, \"none\")){\n\t\t\t\t\t\tcr->log_type = MOSQ_LOG_NONE;\n\t\t\t\t\t}else if(!strcmp(token, \"information\")){\n\t\t\t\t\t\tcr->log_type |= MOSQ_LOG_INFO;\n\t\t\t\t\t}else if(!strcmp(token, \"notice\")){\n\t\t\t\t\t\tcr->log_type |= MOSQ_LOG_NOTICE;\n\t\t\t\t\t}else if(!strcmp(token, \"warning\")){\n\t\t\t\t\t\tcr->log_type |= MOSQ_LOG_WARNING;\n\t\t\t\t\t}else if(!strcmp(token, \"error\")){\n\t\t\t\t\t\tcr->log_type |= MOSQ_LOG_ERR;\n\t\t\t\t\t}else if(!strcmp(token, \"debug\")){\n\t\t\t\t\t\tcr->log_type |= MOSQ_LOG_DEBUG;\n\t\t\t\t\t}else if(!strcmp(token, \"subscribe\")){\n\t\t\t\t\t\tcr->log_type |= MOSQ_LOG_SUBSCRIBE;\n\t\t\t\t\t}else if(!strcmp(token, \"unsubscribe\")){\n\t\t\t\t\t\tcr->log_type |= MOSQ_LOG_UNSUBSCRIBE;\n\t\t\t\t\t}else if(!strcmp(token, \"internal\")){\n\t\t\t\t\t\tcr->log_type |= MOSQ_LOG_INTERNAL;\n#ifdef WITH_WEBSOCKETS\n\t\t\t\t\t}else if(!strcmp(token, \"websockets\")){\n\t\t\t\t\t\tcr->log_type |= MOSQ_LOG_WEBSOCKETS;\n#endif\n\t\t\t\t\t}else if(!strcmp(token, \"all\")){\n\t\t\t\t\t\tcr->log_type = MOSQ_LOG_ALL;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'log_type' value (%s).\", token);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"max_connections\")){\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(conf__parse_int(&token, token, &cur_listener->max_connections, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(cur_listener->max_connections < 0){\n\t\t\t\t\t\tcur_listener->max_connections = -1;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"maximum_qos\") || !strcmp(token, \"max_qos\")){\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(conf__parse_int(&token, token, &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0 || tmp_int > 2){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: 'max_qos' must be between 0 and 2 inclusive.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_listener->max_qos = (uint8_t)tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"max_inflight_bytes\")){\n\t\t\t\t\tif(conf__parse_int(&token, \"max_inflight_bytes\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0){\n\t\t\t\t\t\ttmp_int = 0;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->max_inflight_bytes = (size_t)tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"max_inflight_messages\")){\n\t\t\t\t\tif(conf__parse_int(&token, \"max_inflight_messages\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0 || tmp_int == UINT16_MAX){\n\t\t\t\t\t\ttmp_int = 0;\n\t\t\t\t\t}else if(tmp_int > UINT16_MAX){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: 'max_inflight_messages' must be <= 65535.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->max_inflight_messages = (uint16_t)tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"max_keepalive\")){\n\t\t\t\t\tif(conf__parse_int(&token, \"max_keepalive\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0 || tmp_int > UINT16_MAX){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'max_keepalive' value (%d).\", tmp_int);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->max_keepalive = (uint16_t)tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"max_packet_size\")){\n\t\t\t\t\tif(conf__parse_int(&token, \"max_packet_size\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 20){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: 'max_packet_size' must be >= 20.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->max_packet_size = (uint32_t)tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"max_queued_bytes\")){\n\t\t\t\t\tif(conf__parse_int(&token, \"max_queued_bytes\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0){\n\t\t\t\t\t\ttmp_int = 0;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->max_queued_bytes = (size_t)tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"max_queued_messages\")){\n\t\t\t\t\tif(conf__parse_int(&token, \"max_queued_messages\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0){\n\t\t\t\t\t\ttmp_int = 0;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->max_queued_messages = tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"memory_limit\")){\n\t\t\t\t\tssize_t lim;\n\t\t\t\t\tif(conf__parse_ssize_t(&token, \"memory_limit\", &lim, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(lim < 0){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'memory_limit' value (%ld).\", lim);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_memory_set_limit((size_t)lim);\n\t\t\t\t}else if(!strcmp(token, \"message_size_limit\")){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Note: It is recommended to replace `message_size_limit` with `max_packet_size`.\");\n\t\t\t\t\tif(conf__parse_int(&token, \"message_size_limit\", (int *)&config->message_size_limit, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(config->message_size_limit > MQTT_MAX_PAYLOAD){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'message_size_limit' value (%u).\", config->message_size_limit);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"mount_point\")){\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tmosquitto_FREE(cur_listener->mount_point);\n\t\t\t\t\tif(conf__parse_string(&token, \"mount_point\", &cur_listener->mount_point, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(mosquitto_pub_topic_check(cur_listener->mount_point) != MOSQ_ERR_SUCCESS){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\t\t\t\t\"Error: Invalid 'mount_point' value (%s). Does it contain a wildcard character?\",\n\t\t\t\t\t\t\t\tcur_listener->mount_point);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"notifications\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"notifications\", &cur_bridge->notifications, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"notifications_local_only\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"notifications_local_only\", &cur_bridge->notifications_local_only, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"notification_topic\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"notification_topic\", &cur_bridge->notification_topic, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"password\") || !strcmp(token, \"remote_password\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge remote_password\", &cur_bridge->remote_password, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"password_file\")){\n\t\t\t\t\tREQUIRE_LISTENER_IF_PER_LISTENER(token);\n\t\t\t\t\tconf__set_cur_security_options(config, &cur_listener, &cur_security_options, token);\n\t\t\t\t\tmosquitto_FREE(cur_security_options->password_data.password_file);\n\t\t\t\t\tif(conf__parse_string(&token, \"password_file\", &cur_security_options->password_data.password_file, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"per_listener_settings\")){\n\t\t\t\t\tOPTION_DEPRECATED(token, \"Please see the documentation for how to achieve the same effect.\");\n\t\t\t\t\tif(config->per_listener_settings){\n\t\t\t\t\t\t/* Once this is set, don't let it be unset. It should be the first config option ideally. */\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif(conf__parse_bool(&token, \"per_listener_settings\", &config->per_listener_settings, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(cur_security_options && config->per_listener_settings){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: per_listener_settings must be set before any other security settings.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"persistence\") || !strcmp(token, \"retained_persistence\")){\n\t\t\t\t\tif(conf__parse_bool(&token, token, &config->persistence, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"persistence_file\")){\n\t\t\t\t\tif(conf__parse_string(&token, \"persistence_file\", &config->persistence_file, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"persistence_location\")){\n\t\t\t\t\tif(conf__parse_string(&token, \"persistence_location\", &config->persistence_location, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"persistent_client_expiration\")){\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"persistent_client_expiration\");\n\n\t\t\t\t\ttime_t expiration_mult;\n\n\t\t\t\t\tswitch(token[strlen(token)-1]){\n\t\t\t\t\t\tcase 's':\n\t\t\t\t\t\t\texpiration_mult = 1;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'h':\n\t\t\t\t\t\t\texpiration_mult = 3600;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'd':\n\t\t\t\t\t\t\texpiration_mult = 86400;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'w':\n\t\t\t\t\t\t\texpiration_mult = 86400*7;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'm':\n\t\t\t\t\t\t\texpiration_mult = 86400*30;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'y':\n\t\t\t\t\t\t\texpiration_mult = 86400*365;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'persistent_client_expiration' duration in configuration.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\ttoken[strlen(token)-1] = '\\0';\n\t\t\t\t\tconfig->persistent_client_expiration = atoi(token)*expiration_mult;\n\t\t\t\t\tif(config->persistent_client_expiration <= 0){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'persistent_client_expiration' duration in configuration.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"pid_file\")){\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* pid file not valid for reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tif(conf__parse_string(&token, \"pid_file\", &config->pid_file, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"port\")){\n\t\t\t\t\tOPTION_DEPRECATED(token, \"Please use 'listener' instead.\");\n\t\t\t\t\tconfig->local_only = false;\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* Listeners not valid for reloading. */\n\n\t\t\t\t\t}\n\t\t\t\t\tif(config__create_default_listener(config, token)){\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tcur_listener = config->default_listener;\n\n\t\t\t\t\tif(config->default_listener->port){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Default listener port specified multiple times. Only the latest will be used.\");\n\t\t\t\t\t}\n\t\t\t\t\tif(conf__parse_int(&token, \"port\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 1 || tmp_int > UINT16_MAX){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'port' value (%d).\", tmp_int);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->default_listener->port = (uint16_t)tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"protocol\")){\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"protocol\");\n\n\t\t\t\t\tif(!strcmp(token, \"mqtt\")){\n\t\t\t\t\t\tcur_listener->protocol = mp_mqtt;\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t}else if(!strcmp(token, \"mqttsn\")){\n\t\t\t\t\t\t    cur_listener->protocol = mp_mqttsn;\n\t\t\t\t\t\t*/\n\t\t\t\t\t}else if(!strcmp(token, \"websockets\")){\n#ifdef WITH_WEBSOCKETS\n\t\t\t\t\t\tcur_listener->protocol = mp_websockets;\n#else\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Websockets support not available.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n#endif\n\t\t\t\t\t}else if(!strcmp(token, \"http_api\")){\n#ifdef WITH_HTTP_API\n\t\t\t\t\t\tcur_listener->protocol = mp_http_api;\n#else\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: HTTP API support not available.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n#endif\n\t\t\t\t\t}else{\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'protocol' value (%s).\", token);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"proxy_protocol_v2_require_tls\")){\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: PROXY support not available with libwebsockets.\");\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n#endif\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"proxy_protocol_v2_require_tls\", &cur_listener->proxy_protocol_v2_require_tls, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"psk_file\")){\n#ifdef FINAL_WITH_TLS_PSK\n\t\t\t\t\tREQUIRE_LISTENER_IF_PER_LISTENER(token);\n\t\t\t\t\tconf__set_cur_security_options(config, &cur_listener, &cur_security_options, token);\n\t\t\t\t\tif(cur_listener && cur_listener->certfile){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Cannot use both certificate and psk encryption in a single listener.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_FREE(cur_security_options->psk_file);\n\t\t\t\t\tif(conf__parse_string(&token, \"psk_file\", &cur_security_options->psk_file, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS/TLS-PSK support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"psk_hint\")){\n#ifdef FINAL_WITH_TLS_PSK\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* PSK file not valid for reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"psk_hint\", &cur_listener->psk_hint, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS/TLS-PSK support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"queue_qos0_messages\")){\n\t\t\t\t\tif(conf__parse_bool(&token, token, &config->queue_qos0_messages, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"require_certificate\")){\n#ifdef WITH_TLS\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"require_certificate\", &cur_listener->require_certificate, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"restart_timeout\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tcur_bridge->backoff_cap = 0; /* set backoff to constant mode, unless cap is specified further down */\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"restart_timeout\");\n\n\t\t\t\t\tcur_bridge->restart_timeout = atoi(token);\n\t\t\t\t\tcur_bridge->backoff_base = 0;\n\t\t\t\t\tcur_bridge->backoff_cap = 0;\n\t\t\t\t\tif(cur_bridge->restart_timeout < 1){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"restart_timeout interval too low, using 1 second.\");\n\t\t\t\t\t\tcur_bridge->restart_timeout = 1;\n\t\t\t\t\t}else if(cur_bridge->restart_timeout > 3600){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"restart_timeout interval too high, using 3600 seconds.\");\n\t\t\t\t\t\tcur_bridge->restart_timeout = 3600;\n\t\t\t\t\t}\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tif(token){\n\t\t\t\t\t\tcur_bridge->backoff_base = cur_bridge->restart_timeout;\n\t\t\t\t\t\tcur_bridge->backoff_cap = atoi(token);\n\t\t\t\t\t\tif(cur_bridge->backoff_cap < cur_bridge->backoff_base){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: backoff cap is lower than the base in restart_timeout.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}else if(cur_bridge->backoff_cap > 7200){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: backoff cap too high, using 7200 seconds.\");\n\t\t\t\t\t\t\tcur_bridge->backoff_cap = 7200;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\t\tif(token){\n\t\t\t\t\t\t\tcur_bridge->stable_connection_period = atoi(token);\n\t\t\t\t\t\t\tif(cur_bridge->stable_connection_period < 0){\n\t\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: stable connection period cannot be negative.\");\n\t\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcur_bridge->restart_timeout *= 1000; /* backoff is tracked in ms */\n\t\t\t\t\tcur_bridge->backoff_base *= 1000;\n\t\t\t\t\tcur_bridge->backoff_cap *= 1000;\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"retain_available\")){\n\t\t\t\t\tif(conf__parse_bool(&token, token, &config->retain_available, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"retain_expiry_interval\")){\n\t\t\t\t\tif(conf__parse_int(&token, token, &config->retain_expiry_interval, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(config->retain_expiry_interval < 1){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Error: retain_expiry_interval must be >= 1.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}else if(config->retain_expiry_interval > 10000000){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: retain_expiry_interval being capped at 19 years.\");\n\t\t\t\t\t\tconfig->retain_expiry_interval = 10000000;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->retain_expiry_interval *= 60;\n\t\t\t\t}else if(!strcmp(token, \"retry_interval\")){\n\t\t\t\t\tOPTION_UNAVAILABLE(token);\n\t\t\t\t}else if(!strcmp(token, \"round_robin\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"round_robin\", &cur_bridge->round_robin, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"set_tcp_nodelay\")){\n\t\t\t\t\tif(conf__parse_bool(&token, \"set_tcp_nodelay\", &config->set_tcp_nodelay, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"start_type\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"start_type\");\n\n\t\t\t\t\tif(!strcmp(token, \"automatic\")){\n\t\t\t\t\t\tcur_bridge->start_type = bst_automatic;\n\t\t\t\t\t}else if(!strcmp(token, \"lazy\")){\n\t\t\t\t\t\tcur_bridge->start_type = bst_lazy;\n\t\t\t\t\t}else if(!strcmp(token, \"manual\")){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Manual start_type not supported.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}else if(!strcmp(token, \"once\")){\n\t\t\t\t\t\tcur_bridge->start_type = bst_once;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'start_type' value in configuration (%s).\", token);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"socket_domain\")){\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* socket_domain not valid for reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"start_type\");\n\n\t\t\t\t\tif(!strcmp(token, \"ipv4\")){\n\t\t\t\t\t\tcur_listener->socket_domain = AF_INET;\n\t\t\t\t\t}else if(!strcmp(token, \"ipv6\")){\n\t\t\t\t\t\tcur_listener->socket_domain = AF_INET6;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'socket_domain' value '%s' in configuration.\", token);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"sys_interval\")){\n\t\t\t\t\tif(conf__parse_int(&token, \"sys_interval\", &config->sys_interval, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(config->sys_interval < 0 || config->sys_interval > 65535){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'sys_interval' value (%d).\", config->sys_interval);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"threshold\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_int(&token, \"threshold\", &cur_bridge->threshold, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(cur_bridge->threshold < 1){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"threshold too low, using 1 message.\");\n\t\t\t\t\t\tcur_bridge->threshold = 1;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"tls_engine\")){\n#ifdef WITH_TLS\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* tls_engine not valid for reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"tls_engine\", &cur_listener->tls_engine, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"tls_engine_kpass_sha1\")){\n#ifdef WITH_TLS\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* tls_engine not valid for reloading. */\n\n\t\t\t\t\t}\n\t\t\t\t\tchar *kpass_sha = NULL, *kpass_sha_bin = NULL;\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"tls_engine_kpass_sha1\", &kpass_sha, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(mosquitto__hex2bin_sha1(kpass_sha, (unsigned char **)&kpass_sha_bin) != MOSQ_ERR_SUCCESS){\n\t\t\t\t\t\tmosquitto_FREE(kpass_sha);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_free(cur_listener->tls_engine_kpass_sha1);\n\t\t\t\t\tcur_listener->tls_engine_kpass_sha1 = kpass_sha_bin;\n\t\t\t\t\tmosquitto_FREE(kpass_sha);\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"tls_keyform\")){\n#ifdef WITH_TLS\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* tls_engine not valid for reloading. */\n\n\t\t\t\t\t}\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tchar *keyform = NULL;\n\t\t\t\t\tif(conf__parse_string(&token, \"tls_keyform\", &keyform, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_listener->tls_keyform = mosq_k_pem;\n\t\t\t\t\tif(!strcmp(keyform, \"engine\")){\n\t\t\t\t\t\tcur_listener->tls_keyform = mosq_k_engine;\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_FREE(keyform);\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"tls_version\")){\n#if defined(WITH_TLS)\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* tls_version not valid for reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"tls_version\", &cur_listener->tls_version, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"topic\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tchar *topic = NULL;\n\t\t\t\t\tenum mosquitto__bridge_direction direction = bd_out;\n\t\t\t\t\tuint8_t qos = 0;\n\t\t\t\t\tchar *local_prefix = NULL, *remote_prefix = NULL;\n\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tREQUIRE_NON_EMPTY_OPTION(token, \"topic\");\n\n\t\t\t\t\t// Check if the topic is quoted (e.g. for spaces within topic names), but not the\n\t\t\t\t\t// special case of \"\"\n\t\t\t\t\tif(token[0] == '\"' && token [1] != '\"'){\n\t\t\t\t\t\tif(strchr(saveptr, '\"') == NULL){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Missing closing quote in topic value (%s).\", saveptr);\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tchar *topic_in_quotes = strtok_r(NULL, \"\\\"\", &saveptr);\n\t\t\t\t\t\tsize_t tlen = 1;\n\t\t\t\t\t\tif(topic_in_quotes){\n\t\t\t\t\t\t\ttlen = strlen(topic_in_quotes);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttopic = mosquitto_malloc(strlen(token) + tlen + 1);\n\t\t\t\t\t\tif(!topic){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstrcpy(topic, token + 1);\n\t\t\t\t\t\tstrcat(topic, \" \");\n\t\t\t\t\t\tif(topic_in_quotes){\n\t\t\t\t\t\t\tstrcat(topic, topic_in_quotes);\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\ttopic = mosquitto_strdup(token);\n\t\t\t\t\t\tif(!topic){\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\tif(token){\n\t\t\t\t\t\tif(!strcasecmp(token, \"out\")){\n\t\t\t\t\t\t\tdirection = bd_out;\n\t\t\t\t\t\t}else if(!strcasecmp(token, \"in\")){\n\t\t\t\t\t\t\tdirection = bd_in;\n\t\t\t\t\t\t}else if(!strcasecmp(token, \"both\")){\n\t\t\t\t\t\t\tdirection = bd_both;\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge topic direction '%s'.\", token);\n\t\t\t\t\t\t\tmosquitto_FREE(topic);\n\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\t\tif(token){\n\t\t\t\t\t\t\tif(token[0] == '#'){\n\t\t\t\t\t\t\t\t(void)strtok_r(NULL, \"\", &saveptr);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tqos = (uint8_t)atoi(token);\n\t\t\t\t\t\t\tif(qos > 2){\n\t\t\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid bridge QoS level '%s'.\", token);\n\t\t\t\t\t\t\t\tmosquitto_FREE(topic);\n\t\t\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\t\t\tif(token){\n\t\t\t\t\t\t\t\tif(!strcmp(token, \"\\\"\\\"\") || token[0] == '#'){\n\t\t\t\t\t\t\t\t\tlocal_prefix = NULL;\n\t\t\t\t\t\t\t\t\tif(token[0] == '#'){\n\t\t\t\t\t\t\t\t\t\t(void)strtok_r(NULL, \"\", &saveptr);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\t\tlocal_prefix = token;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t\t\t\t\t\t\t\tif(token){\n\t\t\t\t\t\t\t\t\tif(!strcmp(token, \"\\\"\\\"\") || token[0] == '#'){\n\t\t\t\t\t\t\t\t\t\tremote_prefix = NULL;\n\t\t\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\t\t\tremote_prefix = token;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif(bridge__add_topic(cur_bridge, topic, direction, qos, local_prefix, remote_prefix)){\n\t\t\t\t\t\tmosquitto_FREE(topic);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_FREE(topic);\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"max_topic_alias\")){\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tif(conf__parse_int(&token, \"max_topic_alias\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0 || tmp_int > UINT16_MAX){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'max_topic_alias' value in configuration.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_listener->max_topic_alias = (uint16_t)tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"max_topic_alias_broker\")){\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tif(conf__parse_int(&token, \"max_topic_alias_broker\", &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0 || tmp_int > UINT16_MAX){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid 'max_topic_alias_broker' value in configuration.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_listener->max_topic_alias_broker = (uint16_t)tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"try_private\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"try_private\", &cur_bridge->try_private, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"upgrade_outgoing_qos\")){\n\t\t\t\t\tif(conf__parse_bool(&token, token, &config->upgrade_outgoing_qos, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"use_identity_as_username\")){\n#ifdef WITH_TLS\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"use_identity_as_username\", &cur_listener->use_identity_as_username, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"use_subject_as_username\")){\n#ifdef WITH_TLS\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"use_subject_as_username\", &cur_listener->use_subject_as_username, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"user\")){\n\t\t\t\t\tif(reload){\n\t\t\t\t\t\tcontinue;        /* Drop privileges user not valid for reloading. */\n\t\t\t\t\t}\n\t\t\t\t\tmosquitto_FREE(config->user);\n\t\t\t\t\tif(conf__parse_string(&token, \"user\", &config->user, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"use_username_as_clientid\")){\n\t\t\t\t\tREQUIRE_LISTENER_OR_DEFAULT_LISTENER(token);\n\t\t\t\t\tif(conf__parse_bool(&token, \"use_username_as_clientid\", &cur_listener->use_username_as_clientid, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}else if(!strcmp(token, \"username\") || !strcmp(token, \"remote_username\")){\n#ifdef WITH_BRIDGE\n\t\t\t\t\tREQUIRE_BRIDGE(token);\n\t\t\t\t\tif(conf__parse_string(&token, \"bridge remote_username\", &cur_bridge->remote_username, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Bridge support not available.\");\n#endif\n\t\t\t\t}else if(!strcmp(token, \"websockets_log_level\")){\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\t\t\t\tif(conf__parse_int(&token, \"websockets_log_level\", &config->websockets_log_level, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n#endif\n\t\t\t\t}else if(!strcmp(token, \"websockets_headers_size\") || !strcmp(token, \"packet_buffer_size\")){\n\t\t\t\t\tif(conf__parse_int(&token, token, &tmp_int, &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tif(tmp_int < 0 || tmp_int > UINT16_MAX){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Error: Packet buffer size must be between 0 and 65535 inclusive.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tconfig->packet_buffer_size = (uint16_t)tmp_int;\n\t\t\t\t}else if(!strcmp(token, \"websockets_origin\")){\n#ifdef WITH_WEBSOCKETS\n#  if LWS_LIBRARY_VERSION_NUMBER >= 3001000 || WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\t\t\tREQUIRE_LISTENER(token);\n\t\t\t\t\tws_origins = mosquitto_realloc(cur_listener->ws_origins, sizeof(char *)*(size_t)(cur_listener->ws_origin_count+1));\n\t\t\t\t\tif(ws_origins == NULL){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t\tws_origins[cur_listener->ws_origin_count] = NULL;\n\t\t\t\t\tcur_listener->ws_origins = ws_origins;\n\t\t\t\t\tif(conf__parse_string(&token, \"websockets_origin\", &ws_origins[cur_listener->ws_origin_count], &saveptr)){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tcur_listener->ws_origin_count++;\n#  else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: websockets_origin support not available, libwebsockets version is too old.\");\n#  endif\n#else\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Websockets support not available.\");\n#endif\n\t\t\t\t}else{\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unknown configuration variable '%s'.\", token);\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint config__read_file(struct mosquitto__config *config, bool reload, const char *file, struct config_recurse *cr, int level, int *lineno)\n{\n\tint rc;\n\tFILE *fptr = NULL;\n\tchar *buf;\n\tint buflen;\n#ifndef WIN32\n\tDIR *dir;\n#endif\n\n#ifndef WIN32\n\tdir = opendir(file);\n\tif(dir){\n\t\tclosedir(dir);\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Config file '%s' is a directory.\", file);\n\t\treturn 1;\n\t}\n#endif\n\n\tfptr = mosquitto_fopen(file, \"rt\", false);\n\tif(!fptr){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to open config file '%s'.\", file);\n\t\treturn 1;\n\t}\n\n\tbuflen = 1000;\n\tbuf = mosquitto_malloc((size_t)buflen);\n\tif(!buf){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\tfclose(fptr);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\trc = config__read_file_core(config, reload, cr, level, lineno, fptr, &buf, &buflen);\n\tmosquitto_FREE(buf);\n\tfclose(fptr);\n\n\treturn rc;\n}\n\n\nstatic int config__check_proxy(struct mosquitto__config *config)\n{\n\tfor(int i=0; i<config->listener_count; i++){\n\t\tstruct mosquitto__listener *l = &config->listeners[i];\n\n\t\tif(l->enable_proxy_protocol == 2){\n#ifdef WITH_TLS\n\t\t\tif(l->use_subject_as_username){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: use_subject_as_username cannot be used with `enable_proxy_protocol 2`.\");\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\n\t\t\tif(l->certfile || l->keyfile){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: certfile and keyfile cannot be used with `enable_proxy_protocol 2`.\");\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n#endif\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int config__check(struct mosquitto__config *config)\n{\n\t/* Checks that are easy to make after the config has been loaded. */\n\n\tconst char *id_prefix;\n\tint id_prefix_len;\n\tif(config->security_options.auto_id_prefix){\n\t\tid_prefix = config->security_options.auto_id_prefix;\n\t\tid_prefix_len = config->security_options.auto_id_prefix_len;\n\t}else{\n\t\tid_prefix = \"auto-\";\n\t\tid_prefix_len = (int)strlen(\"auto-\");\n\t}\n\n\t/* Default to auto_id_prefix = 'auto-' if none set. */\n\tfor(int i=0; i<config->listener_count; i++){\n\t\tif(!config->listeners[i].security_options->auto_id_prefix){\n\t\t\tconfig->listeners[i].security_options->auto_id_prefix = mosquitto_strdup(id_prefix);\n\t\t\tif(!config->listeners[i].security_options->auto_id_prefix){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tconfig->listeners[i].security_options->auto_id_prefix_len = (uint16_t)id_prefix_len;\n\t\t}\n\t}\n\n\treturn config__check_proxy(config);\n}\n\n#ifdef WITH_BRIDGE\n\n\nstatic int config__check_bridges(struct mosquitto__config *config)\n{\n\tstruct mosquitto__bridge *bridge1, *bridge2;\n\tchar hostname[256];\n\tsize_t len;\n\n\t/* Check for bridge duplicate local_clientid, need to generate missing IDs\n\t * first. */\n\tfor(int i=0; i<config->bridge_count; i++){\n\t\tbridge1 = config->bridges[i];\n\n\t\tif(!bridge1->remote_clientid){\n\t\t\tif(!gethostname(hostname, 256)){\n\t\t\t\tlen = strlen(hostname) + strlen(bridge1->name) + 2;\n\t\t\t\tbridge1->remote_clientid = mosquitto_malloc(len);\n\t\t\t\tif(!bridge1->remote_clientid){\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\tsnprintf(bridge1->remote_clientid, len, \"%s.%s\", hostname, bridge1->name);\n\t\t\t}else{\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\n\t\tif(!bridge1->local_clientid){\n\t\t\tlen = strlen(bridge1->remote_clientid) + strlen(\"local.\") + 2;\n\t\t\tbridge1->local_clientid = mosquitto_malloc(len);\n\t\t\tif(!bridge1->local_clientid){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tsnprintf(bridge1->local_clientid, len, \"local.%s\", bridge1->remote_clientid);\n\t\t}\n\t}\n\n\tfor(int i=0; i<config->bridge_count; i++){\n\t\tbridge1 = config->bridges[i];\n\t\tfor(int j=i+1; j<config->bridge_count; j++){\n\t\t\tbridge2 = config->bridges[j];\n\t\t\tif(!strcmp(bridge1->local_clientid, bridge2->local_clientid)){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Bridge local_clientid \"\n\t\t\t\t\t\t\"'%s' is not unique. Try changing or setting the \"\n\t\t\t\t\t\t\"local_clientid value for one of the bridges.\",\n\t\t\t\t\t\tbridge1->local_clientid);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef WITH_TLS\n\t/* Check for missing TLS cafile/capath/certfile/keyfile */\n\tfor(int i=0; i<config->listener_count; i++){\n\t\tbool cafile = !!config->listeners[i].cafile;\n\t\tbool capath = !!config->listeners[i].capath;\n\t\tbool certfile = !!config->listeners[i].certfile;\n\t\tbool keyfile = !!config->listeners[i].keyfile;\n\n\t\tif((certfile && !keyfile) || (!certfile && keyfile)){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Both certfile and keyfile must be provided to enable a TLS listener.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(cafile && !certfile){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: cafile specified without certfile and keyfile.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(capath && !certfile){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: capath specified without certfile and keyfile.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\nstatic int conf__parse_bool(char **token, const char *name, bool *value, char **saveptr)\n{\n\t*token = strtok_r(NULL, \" \", saveptr);\n\tif(*token){\n\t\tif(!strcmp(*token, \"false\") || !strcmp(*token, \"0\")){\n\t\t\t*value = false;\n\t\t}else if(!strcmp(*token, \"true\") || !strcmp(*token, \"1\")){\n\t\t\t*value = true;\n\t\t}else{\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Invalid '%s' value (%s).\", name, *token);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty '%s' value in configuration.\", name);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int conf__parse_int(char **token, const char *name, int *value, char **saveptr)\n{\n\t*token = strtok_r(NULL, \" \", saveptr);\n\tif(*token){\n\t\tchar *endptr = NULL;\n\t\tlong l = strtol(*token, &endptr, 0);\n\t\tif(endptr == *token || endptr[0] != '\\0'){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: '%s' value not a number.\", name);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(l > INT_MAX || l < INT_MIN){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: '%s' value out of range.\", name);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\t*value = (int)l;\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty '%s' value in configuration.\", name);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int conf__parse_ssize_t(char **token, const char *name, ssize_t *value, char **saveptr)\n{\n\t*token = strtok_r(NULL, \" \", saveptr);\n\tif(*token){\n\t\t*value = atol(*token);\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty '%s' value in configuration.\", name);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int conf__parse_string(char **token, const char *name, char **value, char **saveptr)\n{\n\tsize_t tlen;\n\n\t*token = strtok_r(NULL, \"\", saveptr);\n\tif(*token){\n\t\tif(*value){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Duplicate '%s' value in configuration.\", name);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\t/* Deal with multiple spaces at the beginning of the string. */\n\t\t*token = mosquitto_trimblanks(*token);\n\t\tif(strlen(*token) == 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty '%s' value in configuration.\", name);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\ttlen = strlen(*token);\n\t\tif(tlen > UINT16_MAX){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(mosquitto_validate_utf8(*token, (uint16_t)tlen)){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Malformed UTF-8 in configuration.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\t*value = mosquitto_strdup(*token);\n\t\tif(!*value){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty '%s' value in configuration.\", name);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "src/conf_includedir.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <ctype.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n\n#ifdef WIN32\n#else\n#  include <dirent.h>\n#endif\n\n#ifndef WIN32\n#  include <strings.h>\n#  include <netdb.h>\n#  include <sys/socket.h>\n#else\n#  include <winsock2.h>\n#  include <ws2tcpip.h>\n#endif\n\n#if defined(__HAIKU__)\n#  include <syslog.h>\n#elif !defined(WIN32) && !defined(__CYGWIN__) && !defined(__QNX__)\n#  include <sys/syslog.h>\n#endif\n\n#include \"mosquitto_broker_internal.h\"\n#include \"tls_mosq.h\"\n#include \"util_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n\n\nstatic int scmp_p(const void *p1, const void *p2)\n{\n\tconst char *s1 = *(const char **)p1;\n\tconst char *s2 = *(const char **)p2;\n\tint result;\n\n\twhile(s1[0] && s2[0]){\n\t\t/* Sort by case insensitive part first */\n\t\tresult = toupper((unsigned char)s1[0]) - toupper((unsigned char)s2[0]);\n\t\tif(result == 0){\n\t\t\t/* Case insensitive part matched, now distinguish between case */\n\t\t\tresult = s1[0] - s2[0];\n\t\t\tif(result != 0){\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}else{\n\t\t\t/* Return case insensitive match fail */\n\t\t\treturn result;\n\t\t}\n\t\ts1++;\n\t\ts2++;\n\t}\n\n\treturn s1[0] - s2[0];\n}\n\n#ifdef WIN32\n\n\nint config__get_dir_files(const char *include_dir, char ***files, int *file_count)\n{\n\tsize_t len;\n\tchar **l_files = NULL;\n\tint l_file_count = 0;\n\tchar **files_tmp;\n\n\tHANDLE fh;\n\tchar dirpath[MAX_PATH];\n\tWIN32_FIND_DATA find_data;\n\n\tsnprintf(dirpath, MAX_PATH, \"%s\\\\*.conf\", include_dir);\n\tfh = FindFirstFile(dirpath, &find_data);\n\tif(fh == INVALID_HANDLE_VALUE){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to open include_dir '%s'.\", include_dir);\n\t\treturn 1;\n\t}\n\n\tdo{\n\t\tlen = strlen(include_dir)+1+strlen(find_data.cFileName)+1;\n\n\t\tl_file_count++;\n\t\tfiles_tmp = mosquitto_realloc(l_files, l_file_count*sizeof(char *));\n\t\tif(!files_tmp){\n\t\t\tfor(int i=0; i<l_file_count-1; i++){\n\t\t\t\tmosquitto_FREE(l_files[i]);\n\t\t\t}\n\t\t\tmosquitto_FREE(l_files);\n\t\t\tFindClose(fh);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tl_files = files_tmp;\n\n\t\tl_files[l_file_count-1] = mosquitto_malloc(len+1);\n\t\tif(!l_files[l_file_count-1]){\n\t\t\tfor(int i=0; i<l_file_count-1; i++){\n\t\t\t\tmosquitto_FREE(l_files[i]);\n\t\t\t}\n\t\t\tmosquitto_FREE(l_files);\n\t\t\tFindClose(fh);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tsnprintf(l_files[l_file_count-1], len, \"%s/%s\", include_dir, find_data.cFileName);\n\t\tl_files[l_file_count-1][len] = '\\0';\n\t}while(FindNextFile(fh, &find_data));\n\n\tFindClose(fh);\n\n\tif(l_files){\n\t\tqsort(l_files, l_file_count, sizeof(char *), scmp_p);\n\t}\n\t*files = l_files;\n\t*file_count = l_file_count;\n\n\treturn 0;\n}\n#endif\n\n\n#ifndef WIN32\n\n\nint config__get_dir_files(const char *include_dir, char ***files, int *file_count)\n{\n\tchar **l_files = NULL;\n\tint l_file_count = 0;\n\tchar **files_tmp;\n\tsize_t len;\n\n\tDIR *dh;\n\tstruct dirent *de;\n\n\tdh = opendir(include_dir);\n\tif(!dh){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to open include_dir '%s'.\", include_dir);\n\t\treturn 1;\n\t}\n\twhile((de = readdir(dh)) != NULL){\n\t\tif(strlen(de->d_name) > 5){\n\t\t\tif(!strcmp(&de->d_name[strlen(de->d_name)-5], \".conf\")){\n\t\t\t\tlen = strlen(include_dir)+1+strlen(de->d_name)+1;\n\n\t\t\t\tl_file_count++;\n\t\t\t\tfiles_tmp = mosquitto_realloc(l_files, (size_t)l_file_count*sizeof(char *));\n\t\t\t\tif(!files_tmp){\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\tl_files = files_tmp;\n\n\t\t\t\tl_files[l_file_count-1] = mosquitto_malloc(len+1);\n\t\t\t\tif(!l_files[l_file_count-1]){\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\tsnprintf(l_files[l_file_count-1], len, \"%s/%s\", include_dir, de->d_name);\n\t\t\t\tl_files[l_file_count-1][len] = '\\0';\n\t\t\t}\n\t\t}\n\t}\n\tclosedir(dh);\n\n\tif(l_files){\n\t\tqsort(l_files, (size_t)l_file_count, sizeof(char *), scmp_p);\n\t}\n\t*files = l_files;\n\t*file_count = l_file_count;\n\n\treturn 0;\nerror:\n\tfor(int i=0; i<l_file_count-1; i++){\n\t\tmosquitto_FREE(l_files[i]);\n\t}\n\tmosquitto_FREE(l_files);\n\tclosedir(dh);\n\treturn MOSQ_ERR_NOMEM;\n}\n#endif\n\n\n"
  },
  {
    "path": "src/context.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <time.h>\n#if defined(__APPLE__) || defined(_AIX)\n#include <sys/socket.h>\n#endif\n\n#include \"mosquitto_broker_internal.h\"\n#include \"alias_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n#include \"will_mosq.h\"\n\n#include \"uthash.h\"\n\n\nint context__init_sock(struct mosquitto *context, mosq_sock_t sock, bool get_address)\n{\n\tcontext->sock = sock;\n\n\tif((int)context->sock >= 0){\n\t\tif(get_address){\n\t\t\tchar address[1024];\n\n\t\t\tif(!net__socket_get_address(context->sock,\n\t\t\t\t\taddress, sizeof(address),\n\t\t\t\t\t&context->remote_port)){\n\n\t\t\t\tcontext->address = mosquitto_strdup(address);\n\t\t\t}\n\t\t\tif(!context->address){\n\t\t\t\t/* getpeername and inet_ntop failed and not a bridge */\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t\tHASH_ADD(hh_sock, db.contexts_by_sock, sock, sizeof(context->sock), context);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\nstruct mosquitto *context__init(void)\n{\n\tstruct mosquitto *context;\n\n\tcontext = mosquitto_calloc(1, sizeof(struct mosquitto));\n\tif(!context){\n\t\treturn NULL;\n\t}\n\n\tcontext->in_packet.packet_buffer_size = db.config->packet_buffer_size;\n\tcontext->in_packet.packet_buffer = mosquitto_calloc(1, context->in_packet.packet_buffer_size);\n\tif(!context->in_packet.packet_buffer){\n\t\tmosquitto_FREE(context);\n\t\treturn NULL;\n\t}\n\n#if defined(WITH_EPOLL) || defined(WITH_KQUEUE)\n\tcontext->ident = id_client;\n#else\n\tcontext->pollfd_index = -1;\n#endif\n\tmosquitto__set_state(context, mosq_cs_new);\n\tcontext->sock = INVALID_SOCKET;\n\tcontext->last_msg_in = db.now_s;\n\tcontext->next_msg_out = db.now_s + 20;\n\tcontext->keepalive = 20; /* Default to 20s */\n\tcontext->clean_start = true;\n\tcontext->id = NULL;\n\tcontext->last_mid = 0;\n\tcontext->will = NULL;\n\tcontext->username = NULL;\n\tcontext->password = NULL;\n\tcontext->listener = NULL;\n\tcontext->acl_list = NULL;\n\tcontext->retain_available = true;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\tmemset(&context->wsd, 0, sizeof(context->wsd));\n\tcontext->wsd.opcode = UINT8_MAX;\n\tcontext->wsd.mask = UINT8_MAX;\n\tcontext->wsd.disconnect_reason = 0xE8;\n#endif\n\n\t/* is_bridge records whether this client is a bridge or not. This could be\n\t * done by looking at context->bridge for bridges that we create ourself,\n\t * but incoming bridges need some other way of being recorded. */\n\tcontext->is_bridge = false;\n\n\tcontext->in_packet.payload = NULL;\n\tpacket__cleanup(&context->in_packet);\n\tcontext->out_packet = NULL;\n\tcontext->out_packet_count = 0;\n\tcontext->out_packet_bytes = 0;\n\n\tcontext->address = NULL;\n\tcontext->bridge = NULL;\n\tcontext->msgs_in.inflight_maximum = db.config->max_inflight_messages;\n\tcontext->msgs_in.inflight_quota = db.config->max_inflight_messages;\n\tcontext->msgs_out.inflight_maximum = db.config->max_inflight_messages;\n\tcontext->msgs_out.inflight_quota = db.config->max_inflight_messages;\n\tcontext->max_qos = 2;\n#ifdef WITH_TLS\n\tcontext->ssl = NULL;\n#endif\n\n\treturn context;\n}\n\n\nstatic void context__cleanup_out_packets(struct mosquitto *context)\n{\n\tstruct mosquitto__packet *packet;\n\n\tif(!context){\n\t\treturn;\n\t}\n\n\twhile(context->out_packet){\n\t\tpacket = context->out_packet;\n\t\tcontext->out_packet = context->out_packet->next;\n\t\tmosquitto_free(packet);\n\t}\n\tmetrics__int_dec(mosq_gauge_out_packets, context->out_packet_count);\n\tmetrics__int_dec(mosq_gauge_out_packet_bytes, context->out_packet_bytes);\n\tcontext->out_packet_count = 0;\n\tcontext->out_packet_bytes = 0;\n}\n\n\n/*\n * This will result in any outgoing packets going unsent. If we're disconnected\n * forcefully then it is usually an error condition and shouldn't be a problem,\n * but it will mean that CONNACK messages will never get sent for bad protocol\n * versions for example.\n */\nvoid context__cleanup(struct mosquitto *context, bool force_free)\n{\n\tif(!context){\n\t\treturn;\n\t}\n\n\tif(force_free){\n\t\tcontext->clean_start = true;\n\t}\n\n#ifdef WITH_BRIDGE\n\tif(context->bridge){\n\t\tbridge__cleanup(context);\n\t}\n#endif\n\tmosquitto_FREE(context->in_packet.packet_buffer);\n\tcontext->in_packet.packet_buffer_size = 0;\n\n\talias__free_all(context);\n\tkeepalive__remove(context);\n\tcontext__cleanup_out_packets(context);\n\n\tmosquitto_FREE(context->auth_method);\n\tmosquitto_FREE(context->username);\n\tmosquitto_FREE(context->password);\n\n\tnet__socket_close(context);\n\tif(force_free){\n\t\tsub__clean_session(context);\n\t}\n\tdb__messages_delete(context, force_free);\n\n\tmosquitto_FREE(context->address);\n\n\tcontext__send_will(context);\n\n\tif(context->id){\n\t\tcontext__remove_from_by_id(context);\n\t\tmosquitto_FREE(context->id);\n\t}\n\tpacket__cleanup(&(context->in_packet));\n\tcontext__cleanup_out_packets(context);\n#if defined(WITH_BROKER) && defined(__GLIBC__) && defined(WITH_ADNS)\n\tif(context->adns){\n\t\tgai_cancel(context->adns);\n\t\tstruct addrinfo *ar_request = (struct addrinfo *)context->adns->ar_request;\n\t\tmosquitto_FREE(ar_request);\n\t\tmosquitto_FREE(context->adns);\n\t}\n#endif\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\tmosquitto_FREE(context->http_request);\n#endif\n\tmosquitto_FREE(context->proxy.buf);\n\tif(force_free){\n\t\tmosquitto_FREE(context);\n\t}\n}\n\n\nvoid context__send_will(struct mosquitto *ctxt)\n{\n\tif(ctxt->state != mosq_cs_disconnecting && ctxt->will){\n\t\tif(ctxt->session_expiry_interval > 0 && ctxt->will_delay_interval > 0){\n\t\t\twill_delay__add(ctxt);\n\t\t\treturn;\n\t\t}\n\n\t\tif(mosquitto_acl_check(ctxt,\n\t\t\t\tctxt->will->msg.topic,\n\t\t\t\t(uint32_t)ctxt->will->msg.payloadlen,\n\t\t\t\tctxt->will->msg.payload,\n\t\t\t\t(uint8_t)ctxt->will->msg.qos,\n\t\t\t\tctxt->will->msg.retain,\n\t\t\t\tctxt->will->properties,\n\t\t\t\tMOSQ_ACL_WRITE) == MOSQ_ERR_SUCCESS){\n\n\t\t\t/* Unexpected disconnect, queue the client will. */\n\t\t\tdb__messages_easy_queue(ctxt,\n\t\t\t\t\tctxt->will->msg.topic,\n\t\t\t\t\t(uint8_t)ctxt->will->msg.qos,\n\t\t\t\t\t(uint32_t)ctxt->will->msg.payloadlen,\n\t\t\t\t\tctxt->will->msg.payload,\n\t\t\t\t\tctxt->will->msg.retain,\n\t\t\t\t\tctxt->will->expiry_interval,\n\t\t\t\t\t&ctxt->will->properties);\n\t\t}\n\t}\n\twill__clear(ctxt);\n}\n\n\nvoid context__disconnect(struct mosquitto *context, int reason)\n{\n\tif(mosquitto__get_state(context) == mosq_cs_disconnected){\n\t\treturn;\n\t}\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\tif(context->transport == mosq_t_ws){\n\t\tuint8_t buf[4] = {0x88, 0x02, 0x03, context->wsd.disconnect_reason};\n\t\t/* Send the disconnect reason, but don't care if it fails */\n\t\tif(send(context->sock, buf, 4, 0)){\n\t\t}\n\t}\n#endif\n\tif(context->id){\n\t\tstruct mosquitto *context_found;\n\t\tHASH_FIND(hh_id, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context_found);\n\t\tif(context_found == context){\n\t\t\tnet__socket_close(context);\n\t\t\tcontext__add_to_disused(context);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tcontext__send_will(context);\n\n\tif(context->session_expiry_interval == MQTT_SESSION_EXPIRY_IMMEDIATE){\n\t\tplugin__handle_disconnect(context, reason);\n\t}else{\n\t\tplugin__handle_client_offline(context, reason);\n\t}\n\n\tnet__socket_close(context);\n#ifdef WITH_BRIDGE\n\tif(context->bridge == NULL)\n\t/* Outgoing bridge connection never expire */\n#endif\n\t{\n\t\tif(context->session_expiry_interval == MQTT_SESSION_EXPIRY_IMMEDIATE){\n\t\t\tplugin_persist__handle_client_delete(context);\n\t\t\t/* Client session is due to be expired now */\n\t\t\tif(context->will_delay_interval == 0){\n\t\t\t\t/* This will be done later, after the will is published for delay>0. */\n\t\t\t\tcontext__add_to_disused(context);\n\t\t\t}\n\t\t}else{\n\t\t\tsession_expiry__add(context);\n\t\t}\n\t}\n\tkeepalive__remove(context);\n\tmosquitto__set_state(context, mosq_cs_disconnected);\n\talias__free_all(context);\n\tcontext__cleanup_out_packets(context);\n}\n\n\nvoid context__add_to_disused(struct mosquitto *context)\n{\n\tif(context->state == mosq_cs_disused){\n\t\treturn;\n\t}\n\n\tmosquitto__set_state(context, mosq_cs_disused);\n\n\tcontext__remove_from_by_id(context);\n\n\tcontext->for_free_next = db.ll_for_free;\n\tdb.ll_for_free = context;\n}\n\n\nvoid context__free_disused(void)\n{\n\tstruct mosquitto *context, *next;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tstruct mosquitto *last = NULL;\n#endif\n\n\tcontext = db.ll_for_free;\n\tdb.ll_for_free = NULL;\n\twhile(context){\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\tif(context->wsi){\n\t\t\t/* Don't delete yet, lws hasn't finished with it */\n\t\t\tif(last){\n\t\t\t\tlast->for_free_next = context;\n\t\t\t}else{\n\t\t\t\tdb.ll_for_free = context;\n\t\t\t}\n\t\t\tnext = context->for_free_next;\n\t\t\tcontext->for_free_next = NULL;\n\t\t\tlast = context;\n\t\t\tcontext = next;\n\t\t}else\n#endif\n\t\t{\n\t\t\tnext = context->for_free_next;\n\t\t\tcontext__cleanup(context, true);\n\t\t\tcontext = next;\n\t\t}\n\t}\n}\n\n\nvoid context__add_to_by_id(struct mosquitto *context)\n{\n\tif(context->in_by_id == false){\n\t\tcontext->in_by_id = true;\n\t\tHASH_VALUE(context->id, strlen(context->id), context->id_hashv);\n\t\tHASH_ADD_KEYPTR_BYHASHVALUE(hh_id, db.contexts_by_id, context->id, strlen(context->id), context->id_hashv, context);\n\t}\n}\n\n\nvoid context__remove_from_by_id(struct mosquitto *context)\n{\n\tstruct mosquitto *context_found;\n\n\tif(!context->id){\n\t\treturn;\n\t}\n\n\tif(context->in_by_id){\n\t\tHASH_FIND(hh_id, db.contexts_by_id, context->id, strlen(context->id), context_found);\n\t\tif(context_found == context){\n\t\t\tHASH_DELETE(hh_id, db.contexts_by_id, context_found);\n\t\t}\n\t\tcontext->id_hashv = 0;\n\t\tcontext->in_by_id = false;\n\t\treturn;\n\t}\n\n\tHASH_FIND(hh_id, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context_found);\n\tif(context_found == context){\n\t\tHASH_DELETE(hh_id, db.contexts_by_id_delayed_auth, context_found);\n\t}\n}\n"
  },
  {
    "path": "src/control.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <utlist.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"send_mosq.h\"\n\n#ifdef WITH_CONTROL\n\n\nstatic void control__negative_reply(const char *clientid, const char *request_topic)\n{\n\tsize_t response_topic_len;\n\tchar *response_topic;\n\tconst char payload[] = \"{\\\"error\\\": \\\"endpoint not available\\\"}\";\n\n\tresponse_topic_len = strlen(request_topic) + sizeof(\"/response\") + 1;\n\tresponse_topic = mosquitto_calloc(1, response_topic_len);\n\tif(!response_topic){\n\t\treturn;\n\t}\n\tsnprintf(response_topic, response_topic_len, \"%s/response\", request_topic);\n\n\tmosquitto_broker_publish_copy(clientid, response_topic, (int)strlen(payload), payload, 0, false, NULL);\n\tmosquitto_FREE(response_topic);\n}\n\n\n/* Process messages coming in on $CONTROL/<feature>. These messages aren't\n * passed on to other clients. */\nint control__process(struct mosquitto *context, struct mosquitto__base_msg *base_msg)\n{\n\tstruct mosquitto__callback *cb_found;\n\tstruct mosquitto_evt_control event_data;\n\tstruct mosquitto__security_options *opts;\n\tmosquitto_property *properties = NULL;\n\tint rc = MOSQ_ERR_SUCCESS;\n\tint rc2;\n\n\t/* Check global plugins and non-per-listener settings first */\n\topts = &db.config->security_options;\n\tHASH_FIND(hh, opts->plugin_callbacks.control, base_msg->data.topic, strlen(base_msg->data.topic), cb_found);\n\n\t/* If not found, check for per-listener plugins. */\n\tif(cb_found == NULL && db.config->per_listener_settings){\n\t\tif(!context->listener){\n\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: $CONTROL command received from client with no listener, when per_listener_settings is true.\");\n\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"         If this is a bridge, please be aware this does not work.\");\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t\topts = context->listener->security_options;\n\t\tHASH_FIND(hh, opts->plugin_callbacks.control, base_msg->data.topic, strlen(base_msg->data.topic), cb_found);\n\t}\n\tif(cb_found){\n\t\tmemset(&event_data, 0, sizeof(event_data));\n\t\tevent_data.client = context;\n\t\tevent_data.topic = base_msg->data.topic;\n\t\tevent_data.payload = base_msg->data.payload;\n\t\tevent_data.payloadlen = base_msg->data.payloadlen;\n\t\tevent_data.qos = base_msg->data.qos;\n\t\tevent_data.retain = base_msg->data.retain;\n\t\tevent_data.properties = base_msg->data.properties;\n\t\tevent_data.reason_code = MQTT_RC_SUCCESS;\n\t\tevent_data.reason_string = NULL;\n\n\t\trc = cb_found->cb(MOSQ_EVT_CONTROL, &event_data, cb_found->userdata);\n\t\tif(rc){\n\t\t\tif(context->protocol == mosq_p_mqtt5 && event_data.reason_string){\n\t\t\t\t/* Not a critical error if this fails */\n\t\t\t\t(void)mosquitto_property_add_string(&properties, MQTT_PROP_REASON_STRING, event_data.reason_string);\n\t\t\t}\n\t\t}\n\t\tSAFE_FREE(event_data.reason_string);\n\t}else{\n\t\tcontrol__negative_reply(context->id, base_msg->data.topic);\n\t}\n\n\tif(base_msg->data.qos == 1){\n\t\trc2 = send__puback(context, base_msg->data.source_mid, MQTT_RC_SUCCESS, properties);\n\t\tif(rc2){\n\t\t\trc = rc2;\n\t\t}\n\t}else if(base_msg->data.qos == 2){\n\t\trc2 = send__pubrec(context, base_msg->data.source_mid, MQTT_RC_SUCCESS, properties);\n\t\tif(rc2){\n\t\t\trc = rc2;\n\t\t}\n\t}\n\tmosquitto_property_free_all(&properties);\n\n\treturn rc;\n}\n#endif\n\n\nint control__register_callback(mosquitto_plugin_id_t *pid, MOSQ_FUNC_generic_callback cb_func, const char *topic, void *userdata)\n{\n#ifdef WITH_CONTROL\n\tstruct mosquitto__security_options *opts;\n\tstruct mosquitto__callback *cb_found, *cb_new;\n\tsize_t topic_len;\n\n\tif(topic == NULL || cb_func == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\ttopic_len = strlen(topic);\n\tif(topic_len == 0 || topic_len > 65535){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(strncmp(topic, \"$CONTROL/\", strlen(\"$CONTROL/\")) || strlen(topic) < strlen(\"$CONTROL/A/v1\")){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\topts = &db.config->security_options;\n\n\tHASH_FIND(hh, opts->plugin_callbacks.control, topic, topic_len, cb_found);\n\tif(cb_found){\n\t\treturn MOSQ_ERR_ALREADY_EXISTS;\n\t}\n\n\tcb_new = mosquitto_calloc(1, sizeof(struct mosquitto__callback));\n\tif(cb_new == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcb_new->data.topic = mosquitto_strdup(topic);\n\tif(cb_new->data.topic == NULL){\n\t\tmosquitto_FREE(cb_new);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcb_new->identifier = pid;\n\tcb_new->cb = cb_func;\n\tcb_new->userdata = userdata;\n\tHASH_ADD_KEYPTR(hh, opts->plugin_callbacks.control, cb_new->data.topic, strlen(cb_new->data.topic), cb_new);\n\n\tif(pid->plugin_name){\n\t\tstruct control_endpoint *ep;\n\t\tep = mosquitto_malloc(sizeof(struct control_endpoint) + topic_len + 2);\n\t\tif(ep){\n\t\t\tep->next = NULL;\n\t\t\tep->prev = NULL;\n\t\t\tsnprintf(ep->topic, topic_len+1, \"%s\", topic);\n\t\t\tDL_APPEND(pid->control_endpoints, ep);\n\t\t}\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Plugin %s has registered to receive 'control' events on topic %s.\",\n\t\t\t\tpid->plugin_name, topic);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n#else\n\tUNUSED(pid);\n\tUNUSED(cb_func);\n\tUNUSED(topic);\n\tUNUSED(userdata);\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\nint control__unregister_callback(mosquitto_plugin_id_t *identifier, MOSQ_FUNC_generic_callback cb_func, const char *topic)\n{\n#ifdef WITH_CONTROL\n\tstruct mosquitto__security_options *opts;\n\n\tstruct mosquitto__callback *cb_found;\n\tsize_t topic_len;\n\tstruct control_endpoint *ep;\n\n\tif(topic == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\ttopic_len = strlen(topic);\n\tif(topic_len == 0 || topic_len > 65535){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(strncmp(topic, \"$CONTROL/\", strlen(\"$CONTROL/\"))){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\topts = &db.config->security_options;\n\n\tHASH_FIND(hh, opts->plugin_callbacks.control, topic, topic_len, cb_found);\n\tif(cb_found && cb_found->cb == cb_func){\n\t\tHASH_DELETE(hh, opts->plugin_callbacks.control, cb_found);\n\t\tmosquitto_FREE(cb_found->data.topic);\n\t\tmosquitto_FREE(cb_found);\n\n\t\tDL_FOREACH(identifier->control_endpoints, ep){\n\t\t\tif(!strcmp(topic, ep->topic)){\n\t\t\t\tDL_DELETE(identifier->control_endpoints, ep);\n\t\t\t\tmosquitto_FREE(ep);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\treturn MOSQ_ERR_NOT_FOUND;\n#else\n\tUNUSED(identifier);\n\tUNUSED(cb_func);\n\tUNUSED(topic);\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n}\n\n\n/* Unregister all control callbacks for a single plugin */\nvoid control__unregister_all_callbacks(mosquitto_plugin_id_t *identifier)\n{\n\tstruct mosquitto__security_options *opts;\n\n\tstruct mosquitto__callback *cb_found;\n\tstruct control_endpoint *ep, *ep_tmp;\n\n\topts = &db.config->security_options;\n\n\tDL_FOREACH_SAFE(identifier->control_endpoints, ep, ep_tmp){\n\t\tHASH_FIND(hh, opts->plugin_callbacks.control, ep->topic, strlen(ep->topic), cb_found);\n\t\tif(cb_found){\n\t\t\tHASH_DELETE(hh, opts->plugin_callbacks.control, cb_found);\n\t\t\tmosquitto_FREE(cb_found->data.topic);\n\t\t\tmosquitto_FREE(cb_found);\n\t\t}\n\n\t\tDL_DELETE(identifier->control_endpoints, ep);\n\t\tmosquitto_FREE(ep);\n\t}\n}\n"
  },
  {
    "path": "src/control_common.c",
    "content": "#include \"config.h\"\n\n#include <mosquitto/mqtt_protocol.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_control.h>\n\n#include <stdlib.h>\n#include <string.h>\n\n#include <cjson/cJSON.h>\n#define CJSON_VERSION_FULL (CJSON_VERSION_MAJOR*1000000+CJSON_VERSION_MINOR*1000+CJSON_VERSION_PATCH)\n\n\nvoid mosquitto_control_command_reply(struct mosquitto_control_cmd *cmd, const char *error)\n{\n\tcJSON *j_response;\n\n\tj_response = cJSON_CreateObject();\n\tif(j_response == NULL){\n\t\treturn;\n\t}\n\n\tif(cJSON_AddStringToObject(j_response, \"command\", cmd->command_name) == NULL\n\t\t\t|| (error && cJSON_AddStringToObject(j_response, \"error\", error) == NULL)\n\t\t\t|| (cmd->correlation_data && cJSON_AddStringToObject(j_response, \"correlationData\", cmd->correlation_data) == NULL)\n\t\t\t){\n\n\t\tcJSON_Delete(j_response);\n\t\treturn;\n\t}\n\n\tcJSON_AddItemToArray(cmd->j_responses, j_response);\n}\n\n\nvoid mosquitto_control_send_response(cJSON *tree, const char *topic)\n{\n\tchar *payload;\n\tsize_t payload_len;\n\n\tpayload = cJSON_PrintUnformatted(tree);\n\tcJSON_Delete(tree);\n\tif(payload == NULL){\n\t\treturn;\n\t}\n\n\tpayload_len = strlen(payload);\n\tif(payload_len > MQTT_MAX_PAYLOAD){\n\t\tfree(payload);\n\t\treturn;\n\t}\n\tmosquitto_broker_publish(NULL, topic, (int)payload_len, payload, 0, 0, NULL);\n}\n\n\nstatic int control__generic_handle_commands(struct mosquitto_control_cmd *cmd, cJSON *commands, void *userdata, int (*cmd_cb)(struct mosquitto_control_cmd *cmd, void *userdata))\n{\n\tcJSON *aiter;\n\n\tcJSON_ArrayForEach(aiter, commands){\n\t\tcmd->command_name = \"Unknown command\";\n\t\tif(cJSON_IsObject(aiter)){\n\t\t\tcJSON *j_tmp = cJSON_GetObjectItem(aiter, \"command\");\n\t\t\tconst char *command = cJSON_GetStringValue(j_tmp);\n\t\t\tif(command){\n\t\t\t\tcmd->j_command = aiter;\n\t\t\t\tcmd->correlation_data = NULL;\n\t\t\t\tcmd->command_name = command;\n\n\t\t\t\tj_tmp = cJSON_GetObjectItem(aiter, \"correlationData\");\n\t\t\t\tif(j_tmp){\n\t\t\t\t\tif(cJSON_IsString(j_tmp)){\n\t\t\t\t\t\tcmd->correlation_data = cJSON_GetStringValue(j_tmp);\n\t\t\t\t\t}else{\n\t\t\t\t\t\tmosquitto_control_command_reply(cmd, \"Invalid correlationData data type.\");\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcmd_cb(cmd, userdata);\n\t\t\t}else{\n\t\t\t\tmosquitto_control_command_reply(cmd, \"Missing command\");\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}else{\n\t\t\tmosquitto_control_command_reply(cmd, \"Command not an object\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_control_generic_callback(struct mosquitto_evt_control *event_data, const char *response_topic, void *userdata,\n\t\tint (*cmd_cb)(struct mosquitto_control_cmd *cmd, void *userdata))\n\n{\n\tstruct mosquitto_evt_control *ed = event_data;\n\tstruct mosquitto_control_cmd cmd;\n\tcJSON *tree, *commands;\n\tcJSON *j_response_tree;\n\n\tif(!event_data || !cmd_cb){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmemset(&cmd, 0, sizeof(cmd));\n\tcmd.command_name = \"Unknown command\";\n\tcmd.client = ed->client;\n\n\t/* Create object for responses */\n\tj_response_tree = cJSON_CreateObject();\n\tif(j_response_tree == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcmd.j_responses = cJSON_AddArrayToObject(j_response_tree, \"responses\");\n\tif(cmd.j_responses == NULL){\n\t\tcJSON_Delete(j_response_tree);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t/* Parse cJSON tree.\n\t * Using cJSON_ParseWithLength() is the best choice here, but Mosquitto\n\t * always adds an extra 0 to the end of the payload memory, so using\n\t * cJSON_Parse() on its own will still not overrun. */\n#if CJSON_VERSION_FULL < 1007013\n\ttree = cJSON_Parse(ed->payload);\n#else\n\ttree = cJSON_ParseWithLength(ed->payload, ed->payloadlen);\n#endif\n\tif(tree == NULL){\n\t\tmosquitto_control_command_reply(&cmd, \"Payload not valid JSON\");\n\t\tmosquitto_control_send_response(j_response_tree, response_topic);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tcommands = cJSON_GetObjectItem(tree, \"commands\");\n\tif(commands == NULL || !cJSON_IsArray(commands)){\n\t\tcJSON_Delete(tree);\n\t\tmosquitto_control_command_reply(&cmd, \"Invalid/missing commands\");\n\t\tmosquitto_control_send_response(j_response_tree, response_topic);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\t/* Handle commands */\n\tcontrol__generic_handle_commands(&cmd, commands, userdata, cmd_cb);\n\tcJSON_Delete(tree);\n\n\tmosquitto_control_send_response(j_response_tree, response_topic);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "src/database.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <utlist.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"send_mosq.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n\n\n/**\n * Is this context ready to take more in flight messages right now?\n * @param context the client context of interest\n * @param qos qos for the packet of interest\n * @return true if more in flight are allowed.\n */\nbool db__ready_for_flight(struct mosquitto *context, enum mosquitto_msg_direction dir, int qos)\n{\n\tstruct mosquitto_msg_data *msgs;\n\tbool valid_bytes;\n\tbool valid_count;\n\n\tif(dir == mosq_md_out){\n\t\tmsgs = &context->msgs_out;\n\t}else{\n\t\tmsgs = &context->msgs_in;\n\t}\n\n\tif(msgs->inflight_maximum == 0 && db.config->max_inflight_bytes == 0){\n\t\treturn true;\n\t}\n\n\tif(qos == 0){\n\t\t/* Deliver QoS 0 messages unless the queue is already full.\n\t\t * For QoS 0 messages the choice is either \"inflight\" or dropped.\n\t\t * There is no queueing option, unless the client is offline and\n\t\t * queue_qos0_messages is enabled.\n\t\t */\n\t\tif(db.config->max_queued_messages == 0 && db.config->max_inflight_bytes == 0){\n\t\t\treturn true;\n\t\t}\n\t\tvalid_bytes = ((msgs->inflight_bytes - (ssize_t)db.config->max_inflight_bytes) < (ssize_t)db.config->max_queued_bytes);\n\t\tif(dir == mosq_md_out){\n\t\t\tvalid_count = context->out_packet_count < db.config->max_queued_messages;\n\t\t}else{\n\t\t\tvalid_count = msgs->inflight_count - msgs->inflight_maximum < db.config->max_queued_messages;\n\t\t}\n\n\t\tif(db.config->max_queued_messages == 0){\n\t\t\treturn valid_bytes;\n\t\t}\n\t\tif(db.config->max_queued_bytes == 0){\n\t\t\treturn valid_count;\n\t\t}\n\t}else{\n\t\tvalid_bytes = (ssize_t)msgs->inflight_bytes12 < (ssize_t)db.config->max_inflight_bytes;\n\t\tvalid_count = msgs->inflight_quota > 0;\n\n\t\tif(msgs->inflight_maximum == 0){\n\t\t\treturn valid_bytes;\n\t\t}\n\t\tif(db.config->max_inflight_bytes == 0){\n\t\t\treturn valid_count;\n\t\t}\n\t}\n\n\treturn valid_bytes && valid_count;\n}\n\n\n/**\n * For a given client context, are more messages allowed to be queued?\n * It is assumed that inflight checks and queue_qos0 checks have already\n * been made.\n * @param context client of interest\n * @param qos destination qos for the packet of interest\n * @return true if queuing is allowed, false if should be dropped\n */\nbool db__ready_for_queue(struct mosquitto *context, int qos, struct mosquitto_msg_data *msg_data)\n{\n\tint source_count;\n\tint adjust_count;\n\tlong source_bytes;\n\tssize_t adjust_bytes = (ssize_t)db.config->max_inflight_bytes;\n\tbool valid_bytes;\n\tbool valid_count;\n\n\tif(db.config->max_queued_messages == 0 && db.config->max_queued_bytes == 0){\n\t\treturn true;\n\t}\n\n\tif(qos == 0 && db.config->queue_qos0_messages == false){\n\t\treturn false; /* This case is handled in db__ready_for_flight() */\n\t}else{\n\t\tsource_bytes = (ssize_t)msg_data->queued_bytes12;\n\t\tsource_count = msg_data->queued_count12;\n\t}\n\tadjust_count = msg_data->inflight_maximum;\n\n\t/* nothing in flight for offline clients */\n\tif(!net__is_connected(context)){\n\t\tadjust_bytes = 0;\n\t\tadjust_count = 0;\n\t}\n\n\tvalid_bytes = (source_bytes - (ssize_t)adjust_bytes) < (ssize_t)db.config->max_queued_bytes;\n\tvalid_count = source_count - adjust_count < db.config->max_queued_messages;\n\n\tif(db.config->max_queued_bytes == 0){\n\t\treturn valid_count;\n\t}\n\tif(db.config->max_queued_messages == 0){\n\t\treturn valid_bytes;\n\t}\n\n\treturn valid_bytes && valid_count;\n}\n\n\nvoid db__msg_add_to_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *client_msg)\n{\n\tmsg_data->inflight_count++;\n\tmsg_data->inflight_bytes += client_msg->base_msg->data.payloadlen;\n\tif(client_msg->data.qos != 0){\n\t\tmsg_data->inflight_count12++;\n\t\tmsg_data->inflight_bytes12 += client_msg->base_msg->data.payloadlen;\n\t}\n}\n\n\nstatic void db__msg_remove_from_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *client_msg)\n{\n\tmsg_data->inflight_count--;\n\tmsg_data->inflight_bytes -= client_msg->base_msg->data.payloadlen;\n\tif(client_msg->data.qos != 0){\n\t\tmsg_data->inflight_count12--;\n\t\tmsg_data->inflight_bytes12 -= client_msg->base_msg->data.payloadlen;\n\t}\n}\n\n\nvoid db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *client_msg)\n{\n\tmsg_data->queued_count++;\n\tmsg_data->queued_bytes += client_msg->base_msg->data.payloadlen;\n\tif(client_msg->data.qos != 0){\n\t\tmsg_data->queued_count12++;\n\t\tmsg_data->queued_bytes12 += client_msg->base_msg->data.payloadlen;\n\t}\n}\n\n\nstatic void db__msg_remove_from_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *client_msg)\n{\n\tmsg_data->queued_count--;\n\tmsg_data->queued_bytes -= client_msg->base_msg->data.payloadlen;\n\tif(client_msg->data.qos != 0){\n\t\tmsg_data->queued_count12--;\n\t\tmsg_data->queued_bytes12 -= client_msg->base_msg->data.payloadlen;\n\t}\n}\n\n\nint db__open(struct mosquitto__config *config)\n{\n\tif(!config){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tdb.contexts_by_id = NULL;\n\tdb.contexts_by_sock = NULL;\n\tdb.contexts_for_free = NULL;\n#ifdef WITH_BRIDGE\n\tdb.bridges = NULL;\n\tdb.bridge_count = 0;\n#endif\n\n\t/* Initialize the hashtable */\n\tdb.clientid_index_hash = NULL;\n\n\tdb.normal_subs = NULL;\n\tdb.shared_subs = NULL;\n\n\tsub__init();\n\tretain__init();\n\n\tdb.config->security_options.unpwd = NULL;\n\n#ifdef WITH_PERSISTENCE\n\tif(persist__restore()){\n\t\treturn 1;\n\t}\n#endif\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void subhier_clean(struct mosquitto__subhier **subhier)\n{\n\tstruct mosquitto__subhier *peer, *subhier_tmp;\n\tstruct mosquitto__subleaf *leaf, *nextleaf;\n\n\tHASH_ITER(hh, *subhier, peer, subhier_tmp){\n\t\tleaf = peer->subs;\n\t\twhile(leaf){\n\t\t\tnextleaf = leaf->next;\n\t\t\tmosquitto_FREE(leaf);\n\t\t\tleaf = nextleaf;\n\t\t}\n\t\tsubhier_clean(&peer->children);\n\n\t\tHASH_DELETE(hh, *subhier, peer);\n\t\tmosquitto_FREE(peer);\n\t}\n}\n\n\nint db__close(void)\n{\n\tsubhier_clean(&db.normal_subs);\n\tsubhier_clean(&db.shared_subs);\n\tretain__clean(&db.retains);\n\tdb__msg_store_clean();\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint db__msg_store_add(struct mosquitto__base_msg *base_msg)\n{\n\tstruct mosquitto__base_msg *found;\n\tunsigned hashv;\n\n\tHASH_VALUE(&base_msg->data.store_id, sizeof(base_msg->data.store_id), hashv);\n\tHASH_FIND_BYHASHVALUE(hh, db.msg_store, &base_msg->data.store_id, sizeof(base_msg->data.store_id), hashv, found);\n\tif(found == NULL){\n\t\tHASH_ADD_KEYPTR_BYHASHVALUE(hh, db.msg_store, &base_msg->data.store_id, sizeof(base_msg->data.store_id), hashv, base_msg);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_ALREADY_EXISTS;\n\t}\n}\n\n\nvoid db__msg_store_free(struct mosquitto__base_msg *base_msg)\n{\n\tmosquitto_FREE(base_msg->data.source_id);\n\tmosquitto_FREE(base_msg->data.source_username);\n\tif(base_msg->dest_ids){\n\t\tfor(int i=0; i<base_msg->dest_id_count; i++){\n\t\t\tmosquitto_FREE(base_msg->dest_ids[i]);\n\t\t}\n\t\tmosquitto_FREE(base_msg->dest_ids);\n\t}\n\tmosquitto_FREE(base_msg->data.topic);\n\tmosquitto_property_free_all(&base_msg->data.properties);\n\tmosquitto_FREE(base_msg->data.payload);\n\tmosquitto_FREE(base_msg);\n}\n\n\nvoid db__msg_store_remove(struct mosquitto__base_msg *base_msg, bool notify)\n{\n\tif(base_msg == NULL){\n\t\treturn;\n\t}\n\tHASH_DELETE(hh, db.msg_store, base_msg);\n\tdb.msg_store_count--;\n\tdb.msg_store_bytes -= base_msg->data.payloadlen;\n\tif(notify == true){\n\t\tplugin_persist__handle_base_msg_delete(base_msg);\n\t}\n\tdb__msg_store_free(base_msg);\n}\n\n\nvoid db__msg_store_clean(void)\n{\n\tstruct mosquitto__base_msg *base_msg, *base_msg_tmp;\n\n\tHASH_ITER(hh, db.msg_store, base_msg, base_msg_tmp){\n\t\tdb__msg_store_remove(base_msg, false);\n\t}\n}\n\n\nvoid db__msg_store_ref_inc(struct mosquitto__base_msg *base_msg)\n{\n\tbase_msg->ref_count++;\n}\n\n\nvoid db__msg_store_ref_dec(struct mosquitto__base_msg **base_msg)\n{\n\t(*base_msg)->ref_count--;\n\tif((*base_msg)->ref_count == 0){\n\t\tdb__msg_store_remove(*base_msg, true);\n\t\t*base_msg = NULL;\n\t}\n}\n\n\nvoid db__msg_store_compact(void)\n{\n\tstruct mosquitto__base_msg *base_msg, *base_msg_tmp;\n\n\tHASH_ITER(hh, db.msg_store, base_msg, base_msg_tmp){\n\t\tif(base_msg->ref_count < 1){\n\t\t\tdb__msg_store_remove(base_msg, true);\n\t\t}\n\t}\n}\n\n\nstatic void db__message_remove_inflight(struct mosquitto *context, struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *item)\n{\n\tif(!context || !msg_data || !item){\n\t\treturn;\n\t}\n\n\tplugin_persist__handle_client_msg_delete(context, item);\n\n\tDL_DELETE(msg_data->inflight, item);\n\tif(item->base_msg){\n\t\tdb__msg_remove_from_inflight_stats(msg_data, item);\n\t\tdb__msg_store_ref_dec(&item->base_msg);\n\t}\n\n\tmosquitto_FREE(item);\n}\n\n\nstatic void db__message_remove_queued(struct mosquitto *context, struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *item)\n{\n\tif(!context || !msg_data || !item){\n\t\treturn;\n\t}\n\n\tplugin_persist__handle_client_msg_delete(context, item);\n\n\tDL_DELETE(msg_data->queued, item);\n\tif(item->base_msg){\n\t\tdb__msg_remove_from_queued_stats(msg_data, item);\n\t\tdb__msg_store_ref_dec(&item->base_msg);\n\t}\n\n\tmosquitto_FREE(item);\n}\n\n\nstatic void db__fill_inflight_out_from_queue(struct mosquitto *context)\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\n\tDL_FOREACH_SAFE(context->msgs_out.queued, client_msg, tmp){\n\t\tif(!db__ready_for_flight(context, mosq_md_out, client_msg->data.qos)){\n\t\t\treturn;\n\t\t}\n\t\tswitch(client_msg->data.qos){\n\t\t\tcase 0:\n\t\t\t\tclient_msg->data.state = mosq_ms_publish_qos0;\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tclient_msg->data.state = mosq_ms_publish_qos1;\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tclient_msg->data.state = mosq_ms_publish_qos2;\n\t\t\t\tbreak;\n\t\t}\n\t\tif(client_msg->base_msg->data.expiry_time && db.now_real_s > client_msg->base_msg->data.expiry_time){\n\t\t\tdb__message_remove_queued(context, &context->msgs_out, client_msg);\n\t\t\tcontinue;\n\t\t}\n\t\tplugin_persist__handle_client_msg_update(context, client_msg);\n\t\tdb__message_dequeue_first(context, &context->msgs_out);\n\t}\n}\n\n\nvoid db__message_dequeue_first(struct mosquitto *context, struct mosquitto_msg_data *msg_data)\n{\n\tstruct mosquitto__client_msg *client_msg;\n\n\tUNUSED(context);\n\n\tclient_msg = msg_data->queued;\n\tDL_DELETE(msg_data->queued, client_msg);\n\tDL_APPEND(msg_data->inflight, client_msg);\n\tif(msg_data->inflight_quota > 0){\n\t\tmsg_data->inflight_quota--;\n\t}\n\n\tdb__msg_remove_from_queued_stats(msg_data, client_msg);\n\tdb__msg_add_to_inflight_stats(msg_data, client_msg);\n}\n\n\nint db__message_delete_outgoing(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_state expect_state, int qos)\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\tbool deleted = false;\n\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tDL_FOREACH_SAFE(context->msgs_out.inflight, client_msg, tmp){\n\t\tif(client_msg->data.mid == mid){\n\t\t\tif(client_msg->data.qos != qos){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: Mismatched QoS (%d:%d) when deleting outgoing message.\",\n\t\t\t\t\t\tcontext->id, client_msg->data.qos, qos);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}else if(qos == 2 && client_msg->data.state != expect_state && expect_state != mosq_ms_any){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: Mismatched state (%d:%d) when deleting outgoing message.\",\n\t\t\t\t\t\tcontext->id, client_msg->data.state, expect_state);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tdb__message_remove_inflight(context, &context->msgs_out, client_msg);\n\t\t\tdeleted = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(deleted == false){\n\t\tDL_FOREACH_SAFE(context->msgs_out.queued, client_msg, tmp){\n\t\t\tif(client_msg->data.mid == mid){\n\t\t\t\tif(client_msg->data.qos != qos){\n\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t}else if(qos == 2 && client_msg->data.state != expect_state && expect_state != mosq_ms_any){\n\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t}\n\t\t\t\tdb__message_remove_queued(context, &context->msgs_out, client_msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tdb__fill_inflight_out_from_queue(context);\n#ifdef WITH_PERSISTENCE\n\tdb.persistence_changes++;\n#endif\n\n\treturn db__message_write_inflight_out_latest(context);\n}\n\n\n/* Only for QoS 2 messages */\nint db__message_insert_incoming(struct mosquitto *context, uint64_t cmsg_id, struct mosquitto__base_msg *base_msg, bool persist)\n{\n\tstruct mosquitto__client_msg *client_msg;\n\tstruct mosquitto_msg_data *msg_data;\n\tenum mosquitto_msg_state state = mosq_ms_invalid;\n\tint rc = 0;\n\n\tassert(base_msg);\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!context->id){\n\t\t/* Protect against unlikely \"client is disconnected but not entirely freed\" scenario */\n\t\treturn MOSQ_ERR_SUCCESS;\n\n\t}\n\tmsg_data = &context->msgs_in;\n\n\tif(db__ready_for_flight(context, mosq_md_in, base_msg->data.qos)){\n\t\tstate = mosq_ms_wait_for_pubrel;\n\t}else if(base_msg->data.qos != 0 && db__ready_for_queue(context, base_msg->data.qos, msg_data)){\n\t\tstate = mosq_ms_queued;\n\t\trc = 2;\n\t}else{\n\t\t/* Dropping message due to full queue. */\n\t\tif(context->is_dropping == false){\n\t\t\tcontext->is_dropping = true;\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE,\n\t\t\t\t\t\"Outgoing messages are being dropped for client %s.\",\n\t\t\t\t\tcontext->id);\n\t\t}\n\t\tmetrics__int_inc(mosq_counter_mqtt_publish_dropped, 1);\n\t\tcontext->stats.messages_dropped++;\n\n\t\treturn 2;\n\t}\n\n\tassert(state != mosq_ms_invalid);\n\n#ifdef WITH_PERSISTENCE\n\tif(state == mosq_ms_queued){\n\t\tdb.persistence_changes++;\n\t}\n#endif\n\n\tclient_msg = mosquitto_malloc(sizeof(struct mosquitto__client_msg));\n\tif(!client_msg){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tclient_msg->prev = NULL;\n\tclient_msg->next = NULL;\n\tif(cmsg_id){\n\t\tclient_msg->data.cmsg_id = cmsg_id;\n\t}else{\n\t\tclient_msg->data.cmsg_id = ++context->last_cmsg_id;\n\t}\n\tclient_msg->base_msg = base_msg;\n\tdb__msg_store_ref_inc(client_msg->base_msg);\n\tclient_msg->data.mid = base_msg->data.source_mid;\n\tclient_msg->data.direction = mosq_md_in;\n\tclient_msg->data.state = (uint8_t)state;\n\tclient_msg->data.dup = false;\n\tif(base_msg->data.qos > context->max_qos){\n\t\tclient_msg->data.qos = context->max_qos;\n\t}else{\n\t\tclient_msg->data.qos = base_msg->data.qos;\n\t}\n\tclient_msg->data.retain = base_msg->data.retain;\n\tclient_msg->data.subscription_identifier = 0;\n\n\tif(state == mosq_ms_queued){\n\t\tDL_APPEND(msg_data->queued, client_msg);\n\t\tdb__msg_add_to_queued_stats(msg_data, client_msg);\n\t}else{\n\t\tDL_APPEND(msg_data->inflight, client_msg);\n\t\tdb__msg_add_to_inflight_stats(msg_data, client_msg);\n\t}\n\n\tif(persist && context->is_persisted){\n\t\tplugin_persist__handle_base_msg_add(client_msg->base_msg);\n\t\tplugin_persist__handle_client_msg_add(context, client_msg);\n\t}\n\n\tif(client_msg->base_msg->data.qos > 0){\n\t\tutil__decrement_receive_quota(context);\n\t}\n\treturn rc;\n}\n\n\nint db__message_insert_outgoing(struct mosquitto *context, uint64_t cmsg_id, uint16_t mid, uint8_t qos, bool retain, struct mosquitto__base_msg *base_msg, uint32_t subscription_identifier, bool update, bool persist)\n{\n\tstruct mosquitto__client_msg *client_msg;\n\tstruct mosquitto_msg_data *msg_data;\n\tenum mosquitto_msg_state state = mosq_ms_invalid;\n\tint rc = 0;\n\tchar **dest_ids;\n\n\tassert(base_msg);\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!context->id){\n\t\t/* Protect against unlikely \"client is disconnected but not entirely freed\" scenario */\n\t\treturn MOSQ_ERR_SUCCESS;\n\n\t}\n\tcontext->stats.messages_sent++;\n\n\tmsg_data = &context->msgs_out;\n\n\t/* Check whether we've already sent this message to this client\n\t * for outgoing messages only.\n\t * If retain==true then this is a stale retained message and so should be\n\t * sent regardless. FIXME - this does mean retained messages will received\n\t * multiple times for overlapping subscriptions, although this is only the\n\t * case for SUBSCRIPTION with multiple subs in so is a minor concern.\n\t */\n\tif(context->protocol != mosq_p_mqtt5\n\t\t\t&& db.config->allow_duplicate_messages == false\n\t\t\t&& retain == false && base_msg->dest_ids){\n\n\t\tfor(int i=0; i<base_msg->dest_id_count; i++){\n\t\t\tif(base_msg->dest_ids[i] && !strcmp(base_msg->dest_ids[i], context->id)){\n\t\t\t\t/* We have already sent this message to this client. */\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t}\n\t}\n\tif(!net__is_connected(context)){\n\t\t/* Client is not connected only queue messages with QoS>0. */\n\t\tif(qos == 0 && !db.config->queue_qos0_messages){\n\t\t\tif(!context->bridge){\n\t\t\t\treturn 2;\n\t\t\t}else{\n\t\t\t\tif(context->bridge->start_type != bst_lazy){\n\t\t\t\t\treturn 2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif(context->bridge && context->bridge->clean_start_local == true){\n\t\t\treturn 2;\n\t\t}\n\t}\n\n\tif(net__is_connected(context)){\n\t\tif(db__ready_for_flight(context, mosq_md_out, qos)){\n\t\t\tswitch(qos){\n\t\t\t\tcase 0:\n\t\t\t\t\tstate = mosq_ms_publish_qos0;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tstate = mosq_ms_publish_qos1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tstate = mosq_ms_publish_qos2;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}else if(qos != 0 && db__ready_for_queue(context, qos, msg_data)){\n\t\t\tstate = mosq_ms_queued;\n\t\t\trc = 2;\n\t\t}else{\n\t\t\t/* Dropping message due to full queue. */\n\t\t\tif(context->is_dropping == false){\n\t\t\t\tcontext->is_dropping = true;\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE,\n\t\t\t\t\t\t\"Outgoing messages are being dropped for client %s.\",\n\t\t\t\t\t\tcontext->id);\n\t\t\t}\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_publish_dropped, 1);\n\t\t\treturn 2;\n\t\t}\n\t}else{\n\t\tif(db__ready_for_queue(context, qos, msg_data)){\n\t\t\tstate = mosq_ms_queued;\n\t\t}else{\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_publish_dropped, 1);\n\t\t\tif(context->is_dropping == false){\n\t\t\t\tcontext->is_dropping = true;\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE,\n\t\t\t\t\t\t\"Outgoing messages are being dropped for client %s.\",\n\t\t\t\t\t\tcontext->id);\n\t\t\t}\n\t\t\treturn 2;\n\t\t}\n\t}\n\tassert(state != mosq_ms_invalid);\n\n#ifdef WITH_PERSISTENCE\n\tif(state == mosq_ms_queued){\n\t\tdb.persistence_changes++;\n\t}\n#endif\n\n\tclient_msg = mosquitto_malloc(sizeof(struct mosquitto__client_msg));\n\tif(!client_msg){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tclient_msg->prev = NULL;\n\tclient_msg->next = NULL;\n\tif(cmsg_id){\n\t\tclient_msg->data.cmsg_id = cmsg_id;\n\t}else{\n\t\tclient_msg->data.cmsg_id = ++context->last_cmsg_id;\n\t}\n\tclient_msg->base_msg = base_msg;\n\tdb__msg_store_ref_inc(client_msg->base_msg);\n\tclient_msg->data.mid = mid;\n\tclient_msg->data.direction = mosq_md_out;\n\tclient_msg->data.state = (uint8_t)state;\n\tclient_msg->data.dup = false;\n\tif(qos > context->max_qos){\n\t\tclient_msg->data.qos = context->max_qos;\n\t}else{\n\t\tclient_msg->data.qos = qos;\n\t}\n\tclient_msg->data.retain = retain;\n\tclient_msg->data.subscription_identifier = subscription_identifier;\n\n\tif(state == mosq_ms_queued){\n\t\tDL_APPEND(msg_data->queued, client_msg);\n\t\tdb__msg_add_to_queued_stats(msg_data, client_msg);\n\t}else{\n\t\tDL_APPEND(msg_data->inflight, client_msg);\n\t\tdb__msg_add_to_inflight_stats(msg_data, client_msg);\n\t}\n\n\tif(persist && context->is_persisted){\n\t\tplugin_persist__handle_base_msg_add(client_msg->base_msg);\n\t\tplugin_persist__handle_client_msg_add(context, client_msg);\n\t}\n\n\tif(db.config->allow_duplicate_messages == false && retain == false){\n\t\t/* Record which client ids this message has been sent to so we can avoid duplicates.\n\t\t * Outgoing messages only.\n\t\t * If retain==true then this is a stale retained message and so should be\n\t\t * sent regardless. FIXME - this does mean retained messages will received\n\t\t * multiple times for overlapping subscriptions, although this is only the\n\t\t * case for SUBSCRIPTION with multiple subs in so is a minor concern.\n\t\t */\n\t\tdest_ids = mosquitto_realloc(base_msg->dest_ids, sizeof(char *)*(size_t)(base_msg->dest_id_count+1));\n\t\tif(dest_ids){\n\t\t\tbase_msg->dest_ids = dest_ids;\n\t\t\tbase_msg->dest_id_count++;\n\t\t\tbase_msg->dest_ids[base_msg->dest_id_count-1] = mosquitto_strdup(context->id);\n\t\t\tif(!base_msg->dest_ids[base_msg->dest_id_count-1]){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}else{\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n#ifdef WITH_BRIDGE\n\tif(context->bridge && context->bridge->start_type == bst_lazy\n\t\t\t&& !net__is_connected(context)\n\t\t\t&& context->msgs_out.inflight_count + context->msgs_out.queued_count >= context->bridge->threshold){\n\n\t\tcontext->bridge->lazy_reconnect = true;\n\t}\n#endif\n\n\tif(client_msg->data.qos > 0 && state != mosq_ms_queued){\n\t\tutil__decrement_send_quota(context);\n\t}\n\n\tif(update){\n\t\trc = db__message_write_inflight_out_latest(context);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\trc = db__message_write_queued_out(context);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\nstatic inline int db__message_update_outgoing_state(struct mosquitto *context, struct mosquitto__client_msg *head,\n\t\tuint16_t mid, enum mosquitto_msg_state state, int qos, bool persist)\n{\n\tstruct mosquitto__client_msg *client_msg;\n\n\tDL_FOREACH(head, client_msg){\n\t\tif(client_msg->data.mid == mid){\n\t\t\tif(client_msg->data.qos != qos){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: Mismatched QoS (%d:%d) when updating outgoing message.\",\n\t\t\t\t\t\tcontext->id, client_msg->data.qos, qos);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tclient_msg->data.state = (uint8_t)state;\n\t\t\tif(persist){\n\t\t\t\tplugin_persist__handle_client_msg_update(context, client_msg);\n\t\t\t}\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\treturn MOSQ_ERR_NOT_FOUND;\n}\n\n\nint db__message_update_outgoing(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_state state, int qos, bool persist)\n{\n\tint rc;\n\n\trc = db__message_update_outgoing_state(context, context->msgs_out.inflight, mid, state, qos, persist);\n\tif(!persist && rc == MOSQ_ERR_NOT_FOUND){\n\t\trc = db__message_update_outgoing_state(context, context->msgs_out.queued, mid, state, qos, persist);\n\t}\n\treturn rc;\n}\n\n\nstatic void db__messages_delete_list(struct mosquitto__client_msg **head)\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\n\tDL_FOREACH_SAFE(*head, client_msg, tmp){\n\t\tDL_DELETE(*head, client_msg);\n\t\tdb__msg_store_ref_dec(&client_msg->base_msg);\n\t\tmosquitto_FREE(client_msg);\n\t}\n\t*head = NULL;\n}\n\n\nint db__messages_delete_incoming(struct mosquitto *context)\n{\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tdb__messages_delete_list(&context->msgs_in.inflight);\n\tdb__messages_delete_list(&context->msgs_in.queued);\n\tcontext->msgs_in.inflight_bytes = 0;\n\tcontext->msgs_in.inflight_bytes12 = 0;\n\tcontext->msgs_in.inflight_count = 0;\n\tcontext->msgs_in.inflight_count12 = 0;\n\tcontext->msgs_in.queued_bytes = 0;\n\tcontext->msgs_in.queued_bytes12 = 0;\n\tcontext->msgs_in.queued_count = 0;\n\tcontext->msgs_in.queued_count12 = 0;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint db__messages_delete_outgoing(struct mosquitto *context)\n{\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tdb__messages_delete_list(&context->msgs_out.inflight);\n\tdb__messages_delete_list(&context->msgs_out.queued);\n\tcontext->msgs_out.inflight_bytes = 0;\n\tcontext->msgs_out.inflight_bytes12 = 0;\n\tcontext->msgs_out.inflight_count = 0;\n\tcontext->msgs_out.inflight_count12 = 0;\n\tcontext->msgs_out.queued_bytes = 0;\n\tcontext->msgs_out.queued_bytes12 = 0;\n\tcontext->msgs_out.queued_count = 0;\n\tcontext->msgs_out.queued_count12 = 0;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint db__messages_delete(struct mosquitto *context, bool force_free)\n{\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(force_free || context->clean_start || (context->bridge && context->bridge->clean_start)){\n\t\tdb__messages_delete_incoming(context);\n\t}\n\n\tif(force_free || (context->bridge && context->bridge->clean_start_local)\n\t\t\t|| (context->bridge == NULL && context->clean_start)){\n\n\t\tdb__messages_delete_outgoing(context);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint db__messages_easy_queue(struct mosquitto *context, const char *topic, uint8_t qos, uint32_t payloadlen, const void *payload, int retain, uint32_t message_expiry_interval, mosquitto_property **properties)\n{\n\tstruct mosquitto__base_msg *base_msg;\n\tconst char *source_id;\n\tenum mosquitto_msg_origin origin;\n\n\tif(!topic){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tbase_msg = mosquitto_calloc(1, sizeof(struct mosquitto__base_msg));\n\tif(base_msg == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tbase_msg->data.topic = mosquitto_strdup(topic);\n\tif(base_msg->data.topic == NULL){\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tbase_msg->data.qos = qos;\n\tif(db.config->retain_available == false){\n\t\tbase_msg->data.retain = 0;\n\t}else{\n\t\tbase_msg->data.retain = retain;\n\t}\n\n\tbase_msg->data.payloadlen = payloadlen;\n\tif(payloadlen > 0){\n\t\tbase_msg->data.payload = mosquitto_malloc(base_msg->data.payloadlen+1);\n\t\tif(base_msg->data.payload == NULL){\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\t/* Ensure payload is always zero terminated, this is the reason for the extra byte above */\n\t\t((uint8_t *)base_msg->data.payload)[base_msg->data.payloadlen] = 0;\n\t\tmemcpy(base_msg->data.payload, payload, base_msg->data.payloadlen);\n\t}\n\n\tif(context && context->id){\n\t\tsource_id = context->id;\n\t}else{\n\t\tsource_id = \"\";\n\t}\n\tif(properties){\n\t\tbase_msg->data.properties = *properties;\n\t\t*properties = NULL;\n\t}\n\n\tif(context){\n\t\torigin = mosq_mo_client;\n\t}else{\n\t\torigin = mosq_mo_broker;\n\t}\n\tif(db__message_store(context, base_msg, &message_expiry_interval, origin)){\n\t\treturn 1;\n\t}\n\n\treturn sub__messages_queue(source_id, base_msg->data.topic, base_msg->data.qos, base_msg->data.retain, &base_msg);\n}\n\n\n#define MOSQ_UUID_EPOCH 1637168273\n\n\n/* db__new_msg_id() attempts to generate a new unique id on the broker, or a\n * number of brokers. It uses the 10-bit node ID, which can be set by plugins\n * to allow different brokers to share the same plugin persistence database\n * without overlapping one another.\n *\n * The message ID is a 64-bit unsigned integer arranged as follows:\n *\n * 10-bit ID  31-bit seconds                 23-bit fractional seconds\n * iiiiiiiiiisssssssssssssssssssssssssssssssnnnnnnnnnnnnnnnnnnnnnnn\n *\n * 10-bit ID gives a total of 1024 brokers can produce unique values (complete overkill)\n * 31-bit seconds gives a roll over date of 68 years after MOSQ_UUID_EPOCH - 2089.\n *    This roll over date would affect messages that have been queued waiting\n *    for a client to receive them, or retained messages only. If either of\n *    those remains for 68 years unchanged, then there will potentially be a\n *    collision. Ideally we need to ensure, however, that the message id is\n *    continually increasing for sorting purposes.\n * 23-bit fractional seconds gives a resolution of 120ns, or 8.4 million\n *    messages per second per broker.\n */\nuint64_t db__new_msg_id(void)\n{\n#ifdef WIN32\n\tFILETIME ftime;\n\tuint64_t ftime64;\n#else\n\tstruct timespec ts;\n#endif\n\tuint64_t id;\n\tuint64_t tmp;\n\ttime_t sec;\n\tlong nsec;\n\n\tid = db.node_id_shifted; /* Top 10-bits */\n\n#ifdef WIN32\n\tGetSystemTimePreciseAsFileTime(&ftime);\n\tftime64 = (((uint64_t)ftime.dwHighDateTime)<<32) + ftime.dwLowDateTime;\n\ttmp = ftime64 - 116444736000000000LL; /* Convert offset to unix epoch, still in counts of 100ns */\n\tsec = tmp / 10000000; /* Convert to seconds */\n\tnsec = (long)(tmp - sec)*100; /* Remove seconds, convert to counts of 1ns */\n#else\n\tclock_gettime(CLOCK_REALTIME, &ts);\n\tsec = ts.tv_sec;\n\tnsec = ts.tv_nsec;\n#endif\n\ttmp = (sec - MOSQ_UUID_EPOCH) & 0x7FFFFFFF;\n\tid = id | (tmp << 23); /* Seconds, 31-bits (68 years) */\n\n\ttmp = (nsec & 0x7FFFFF80); /* top 23-bits of the bottom 30 bits (1 billion ns), ~100 ns resolution */\n\tid = id | (tmp >> 7);\n\n\tif(id <= db.last_db_id){\n\t\tid = db.last_db_id + 1;\n\t}\n\tdb.last_db_id = id;\n\n\treturn id;\n}\n\n\n/* This function requires topic to be allocated on the heap. Once called, it owns topic and will free it on error. Likewise payload and properties. */\nint db__message_store(const struct mosquitto *source, struct mosquitto__base_msg *base_msg, uint32_t *message_expiry_interval, enum mosquitto_msg_origin origin)\n{\n\tint rc;\n\n\tassert(base_msg);\n\n\tif(source && source->id){\n\t\tbase_msg->data.source_id = mosquitto_strdup(source->id);\n\t}else{\n\t\tbase_msg->data.source_id = mosquitto_strdup(\"\");\n\t}\n\tif(!base_msg->data.source_id){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(source && source->username){\n\t\tbase_msg->data.source_username = mosquitto_strdup(source->username);\n\t\tif(!base_msg->data.source_username){\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\tif(source){\n\t\tbase_msg->source_listener = source->listener;\n\t}\n\tbase_msg->origin = origin;\n\tif(message_expiry_interval){\n\t\tif(*message_expiry_interval > 0 && *message_expiry_interval != MSG_EXPIRY_INFINITE){\n\t\t\tbase_msg->data.expiry_time = db.now_real_s + *message_expiry_interval;\n\t\t}else{\n\t\t\tbase_msg->data.expiry_time = 0;\n\t\t}\n\t}\n\n\tbase_msg->dest_ids = NULL;\n\tbase_msg->dest_id_count = 0;\n\tdb.msg_store_count++;\n\tdb.msg_store_bytes += base_msg->data.payloadlen;\n\n\tif(!base_msg->data.store_id){\n\t\tbase_msg->data.store_id = db__new_msg_id();\n\t}\n\n\trc = db__msg_store_add(base_msg);\n\tif(rc){\n\t\tdb__msg_store_free(base_msg);\n\t\treturn rc;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint db__message_store_find(struct mosquitto *context, uint16_t mid, struct mosquitto__client_msg **client_msg)\n{\n\tstruct mosquitto__client_msg *cmsg;\n\n\t*client_msg = NULL;\n\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tDL_FOREACH(context->msgs_in.inflight, cmsg){\n\t\tif(cmsg->base_msg->data.source_mid == mid){\n\t\t\t*client_msg = cmsg;\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\n\tDL_FOREACH(context->msgs_in.queued, cmsg){\n\t\tif(cmsg->base_msg->data.source_mid == mid){\n\t\t\t*client_msg = cmsg;\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\n\n/* Called on reconnect to set outgoing messages to a sensible state and force a\n * retry, and to set incoming messages to expect an appropriate retry. */\nstatic int db__message_reconnect_reset_outgoing(struct mosquitto *context)\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\n\tcontext->msgs_out.inflight_bytes = 0;\n\tcontext->msgs_out.inflight_bytes12 = 0;\n\tcontext->msgs_out.inflight_count = 0;\n\tcontext->msgs_out.inflight_count12 = 0;\n\tcontext->msgs_out.queued_bytes = 0;\n\tcontext->msgs_out.queued_bytes12 = 0;\n\tcontext->msgs_out.queued_count = 0;\n\tcontext->msgs_out.queued_count12 = 0;\n\tcontext->msgs_out.inflight_quota = context->msgs_out.inflight_maximum;\n\n\tDL_FOREACH_SAFE(context->msgs_out.inflight, client_msg, tmp){\n\t\tdb__msg_add_to_inflight_stats(&context->msgs_out, client_msg);\n\t\tif(client_msg->data.qos > 0){\n\t\t\tutil__decrement_send_quota(context);\n\t\t}\n\n\t\tswitch(client_msg->data.qos){\n\t\t\tcase 0:\n\t\t\t\tclient_msg->data.state = mosq_ms_publish_qos0;\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tclient_msg->data.state = mosq_ms_publish_qos1;\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tif(client_msg->data.state == mosq_ms_wait_for_pubcomp){\n\t\t\t\t\tclient_msg->data.state = mosq_ms_resend_pubrel;\n\t\t\t\t}else{\n\t\t\t\t\tclient_msg->data.state = mosq_ms_publish_qos2;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\tplugin_persist__handle_client_msg_update(context, client_msg);\n\t}\n\t/* Messages received when the client was disconnected are put\n\t * in the mosq_ms_queued state. If we don't change them to the\n\t * appropriate \"publish\" state, then the queued messages won't\n\t * get sent until the client next receives a message - and they\n\t * will be sent out of order.\n\t */\n\tDL_FOREACH_SAFE(context->msgs_out.queued, client_msg, tmp){\n\t\tdb__msg_add_to_queued_stats(&context->msgs_out, client_msg);\n\t}\n\tdb__fill_inflight_out_from_queue(context);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* Called on reconnect to set incoming messages to expect an appropriate retry. */\nstatic int db__message_reconnect_reset_incoming(struct mosquitto *context)\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\n\tcontext->msgs_in.inflight_bytes = 0;\n\tcontext->msgs_in.inflight_bytes12 = 0;\n\tcontext->msgs_in.inflight_count = 0;\n\tcontext->msgs_in.inflight_count12 = 0;\n\tcontext->msgs_in.queued_bytes = 0;\n\tcontext->msgs_in.queued_bytes12 = 0;\n\tcontext->msgs_in.queued_count = 0;\n\tcontext->msgs_in.queued_count12 = 0;\n\tcontext->msgs_in.inflight_quota = context->msgs_in.inflight_maximum;\n\n\tDL_FOREACH_SAFE(context->msgs_in.inflight, client_msg, tmp){\n\t\tdb__msg_add_to_inflight_stats(&context->msgs_in, client_msg);\n\t\tif(client_msg->data.qos > 0){\n\t\t\tutil__decrement_receive_quota(context);\n\t\t}\n\n\t\tif(client_msg->data.qos != 2){\n\t\t\t/* Anything <QoS 2 can be completely retried by the client at\n\t\t\t * no harm. */\n\t\t\tdb__message_remove_inflight(context, &context->msgs_in, client_msg);\n\t\t}else{\n\t\t\t/* Message state can be preserved here because it should match\n\t\t\t * whatever the client has got. */\n\t\t\tclient_msg->data.dup = 0;\n\t\t}\n\t}\n\n\t/* Messages received when the client was disconnected are put\n\t * in the mosq_ms_queued state. If we don't change them to the\n\t * appropriate \"publish\" state, then the queued messages won't\n\t * get sent until the client next receives a message - and they\n\t * will be sent out of order.\n\t */\n\tDL_FOREACH_SAFE(context->msgs_in.queued, client_msg, tmp){\n\t\tclient_msg->data.dup = 0;\n\t\tdb__msg_add_to_queued_stats(&context->msgs_in, client_msg);\n\t\tif(db__ready_for_flight(context, mosq_md_in, client_msg->data.qos)){\n\t\t\tswitch(client_msg->data.qos){\n\t\t\t\tcase 0:\n\t\t\t\t\tclient_msg->data.state = mosq_ms_publish_qos0;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tclient_msg->data.state = mosq_ms_publish_qos1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tclient_msg->data.state = mosq_ms_publish_qos2;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdb__message_dequeue_first(context, &context->msgs_in);\n\t\t\tplugin_persist__handle_client_msg_update(context, client_msg);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint db__message_reconnect_reset(struct mosquitto *context)\n{\n\tint rc;\n\n\trc = db__message_reconnect_reset_outgoing(context);\n\tif(rc){\n\t\treturn rc;\n\t}\n\treturn db__message_reconnect_reset_incoming(context);\n}\n\n\nint db__message_remove_incoming(struct mosquitto *context, uint16_t mid)\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tDL_FOREACH_SAFE(context->msgs_in.inflight, client_msg, tmp){\n\t\tif(client_msg->data.mid == mid){\n\t\t\tif(client_msg->base_msg->data.qos != 2){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: Incorrect QoS (%d) when deleting incoming message.\",\n\t\t\t\t\t\tcontext->id, client_msg->base_msg->data.qos);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tdb__message_remove_inflight(context, &context->msgs_in, client_msg);\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_NOT_FOUND;\n}\n\n\nint db__message_release_incoming(struct mosquitto *context, uint16_t mid)\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\tint retain;\n\tchar *topic;\n\tchar *source_id;\n\tbool deleted = false;\n\tint rc;\n\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tDL_FOREACH_SAFE(context->msgs_in.inflight, client_msg, tmp){\n\t\tif(client_msg->data.mid == mid){\n\t\t\tif(client_msg->base_msg->data.qos != 2){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: Incorrect QoS (%d) when releasing incoming message.\",\n\t\t\t\t\t\tcontext->id, client_msg->base_msg->data.qos);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\ttopic = client_msg->base_msg->data.topic;\n\t\t\tretain = client_msg->data.retain;\n\t\t\tsource_id = client_msg->base_msg->data.source_id;\n\n\t\t\t/* topic==NULL should be a QoS 2 message that was\n\t\t\t * denied/dropped and is being processed so the client doesn't\n\t\t\t * keep resending it. That means we don't send it to other\n\t\t\t * clients. */\n\t\t\tif(topic == NULL){\n\t\t\t\tdb__message_remove_inflight(context, &context->msgs_in, client_msg);\n\t\t\t\tdeleted = true;\n\t\t\t}else{\n\t\t\t\trc = sub__messages_queue(source_id, topic, 2, retain, &client_msg->base_msg);\n\t\t\t\tif(rc == MOSQ_ERR_SUCCESS || rc == MOSQ_ERR_NO_SUBSCRIBERS){\n\t\t\t\t\tdb__message_remove_inflight(context, &context->msgs_in, client_msg);\n\t\t\t\t\tdeleted = true;\n\t\t\t\t}else{\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tDL_FOREACH_SAFE(context->msgs_in.queued, client_msg, tmp){\n\t\tif(db__ready_for_flight(context, mosq_md_in, client_msg->data.qos)){\n\t\t\tbreak;\n\t\t}\n\n\t\tif(client_msg->data.qos == 2){\n\t\t\tsend__pubrec(context, client_msg->data.mid, 0, NULL);\n\t\t\tclient_msg->data.state = mosq_ms_wait_for_pubrel;\n\t\t\tdb__message_dequeue_first(context, &context->msgs_in);\n\t\t\tplugin_persist__handle_client_msg_update(context, client_msg);\n\t\t}\n\t}\n\tif(deleted){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n}\n\n\nvoid db__expire_all_messages(struct mosquitto *context)\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\n\tDL_FOREACH_SAFE(context->msgs_out.inflight, client_msg, tmp){\n\t\tif(client_msg->base_msg->data.expiry_time && db.now_real_s > client_msg->base_msg->data.expiry_time){\n\t\t\tif(client_msg->data.qos > 0){\n\t\t\t\tutil__increment_send_quota(context);\n\t\t\t}\n\t\t\tdb__message_remove_inflight(context, &context->msgs_out, client_msg);\n\t\t}\n\t}\n\tdb__fill_inflight_out_from_queue(context);\n\tDL_FOREACH_SAFE(context->msgs_out.queued, client_msg, tmp){\n\t\tif(client_msg->base_msg->data.expiry_time && db.now_real_s > client_msg->base_msg->data.expiry_time){\n\t\t\tdb__message_remove_queued(context, &context->msgs_out, client_msg);\n\t\t}\n\t}\n\tDL_FOREACH_SAFE(context->msgs_in.inflight, client_msg, tmp){\n\t\tif(client_msg->base_msg->data.expiry_time && db.now_real_s > client_msg->base_msg->data.expiry_time){\n\t\t\tif(client_msg->data.qos > 0){\n\t\t\t\tutil__increment_receive_quota(context);\n\t\t\t}\n\t\t\tdb__message_remove_inflight(context, &context->msgs_in, client_msg);\n\t\t}\n\t}\n\tDL_FOREACH_SAFE(context->msgs_in.queued, client_msg, tmp){\n\t\tif(client_msg->base_msg->data.expiry_time && db.now_real_s > client_msg->base_msg->data.expiry_time){\n\t\t\tdb__message_remove_queued(context, &context->msgs_in, client_msg);\n\t\t}\n\t}\n}\n\n\nstatic void db__client_messages_check_acl(struct mosquitto *context, struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg **head,\n\t\tvoid (*decrement_stats_fn)(struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *client_msg))\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\tstruct mosquitto__base_msg *base_msg;\n\tint access;\n\n\tDL_FOREACH_SAFE((*head), client_msg, tmp){\n\t\tbase_msg = client_msg->base_msg;\n\t\tif(client_msg->data.direction == mosq_md_out){\n\t\t\taccess = MOSQ_ACL_READ;\n\t\t}else{\n\t\t\taccess = MOSQ_ACL_WRITE;\n\t\t}\n\t\tif(mosquitto_acl_check(context, base_msg->data.topic,\n\t\t\t\tbase_msg->data.payloadlen, base_msg->data.payload,\n\t\t\t\tbase_msg->data.qos, base_msg->data.retain,\n\t\t\t\tbase_msg->data.properties, access) != MOSQ_ERR_SUCCESS){\n\n\t\t\tDL_DELETE((*head), client_msg);\n\t\t\tdecrement_stats_fn(msg_data, client_msg);\n\t\t\tplugin_persist__handle_client_msg_delete(context, client_msg);\n\t\t\tdb__msg_store_ref_dec(&client_msg->base_msg);\n\t\t\tmosquitto_FREE(client_msg);\n\t\t}\n\t}\n}\n\n\nvoid db__check_acl_of_all_messages(struct mosquitto *context)\n{\n\tdb__client_messages_check_acl(context, &context->msgs_in, &context->msgs_in.inflight, &db__msg_remove_from_inflight_stats);\n\tdb__client_messages_check_acl(context, &context->msgs_in, &context->msgs_in.queued, &db__msg_remove_from_queued_stats);\n\tdb__client_messages_check_acl(context, &context->msgs_out, &context->msgs_out.inflight, &db__msg_remove_from_inflight_stats);\n\tdb__client_messages_check_acl(context, &context->msgs_out, &context->msgs_out.queued, &db__msg_remove_from_queued_stats);\n}\n\n\nstatic int db__message_write_inflight_out_single(struct mosquitto *context, struct mosquitto__client_msg *client_msg)\n{\n\tstruct mosquitto__base_msg *base_msg;\n\tmosquitto_property *base_msg_props = NULL;\n\tint rc;\n\tuint16_t mid;\n\tint retries;\n\tint retain;\n\tconst char *topic;\n\tuint8_t qos;\n\tuint32_t payloadlen;\n\tconst void *payload;\n\tuint32_t expiry_interval;\n\tuint32_t subscription_id;\n\n\tbase_msg = client_msg->base_msg;\n\n\texpiry_interval = 0;\n\tif(base_msg->data.expiry_time){\n\t\tif(db.now_real_s > base_msg->data.expiry_time){\n\t\t\t/* Message is expired, must not send. */\n\t\t\tif(client_msg->data.direction == mosq_md_out && client_msg->data.qos > 0){\n\t\t\t\tutil__increment_send_quota(context);\n\t\t\t}\n\t\t\tdb__message_remove_inflight(context, &context->msgs_out, client_msg);\n\t\t\tdb__fill_inflight_out_from_queue(context);\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\texpiry_interval = (uint32_t)(base_msg->data.expiry_time - db.now_real_s);\n\t\t}\n\t}\n\tmid = client_msg->data.mid;\n\tretries = client_msg->data.dup;\n\tretain = client_msg->data.retain;\n\ttopic = base_msg->data.topic;\n\tqos = (uint8_t)client_msg->data.qos;\n\tpayloadlen = base_msg->data.payloadlen;\n\tpayload = base_msg->data.payload;\n\tsubscription_id = client_msg->data.subscription_identifier;\n\tbase_msg_props = base_msg->data.properties;\n\n\tswitch(client_msg->data.state){\n\t\tcase mosq_ms_publish_qos0:\n\t\t\trc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries, subscription_id, base_msg_props, expiry_interval);\n\t\t\tif(rc == MOSQ_ERR_SUCCESS || rc == MOSQ_ERR_OVERSIZE_PACKET){\n\t\t\t\tdb__message_remove_inflight(context, &context->msgs_out, client_msg);\n\t\t\t}else{\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase mosq_ms_publish_qos1:\n\t\t\trc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries, subscription_id, base_msg_props, expiry_interval);\n\t\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\t\tclient_msg->data.dup = 1; /* Any retry attempts are a duplicate. */\n\t\t\t\tclient_msg->data.state = mosq_ms_wait_for_puback;\n\t\t\t\tplugin_persist__handle_client_msg_update(context, client_msg);\n\t\t\t}else if(rc == MOSQ_ERR_OVERSIZE_PACKET){\n\t\t\t\tdb__message_remove_inflight(context, &context->msgs_out, client_msg);\n\t\t\t}else{\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase mosq_ms_publish_qos2:\n\t\t\trc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries, subscription_id, base_msg_props, expiry_interval);\n\t\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\t\tclient_msg->data.dup = 1; /* Any retry attempts are a duplicate. */\n\t\t\t\tclient_msg->data.state = mosq_ms_wait_for_pubrec;\n\t\t\t\tplugin_persist__handle_client_msg_update(context, client_msg);\n\t\t\t}else if(rc == MOSQ_ERR_OVERSIZE_PACKET){\n\t\t\t\tdb__message_remove_inflight(context, &context->msgs_out, client_msg);\n\t\t\t}else{\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase mosq_ms_resend_pubrel:\n\t\t\trc = send__pubrel(context, mid, NULL);\n\t\t\tif(!rc){\n\t\t\t\tclient_msg->data.state = mosq_ms_wait_for_pubcomp;\n\t\t\t\tplugin_persist__handle_client_msg_update(context, client_msg);\n\t\t\t}else{\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase mosq_ms_invalid:\n\t\tcase mosq_ms_send_pubrec:\n\t\tcase mosq_ms_resend_pubcomp:\n\t\tcase mosq_ms_wait_for_puback:\n\t\tcase mosq_ms_wait_for_pubrec:\n\t\tcase mosq_ms_wait_for_pubrel:\n\t\tcase mosq_ms_wait_for_pubcomp:\n\t\tcase mosq_ms_queued:\n\t\t\tbreak;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint db__message_write_inflight_out_all(struct mosquitto *context)\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\tint rc;\n\n\tif(context->state != mosq_cs_active || !net__is_connected(context)){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tDL_FOREACH_SAFE(context->msgs_out.inflight, client_msg, tmp){\n\t\trc = db__message_write_inflight_out_single(context, client_msg);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint db__message_write_inflight_out_latest(struct mosquitto *context)\n{\n\tstruct mosquitto__client_msg *client_msg, *next;\n\tint rc;\n\n\tif(context->state != mosq_cs_active\n\t\t\t|| !net__is_connected(context)\n\t\t\t|| context->msgs_out.inflight == NULL){\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(context->msgs_out.inflight->prev == context->msgs_out.inflight){\n\t\t/* Only one message */\n\t\treturn db__message_write_inflight_out_single(context, context->msgs_out.inflight);\n\t}\n\n\t/* Start at the end of the list and work backwards looking for the first\n\t * message in a non-publish state */\n\tclient_msg = context->msgs_out.inflight->prev;\n\twhile(client_msg != context->msgs_out.inflight &&\n\t\t\t(client_msg->data.state == mosq_ms_publish_qos0\n\t\t\t|| client_msg->data.state == mosq_ms_publish_qos1\n\t\t\t|| client_msg->data.state == mosq_ms_publish_qos2)){\n\n\t\tclient_msg = client_msg->prev;\n\t}\n\n\t/* Tail is now either the head of the list, if that message is waiting for\n\t * publish, or the oldest message not waiting for a publish. In the latter\n\t * case, any pending publishes should be next after this message. */\n\tif(client_msg != context->msgs_out.inflight){\n\t\tclient_msg = client_msg->next;\n\t}\n\n\twhile(client_msg){\n\t\tnext = client_msg->next;\n\t\trc = db__message_write_inflight_out_single(context, client_msg);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\tclient_msg = next;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint db__message_write_queued_in(struct mosquitto *context)\n{\n\tstruct mosquitto__client_msg *client_msg, *tmp;\n\tint rc;\n\n\tif(context->state != mosq_cs_active){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tDL_FOREACH_SAFE(context->msgs_in.queued, client_msg, tmp){\n\t\tif(context->msgs_in.inflight_maximum != 0 && context->msgs_in.inflight_quota == 0){\n\t\t\tbreak;\n\t\t}\n\n\t\tif(client_msg->data.qos == 2){\n\t\t\tclient_msg->data.state = mosq_ms_send_pubrec;\n\t\t\tdb__message_dequeue_first(context, &context->msgs_in);\n\t\t\trc = send__pubrec(context, client_msg->data.mid, 0, NULL);\n\t\t\tif(!rc){\n\t\t\t\tclient_msg->data.state = mosq_ms_wait_for_pubrel;\n\t\t\t\tplugin_persist__handle_client_msg_update(context, client_msg);\n\t\t\t}else{\n\t\t\t\tplugin_persist__handle_client_msg_update(context, client_msg);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint db__message_write_queued_out(struct mosquitto *context)\n{\n\tif(context->state != mosq_cs_active){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tdb__fill_inflight_out_from_queue(context);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "src/handle_auth.c",
    "content": "/*\nCopyright (c) 2018-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n#include \"will_mosq.h\"\n\n\nint handle__auth(struct mosquitto *context)\n{\n\tint rc = 0;\n\tuint8_t reason_code = 0;\n\tmosquitto_property *properties = NULL;\n\tchar *auth_method = NULL;\n\tvoid *auth_data = NULL;\n\tuint16_t auth_data_len = 0;\n\tvoid *auth_data_out = NULL;\n\tuint16_t auth_data_out_len = 0;\n\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(context->protocol != mosq_p_mqtt5){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: AUTH packet when session not MQTT v5.0.\", context->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(context->auth_method == NULL){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: AUTH packet without existing auth-method.\", context->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(context->in_packet.command != CMD_AUTH){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tif(context->in_packet.remaining_length > 0){\n\t\tif(packet__read_byte(&context->in_packet, &reason_code)){\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t\tif(reason_code != MQTT_RC_CONTINUE_AUTHENTICATION\n\t\t\t\t&& reason_code != MQTT_RC_REAUTHENTICATE){\n\n\t\t\tsend__disconnect(context, MQTT_RC_PROTOCOL_ERROR, NULL);\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: AUTH packet with reason-code = %d.\",\n\t\t\t\t\tcontext->id, reason_code);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\n\t\tif((reason_code == MQTT_RC_REAUTHENTICATE && context->state != mosq_cs_active)\n\t\t\t\t|| (reason_code == MQTT_RC_CONTINUE_AUTHENTICATION\n\t\t\t\t&& context->state != mosq_cs_authenticating && context->state != mosq_cs_reauthenticating)){\n\n\t\t\tsend__disconnect(context, MQTT_RC_PROTOCOL_ERROR, NULL);\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: AUTH reauthentication packet before session is active,\",\n\t\t\t\t\tcontext->id);\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"     or attempted to continue authentication when no authentication in progress.\");\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\n\t\trc = property__read_all(CMD_AUTH, &context->in_packet, &properties);\n\t\tif(rc){\n\t\t\tsend__disconnect(context, MQTT_RC_UNSPECIFIED, NULL);\n\t\t\treturn rc;\n\t\t}\n\n\n\t\tif(mosquitto_property_read_string(properties, MQTT_PROP_AUTHENTICATION_METHOD, &auth_method, false) == NULL){\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\tsend__disconnect(context, MQTT_RC_UNSPECIFIED, NULL);\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: AUTH packet without auth-method property.\",\n\t\t\t\t\tcontext->id);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\n\t\tif(!auth_method || strcmp(auth_method, context->auth_method)){\n\t\t\t/* No method, or non-matching method */\n\t\t\tmosquitto_FREE(auth_method);\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\tsend__disconnect(context, MQTT_RC_PROTOCOL_ERROR, NULL);\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: AUTH packet with non-matching auth-method property (%s:%s).\",\n\t\t\t\t\tcontext->id, context->auth_method, auth_method);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t\tmosquitto_FREE(auth_method);\n\n\t\tmosquitto_property_read_binary(properties, MQTT_PROP_AUTHENTICATION_DATA, &auth_data, &auth_data_len, false);\n\n\t\tmosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */\n\t}\n\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received AUTH from %s (rc%d, %s)\", context->id, reason_code, context->auth_method);\n\n\n\tif(reason_code == MQTT_RC_REAUTHENTICATE){\n\t\t/* This is a re-authentication attempt */\n\t\tmosquitto__set_state(context, mosq_cs_reauthenticating);\n\t\trc = mosquitto_security_auth_start(context, true, auth_data, auth_data_len, &auth_data_out, &auth_data_out_len);\n\t}else{\n\t\tif(context->state != mosq_cs_reauthenticating){\n\t\t\tmosquitto__set_state(context, mosq_cs_authenticating);\n\t\t}\n\t\trc = mosquitto_security_auth_continue(context, auth_data, auth_data_len, &auth_data_out, &auth_data_out_len);\n\t}\n\tmosquitto_FREE(auth_data);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tif(context->state == mosq_cs_authenticating){\n\t\t\treturn connect__on_authorised(context, auth_data_out, auth_data_out_len);\n\t\t}else{\n\t\t\tmosquitto__set_state(context, mosq_cs_active);\n\t\t\trc = send__auth(context, MQTT_RC_SUCCESS, auth_data_out, auth_data_out_len);\n\t\t\tSAFE_FREE(auth_data_out);\n\t\t\treturn rc;\n\t\t}\n\t}else if(rc == MOSQ_ERR_AUTH_CONTINUE){\n\t\trc = send__auth(context, MQTT_RC_CONTINUE_AUTHENTICATION, auth_data_out, auth_data_out_len);\n\t\tSAFE_FREE(auth_data_out);\n\t\treturn rc;\n\t}else{\n\t\tSAFE_FREE(auth_data_out);\n\t\tif(context->state == mosq_cs_authenticating && context->will){\n\t\t\t/* Free will without sending if this is our first authentication attempt */\n\t\t\twill__clear(context);\n\t\t}\n\t\tif(rc == MOSQ_ERR_AUTH){\n\t\t\tif(context->state == mosq_cs_authenticating){\n\t\t\t\tsend__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL);\n\t\t\t\tmosquitto_FREE(context->id);\n\t\t\t}else{\n\t\t\t\tsend__disconnect(context, MQTT_RC_NOT_AUTHORIZED, NULL);\n\t\t\t}\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}else if(rc == MOSQ_ERR_NOT_SUPPORTED){\n\t\t\t/* Client has requested extended authentication, but we don't support it. */\n\t\t\tif(context->state == mosq_cs_authenticating){\n\t\t\t\tsend__connack(context, 0, MQTT_RC_BAD_AUTHENTICATION_METHOD, NULL);\n\t\t\t\tmosquitto_FREE(context->id);\n\t\t\t}else{\n\t\t\t\tsend__disconnect(context, MQTT_RC_BAD_AUTHENTICATION_METHOD, NULL);\n\t\t\t}\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}else{\n\t\t\tif(context->state == mosq_cs_authenticating){\n\t\t\t\tmosquitto_FREE(context->id);\n\t\t\t}\n\t\t\treturn rc;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/handle_connack.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nstatic int handle__connack_properties(struct mosquitto *context)\n{\n\tint rc;\n\tmosquitto_property *properties = NULL;\n\tuint16_t inflight_maximum;\n\tuint8_t max_qos = 255;\n\tuint32_t maximum_packet_size;\n\tuint16_t server_keepalive;\n\tuint16_t max_topic_alias;\n\tuint8_t retain_available;\n\n\trc = property__read_all(CMD_CONNACK, &context->in_packet, &properties);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\t/* maximum-qos */\n\tmosquitto_property_read_byte(properties, MQTT_PROP_MAXIMUM_QOS,\n\t\t\t&max_qos, false);\n\tif(max_qos != 255){\n\t\tcontext->max_qos = max_qos;\n\t}\n\n\t/* maximum-packet-size */\n\tif(mosquitto_property_read_int32(properties, MQTT_PROP_MAXIMUM_PACKET_SIZE,\n\t\t\t&maximum_packet_size, false)){\n\n\t\tif(context->maximum_packet_size == 0 || context->maximum_packet_size > maximum_packet_size){\n\t\t\tcontext->maximum_packet_size = maximum_packet_size;\n\t\t}\n\t}\n\n\t/* receive-maximum */\n\tinflight_maximum = context->msgs_out.inflight_maximum;\n\tmosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &inflight_maximum, false);\n\tif(context->msgs_out.inflight_maximum != inflight_maximum){\n\t\tcontext->msgs_out.inflight_maximum = inflight_maximum;\n\t\tdb__message_reconnect_reset(context);\n\t}\n\n\t/* retain-available */\n\tif(mosquitto_property_read_byte(properties, MQTT_PROP_RETAIN_AVAILABLE,\n\t\t\t&retain_available, false)){\n\n\t\t/* Only use broker provided value if the local config is set to available==true */\n\t\tif(context->retain_available){\n\t\t\tcontext->retain_available = retain_available;\n\t\t}\n\t}\n\n\t/* server-keepalive */\n\tif(mosquitto_property_read_int16(properties, MQTT_PROP_SERVER_KEEP_ALIVE,\n\t\t\t&server_keepalive, false)){\n\n\t\tcontext->keepalive = server_keepalive;\n\t}\n\n\t/* topic-alias-maximum */\n\tif(mosquitto_property_read_int16(properties, MQTT_PROP_TOPIC_ALIAS_MAXIMUM,\n\t\t\t&max_topic_alias, false)){\n\n\t\tif(max_topic_alias < context->bridge->max_topic_alias){\n\t\t\tcontext->alias_max_l2r = max_topic_alias;\n\t\t}else{\n\t\t\tcontext->alias_max_l2r = context->bridge->max_topic_alias;\n\t\t}\n\t}\n\n\tmosquitto_property_free_all(&properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint handle__connack(struct mosquitto *context)\n{\n\tint rc;\n\tuint8_t connect_acknowledge;\n\tuint8_t reason_code;\n\n\tif(context == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(context->bridge == NULL){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: CONNACK when not a bridge.\", context->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(context->in_packet.command != CMD_CONNACK){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received CONNACK on connection %s.\", context->id);\n\tif(packet__read_byte(&context->in_packet, &connect_acknowledge)){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\tif(packet__read_byte(&context->in_packet, &reason_code)){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tif(context->protocol == mosq_p_mqtt5){\n\t\tif(context->in_packet.remaining_length == 2 && reason_code == CONNACK_REFUSED_PROTOCOL_VERSION){\n\t\t\t/* We have connected to a MQTT v3.x broker that doesn't support MQTT v5.0\n\t\t\t * It has correctly replied with a CONNACK code of a bad protocol version.\n\t\t\t */\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE,\n\t\t\t\t\t\"Warning: Remote bridge %s does not support MQTT v5.0, reconnecting using MQTT v3.1.1.\",\n\t\t\t\t\tcontext->bridge->name);\n\n\t\t\tcontext->protocol = mosq_p_mqtt311;\n\t\t\tcontext->bridge->protocol_version = mosq_p_mqtt311;\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\n\t\trc = handle__connack_properties(context);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tif(reason_code == MQTT_RC_SUCCESS){\n#ifdef WITH_BRIDGE\n\t\tif(context->bridge){\n\t\t\trc = bridge__on_connect(context);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n#endif\n\t\tmosquitto__set_state(context, mosq_cs_active);\n\t\trc = db__message_write_queued_out(context);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\trc = db__message_write_inflight_out_all(context);\n\t\treturn rc;\n\t}else{\n\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\tswitch(reason_code){\n\t\t\t\tcase MQTT_RC_RETAIN_NOT_SUPPORTED:\n\t\t\t\t\tcontext->retain_available = 0;\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Connection Refused: retain not available (will retry)\");\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\tcase MQTT_RC_QOS_NOT_SUPPORTED:\n\t\t\t\t\tif(context->max_qos != 0){\n\t\t\t\t\t\tcontext->max_qos--;\n\t\t\t\t\t}\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Connection Refused: QoS not supported (will retry)\");\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\tdefault:\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Connection Refused: %s\", mosquitto_reason_string(reason_code));\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t}\n\t\t}else{\n\t\t\tswitch(reason_code){\n\t\t\t\tcase CONNACK_REFUSED_PROTOCOL_VERSION:\n\t\t\t\t\tif(context->bridge){\n\t\t\t\t\t\tcontext->bridge->try_private_accepted = false;\n\t\t\t\t\t}\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Connection Refused: unacceptable protocol version\");\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\tcase CONNACK_REFUSED_IDENTIFIER_REJECTED:\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Connection Refused: identifier rejected\");\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\tcase CONNACK_REFUSED_SERVER_UNAVAILABLE:\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Connection Refused: broker unavailable\");\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\tcase CONNACK_REFUSED_BAD_USERNAME_PASSWORD:\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Connection Refused: bad user name or password\");\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\tcase CONNACK_REFUSED_NOT_AUTHORIZED:\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Connection Refused: not authorised\");\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\tdefault:\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Connection Refused: unknown reason\");\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_CONN_LOST;\n}\n\n"
  },
  {
    "path": "src/handle_connect.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <utlist.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"send_mosq.h\"\n#include \"sys_tree.h\"\n#include \"tls_mosq.h\"\n#include \"util_mosq.h\"\n#include \"will_mosq.h\"\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n#  include <libwebsockets.h>\n#endif\n\n\nstatic char nibble_to_hex(uint8_t value)\n{\n\tif(value < 0x0A){\n\t\treturn (char)('0'+value);\n\t}else{\n\t\treturn (char)(65 /*'A'*/ +value-10);\n\t}\n}\n\n\nstatic char *clientid_gen(uint16_t *idlen, const char *auto_id_prefix, uint16_t auto_id_prefix_len)\n{\n\tchar *clientid;\n\tuint8_t rnd[16];\n\tint pos;\n\n\tif(mosquitto_getrandom(rnd, 16)){\n\t\treturn NULL;\n\t}\n\n\t*idlen = (uint16_t)(auto_id_prefix_len + 36);\n\n\tclientid = (char *)mosquitto_calloc((size_t)(*idlen) + 1, sizeof(char));\n\tif(!clientid){\n\t\treturn NULL;\n\t}\n\tif(auto_id_prefix){\n\t\tmemcpy(clientid, auto_id_prefix, auto_id_prefix_len);\n\t}\n\n\tpos = 0;\n\tfor(int i=0; i<16; i++){\n\t\tclientid[auto_id_prefix_len + pos + 0] = nibble_to_hex(rnd[i] & 0x0F);\n\t\tclientid[auto_id_prefix_len + pos + 1] = nibble_to_hex((rnd[i] >> 4) & 0x0F);\n\t\tpos += 2;\n\t\tif(pos == 8 || pos == 13 || pos == 18 || pos == 23){\n\t\t\tclientid[auto_id_prefix_len + pos] = '-';\n\t\t\tpos++;\n\t\t}\n\t}\n\n\treturn clientid;\n}\n\n\nint connect__on_authorised(struct mosquitto *context, void *auth_data_out, uint16_t auth_data_out_len)\n{\n\tstruct mosquitto *found_context;\n\tstruct mosquitto__subleaf *leaf;\n\tmosquitto_property *connack_props = NULL;\n\tuint8_t connect_ack = 0;\n\tint rc;\n\tint in_quota, out_quota;\n\tuint16_t in_maximum, out_maximum;\n\n\t/* Find if this client already has an entry. This must be done *after* any security checks. */\n\tHASH_FIND(hh_id, db.contexts_by_id, context->id, strlen(context->id), found_context);\n\tif(found_context){\n\t\t/* Found a matching client */\n\t\tif(!net__is_connected(found_context)){\n\t\t\t/* Client is reconnecting after a disconnect */\n\t\t\t/* FIXME - does anything need to be done here? */\n\t\t}else{\n\t\t\t/* Client is already connected, disconnect old version. This is\n\t\t\t * done in context__cleanup() below. */\n\t\t}\n\n\t\tif(context->clean_start == true){\n\t\t\tsub__clean_session(found_context);\n\t\t\tfound_context->session_expiry_interval = MQTT_SESSION_EXPIRY_IMMEDIATE;\n\t\t\tplugin_persist__handle_client_delete(found_context);\n\t\t}\n\t\tcontext->is_persisted = found_context->is_persisted;\n\t\tfound_context->is_persisted = false; /* stops persistence for context being removed */\n\n\t\tif(context->clean_start == false && found_context->session_expiry_interval != MQTT_SESSION_EXPIRY_IMMEDIATE){\n\t\t\tif(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){\n\t\t\t\tconnect_ack |= 0x01;\n\t\t\t}\n\n\t\t\tif(found_context->msgs_in.inflight || found_context->msgs_in.queued\n\t\t\t\t\t|| found_context->msgs_out.inflight || found_context->msgs_out.queued){\n\n\t\t\t\tin_quota = context->msgs_in.inflight_quota;\n\t\t\t\tout_quota = context->msgs_out.inflight_quota;\n\t\t\t\tin_maximum = context->msgs_in.inflight_maximum;\n\t\t\t\tout_maximum = context->msgs_out.inflight_maximum;\n\n\t\t\t\tmemcpy(&context->msgs_in, &found_context->msgs_in, sizeof(struct mosquitto_msg_data));\n\t\t\t\tmemcpy(&context->msgs_out, &found_context->msgs_out, sizeof(struct mosquitto_msg_data));\n\t\t\t\tcontext->last_cmsg_id = found_context->last_cmsg_id;\n\n\t\t\t\tmemset(&found_context->msgs_in, 0, sizeof(struct mosquitto_msg_data));\n\t\t\t\tmemset(&found_context->msgs_out, 0, sizeof(struct mosquitto_msg_data));\n\n\t\t\t\tcontext->msgs_in.inflight_quota = in_quota;\n\t\t\t\tcontext->msgs_out.inflight_quota = out_quota;\n\t\t\t\tcontext->msgs_in.inflight_maximum = in_maximum;\n\t\t\t\tcontext->msgs_out.inflight_maximum = out_maximum;\n\n\t\t\t\tdb__message_reconnect_reset(context);\n\t\t\t}\n\t\t\tcontext->subs = found_context->subs;\n\t\t\tfound_context->subs = NULL;\n\t\t\tcontext->subs_capacity = found_context->subs_capacity;\n\t\t\tcontext->subs_count = found_context->subs_count;\n\t\t\tfound_context->subs_capacity = 0;\n\t\t\tfound_context->subs_count = 0;\n\t\t\tcontext->last_mid = found_context->last_mid;\n\n\t\t\tfor(int i=0; i<context->subs_capacity; i++){\n\t\t\t\tif(context->subs[i]){\n\t\t\t\t\tleaf = context->subs[i]->hier->subs;\n\t\t\t\t\twhile(leaf){\n\t\t\t\t\t\tif(leaf->context == found_context){\n\t\t\t\t\t\t\tleaf->context = context;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tleaf = leaf->next;\n\t\t\t\t\t}\n\n\t\t\t\t\tif(context->subs[i]->shared){\n\t\t\t\t\t\tleaf = context->subs[i]->shared->subs;\n\t\t\t\t\t\twhile(leaf){\n\t\t\t\t\t\t\tif(leaf->context == found_context){\n\t\t\t\t\t\t\t\tleaf->context = context;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tleaf = leaf->next;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif((found_context->protocol == mosq_p_mqtt5 && found_context->session_expiry_interval == MQTT_SESSION_EXPIRY_IMMEDIATE)\n\t\t\t\t|| (found_context->protocol != mosq_p_mqtt5 && found_context->clean_start == true)\n\t\t\t\t|| (context->clean_start == true)\n\t\t\t\t){\n\n\t\t\tcontext__send_will(found_context);\n\t\t}\n\n\t\tsession_expiry__remove(found_context);\n\t\twill_delay__remove(found_context);\n\t\twill__clear(found_context);\n\n\t\tfound_context->clean_start = true;\n\t\tfound_context->session_expiry_interval = MQTT_SESSION_EXPIRY_IMMEDIATE;\n\t\tmosquitto__set_state(found_context, mosq_cs_duplicate);\n\t\tif(found_context->protocol == mosq_p_mqtt5){\n\t\t\tsend__disconnect(found_context, MQTT_RC_SESSION_TAKEN_OVER, NULL);\n\t\t}\n\t\tdo_disconnect(found_context, MOSQ_ERR_SESSION_TAKEN_OVER);\n\t}\n\n\tif(db.config->global_max_clients > 0 && HASH_CNT(hh_id, db.contexts_by_id) >= (unsigned int)db.config->global_max_clients){\n\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\tsend__connack(context, 0, MQTT_RC_SERVER_BUSY, NULL);\n\t\t}\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto error;\n\t}\n\n\tif(db.config->connection_messages == true){\n\t\tif(context->is_bridge){\n\t\t\tif(context->username){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"New bridge connected from %s:%d as %s (p%d, c%d, k%d, u'%s').\",\n\t\t\t\t\t\tcontext->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive, context->username);\n\t\t\t}else{\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"New bridge connected from %s:%d as %s (p%d, c%d, k%d).\",\n\t\t\t\t\t\tcontext->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive);\n\t\t\t}\n\t\t}else{\n\t\t\tif(context->username){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"New client connected from %s:%d as %s (p%d, c%d, k%d, u'%s').\",\n\t\t\t\t\t\tcontext->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive, context->username);\n\t\t\t}else{\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"New client connected from %s:%d as %s (p%d, c%d, k%d).\",\n\t\t\t\t\t\tcontext->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive);\n\t\t\t}\n\t\t}\n\n\t\tif(context->will){\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Will message specified (%ld bytes) (r%d, q%d).\",\n\t\t\t\t\t(long)context->will->msg.payloadlen,\n\t\t\t\t\tcontext->will->msg.retain,\n\t\t\t\t\tcontext->will->msg.qos);\n\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"\\t%s\", context->will->msg.topic);\n\t\t}else{\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"No will message specified.\");\n\t\t}\n\t}\n#ifdef WITH_TLS\n\tif(context->ssl){\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s negotiated %s cipher %s\",\n\t\t\t\tcontext->id,\n\t\t\t\tSSL_get_cipher_version(context->ssl),\n\t\t\t\tSSL_get_cipher_name(context->ssl));\n\t}\n#endif\n\n\tcontext->ping_t = 0;\n\tcontext->is_dropping = false;\n\n\t/* Remove any queued messages that are no longer allowed through ACL,\n\t * assuming a possible change of username. */\n\tdb__check_acl_of_all_messages(context);\n\tcontext__add_to_by_id(context);\n\n#ifdef WITH_PERSISTENCE\n\tif(!context->clean_start){\n\t\tdb.persistence_changes++;\n\t}\n#endif\n\tcontext->max_qos = context->listener->max_qos;\n\n\tif(db.config->max_keepalive &&\n\t\t\t(context->keepalive > db.config->max_keepalive || context->keepalive == 0)){\n\n\t\tkeepalive__remove(context);\n\t\tcontext->keepalive = db.config->max_keepalive;\n\t\tkeepalive__add(context);\n\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\tif(mosquitto_property_add_int16(&connack_props, MQTT_PROP_SERVER_KEEP_ALIVE, context->keepalive)){\n\t\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else{\n\t\t\tsend__connack(context, connect_ack, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL);\n\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\n\tif(context->protocol == mosq_p_mqtt5){\n\t\tif(context->listener->max_topic_alias > 0){\n\t\t\tif(mosquitto_property_add_int16(&connack_props, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, context->listener->max_topic_alias)){\n\t\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}\n\t\tif(context->assigned_id){\n\t\t\tif(mosquitto_property_add_string(&connack_props, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, context->id)){\n\t\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}\n\t\tif(context->auth_method){\n\t\t\tif(mosquitto_property_add_string(&connack_props, MQTT_PROP_AUTHENTICATION_METHOD, context->auth_method)){\n\t\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\t\tgoto error;\n\t\t\t}\n\n\t\t\tif(auth_data_out && auth_data_out_len > 0){\n\t\t\t\tif(mosquitto_property_add_binary(&connack_props, MQTT_PROP_AUTHENTICATION_DATA, auth_data_out, auth_data_out_len)){\n\t\t\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tSAFE_FREE(auth_data_out);\n\n\tmosquitto__set_state(context, mosq_cs_active);\n\trc = send__connack(context, connect_ack, CONNACK_ACCEPTED, connack_props);\n\tmosquitto_property_free_all(&connack_props);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tdb__expire_all_messages(context);\n\trc = db__message_write_queued_out(context);\n\tif(rc){\n\t\treturn rc;\n\t}\n\trc = db__message_write_inflight_out_all(context);\n\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tplugin__handle_connect(context);\n\n\t\tif(context->session_expiry_interval != MQTT_SESSION_EXPIRY_IMMEDIATE){\n\t\t\tplugin_persist__handle_client_add(context);\n\t\t}else if(context->will){\n\t\t\tplugin_persist__handle_will_add(context);\n\t\t}\n\t}\n\treturn rc;\nerror:\n\tSAFE_FREE(auth_data_out);\n\tmosquitto_property_free_all(&connack_props);\n\treturn rc;\n}\n\n\nstatic int will__read(struct mosquitto *context, const char *clientid, struct mosquitto_message_all **will, uint8_t will_qos, int will_retain)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\tsize_t slen;\n\tuint16_t tlen;\n\tstruct mosquitto_message_all *will_struct = NULL;\n\tchar *will_topic_mount = NULL;\n\tuint16_t payloadlen;\n\tmosquitto_property *properties = NULL;\n\n\twill_struct = mosquitto_calloc(1, sizeof(struct mosquitto_message_all));\n\tif(!will_struct){\n\t\trc = MOSQ_ERR_NOMEM;\n\t\tgoto error_cleanup;\n\t}\n\tif(context->protocol == PROTOCOL_VERSION_v5){\n\t\trc = property__read_all(CMD_WILL, &context->in_packet, &properties);\n\t\tif(rc){\n\t\t\tgoto error_cleanup;\n\t\t}\n\n\t\trc = property__process_will(context, will_struct, &properties);\n\t\tmosquitto_property_free_all(&properties);\n\t\tif(rc){\n\t\t\tgoto error_cleanup;\n\t\t}\n\t}\n\trc = packet__read_string(&context->in_packet, &will_struct->msg.topic, &tlen);\n\tif(rc){\n\t\tgoto error_cleanup;\n\t}\n\tif(!tlen){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: Will with empty topic.\",\n\t\t\t\tcontext->id);\n\t\trc = MOSQ_ERR_PROTOCOL;\n\t\tgoto error_cleanup;\n\t}\n\n\tif(context->listener->mount_point){\n\t\tslen = strlen(context->listener->mount_point) + strlen(will_struct->msg.topic) + 1;\n\t\twill_topic_mount = mosquitto_malloc(slen+1);\n\t\tif(!will_topic_mount){\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error_cleanup;\n\t\t}\n\n\t\tsnprintf(will_topic_mount, slen, \"%s%s\", context->listener->mount_point, will_struct->msg.topic);\n\t\twill_topic_mount[slen] = '\\0';\n\n\t\tmosquitto_FREE(will_struct->msg.topic);\n\t\twill_struct->msg.topic = will_topic_mount;\n\t}\n\n\tif(!strncmp(will_struct->msg.topic, \"$CONTROL/\", strlen(\"$CONTROL/\"))){\n\t\trc = MOSQ_ERR_ACL_DENIED;\n\t\tgoto error_cleanup;\n\t}\n\trc = mosquitto_pub_topic_check(will_struct->msg.topic);\n\tif(rc){\n\t\tgoto error_cleanup;\n\t}\n\n\trc = packet__read_uint16(&context->in_packet, &payloadlen);\n\tif(rc){\n\t\tgoto error_cleanup;\n\t}\n\n\twill_struct->msg.payloadlen = payloadlen;\n\tif(will_struct->msg.payloadlen > 0){\n\t\tif(db.config->message_size_limit && will_struct->msg.payloadlen > (int)db.config->message_size_limit){\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Client %s connected with too large Will payload\", clientid);\n\t\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\t\tsend__connack(context, 0, MQTT_RC_PACKET_TOO_LARGE, NULL);\n\t\t\t}else{\n\t\t\t\tsend__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL);\n\t\t\t}\n\t\t\trc = MOSQ_ERR_PAYLOAD_SIZE;\n\t\t\tgoto error_cleanup;\n\t\t}\n\t\twill_struct->msg.payload = mosquitto_malloc((size_t)will_struct->msg.payloadlen);\n\t\tif(!will_struct->msg.payload){\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error_cleanup;\n\t\t}\n\n\t\trc = packet__read_bytes(&context->in_packet, will_struct->msg.payload, (uint32_t)will_struct->msg.payloadlen);\n\t\tif(rc){\n\t\t\tgoto error_cleanup;\n\t\t}\n\t}\n\n\twill_struct->msg.qos = will_qos;\n\twill_struct->msg.retain = will_retain;\n\n\t*will = will_struct;\n\treturn MOSQ_ERR_SUCCESS;\n\nerror_cleanup:\n\tif(will_struct){\n\t\tmosquitto_FREE(will_struct->msg.topic);\n\t\tmosquitto_FREE(will_struct->msg.payload);\n\t\tmosquitto_property_free_all(&will_struct->properties);\n\t\tmosquitto_FREE(will_struct);\n\t}\n\treturn rc;\n}\n\n\nstatic int check_protocol_version(struct mosquitto__listener *listener, int protocol_version)\n{\n\t/* Allow bridge protocol as well. */\n\tprotocol_version &= 0x7F;\n\n\tif((protocol_version == 3 && listener->disable_protocol_v3 == false)\n\t\t\t|| (protocol_version == 4 && listener->disable_protocol_v4 == false)\n\t\t\t|| (protocol_version == 5 && listener->disable_protocol_v5 == false)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}\n}\n\n\ninline static int send__connack_error_and_return(struct mosquitto *context, uint8_t err_code, int rc)\n{\n\tsend__connack(context, 0, err_code, NULL);\n\treturn rc;\n}\n\n\ninline static int send__connack_bad_username_or_password_error(struct mosquitto *context, int rc)\n{\n\tuint8_t err_code  = context->protocol == mosq_p_mqtt5\n\t\t\t\t\t\t\t\t\t? (uint8_t)MQTT_RC_BAD_USERNAME_OR_PASSWORD\n\t\t\t\t\t\t\t\t\t: (uint8_t)CONNACK_REFUSED_BAD_USERNAME_PASSWORD;\n\treturn send__connack_error_and_return(context, err_code, rc);\n}\n\n\nstatic int read_protocol_name(struct mosquitto *context, char protocol_name[7])\n{\n\t/* Read protocol name as length then bytes rather than with read_string\n\t * because the length is fixed and we can check that. Removes the need\n\t * for another malloc as well. */\n\n\tuint16_t slen = 0;\n\n\tif(packet__read_uint16(&context->in_packet, &slen)){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with missing protocol string length.\",\n\t\t\t\tcontext->address, context->remote_port);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(slen != 4 /* MQTT */ && slen != 6 /* MQIsdp */){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with incorrect protocol string length (%d).\",\n\t\t\t\tcontext->address, context->remote_port, slen);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(packet__read_bytes(&context->in_packet, protocol_name, slen)){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with missing protocol string.\",\n\t\t\t\tcontext->address, context->remote_port);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tprotocol_name[slen] = '\\0';\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int read_and_verify_protocol_version(struct mosquitto *context, const char *protocol_name,\n\t\tuint8_t *protocol_version)\n{\n\tuint8_t tmp_protocol_version = 0;\n\tif(packet__read_byte(&context->in_packet, &tmp_protocol_version)){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with missing protocol version.\",\n\t\t\t\tcontext->address, context->remote_port);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(check_protocol_version(context->listener, tmp_protocol_version)){\n\t\tif(tmp_protocol_version == 3 || tmp_protocol_version == 4){\n\t\t\tcontext->protocol = mosq_p_mqtt311;\n\t\t\tsend__connack(context, 0, CONNACK_REFUSED_PROTOCOL_VERSION, NULL);\n\t\t}else{\n\t\t\tcontext->protocol = mosq_p_mqtt5;\n\t\t\tsend__connack(context, 0, MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION, NULL);\n\t\t}\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}\n\n\tif(!strcmp(protocol_name, PROTOCOL_NAME_v31)){\n\t\tif((tmp_protocol_version&0x7F) != PROTOCOL_VERSION_v31){\n\t\t\tif(db.config->connection_messages == true){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with invalid protocol version (%d).\",\n\t\t\t\t\t\tcontext->address, context->remote_port, tmp_protocol_version);\n\t\t\t}\n\t\t\tsend__connack(context, 0, CONNACK_REFUSED_PROTOCOL_VERSION, NULL);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t\tcontext->protocol = mosq_p_mqtt31;\n\t\tif((tmp_protocol_version&0x80) == 0x80){\n\t\t\tcontext->is_bridge = true;\n\t\t}\n\t}else if(!strcmp(protocol_name, PROTOCOL_NAME)){\n\t\tif((tmp_protocol_version&0x7F) == PROTOCOL_VERSION_v311){\n\t\t\tcontext->protocol = mosq_p_mqtt311;\n\n\t\t\tif((tmp_protocol_version&0x80) == 0x80){\n\t\t\t\tcontext->is_bridge = true;\n\t\t\t}\n\t\t}else if((tmp_protocol_version&0x7F) == PROTOCOL_VERSION_v5){\n\t\t\tcontext->protocol = mosq_p_mqtt5;\n\t\t}else{\n\t\t\tif(db.config->connection_messages == true){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with invalid protocol version (%d).\",\n\t\t\t\t\t\tcontext->address, context->remote_port, tmp_protocol_version);\n\t\t\t}\n\t\t\tsend__connack(context, 0, CONNACK_REFUSED_PROTOCOL_VERSION, NULL);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t\tif((context->in_packet.command&0x0F) != 0x00){\n\t\t\t/* Reserved flags not set to 0, must disconnect. */\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with non-zero reserved flags (%02X).\",\n\t\t\t\t\tcontext->address, context->remote_port, context->in_packet.command);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t}else{\n\t\tif(db.config->connection_messages == true){\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with invalid protocol \\\"%s\\\".\",\n\t\t\t\t\tcontext->address, context->remote_port, protocol_name);\n\t\t}\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif((tmp_protocol_version&0x7F) != PROTOCOL_VERSION_v31 && context->in_packet.command != CMD_CONNECT){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\t*protocol_version = tmp_protocol_version;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int read_and_verify_connect_flags(struct mosquitto *context, uint8_t *connect_flags)\n{\n\tif(packet__read_byte(&context->in_packet, connect_flags)){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with missing connect flags.\",\n\t\t\t\tcontext->address, context->remote_port);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){\n\t\tif((*connect_flags & 0x01) != 0x00){\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with non-zero connect reserved flag (%02X).\",\n\t\t\t\t\tcontext->address, context->remote_port, *connect_flags);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void set_session_expiry_interval(struct mosquitto *context, uint8_t clean_start, uint8_t protocol_version)\n{\n\t/* session_expiry_interval will be overridden if the properties are read later */\n\tif(clean_start == false && protocol_version != PROTOCOL_VERSION_v5){\n\t\t/* v3* has clean_start == false mean the session never expires */\n\t\tcontext->session_expiry_interval = MQTT_SESSION_EXPIRY_NEVER;\n\t}else{\n\t\tcontext->session_expiry_interval = MQTT_SESSION_EXPIRY_IMMEDIATE;\n\t}\n}\n\n\nstatic int read_and_reset_keepalive(struct mosquitto *context)\n{\n\t/* _remove here because net__socket_accept() uses _add and we must have the\n\t * correct keepalive value */\n\tkeepalive__remove(context);\n\n\tif(packet__read_uint16(&context->in_packet, &(context->keepalive))){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"%s sent CONNECT with missing keepalive.\",\n\t\t\t\tcontext->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tkeepalive__add(context);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int read_and_verify_v5_connect_properties(struct mosquitto *context, mosquitto_property **properties, uint8_t protocol_version)\n{\n\tint rc;\n\n\tif(protocol_version == PROTOCOL_VERSION_v5){\n\t\trc = property__read_all(CMD_CONNECT, &context->in_packet, properties);\n\t\tif(rc == MOSQ_ERR_DUPLICATE_PROPERTY || rc == MOSQ_ERR_PROTOCOL){\n\t\t\tsend__connack(context, 0, MQTT_RC_PROTOCOL_ERROR, NULL);\n\t\t}else if(rc == MOSQ_ERR_MALFORMED_PACKET){\n\t\t\tsend__connack(context, 0, MQTT_RC_MALFORMED_PACKET, NULL);\n\t\t}\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\trc = property__process_connect(context, properties);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn send__connack_error_and_return(context, MQTT_RC_PROTOCOL_ERROR, rc);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int verify_will_options(struct mosquitto *context, uint8_t will, uint8_t will_qos, uint8_t will_retain, uint8_t protocol_version)\n{\n\tif(will_qos == 3){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Invalid Will QoS in CONNECT from %s.\",\n\t\t\t\tcontext->address);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(will && will_retain && db.config->retain_available == false){\n\t\tif(protocol_version == mosq_p_mqtt5){\n\t\t\tsend__connack(context, 0, MQTT_RC_RETAIN_NOT_SUPPORTED, NULL);\n\t\t}\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}\n\n\tif(will && will_qos > context->listener->max_qos){\n\t\tif(protocol_version == mosq_p_mqtt5){\n\t\t\tsend__connack(context, 0, MQTT_RC_QOS_NOT_SUPPORTED, NULL);\n\t\t}\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int handle_zero_length_clientid(struct mosquitto *context, char **clientid, bool *allow_zero_length_clientid,\n\t\tuint8_t clean_start)\n{\n\tif(context->protocol == mosq_p_mqtt31){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: v3.1 CONNECT with zero length clientid.\",\n\t\t\t\tcontext->address, context->remote_port);\n\t\tsend__connack(context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\t/* mqtt311/mqtt5 */\n\tmosquitto_FREE(*clientid);\n\n\tif(db.config->per_listener_settings){\n\t\t*allow_zero_length_clientid = context->listener->security_options->allow_zero_length_clientid;\n\t}else{\n\t\t*allow_zero_length_clientid = db.config->security_options.allow_zero_length_clientid;\n\t}\n\n\tif((context->protocol == mosq_p_mqtt311 && clean_start == 0) || *allow_zero_length_clientid == false){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with zero length clientid when forbidden.\",\n\t\t\t\tcontext->address, context->remote_port);\n\t\tuint8_t err_code = context->protocol == mosq_p_mqtt311 ? (uint8_t)CONNACK_REFUSED_IDENTIFIER_REJECTED : (uint8_t)MQTT_RC_UNSPECIFIED;\n\t\treturn send__connack_error_and_return(context, err_code, MOSQ_ERR_PROTOCOL);\n\t}\n\n\t*clientid = clientid_gen(&(uint16_t){0}, context->listener->security_options->auto_id_prefix,\n\t\t\tcontext->listener->security_options->auto_id_prefix_len);\n\tif(*clientid == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcontext->assigned_id = true;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int check_clientid_prefixes(struct mosquitto *context, const char *clientid)\n{\n\tif(db.config->clientid_prefixes){\n\t\tif(strncmp(db.config->clientid_prefixes, clientid, strlen(db.config->clientid_prefixes))){\n\t\t\tuint8_t err_code = context->protocol == mosq_p_mqtt5 ? (uint8_t)MQTT_RC_NOT_AUTHORIZED : (uint8_t)CONNACK_REFUSED_NOT_AUTHORIZED;\n\t\t\treturn send__connack_error_and_return(context, err_code, MOSQ_ERR_AUTH);\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int read_and_verify_clientid_from_packet(struct mosquitto *context, char **clientid,\n\t\tbool *allow_zero_length_clientid, uint8_t clean_start)\n{\n\tint rc;\n\tuint16_t slen;\n\n\trc = packet__read_string(&context->in_packet, clientid, &slen);\n\tif(rc == MOSQ_ERR_MALFORMED_UTF8){\n\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\tsend__connack(context, 0, MQTT_RC_CLIENTID_NOT_VALID, NULL);\n\t\t}\n\t\treturn MOSQ_ERR_CLIENT_IDENTIFIER_NOT_VALID;\n\t}else if(rc > 0){\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(slen == 0){\n\t\trc = handle_zero_length_clientid(context, clientid, allow_zero_length_clientid, clean_start);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\trc = check_clientid_prefixes(context, *clientid);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int set_username_from_packet(struct mosquitto *context, char **username, const char *clientid)\n{\n\tint rc;\n\n\trc = packet__read_string(&context->in_packet, username, &(uint16_t){0});\n\tif(rc == MOSQ_ERR_NOMEM){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tif(context->protocol == mosq_p_mqtt31){\n\t\t\t/* Username flag given, but no username. Ignore. */\n\t\t\t/* NOTE: Removed setting of username_flag to zero as it is unused afterwards */\n\t\t}else{\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: CONNECT with username flag but no username.\",\n\t\t\t\t\tclientid);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int set_password_from_packet(struct mosquitto *context, char **password, uint16_t *password_len, const char *clientid)\n{\n\tint rc;\n\n\trc = packet__read_binary(&context->in_packet, (uint8_t **)password, password_len);\n\tif(rc == MOSQ_ERR_NOMEM){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(rc == MOSQ_ERR_MALFORMED_PACKET){\n\t\tif(context->protocol == mosq_p_mqtt31){\n\t\t\t/* Password flag given, but no password. Ignore. */\n\t\t}else{\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: CONNECT with password flag but no password.\",\n\t\t\t\t\tclientid);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int read_and_verify_client_credentials_from_packet(struct mosquitto *context,\n\t\tchar **username, uint8_t username_flag,\n\t\tchar **password, uint16_t *password_len, uint8_t password_flag,\n\t\tconst char *clientid)\n{\n\tint rc;\n\n\tif(username_flag){\n\t\trc = set_username_from_packet(context, username, clientid);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}else{\n\t\tif(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt31){\n\t\t\tif(password_flag){\n\t\t\t\t/* username_flag == 0 && password_flag == 1 is forbidden */\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Protocol error from %s: password without username, closing connection.\", clientid);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t}\n\t}\n\tif(password_flag){\n\t\trc = set_password_from_packet(context, password, password_len, clientid);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int check_additional_trailing_data(struct mosquitto *context, uint8_t protocol_version)\n{\n\tif(context->in_packet.pos != context->in_packet.remaining_length){\n\t\t/* Surplus data at end of packet, this must be an error. */\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: CONNECT packet with overlong remaining length (%d:%d).\",\n\t\t\t\tcontext->id, context->in_packet.pos, context->in_packet.remaining_length);\n\t\tif(protocol_version == PROTOCOL_VERSION_v5){\n\t\t\tsend__connack(context, 0, MQTT_RC_MALFORMED_PACKET, NULL);\n\t\t}\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n#ifdef WITH_TLS\n\n\ninline static int get_client_cert_and_subject_name(struct mosquitto *context, X509 **client_cert, X509_NAME **name)\n{\n\t*client_cert = SSL_get_peer_certificate(context->ssl);\n\tif(*client_cert == NULL){\n\t\treturn send__connack_bad_username_or_password_error(context, MOSQ_ERR_AUTH);\n\t}\n\n\t*name = X509_get_subject_name(*client_cert);\n\tif(*name == NULL){\n\t\tX509_free(*client_cert);\n\t\treturn send__connack_bad_username_or_password_error(context, MOSQ_ERR_AUTH);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\ninline static int free_x509_and_send_connack_error(struct mosquitto *context, X509 *client_cert, int rc)\n{\n\tX509_free(client_cert);\n\treturn send__connack_bad_username_or_password_error(context, rc);\n}\n\n\ninline static int free_x509_and_BIO_and_send_connack_error(struct mosquitto *context, X509 *client_cert,\n\t\tBIO *subject_name, int rc)\n{\n\tBIO_free(subject_name);\n\treturn free_x509_and_send_connack_error(context, client_cert, rc);\n}\n\n\nstatic int set_username_from_cert_identity(struct mosquitto *context)\n{\n\tX509 *client_cert = NULL;\n\tX509_NAME *name = NULL;\n\n\tif(get_client_cert_and_subject_name(context, &client_cert, &name)){\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\tint i = -1;\n\tX509_NAME_ENTRY *name_entry = NULL;\n\n\ti = X509_NAME_get_index_by_NID(name, NID_commonName, -1);\n\tif(i == -1){\n\t\treturn free_x509_and_send_connack_error(context, client_cert, MOSQ_ERR_AUTH);\n\t}\n\tname_entry = X509_NAME_get_entry(name, i);\n\tif(!name_entry){\n\t\tgoto success;\n\t}\n\n\tASN1_STRING *name_asn1 = NULL;\n\tname_asn1 = X509_NAME_ENTRY_get_data(name_entry);\n\tif(name_asn1 == NULL){\n\t\treturn free_x509_and_send_connack_error(context, client_cert, MOSQ_ERR_AUTH);\n\t}\n\tconst char *cert_identity = (const char *)ASN1_STRING_get0_data(name_asn1);\n\tif(!cert_identity || mosquitto_validate_utf8(cert_identity, (int)strlen(cert_identity))){\n\t\treturn free_x509_and_send_connack_error(context, client_cert, MOSQ_ERR_AUTH);\n\t}\n\tmosquitto_free(context->username);\n\tcontext->username = mosquitto_strdup(cert_identity);\n\tif(context->username == NULL){\n\t\treturn free_x509_and_send_connack_error(context, client_cert, MOSQ_ERR_NOMEM);\n\t}\n\t/* Make sure there isn't an embedded NUL character in the CN */\n\tif((size_t)ASN1_STRING_length(name_asn1) != strlen(context->username)){\n\t\treturn free_x509_and_send_connack_error(context, client_cert, MOSQ_ERR_AUTH);\n\t}\n\nsuccess:\n\tX509_free(client_cert);\n\tclient_cert = NULL;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int set_username_from_cert_subject_name(struct mosquitto *context)\n{\n\tX509 *client_cert = NULL;\n\tX509_NAME *name = NULL;\n\n\tif(get_client_cert_and_subject_name(context, &client_cert, &name)){\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\tchar *subject = NULL;\n\tchar *data_start = NULL;\n\tBIO *subject_bio = NULL;\n\tlong name_length = 0;\n\n\tsubject_bio = BIO_new(BIO_s_mem());\n\tX509_NAME_print_ex(subject_bio, X509_get_subject_name(client_cert), 0, XN_FLAG_RFC2253);\n\tdata_start = NULL;\n\tname_length = BIO_get_mem_data(subject_bio, &data_start);\n\tsubject = mosquitto_malloc(sizeof(char)*(size_t)(name_length+1));\n\tif(!subject){\n\t\treturn free_x509_and_BIO_and_send_connack_error(context, client_cert, subject_bio, MOSQ_ERR_NOMEM);\n\t}\n\tmemcpy(subject, data_start, (size_t)name_length);\n\tsubject[name_length] = '\\0';\n\tBIO_free(subject_bio);\n\n\tif(mosquitto_validate_utf8(subject, (int)strlen(subject))){\n\t\tmosquitto_free(subject);\n\t\treturn free_x509_and_send_connack_error(context, client_cert, MOSQ_ERR_AUTH);\n\t}\n\tcontext->username = subject;\n\tif(!context->username){\n\t\tX509_free(client_cert);\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\tX509_free(client_cert);\n\tclient_cert = NULL;\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\nstatic int handle_username_from_cert_options(struct mosquitto *context, char **username, char **password, uint16_t password_len)\n{\n\tint rc;\n\n#ifdef WITH_TLS\n\tif(context->listener->ssl_ctx && (context->listener->use_identity_as_username || context->listener->use_subject_as_username)){\n\t\t/* Don't need the username or password if provided */\n\t\tmosquitto_FREE(*username);\n\t\tmosquitto_FREE(*password);\n\n\t\tif(!context->ssl){\n\t\t\treturn send__connack_bad_username_or_password_error(context, MOSQ_ERR_AUTH);\n\t\t}\n#ifdef FINAL_WITH_TLS_PSK\n\t\tif(context->listener->psk_hint){\n\t\t\t/* Client should have provided an identity to get this far. */\n\t\t\tif(!context->username){\n\t\t\t\treturn send__connack_bad_username_or_password_error(context, MOSQ_ERR_AUTH);\n\t\t\t}\n\t\t}else\n#endif /* FINAL_WITH_TLS_PSK */\n\t\t{\n\t\t\tif(context->listener->use_identity_as_username){\n\t\t\t\trc = set_username_from_cert_identity(context);\n\t\t\t}else{   /* use_subject_as_username */\n\t\t\t\trc = set_username_from_cert_subject_name(context);\n\t\t\t}\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}else\n#endif /* WITH_TLS */\n\t{\n#ifdef WITH_TLS\n\t\tif(context->listener->use_identity_as_username && context->listener->require_certificate){\n\t\t\tmosquitto_FREE(*username);\n\t\t\tmosquitto_FREE(*password);\n\n\t\t\tif(!context->username){\n\t\t\t\treturn send__connack_bad_username_or_password_error(context, MOSQ_ERR_AUTH);\n\t\t\t}\n\t\t}else\n#endif\n\t\t{\n\t\t\t/* FIXME - these ensure the mosquitto_clientid() and\n\t\t\t* mosquitto_client_username() functions work, but is hacky */\n\t\t\tcontext->username = *username;\n\t\t\tcontext->password = *password;\n\t\t\tcontext->password_len = password_len;\n\t\t\t*username = NULL; /* Avoid free() in error: below. */\n\t\t\t*password = NULL;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int handle_username_as_clientid_option(struct mosquitto *context)\n{\n\tif(context->username){\n\t\tmosquitto_FREE(context->id);\n\t\tcontext->id = mosquitto_strdup(context->username);\n\t\tif(!context->id){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}else{\n\t\tuint8_t err_code = context->protocol == mosq_p_mqtt5\n\t\t\t\t\t\t\t\t\t\t\t\t? (uint8_t)MQTT_RC_NOT_AUTHORIZED\n\t\t\t\t\t\t\t\t\t\t\t\t: (uint8_t)CONNACK_REFUSED_NOT_AUTHORIZED;\n\t\treturn send__connack_error_and_return(context, err_code, MOSQ_ERR_AUTH);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint handle__connect(struct mosquitto *context)\n{\n\tchar protocol_name[7];\n\tuint8_t protocol_version;\n\tuint8_t connect_flags;\n\tchar *clientid = NULL;\n\tstruct mosquitto *found_context;\n\tstruct mosquitto_message_all *will_struct = NULL;\n\tuint8_t will, will_retain, will_qos, clean_start;\n\tuint8_t username_flag, password_flag;\n\tchar *username = NULL, *password = NULL;\n\tuint16_t password_len = 0;\n\tint rc;\n\tmosquitto_property *properties = NULL;\n\tvoid *auth_data = NULL;\n\tuint16_t auth_data_len = 0;\n\tvoid *auth_data_out = NULL;\n\tuint16_t auth_data_out_len = 0;\n\tbool allow_zero_length_clientid;\n\n\tif(!context->listener){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* Don't accept multiple CONNECT commands. */\n\tif(context->state != mosq_cs_new){\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Bad client %s:%d sending multiple CONNECT messages.\",\n\t\t\t\tcontext->address, context->remote_port);\n\t\trc = MOSQ_ERR_PROTOCOL;\n\t\tgoto handle_connect_error;\n\t}\n\n#ifdef WITH_TLS\n\tif(context->in_packet.command == 0x16 && context->listener->ssl_ctx == NULL){ /* 0x16 is TLS handshake client hello */\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client from %s:%d appears to be using TLS to connect to a non-TLS listener.\",\n\t\t\t\tcontext->address, context->remote_port);\n\t\trc = MOSQ_ERR_PROTOCOL;\n\t\tgoto handle_connect_error;\n\t}\n#endif\n\n\trc = read_protocol_name(context, protocol_name);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto handle_connect_error;\n\t}\n\n\trc = read_and_verify_protocol_version(context, protocol_name, &protocol_version);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tif(rc == MOSQ_ERR_MALFORMED_PACKET){\n\t\t\treturn rc;\n\t\t}\n\t\tgoto handle_connect_error;\n\t}\n\n\trc = read_and_verify_connect_flags(context, &connect_flags);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto handle_connect_error;\n\t}\n\n\tclean_start = (connect_flags & 0x02) >> 1;\n\tset_session_expiry_interval(context, clean_start, protocol_version);\n\n\trc = read_and_reset_keepalive(context);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto handle_connect_error;\n\t}\n\n\trc = read_and_verify_v5_connect_properties(context, &properties, protocol_version);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto handle_connect_error;\n\t}\n\n\twill = connect_flags & 0x04;\n\twill_qos = (connect_flags & 0x18) >> 3;\n\tif(will_qos == 3){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d: CONNECT with invalid Will QoS (%d).\",\n\t\t\t\tcontext->address, context->remote_port, will_qos);\n\t\trc = MOSQ_ERR_PROTOCOL;\n\t\tgoto handle_connect_error;\n\t}\n\twill_retain = ((connect_flags & 0x20) == 0x20);\n\trc = verify_will_options(context, will, will_qos, will_retain, protocol_version);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto handle_connect_error;\n\t}\n\n\tmosquitto_property_read_string(properties, MQTT_PROP_AUTHENTICATION_METHOD, &context->auth_method, false);\n\tmosquitto_property_read_binary(properties, MQTT_PROP_AUTHENTICATION_DATA, &auth_data, &auth_data_len, false);\n\tmosquitto_property_free_all(&properties);\n\n\tif(auth_data && !context->auth_method){\n\t\tsend__connack(context, 0, MQTT_RC_PROTOCOL_ERROR, NULL);\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s:%d CONNECT with missing clientid string.\",\n\t\t\t\tcontext->address, context->remote_port);\n\t\trc = MOSQ_ERR_PROTOCOL;\n\t\tgoto handle_connect_error;\n\t}\n\n\trc = read_and_verify_clientid_from_packet(context, &clientid, &allow_zero_length_clientid, clean_start);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto handle_connect_error;\n\t}\n\n\tif(will){\n\t\trc = will__read(context, clientid, &will_struct, will_qos, will_retain);\n\t\tif(rc){\n\t\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\t\tif(rc == MOSQ_ERR_DUPLICATE_PROPERTY || rc == MOSQ_ERR_PROTOCOL){\n\t\t\t\t\tsend__connack(context, 0, MQTT_RC_PROTOCOL_ERROR, NULL);\n\t\t\t\t}else if(rc == MOSQ_ERR_MALFORMED_PACKET){\n\t\t\t\t\tsend__connack(context, 0, MQTT_RC_MALFORMED_PACKET, NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t\tgoto handle_connect_error;\n\t\t}\n\t}else{\n\t\tif(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){\n\t\t\tif(will_qos != 0 || will_retain != 0){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: CONNECT without Will with non-zero QoS (%d) or retain (%d).\",\n\t\t\t\t\t\tclientid, will_qos, will_retain);\n\t\t\t\trc = MOSQ_ERR_PROTOCOL;\n\t\t\t\tgoto handle_connect_error;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Client credentials\n\tpassword_flag = connect_flags & 0x40;\n\tusername_flag = connect_flags & 0x80;\n\trc = read_and_verify_client_credentials_from_packet(context, &username, username_flag, &password, &password_len, password_flag, clientid);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto handle_connect_error;\n\t}\n\n\trc = check_additional_trailing_data(context, protocol_version);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto handle_connect_error;\n\t}\n\n\t/* Once context->id is set, if we return from this function with an error\n\t * we must make sure that context->id is freed and set to NULL, so that the\n\t * client isn't erroneously removed from the by_id hash table. */\n\tcontext->id = clientid;\n\tclientid = NULL;\n\n\t/* use_identity_as_username or use_subject_as_username */\n\trc = handle_username_from_cert_options(context, &username, &password, password_len);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tgoto handle_connect_error;\n\t}\n\n\t/* use_username_as_clientid */\n\tif(context->listener->use_username_as_clientid){\n\t\trc = handle_username_as_clientid_option(context);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tgoto handle_connect_error;\n\t\t}\n\t}\n\n\t/* Check for an existing delayed auth check, reject if present */\n\tHASH_FIND(hh_id, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), found_context);\n\tif(found_context){\n\t\trc = MOSQ_ERR_UNKNOWN;\n\t\tgoto handle_connect_error;\n\t}\n\n\tcontext->clean_start = clean_start;\n\tcontext->will = will_struct;\n\twill_struct = NULL;\n\n\tif(context->auth_method){\n\t\trc = mosquitto_security_auth_start(context, false, auth_data, auth_data_len, &auth_data_out, &auth_data_out_len);\n\t\tmosquitto_FREE(auth_data);\n\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\treturn connect__on_authorised(context, auth_data_out, auth_data_out_len);\n\t\t}else if(rc == MOSQ_ERR_AUTH_CONTINUE){\n\t\t\tmosquitto__set_state(context, mosq_cs_authenticating);\n\t\t\trc = send__auth(context, MQTT_RC_CONTINUE_AUTHENTICATION, auth_data_out, auth_data_out_len);\n\t\t\tSAFE_FREE(auth_data_out);\n\t\t\treturn rc;\n\t\t}else{\n\t\t\tSAFE_FREE(auth_data_out);\n\t\t\twill__clear(context);\n\t\t\tif(rc == MOSQ_ERR_AUTH){\n\t\t\t\tsend__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL);\n\t\t\t\tmosquitto_FREE(context->id);\n\t\t\t\tgoto handle_connect_error;\n\t\t\t}else if(rc == MOSQ_ERR_NOT_SUPPORTED){\n\t\t\t\t/* Client has requested extended authentication, but we don't support it. */\n\t\t\t\tsend__connack(context, 0, MQTT_RC_BAD_AUTHENTICATION_METHOD, NULL);\n\t\t\t\tmosquitto_FREE(context->id);\n\t\t\t\tgoto handle_connect_error;\n\t\t\t}else{\n\t\t\t\tmosquitto_FREE(context->id);\n\t\t\t\tgoto handle_connect_error;\n\t\t\t}\n\t\t}\n\t}else{\n#ifdef WITH_TLS\n\t\tif(context->listener->ssl_ctx && (context->listener->use_identity_as_username || context->listener->use_subject_as_username)){\n\t\t\t/* Authentication assumed to be cleared */\n\t\t}else\n#endif\n\t\t{\n\t\t\trc = mosquitto_basic_auth(context);\n\t\t\tswitch(rc){\n\t\t\t\tcase MOSQ_ERR_SUCCESS:\n\t\t\t\t\tbreak;\n\t\t\t\tcase MOSQ_ERR_AUTH_DELAYED:\n\t\t\t\t\tmosquitto__set_state(context, mosq_cs_delayed_auth);\n\t\t\t\t\tHASH_ADD_KEYPTR(hh_id, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context);\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t\tbreak;\n\t\t\t\tcase MOSQ_ERR_AUTH:\n\t\t\t\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\t\t\t\tsend__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL);\n\t\t\t\t\t}else{\n\t\t\t\t\t\tsend__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL);\n\t\t\t\t\t}\n\t\t\t\t\tgoto handle_connect_error;\n\t\t\t\t\tbreak;\n\t\t\t\tcase MOSQ_ERR_UNSPECIFIED:\n\t\t\t\tcase MOSQ_ERR_IMPLEMENTATION_SPECIFIC:\n\t\t\t\tcase MOSQ_ERR_CLIENT_IDENTIFIER_NOT_VALID:\n\t\t\t\tcase MOSQ_ERR_BAD_USERNAME_OR_PASSWORD:\n\t\t\t\tcase MOSQ_ERR_SERVER_UNAVAILABLE:\n\t\t\t\tcase MOSQ_ERR_SERVER_BUSY:\n\t\t\t\tcase MOSQ_ERR_BANNED:\n\t\t\t\tcase MOSQ_ERR_BAD_AUTHENTICATION_METHOD:\n\t\t\t\tcase MOSQ_ERR_CONNECTION_RATE_EXCEEDED:\n\t\t\t\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\t\t\t\tsend__connack(context, 0, (uint8_t)rc, NULL);\n\t\t\t\t\t}else{\n\t\t\t\t\t\tsend__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL);\n\t\t\t\t\t}\n\t\t\t\t\tgoto handle_connect_error;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\trc = MOSQ_ERR_UNKNOWN;\n\t\t\t\t\tgoto handle_connect_error;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn connect__on_authorised(context, NULL, 0);\n\t}\n\n\nhandle_connect_error:\n\tmosquitto_property_free_all(&properties);\n\tmosquitto_FREE(auth_data);\n\tmosquitto_FREE(clientid);\n\tmosquitto_FREE(username);\n\tmosquitto_FREE(password);\n\tif(will_struct){\n\t\tmosquitto_property_free_all(&will_struct->properties);\n\t\tmosquitto_FREE(will_struct->msg.payload);\n\t\tmosquitto_FREE(will_struct->msg.topic);\n\t\tmosquitto_FREE(will_struct);\n\t}\n\twill__clear(context);\n\t/* We return an error here which means the client is freed later on. */\n\tcontext->clean_start = true;\n\tcontext->session_expiry_interval = MQTT_SESSION_EXPIRY_IMMEDIATE;\n\tcontext->will_delay_interval = 0;\n\treturn rc;\n}\n"
  },
  {
    "path": "src/handle_disconnect.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n#include \"will_mosq.h\"\n\n\nint handle__disconnect(struct mosquitto *context)\n{\n\tint rc;\n\tuint8_t reason_code = 0;\n\tmosquitto_property *properties = NULL;\n\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(context->in_packet.command != CMD_DISCONNECT){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tif(context->protocol == mosq_p_mqtt5 && context->in_packet.remaining_length > 0){\n\t\t/* FIXME - must handle reason code */\n\t\trc = packet__read_byte(&context->in_packet, &reason_code);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n\t\tif(context->in_packet.remaining_length > 1){\n\t\t\trc = property__read_all(CMD_DISCONNECT, &context->in_packet, &properties);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}\n\trc = property__process_disconnect(context, &properties);\n\tif(rc){\n\t\tmosquitto_property_free_all(&properties);\n\t\treturn rc;\n\t}\n\tmosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */\n\n\tif(context->in_packet.pos != context->in_packet.remaining_length){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: DISCONNECT packet with overlong remaining length (%d:%d).\",\n\t\t\t\tcontext->id, context->in_packet.pos, context->in_packet.remaining_length);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received DISCONNECT from %s\", context->id);\n\tif(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){\n\t\tif((context->in_packet.command&0x0F) != 0x00){\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: DISCONNECT packet with incorrect flags %02X.\",\n\t\t\t\t\tcontext->id, context->in_packet.command);\n\t\t\tdo_disconnect(context, MOSQ_ERR_PROTOCOL);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t}\n\tif(reason_code == MQTT_RC_DISCONNECT_WITH_WILL_MSG){\n\t\tmosquitto__set_state(context, mosq_cs_disconnect_with_will);\n\t}else{\n\t\twill__clear(context);\n\t\tmosquitto__set_state(context, mosq_cs_disconnecting);\n\t}\n\tdo_disconnect(context, MOSQ_ERR_SUCCESS);\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "src/handle_publish.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"alias_mosq.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n\n\nstatic int process_bad_message(struct mosquitto *context, struct mosquitto__base_msg *base_msg, uint8_t reason_code)\n{\n\tint rc = MOSQ_ERR_UNKNOWN;\n\tif(base_msg){\n\t\tswitch(base_msg->data.qos){\n\t\t\tcase 0:\n\t\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tif(context){\n\t\t\t\t\trc = send__puback(context, base_msg->data.source_mid, reason_code, NULL);\n\t\t\t\t}else{\n\t\t\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tif(context){\n\t\t\t\t\trc = send__pubrec(context, base_msg->data.source_mid, reason_code, NULL);\n\t\t\t\t}else{\n\t\t\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\tdb__msg_store_free(base_msg);\n\t}\n\tif(context && db.config->max_queued_messages > 0 && context->out_packet_count >= db.config->max_queued_messages){\n\t\trc = MQTT_RC_QUOTA_EXCEEDED;\n\t}\n\treturn rc;\n}\n\n\nint handle__accepted_publish(struct mosquitto *context, struct mosquitto__base_msg *base_msg, uint16_t mid, int dup, uint32_t *message_expiry_interval)\n{\n\tint rc;\n\tint rc2;\n\tstruct mosquitto__base_msg *stored = NULL;\n\tstruct mosquitto__client_msg *cmsg_stored = NULL;\n\n\t{\n\t\trc = plugin__handle_message_in(context, &base_msg->data);\n\t\tif(rc == MOSQ_ERR_ACL_DENIED){\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG,\n\t\t\t\t\t\"Denied PUBLISH from %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))\",\n\t\t\t\t\tcontext->id, dup, base_msg->data.qos, base_msg->data.retain, base_msg->data.source_mid, base_msg->data.topic,\n\t\t\t\t\t(long)base_msg->data.payloadlen);\n\n\t\t\treturn process_bad_message(context, base_msg, MQTT_RC_NOT_AUTHORIZED);\n\t\t}else if(rc == MOSQ_ERR_QUOTA_EXCEEDED){\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG,\n\t\t\t\t\t\"Rejected PUBLISH from %s, quota exceeded.\", context->id);\n\n\t\t\treturn process_bad_message(context, base_msg, MQTT_RC_QUOTA_EXCEEDED);\n\t\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tif(base_msg->data.qos > 0){\n\t\tdb__message_store_find(context, base_msg->data.source_mid, &cmsg_stored);\n\t}\n\n\tif(cmsg_stored && base_msg->data.source_mid != 0 &&\n\t\t\t(cmsg_stored->base_msg->data.qos != base_msg->data.qos\n\t\t\t|| cmsg_stored->base_msg->data.payloadlen != base_msg->data.payloadlen\n\t\t\t|| strcmp(cmsg_stored->base_msg->data.topic, base_msg->data.topic)\n\t\t\t|| memcmp(cmsg_stored->base_msg->data.payload, base_msg->data.payload, base_msg->data.payloadlen))){\n\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Reused message ID %u from %s detected. Clearing from storage.\", base_msg->data.source_mid, context->id);\n\t\tdb__message_remove_incoming(context, base_msg->data.source_mid);\n\t\tcmsg_stored = NULL;\n\t}\n\n\tif(!cmsg_stored){\n\t\tif(base_msg->data.qos > 0 && context->msgs_in.inflight_quota == 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Client %s has exceeded its receive-maximum quota. This behaviour must be fixed on the client.\", context->id);\n#if 0\n\t\t\t/* Badly behaving clients like on the esp32 fall foul of this\n\t\t\t * check, so report it for now but don't disconnect, to give chance\n\t\t\t * for the bad behaviour to be fixed. */\n\t\t\t/* Client isn't allowed any more incoming messages, so fail early */\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\treturn MOSQ_ERR_RECEIVE_MAXIMUM_EXCEEDED;\n#endif\n\t\t}\n\n\t\tif(base_msg->data.qos == 0\n\t\t\t\t|| db__ready_for_flight(context, mosq_md_in, base_msg->data.qos)\n\t\t\t\t){\n\n\t\t\tdup = 0;\n\t\t\trc = db__message_store(context, base_msg, message_expiry_interval, mosq_mo_client);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}else{\n\t\t\t/* Client isn't allowed any more incoming messages, so fail early */\n\t\t\treturn process_bad_message(context, base_msg, MQTT_RC_QUOTA_EXCEEDED);\n\t\t}\n\t\tstored = base_msg;\n\t\tbase_msg = NULL;\n\t\tdup = 0;\n\t}else{\n\t\tdb__msg_store_free(base_msg);\n\t\tbase_msg = NULL;\n\t\tstored = cmsg_stored->base_msg;\n\t\tcmsg_stored->data.dup++;\n\t\tdup = cmsg_stored->data.dup;\n\t}\n\n\tswitch(stored->data.qos){\n\t\tcase 0:\n\t\t\trc2 = sub__messages_queue(context->id, stored->data.topic, stored->data.qos, stored->data.retain, &stored);\n\t\t\tif(rc2 > 0){\n\t\t\t\trc = rc2;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tutil__decrement_receive_quota(context);\n\t\t\trc2 = sub__messages_queue(context->id, stored->data.topic, stored->data.qos, stored->data.retain, &stored);\n\t\t\t/* stored may now be free, so don't refer to it */\n\t\t\tif(rc2 == MOSQ_ERR_SUCCESS || context->protocol != mosq_p_mqtt5){\n\t\t\t\trc2 = send__puback(context, mid, 0, NULL);\n\t\t\t\tif(rc2){\n\t\t\t\t\trc = rc2;\n\t\t\t\t}\n\t\t\t}else if(rc2 == MOSQ_ERR_NO_SUBSCRIBERS){\n\t\t\t\trc2 = send__puback(context, mid, MQTT_RC_NO_MATCHING_SUBSCRIBERS, NULL);\n\t\t\t\tif(rc2){\n\t\t\t\t\trc = rc2;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\trc = rc2;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\t{\n\t\t\t\tint res;\n\t\t\t\tif(dup == 0){\n\t\t\t\t\tres = db__message_insert_incoming(context, 0, stored, true);\n\t\t\t\t}else{\n\t\t\t\t\tres = 0;\n\t\t\t\t}\n\n\t\t\t\t/* db__message_insert() returns 2 to indicate dropped message\n\t\t\t\t * due to queue. This isn't an error so don't disconnect them. */\n\t\t\t\t/* FIXME - this is no longer necessary due to failing early above */\n\t\t\t\tif(!res){\n\t\t\t\t\tif(dup == 0 || dup == 1){\n\t\t\t\t\t\trc2 = send__pubrec(context, stored->data.source_mid, 0, NULL);\n\t\t\t\t\t\tif(rc2){\n\t\t\t\t\t\t\trc = rc2;\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBLISH with dup = %d.\", context->id, dup);\n\t\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\trc = res;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n\n\tdb__message_write_queued_in(context);\n\treturn rc;\n}\n\n\nint handle__publish(struct mosquitto *context)\n{\n\tuint8_t dup;\n\tint rc = 0;\n\tuint8_t header = context->in_packet.command;\n\tstruct mosquitto__base_msg *base_msg;\n\tsize_t len;\n\tuint16_t slen;\n\tchar *topic_mount;\n\tmosquitto_property *properties = NULL;\n\tuint32_t message_expiry_interval = MSG_EXPIRY_INFINITE;\n\tint topic_alias = -1;\n\tuint16_t mid = 0;\n\n\tif(context->state != mosq_cs_active){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBLISH before session is active.\", context->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tcontext->stats.messages_received++;\n\n\tbase_msg = mosquitto_calloc(1, sizeof(struct mosquitto__base_msg));\n\tif(base_msg == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tdup = (header & 0x08)>>3;\n\tbase_msg->data.qos = (header & 0x06)>>1;\n\tif(dup == 1 && base_msg->data.qos == 0){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\"Invalid PUBLISH (QoS=0 and DUP=1) from %s, disconnecting.\", context->id);\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\tif(base_msg->data.qos == 3){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\"Invalid QoS in PUBLISH from %s, disconnecting.\", context->id);\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\tif(base_msg->data.qos > context->max_qos){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\"Too high QoS in PUBLISH from %s, disconnecting.\", context->id);\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_QOS_NOT_SUPPORTED;\n\t}\n\tbase_msg->data.retain = (header & 0x01);\n\n\tif(base_msg->data.retain && db.config->retain_available == false){\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_RETAIN_NOT_SUPPORTED;\n\t}\n\n\tif(packet__read_string(&context->in_packet, &base_msg->data.topic, &slen)){\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\tif(!slen && context->protocol != mosq_p_mqtt5){\n\t\t/* Invalid publish topic, disconnect client. */\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tif(base_msg->data.qos > 0){\n\t\tif(packet__read_uint16(&context->in_packet, &mid)){\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t\tif(mid == 0){\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBLISH packet with mid = 0.\", context->id);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t\t/* It is important to have a separate copy of mid, because msg may be\n\t\t * freed before we want to send a PUBACK/PUBREC. */\n\t\tbase_msg->data.source_mid = mid;\n\t}\n\n\t/* Handle properties */\n\tif(context->protocol == mosq_p_mqtt5){\n\t\trc = property__read_all(CMD_PUBLISH, &context->in_packet, &properties);\n\t\tif(rc){\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\treturn rc;\n\t\t}\n\n\t\trc = property__process_publish(base_msg, &properties, &topic_alias, &message_expiry_interval, context->bridge);\n\t\tif(rc){\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\t}\n\tmosquitto_property_free_all(&properties);\n\n\tif(topic_alias == 0 || (context->listener && topic_alias > context->listener->max_topic_alias)){\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_TOPIC_ALIAS_INVALID;\n\t}else if(topic_alias > 0){\n\t\tif(base_msg->data.topic){\n\t\t\trc = alias__add_r2l(context, base_msg->data.topic, (uint16_t)topic_alias);\n\t\t\tif(rc){\n\t\t\t\tdb__msg_store_free(base_msg);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}else{\n\t\t\trc = alias__find_by_alias(context, ALIAS_DIR_R2L, (uint16_t)topic_alias, &base_msg->data.topic);\n\t\t\tif(rc){\n\t\t\t\tdb__msg_store_free(base_msg);\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: PUBLISH invalid topic alias (%d).\",\n\t\t\t\t\t\tcontext->id, topic_alias);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef WITH_BRIDGE\n\trc = bridge__remap_topic_in(context, &base_msg->data.topic);\n\tif(rc){\n\t\tdb__msg_store_free(base_msg);\n\t\treturn rc;\n\t}\n\n#endif\n\tif(mosquitto_pub_topic_check(base_msg->data.topic) != MOSQ_ERR_SUCCESS){\n\t\t/* Invalid publish topic, just swallow it. */\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tbase_msg->data.payloadlen = context->in_packet.remaining_length - context->in_packet.pos;\n\tmetrics__int_inc(mosq_counter_pub_bytes_received, base_msg->data.payloadlen);\n\tif(context->listener && context->listener->mount_point){\n\t\tlen = strlen(context->listener->mount_point) + strlen(base_msg->data.topic) + 1;\n\t\ttopic_mount = mosquitto_malloc(len+1);\n\t\tif(!topic_mount){\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tsnprintf(topic_mount, len, \"%s%s\", context->listener->mount_point, base_msg->data.topic);\n\t\ttopic_mount[len] = '\\0';\n\n\t\tmosquitto_FREE(base_msg->data.topic);\n\t\tbase_msg->data.topic = topic_mount;\n\t}\n\n\tif(base_msg->data.payloadlen){\n\t\tif(db.config->message_size_limit && base_msg->data.payloadlen > db.config->message_size_limit){\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Dropped too large PUBLISH from %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))\", context->id, dup, base_msg->data.qos, base_msg->data.retain, base_msg->data.source_mid, base_msg->data.topic, (long)base_msg->data.payloadlen);\n\t\t\treturn process_bad_message(context, base_msg, MQTT_RC_PACKET_TOO_LARGE);\n\t\t}\n\t\tbase_msg->data.payload = mosquitto_malloc(base_msg->data.payloadlen+1);\n\t\tif(base_msg->data.payload == NULL){\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\t/* Ensure payload is always zero terminated, this is the reason for the extra byte above */\n\t\t((uint8_t *)base_msg->data.payload)[base_msg->data.payloadlen] = 0;\n\n\t\tif(packet__read_bytes(&context->in_packet, base_msg->data.payload, base_msg->data.payloadlen)){\n\t\t\tdb__msg_store_free(base_msg);\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t}\n\n\t/* Check for topic access */\n\trc = mosquitto_acl_check(context,\n\t\t\tbase_msg->data.topic, base_msg->data.payloadlen, base_msg->data.payload,\n\t\t\tbase_msg->data.qos, base_msg->data.retain, base_msg->data.properties,\n\t\t\tMOSQ_ACL_WRITE);\n\tif(rc == MOSQ_ERR_ACL_DENIED){\n\t\tlog__printf(NULL, MOSQ_LOG_DEBUG,\n\t\t\t\t\"Denied PUBLISH from %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))\",\n\t\t\t\tcontext->id, dup, base_msg->data.qos, base_msg->data.retain, base_msg->data.source_mid, base_msg->data.topic,\n\t\t\t\t(long)base_msg->data.payloadlen);\n\t\treturn process_bad_message(context, base_msg, MQTT_RC_NOT_AUTHORIZED);\n\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\tdb__msg_store_free(base_msg);\n\t\treturn rc;\n\t}\n\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received PUBLISH from %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))\", context->id, dup, base_msg->data.qos, base_msg->data.retain, base_msg->data.source_mid, base_msg->data.topic, (long)base_msg->data.payloadlen);\n\n\tif(!strncmp(base_msg->data.topic, \"$CONTROL/\", 9)){\n#ifdef WITH_CONTROL\n\t\trc = control__process(context, base_msg);\n\t\tdb__msg_store_free(base_msg);\n\t\treturn rc;\n#else\n\t\treturn process_bad_message(context, base_msg, MQTT_RC_IMPLEMENTATION_SPECIFIC);\n#endif\n\t}\n\n\treturn handle__accepted_publish(context, base_msg, mid, dup, &message_expiry_interval);\n}\n"
  },
  {
    "path": "src/handle_subscribe.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n\n\nint handle__subscribe(struct mosquitto *context)\n{\n\tint rc = 0;\n\tint rc2;\n\tuint16_t mid;\n\tuint8_t qos;\n\tuint8_t retain_handling = 0;\n\tuint8_t *payload = NULL, *tmp_payload;\n\tuint32_t payloadlen = 0;\n\tsize_t len;\n\tuint16_t slen;\n\tchar *sub_mount;\n\tmosquitto_property *properties = NULL;\n\tbool allowed;\n\tstruct mosquitto_subscription sub;\n\tuint32_t subscription_identifier = 0;\n\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(context->state != mosq_cs_active){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: SUBSCRIBE before session is active.\", context->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(context->in_packet.command != (CMD_SUBSCRIBE|2)){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received SUBSCRIBE from %s\", context->id);\n\n\tif(context->protocol != mosq_p_mqtt31){\n\t\tif((context->in_packet.command&0x0F) != 0x02){\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t}\n\tif(packet__read_uint16(&context->in_packet, &mid)){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\tif(mid == 0){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tif(context->protocol == mosq_p_mqtt5){\n\t\trc = property__read_all(CMD_SUBSCRIBE, &context->in_packet, &properties);\n\t\tif(rc){\n\t\t\t/* FIXME - it would be better if property__read_all() returned\n\t\t\t * MOSQ_ERR_MALFORMED_PACKET, but this is would change the library\n\t\t\t * return codes so needs doc changes as well. */\n\t\t\tif(rc == MOSQ_ERR_PROTOCOL){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: SUBSCRIBE packet with invalid properties.\", context->id);\n\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t}else{\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\n\t\tif(mosquitto_property_read_varint(properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER,\n\t\t\t\t&subscription_identifier, false)){\n\n\t\t\t/* If the identifier was force set to 0, this is an error */\n\t\t\tif(subscription_identifier == 0){\n\t\t\t\tmosquitto_property_free_all(&properties);\n\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t}\n\t\t}\n\n\t\tmosquitto_property_free_all(&properties);\n\t\t/* Note - User Property not handled */\n\t}\n\n\twhile(context->in_packet.pos < context->in_packet.remaining_length){\n\t\tmemset(&sub, 0, sizeof(sub));\n\t\tsub.identifier = subscription_identifier;\n\t\tsub.properties = properties;\n\t\tif(packet__read_string(&context->in_packet, &sub.topic_filter, &slen)){\n\t\t\tmosquitto_FREE(payload);\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\n\t\tif(sub.topic_filter){\n\t\t\tif(!slen){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\t\t\"Empty subscription string from %s, disconnecting.\",\n\t\t\t\t\t\tcontext->address);\n\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t}\n\t\t\tif(mosquitto_sub_topic_check(sub.topic_filter)){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\t\t\"Invalid subscription string from %s, disconnecting.\",\n\t\t\t\t\t\tcontext->address);\n\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t}\n\n\t\t\tif(packet__read_byte(&context->in_packet, &sub.options)){\n\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t}\n\t\t\tif(sub.options & MQTT_SUB_OPT_NO_LOCAL && !strncmp(sub.topic_filter, \"$share/\", strlen(\"$share/\"))){\n\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: $share subscription with no-local set.\", context->id);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\n\t\t\tif(context->protocol == mosq_p_mqtt31 || context->protocol == mosq_p_mqtt311){\n\t\t\t\tqos = sub.options;\n\t\t\t\tsub.options = 0;\n\t\t\t\tif(context->is_bridge){\n\t\t\t\t\tsub.options |= MQTT_SUB_OPT_RETAIN_AS_PUBLISHED | MQTT_SUB_OPT_NO_LOCAL;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tqos = sub.options & 0x03;\n\t\t\t\tsub.options &= 0xFC;\n\n\t\t\t\tif(MQTT_SUB_OPT_GET_NO_LOCAL(sub.options) && !strncmp(sub.topic_filter, \"$share/\", 7)){\n\t\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t}\n\t\t\t\tretain_handling = MQTT_SUB_OPT_GET_RETAIN_HANDLING(sub.options);\n\t\t\t\tif(retain_handling == 0x30 || (sub.options & 0xC0) != 0){\n\t\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(qos > 2){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\t\t\"Invalid QoS in subscription command from %s, disconnecting.\",\n\t\t\t\t\t\tcontext->address);\n\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t}\n\t\t\tif(qos > context->max_qos){\n\t\t\t\tqos = context->max_qos;\n\t\t\t}\n\t\t\tsub.options |= qos;\n\n\n\t\t\tif(context->listener && context->listener->mount_point){\n\t\t\t\tlen = strlen(context->listener->mount_point) + slen + 1;\n\t\t\t\tsub_mount = mosquitto_malloc(len+1);\n\t\t\t\tif(!sub_mount){\n\t\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\tsnprintf(sub_mount, len, \"%s%s\", context->listener->mount_point, sub.topic_filter);\n\t\t\t\tsub_mount[len] = '\\0';\n\n\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\tsub.topic_filter = sub_mount;\n\n\t\t\t}\n\n\t\t\tallowed = true;\n\t\t\trc2 = mosquitto_acl_check(context, sub.topic_filter, 0, NULL, qos, false, properties, MOSQ_ACL_SUBSCRIBE);\n\t\t\tswitch(rc2){\n\t\t\t\tcase MOSQ_ERR_SUCCESS:\n\t\t\t\t\tbreak;\n\t\t\t\tcase MOSQ_ERR_ACL_DENIED:\n\t\t\t\t\tallowed = false;\n\t\t\t\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\t\t\t\tqos = MQTT_RC_NOT_AUTHORIZED;\n\t\t\t\t\t}else if(context->protocol == mosq_p_mqtt311){\n\t\t\t\t\t\tqos = 0x80;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\t\treturn rc2;\n\t\t\t}\n\t\t\tif(qos > 127){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"\\t%s (denied)\", sub.topic_filter);\n\t\t\t}else{\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"\\t%s (QoS %d)\", sub.topic_filter, qos);\n\t\t\t}\n\n\t\t\tif(allowed){\n\t\t\t\trc2 = plugin__handle_subscribe(context, &sub);\n\t\t\t\tif(rc2){\n\t\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\t\treturn rc2;\n\t\t\t\t}\n\n\t\t\t\trc2 = sub__add(context, &sub);\n\t\t\t\tif(rc2 > 0){\n\t\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\t\treturn rc2;\n\t\t\t\t}\n\t\t\t\tif(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt31){\n\t\t\t\t\tif(rc2 == MOSQ_ERR_SUCCESS || rc2 == MOSQ_ERR_SUB_EXISTS){\n\t\t\t\t\t\tif(retain__queue(context, &sub)){\n\t\t\t\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\t\t\t\treturn rc;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tif((retain_handling == MQTT_SUB_OPT_SEND_RETAIN_ALWAYS)\n\t\t\t\t\t\t\t|| (rc2 == MOSQ_ERR_SUCCESS && retain_handling == MQTT_SUB_OPT_SEND_RETAIN_NEW)){\n\n\t\t\t\t\t\tif(retain__queue(context, &sub)){\n\t\t\t\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\t\t\t\tmosquitto_FREE(payload);\n\t\t\t\t\t\t\treturn rc;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_SUBSCRIBE, \"%s %d %s\", context->id, qos, sub.topic_filter);\n\n\t\t\t\tplugin_persist__handle_subscription_add(context, &sub);\n\t\t\t}\n\t\t\tmosquitto_FREE(sub.topic_filter);\n\n\t\t\ttmp_payload = mosquitto_realloc(payload, payloadlen + 1);\n\t\t\tif(tmp_payload){\n\t\t\t\tpayload = tmp_payload;\n\t\t\t\tpayload[payloadlen] = qos;\n\t\t\t\tpayloadlen++;\n\t\t\t}else{\n\t\t\t\tmosquitto_FREE(payload);\n\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(context->protocol != mosq_p_mqtt31){\n\t\tif(payloadlen == 0){\n\t\t\t/* No subscriptions specified, protocol error. */\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t}\n\tif(send__suback(context, mid, payloadlen, payload)){\n\t\trc = 1;\n\t}\n\tmosquitto_FREE(payload);\n\n#ifdef WITH_PERSISTENCE\n\tdb.persistence_changes++;\n#endif\n\n\tif(context->out_packet == NULL){\n\t\trc = db__message_write_queued_out(context);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\trc = db__message_write_inflight_out_latest(context);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\treturn rc;\n}\n"
  },
  {
    "path": "src/handle_unsubscribe.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"send_mosq.h\"\n\n\nint handle__unsubscribe(struct mosquitto *context)\n{\n\tuint16_t mid;\n\tuint16_t slen;\n\tint rc;\n\tuint8_t reason = 0;\n\tint reason_code_count = 0;\n\tint reason_code_max;\n\tuint8_t *reason_codes = NULL, *reason_tmp;\n\tmosquitto_property *properties = NULL;\n\tbool allowed;\n\tstruct mosquitto_subscription sub;\n\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(context->state != mosq_cs_active){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: UNSUBSCRIBE before session is active.\", context->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\tif(context->in_packet.command != (CMD_UNSUBSCRIBE|2)){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Received UNSUBSCRIBE from %s\", context->id);\n\n\tif(context->protocol != mosq_p_mqtt31){\n\t\tif((context->in_packet.command&0x0F) != 0x02){\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t}\n\tif(packet__read_uint16(&context->in_packet, &mid)){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\tif(mid == 0){\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tif(context->protocol == mosq_p_mqtt5){\n\t\trc = property__read_all(CMD_UNSUBSCRIBE, &context->in_packet, &properties);\n\t\tif(rc){\n\t\t\t/* FIXME - it would be better if property__read_all() returned\n\t\t\t * MOSQ_ERR_MALFORMED_PACKET, but this is would change the library\n\t\t\t * return codes so needs doc changes as well. */\n\t\t\tif(rc == MOSQ_ERR_PROTOCOL){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: UNSUBSCRIBE packet with invalid properties.\", context->id);\n\t\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t\t}else{\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t\t/* Immediately free, we don't do anything with User Property at the moment */\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\n\tif(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){\n\t\tif(context->in_packet.pos == context->in_packet.remaining_length){\n\t\t\t/* No topic specified, protocol error. */\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t}\n\n\treason_code_max = 10;\n\treason_codes = mosquitto_malloc((size_t)reason_code_max);\n\tif(!reason_codes){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\twhile(context->in_packet.pos < context->in_packet.remaining_length){\n\t\tmemset(&sub, 0, sizeof(sub));\n\t\tsub.properties = properties;\n\t\tif(packet__read_string(&context->in_packet, &sub.topic_filter, &slen)){\n\t\t\tmosquitto_FREE(reason_codes);\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\n\t\tif(!slen){\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\t\"Empty unsubscription string from %s, disconnecting.\",\n\t\t\t\t\tcontext->id);\n\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\tmosquitto_FREE(reason_codes);\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\t\tif(mosquitto_sub_topic_check(sub.topic_filter)){\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\t\"Invalid unsubscription string from %s, disconnecting.\",\n\t\t\t\t\tcontext->id);\n\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\tmosquitto_FREE(reason_codes);\n\t\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t\t}\n\n\t\t/* ACL check */\n\t\tallowed = true;\n\t\trc = mosquitto_acl_check(context, sub.topic_filter, 0, NULL, 0, false, properties, MOSQ_ACL_UNSUBSCRIBE);\n\t\tswitch(rc){\n\t\t\tcase MOSQ_ERR_SUCCESS:\n\t\t\t\tbreak;\n\t\t\tcase MOSQ_ERR_ACL_DENIED:\n\t\t\t\tallowed = false;\n\t\t\t\treason = MQTT_RC_NOT_AUTHORIZED;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\tmosquitto_FREE(reason_codes);\n\t\t\t\treturn rc;\n\t\t}\n\n\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"\\t%s\", sub.topic_filter);\n\t\tif(allowed){\n\t\t\trc = plugin__handle_unsubscribe(context, &sub);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_FREE(sub.topic_filter);\n\t\t\t\tmosquitto_FREE(reason_codes);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\trc = sub__remove(context, sub.topic_filter, &reason);\n\t\t\tplugin_persist__handle_subscription_delete(context, sub.topic_filter);\n\t\t}else{\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t}\n\t\tlog__printf(NULL, MOSQ_LOG_UNSUBSCRIBE, \"%s %s\", context->id, sub.topic_filter);\n\t\tmosquitto_FREE(sub.topic_filter);\n\t\tif(rc){\n\t\t\tmosquitto_FREE(reason_codes);\n\t\t\treturn rc;\n\t\t}\n\n\t\treason_codes[reason_code_count] = reason;\n\t\treason_code_count++;\n\t\tif(reason_code_count == reason_code_max){\n\t\t\treason_tmp = mosquitto_realloc(reason_codes, (size_t)(reason_code_max*2));\n\t\t\tif(!reason_tmp){\n\t\t\t\tmosquitto_FREE(reason_codes);\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\treason_codes = reason_tmp;\n\t\t\treason_code_max *= 2;\n\t\t}\n\t}\n#ifdef WITH_PERSISTENCE\n\tdb.persistence_changes++;\n#endif\n\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending UNSUBACK to %s\", context->id);\n\n\t/* We don't use Reason String or User Property yet. */\n\trc = send__unsuback(context, mid, reason_code_count, reason_codes, NULL);\n\tmosquitto_FREE(reason_codes);\n\treturn rc;\n}\n"
  },
  {
    "path": "src/http_api.c",
    "content": "/*\nCopyright (c) 2025 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n\t https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n\thttp://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n\t Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_HTTP_API\n\n#include <assert.h>\n#include <errno.h>\n#include <limits.h>\n#include <microhttpd.h>\n#include <string.h>\n#include <sys/stat.h>\n#ifndef WIN32\n#  include <netdb.h>\n#endif\n\n#include \"json_help.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"sys_tree.h\"\n\n#ifndef HTTP_API_DIR\n#  define HTTP_API_DIR \"\"\n#endif\n\nstruct metric {\n\tint64_t current;\n\tint64_t next;\n\tconst char *topic, *topic_alias;\n\tbool is_max;\n};\n\ntime_t broker_uptime(void);\nstruct MHD_Daemon *mhd = NULL;\n\n#ifdef WITH_SYS_TREE\nextern struct metric metrics[mosq_metric_max];\n#endif\n\n#define HTTP_RESPONSE_BUFLEN 10000\n\n#ifdef WIN32\n#define DIR_SEP '\\\\'\n#define PATH_MAX MAX_PATH\n#else\n#define DIR_SEP '/'\n#endif\n\n#ifndef S_ISREG\n#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG)\n#endif\n\n\nstatic char *http__canonical_filename(\n\t\tconst char *url,\n\t\tconst char *http_dir,\n\t\tint *error_code)\n{\n\tsize_t urllen, slen;\n\tchar *filename, *filename_canonical;\n\n\turllen = strlen(url);\n\tif(url[urllen-1] == '/'){\n\t\tslen = strlen(http_dir) + urllen + strlen(\"/index.html\") + 2;\n\t}else{\n\t\tslen = strlen(http_dir) + urllen + 2;\n\t}\n\tfilename = mosquitto_malloc(slen);\n\tif(!filename){\n\t\t*error_code = MHD_HTTP_INTERNAL_SERVER_ERROR;\n\t\treturn NULL;\n\t}\n\tif(((char *)url)[urllen-1] == '/'){\n\t\tsnprintf(filename, slen, \"%s%c%sindex.html\", http_dir, DIR_SEP, url);\n\t}else{\n\t\tsnprintf(filename, slen, \"%s%c%s\", http_dir, DIR_SEP, url);\n\t}\n\n\n\t/* Get canonical path and check it is within our http_dir */\n\tfilename_canonical = mosquitto_calloc(1, PATH_MAX);\n\tif(!filename_canonical){\n\t\t*error_code = MHD_HTTP_INTERNAL_SERVER_ERROR;\n\t\treturn NULL;\n\t}\n#ifdef WIN32\n\tchar *resolved = _fullpath(filename_canonical, filename, PATH_MAX);\n\tmosquitto_FREE(filename);\n\tif(!resolved){\n\t\t*error_code = MHD_HTTP_INTERNAL_SERVER_ERROR;\n\t\tmosquitto_FREE(filename_canonical);\n\t\treturn NULL;\n\t}\n#else\n\tchar *resolved = realpath(filename, filename_canonical);\n\tmosquitto_FREE(filename);\n\tif(!resolved){\n\t\tif(errno == EACCES){\n\t\t\t*error_code = MHD_HTTP_FORBIDDEN;\n\t\t}else if(errno == EINVAL || errno == EIO || errno == ELOOP){\n\t\t\t*error_code = MHD_HTTP_INTERNAL_SERVER_ERROR;\n\t\t}else if(errno == ENAMETOOLONG){\n\t\t\t*error_code = MHD_HTTP_URI_TOO_LONG;\n\t\t}else if(errno == ENOENT || errno == ENOTDIR){\n\t\t\t*error_code = MHD_HTTP_NOT_FOUND;\n\t\t}\n\t\tmosquitto_FREE(filename_canonical);\n\t\treturn NULL;\n\t}\n#endif\n\tif(strncmp(http_dir, filename_canonical, strlen(http_dir))){\n\t\t/* Requested file isn't within http_dir, deny access because it's not found. */\n\t\tmosquitto_FREE(filename_canonical);\n\t\t*error_code = MHD_HTTP_NOT_FOUND;\n\t\treturn NULL;\n\t}\n\n\treturn filename_canonical;\n}\n\nstatic enum MHD_Result http_api__send_error_response(struct MHD_Connection *connection, const char *error_str, unsigned int error_code)\n{\n\tstruct MHD_Response *response = MHD_create_response_from_buffer(strlen(error_str), (void *)error_str, MHD_RESPMEM_MUST_COPY);\n\tenum MHD_Result ret = MHD_queue_response(connection, error_code, response);\n\tMHD_destroy_response(response);\n\treturn ret;\n}\n\nstatic enum MHD_Result http_api__send_response_with_headers(struct MHD_Connection *connection, const char *buf)\n{\n\tenum MHD_Result ret;\n\tstruct MHD_Response *response = MHD_create_response_from_buffer(strlen(buf), (void *)buf, MHD_RESPMEM_MUST_COPY);\n\n\tret = MHD_add_response_header(response, \"Access-Control-Allow-Origin\", \"*\");\n\tif(ret != MHD_YES){\n\t\tMHD_destroy_response(response);\n\t\treturn ret;\n\t}\n\tret = MHD_queue_response(connection, MHD_HTTP_OK, response);\n\tMHD_destroy_response(response);\n\treturn ret;\n}\n\nstatic enum MHD_Result http_api__process_version(struct MHD_Connection *connection)\n{\n\treturn http_api__send_response_with_headers(connection, VERSION);\n}\n\nstatic enum MHD_Result http_api__process_listeners(struct MHD_Connection *connection)\n{\n\tchar *buf;\n\tenum MHD_Result ret;\n\n\tcJSON *j_tree = cJSON_CreateObject();\n\tif(!j_tree){\n\t\treturn http_api__send_error_response(connection, \"Internal server error.\\n\", MHD_HTTP_INTERNAL_SERVER_ERROR);\n\t}\n\n\tcJSON *j_listeners = cJSON_AddArrayToObject(j_tree, \"listeners\");\n\tif(!j_listeners){\n\t\tcJSON_Delete(j_tree);\n\t\treturn http_api__send_error_response(connection, \"Internal server error.\\n\", MHD_HTTP_INTERNAL_SERVER_ERROR);\n\t}\n\n\tfor(int i=0; i<db.config->listener_count; i++){\n\t\tcJSON *j_listener = cJSON_CreateObject();\n\t\tif(!j_listener){\n\t\t\tcJSON_Delete(j_tree);\n\t\t\treturn http_api__send_error_response(connection, \"Internal server error.\\n\", MHD_HTTP_INTERNAL_SERVER_ERROR);\n\t\t}\n\t\tcJSON_AddItemToArray(j_listeners, j_listener);\n\n\t\tstruct mosquitto__listener *listener = &db.config->listeners[i];\n#ifdef WITH_UNIX_SOCKETS\n\t\tif(listener->unix_socket_path){\n\t\t\tcJSON_AddStringToObject(j_listener, \"path\", listener->unix_socket_path);\n\t\t}else\n#endif\n\t\t{\n\t\t\tcJSON_AddIntToObject(j_listener, \"port\", listener->port);\n\t\t}\n\n\t\tswitch(listener->protocol){\n\t\t\tcase mp_mqtt:\n\t\t\t\tcJSON_AddStringToObject(j_listener, \"protocol\", \"mqtt\");\n\t\t\t\tbreak;\n\t\t\tcase mp_mqttsn:\n\t\t\t\tcJSON_AddStringToObject(j_listener, \"protocol\", \"mqtt-sn\");\n\t\t\t\tbreak;\n\t\t\tcase mp_websockets:\n\t\t\t\tcJSON_AddStringToObject(j_listener, \"protocol\", \"websockets\");\n\t\t\t\tbreak;\n\t\t\tcase mp_http_api:\n\t\t\t\tcJSON_AddStringToObject(j_listener, \"protocol\", \"httpapi\");\n\t\t\t\tbreak;\n\t\t}\n\n#ifdef WITH_TLS\n\t\tcJSON_AddBoolToObject(j_listener, \"tls\", listener->certfile && listener->keyfile);\n\t\tcJSON_AddBoolToObject(j_listener, \"mtls\", listener->require_certificate);\n#endif\n\t\tif(listener->security_options->allow_anonymous == -1){\n\t\t\tcJSON_AddBoolToObject(j_listener, \"allow_anonymous\", db.config->security_options.allow_anonymous);\n\t\t}else{\n\t\t\tcJSON_AddBoolToObject(j_listener, \"allow_anonymous\", listener->security_options->allow_anonymous);\n\t\t}\n\t}\n\tbuf = cJSON_Print(j_tree);\n\tcJSON_Delete(j_tree);\n\tif(buf){\n\t\tret = http_api__send_response_with_headers(connection, buf);\n\t\tfree(buf);\n\t}else{\n\t\tret = http_api__send_error_response(connection, \"Internal server error.\\n\", MHD_HTTP_INTERNAL_SERVER_ERROR);\n\t}\n\n\treturn ret;\n}\n\n\nstatic enum MHD_Result http_api__process_systree(struct MHD_Connection *connection)\n{\n\tenum MHD_Result ret;\n#ifdef WITH_SYS_TREE\n\tchar *buf;\n\n\tcJSON *j_tree = cJSON_CreateObject();\n\tfor(int i=0; i<mosq_metric_max; i++){\n\t\tif(metrics[i].topic){\n\t\t\tcJSON_AddIntToObject(j_tree, metrics[i].topic, metrics[i].current);\n\t\t}\n\t}\n\tcJSON_AddIntToObject(j_tree, \"$SYS/broker/uptime\", broker_uptime());\n\n\tbuf = cJSON_Print(j_tree);\n\tcJSON_Delete(j_tree);\n\n\tif(buf){\n\t\tret = http_api__send_response_with_headers(connection, buf);\n\t\tfree(buf);\n\t}else{\n\t\tret = http_api__send_error_response(connection, \"Internal server error.\\n\", MHD_HTTP_INTERNAL_SERVER_ERROR);\n\t}\n#else\n\tret = http_api__send_error_response(connection, \"Not found.\\n\", 404);\n#endif\n\n\treturn ret;\n}\n\n\nstatic ssize_t http_file_read_cb(void *cls, uint64_t pos, char *buf, size_t max)\n{\n\tFILE *fptr = cls;\n\tif(fseek(fptr, (long )pos, SEEK_SET) < 0){\n\t\treturn -1;\n\t}\n\treturn (ssize_t )fread(buf, 1, max, fptr);\n}\n\n\nstatic void http_file_free_cb(void *cls)\n{\n\tFILE *fptr = cls;\n\tfclose(fptr);\n}\n\n\nstatic enum MHD_Result http_api__process_file(struct mosquitto__listener *listener, struct MHD_Connection *connection, const char *url)\n{\n\tint error_code = MHD_HTTP_INTERNAL_SERVER_ERROR;\n\tchar *canonical_filename;\n\n\tif(!listener || !listener->http_dir){\n\t\thttp_api__send_error_response(connection, \"Not found.\\n\", 404);\n\t\treturn MHD_YES;\n\t}\n\n\tcanonical_filename = http__canonical_filename(url, listener->http_dir, &error_code);\n\tif(!canonical_filename){\n\t\thttp_api__send_error_response(connection, \"Not found.\\n\", 404);\n\t\treturn MHD_YES;\n\t}\n\n\tFILE *fptr = fopen(canonical_filename, \"rb\");\n\tmosquitto_FREE(canonical_filename);\n\tif(!fptr){\n\t\thttp_api__send_error_response(connection, \"Not found.\\n\", 404);\n\t\treturn MHD_YES;\n\t}\n\n\tstruct stat statbuf;\n\tif(fstat(fileno(fptr), &statbuf)){\n\t\tfclose(fptr);\n\t\thttp_api__send_error_response(connection, \"Internal server error.\\n\", 500);\n\t\treturn MHD_YES;\n\t}\n\n\tif(!S_ISREG(statbuf.st_mode)){\n\t\tfclose(fptr);\n\t\thttp_api__send_error_response(connection, \"Not found.\\n\", 404);\n\t\treturn MHD_YES;\n\t}\n\tuint64_t flen = (uint64_t )statbuf.st_size;\n\n\t/* Using MHD_create_response_from_fd would be easier here, but is less portable */\n\tstruct MHD_Response *response = MHD_create_response_from_callback(\n\t\t\tflen,\n\t\t\t65536,\n\t\t\thttp_file_read_cb,\n\t\t\tfptr,\n\t\t\thttp_file_free_cb);\n\n\tenum MHD_Result ret = MHD_queue_response(connection, 200, response);\n\tMHD_destroy_response(response);\n\treturn ret;\n}\n\n\nstatic enum MHD_Result http_api__process_api(struct MHD_Connection *connection, const char *url)\n{\n\tif(strcmp(url, \"/api/v1/systree\") == 0){\n\t\treturn http_api__process_systree(connection);\n\t}else if(strcmp(url, \"/api/v1/listeners\") == 0){\n\t\treturn http_api__process_listeners(connection);\n\t}else if(strcmp(url, \"/api/v1/version\") == 0){\n\t\treturn http_api__process_version(connection);\n\t}else{\n\t\treturn http_api__send_error_response(connection, \"Not found.\\n\", 404);\n\t}\n}\n\n\nstatic int check_access(struct mosquitto__listener *listener, struct MHD_Connection *connection, const char *url)\n{\n\tstruct mosquitto context = {0};\n\tint auth_rc, acl_rc = MOSQ_ERR_SUCCESS;\n\n\tcontext.listener = listener;\n\n\tcontext.id = (char *)\"http-api\";\n\tcontext.username = MHD_basic_auth_get_username_password(connection, &context.password);\n\n\t/* Authentication */\n\tauth_rc = mosquitto_basic_auth(&context);\n\tif(auth_rc == MOSQ_ERR_SUCCESS){\n\t\tacl_rc = mosquitto_acl_check(&context, url, 0, NULL, 0, false, NULL, MOSQ_ACL_READ);\n\t}\n\tMHD_free(context.username);\n\tMHD_free(context.password);\n\n\tif(auth_rc || acl_rc){\n\t\tconst char *buf = \"Not authorised\\n\";\n\t\tstruct MHD_Response *response = MHD_create_response_from_buffer(strlen(buf), (void *)buf, MHD_RESPMEM_MUST_COPY);\n\t\tMHD_queue_basic_auth_fail_response(connection, \"Mosquitto API\", response);\n\t\tMHD_destroy_response(response);\n\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\nstatic enum MHD_Result http_api_handler(void *cls, struct MHD_Connection\n\t\t*connection, const char *url, const char *method, const char *version,\n\t\tconst char *upload_data, size_t *upload_data_size, void **con_cls)\n{\n\tUNUSED(version);\n\tUNUSED(upload_data);\n\tUNUSED(upload_data_size);\n\tUNUSED(con_cls);\n\n\tstruct mosquitto__listener *listener = cls;\n\n\tif(strcmp(method, \"GET\") != 0){\n\t\tchar *buf = \"Invalid HTTP Method\\n\";\n\t\tstruct MHD_Response *response = MHD_create_response_from_buffer(strlen(buf), (void *)buf, MHD_RESPMEM_MUST_COPY);\n\t\tenum MHD_Result ret = MHD_queue_response(connection, MHD_HTTP_METHOD_NOT_ALLOWED, response);\n\t\tMHD_destroy_response(response);\n\t\treturn ret;\n\t}\n\n\tif(check_access(listener, connection, url) != MOSQ_ERR_SUCCESS){\n\t\treturn MHD_YES;\n\t}\n\n\tif(!strncasecmp(url, \"/api/\", strlen(\"/api/\"))){\n\t\treturn http_api__process_api(connection, url);\n\t}else{\n\t\treturn http_api__process_file(listener, connection, url);\n\t}\n}\n\n\nint http_api__start_local(struct mosquitto__listener *listener)\n{\n\tlistener->security_options = mosquitto_calloc(1, sizeof(struct mosquitto__security_options));\n\tif(listener->security_options == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tlistener->host = mosquitto_strdup(\"127.0.0.1\");\n\tif(!listener->host){\n\t\tmosquitto_FREE(listener->security_options);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tlistener->port = 9883;\n\n\tif(db.config->security_options.allow_anonymous == -1){\n\t\tlistener->security_options->allow_anonymous = true;\n\t}else{\n\t\tlistener->security_options->allow_anonymous = db.config->security_options.allow_anonymous;\n\t}\n\n\tif(listener->http_dir == NULL && strlen(HTTP_API_DIR) > 0){\n#ifdef WIN32\n\t\tchar *http_dir_canonical = _fullpath(NULL, HTTP_API_DIR, 0);\n#else\n\t\tchar *http_dir_canonical = realpath(HTTP_API_DIR, NULL);\n#endif\n\t\tif(!http_dir_canonical){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tmosquitto_FREE(listener->http_dir);\n\t\tlistener->http_dir = mosquitto_strdup(http_dir_canonical);\n\t\tmosquitto_FREE(http_dir_canonical);\n\t}\n\n\treturn http_api__start(listener);\n}\n\n\nint http_api__start(struct mosquitto__listener *listener)\n{\n\tunsigned int flags = MHD_USE_AUTO_INTERNAL_THREAD;\n\tconst char *bind_address;\n\tuint16_t port = 9883;\n\tchar *x509_cert = NULL;\n\tchar *x509_key = NULL;\n\n\tif(!listener->security_options){\n\t\tlistener->security_options = mosquitto_calloc(1, sizeof(struct mosquitto__security_options));\n\t\tif(listener->security_options == NULL){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\tlistener->protocol = mp_http_api;\n\n\tbind_address = listener->host;\n\tport = listener->port;\n\n#ifdef WITH_TLS\n\tif(listener->certfile && listener->keyfile){\n\t\tif(mosquitto_read_file(listener->certfile, false, &x509_cert, NULL)){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load server certificate \\\"%s\\\". Check certfile.\", listener->certfile);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(mosquitto_read_file(listener->keyfile, false, &x509_key, NULL)){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load server key file \\\"%s\\\". Check keyfile.\", listener->keyfile);\n\t\t\tmosquitto_FREE(x509_cert);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tflags |= MHD_USE_TLS;\n\t}\n#endif\n\n\tif(bind_address){\n\t\tchar service[10];\n\t\tstruct addrinfo hints;\n\t\tstruct addrinfo *ainfo, *rp;\n\n\t\tsnprintf(service, sizeof(service), \"%d\", port);\n\n\t\tmemset(&hints, 0, sizeof(struct addrinfo));\n\t\thints.ai_family = AF_UNSPEC;\n\t\thints.ai_flags = AI_PASSIVE;\n\t\thints.ai_socktype = SOCK_STREAM;\n\n\t\tint rc = getaddrinfo(bind_address, service, &hints, &ainfo);\n\t\tif(rc){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Unable to start http api listener.\");\n\t\t\tmosquitto_FREE(x509_cert);\n\t\t\tmosquitto_FREE(x509_key);\n\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t}\n\n\t\tfor(rp = ainfo; rp; rp = rp->ai_next){\n\t\t\tif(rp->ai_family == AF_INET || rp->ai_family == AF_INET6){\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(!rp){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Unable to start http api listener, could not find address.\");\n\t\t\tfreeaddrinfo(ainfo);\n\t\t\tmosquitto_FREE(x509_cert);\n\t\t\tmosquitto_FREE(x509_key);\n\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\tif(rp->ai_family == AF_INET6){\n\t\t\tflags |= MHD_USE_IPv6;\n\t\t}\n\t\tif(x509_cert && x509_key){\n\t\t\tlistener->mhd = MHD_start_daemon(flags, port, NULL, NULL, &http_api_handler, listener,\n\t\t\t\t\tMHD_OPTION_SOCK_ADDR, rp->ai_addr,\n\t\t\t\t\tMHD_OPTION_HTTPS_MEM_CERT, x509_cert,\n\t\t\t\t\tMHD_OPTION_HTTPS_MEM_KEY, x509_key,\n\t\t\t\t\tMHD_OPTION_END);\n\t\t}else{\n\t\t\tlistener->mhd = MHD_start_daemon(flags, port, NULL, NULL, &http_api_handler, listener,\n\t\t\t\t\tMHD_OPTION_SOCK_ADDR, rp->ai_addr,\n\t\t\t\t\tMHD_OPTION_END);\n\t\t}\n\t\tfreeaddrinfo(ainfo);\n\t}else{\n\t\tif(x509_cert && x509_key){\n\t\t\tlistener->mhd = MHD_start_daemon(flags | MHD_USE_DUAL_STACK, port, NULL, NULL, &http_api_handler, listener,\n\t\t\t\t\tMHD_OPTION_HTTPS_MEM_CERT, x509_cert,\n\t\t\t\t\tMHD_OPTION_HTTPS_MEM_KEY, x509_key,\n\t\t\t\t\tMHD_OPTION_END);\n\t\t}else{\n\t\t\tlistener->mhd = MHD_start_daemon(flags | MHD_USE_DUAL_STACK, port, NULL, NULL, &http_api_handler, listener,\n\t\t\t\t\tMHD_OPTION_END);\n\t\t}\n\t}\n\n\tmosquitto_FREE(x509_cert);\n\tmosquitto_FREE(x509_key);\n\n\tif(listener->mhd){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Opening http api listen socket on port %d.\", port);\n\t\tif(listener->http_dir){\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Using http_dir %s\", listener->http_dir);\n\t\t}\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n}\n\n\nvoid http_api__stop(struct mosquitto__listener *listener)\n{\n\tMHD_stop_daemon(listener->mhd);\n\tlistener->mhd = NULL;\n}\n\n#endif\n"
  },
  {
    "path": "src/http_serv.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\n#include <assert.h>\n#include <errno.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"packet_mosq.h\"\n#include \"read_handle.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n#include \"picohttpparser.h\"\n\n\nint http__context_init(struct mosquitto *context)\n{\n\tcontext->transport = mosq_t_http;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint http__context_cleanup(struct mosquitto *context)\n{\n\tUNUSED(context);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint http__read(struct mosquitto *mosq)\n{\n\tssize_t read_length;\n\tssize_t header_length;\n\tenum mosquitto_client_state state;\n\tsize_t hlen;\n\tconst char *http_method, *http_path;\n\tsize_t http_method_len, http_path_len;\n\tint http_minor_version;\n\tsize_t http_header_count = 100;\n\tstruct phr_header http_headers[100];\n\tconst char *client_key = NULL;\n\tsize_t client_key_len = 0;\n\tchar *accept_key;\n\tbool header_have_upgrade;\n\tbool header_have_connection;\n\tstruct mosquitto__packet *packet;\n\tint rc;\n\tconst char *subprotocol = NULL;\n\tint subprotocol_len = 0;\n\n\tif(!mosq){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(mosq->sock == INVALID_SOCKET){\n\t\treturn MOSQ_ERR_NO_CONN;\n\t}\n\n\tstate = mosquitto__get_state(mosq);\n\tif(state == mosq_cs_connect_pending){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\thlen = strlen((char *)mosq->in_packet.packet_buffer);\n\tread_length = net__read(mosq, &mosq->in_packet.packet_buffer[hlen], mosq->in_packet.packet_buffer_size-hlen);\n\tif(read_length <= 0){\n\t\tif(read_length == 0){\n\t\t\treturn MOSQ_ERR_CONN_LOST; /* EOF */\n\t\t}\n#ifdef WIN32\n\t\terrno = WSAGetLastError();\n#endif\n\t\tif(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\tswitch(errno){\n\t\t\t\tcase COMPAT_ECONNRESET:\n\t\t\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t\t\tcase COMPAT_EINTR:\n\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\tdefault:\n\t\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t}\n\t\t}\n\t}\n\n\tmosq->in_packet.packet_buffer[mosq->in_packet.packet_buffer_size-1] = '\\0'; /* Always 0 terminate */\n\theader_length = phr_parse_request((char *)mosq->in_packet.packet_buffer, strlen((char *)mosq->in_packet.packet_buffer),\n\t\t\t&http_method, &http_method_len,\n\t\t\t&http_path, &http_path_len,\n\t\t\t&http_minor_version,\n\t\t\thttp_headers, &http_header_count,\n\t\t\t0);\n\t// FIXME - deal with partial read !\n\tif(header_length == -2){\n\t\t// Partial read\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(header_length == -1){\n\t\t// Error\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}else if(header_length < read_length){\n\t\t/* Excess data which can't be handled because the client doesn't have a key yet */\n\t\treturn MOSQ_ERR_MALFORMED_PACKET;\n\t}\n\n\tif(strncmp(http_method, \"GET\", http_method_len) && strncmp(http_method, \"HEAD\", http_method_len)){\n\t\t/* FIXME Not supported - send 501 response */\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\theader_have_upgrade = false;\n\theader_have_connection = false;\n\tsubprotocol = NULL;\n\n\tfor(size_t i=0; i<http_header_count; i++){\n\t\tif(!strncasecmp(http_headers[i].name, \"Upgrade\", http_headers[i].name_len)){\n\t\t\tif(!strncasecmp(http_headers[i].value, \"websocket\", http_headers[i].value_len)){\n\t\t\t\theader_have_upgrade = true;\n\t\t\t}\n\t\t}else if(!strncasecmp(http_headers[i].name, \"Connection\", http_headers[i].name_len)){\n\t\t\t/* Check for \"upgrade\" */\n\t\t\tconst char *str = http_headers[i].value;\n\t\t\tsize_t start = 0;\n\t\t\tsize_t j;\n\t\t\tfor(j=0; j<http_headers[i].value_len; j++){\n\t\t\t\tif(str[j] == ','){\n\t\t\t\t\tif(!strncasecmp(&str[start], \"upgrade\", http_headers[i].value_len-j)){\n\t\t\t\t\t\theader_have_connection = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tstart = j+1;\n\t\t\t\t\t}\n\t\t\t\t}else if(str[j] == ' '){\n\t\t\t\t\tstart = j+1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(!strncasecmp(&str[start], \"upgrade\", http_headers[i].value_len-j)){\n\t\t\t\theader_have_connection = true;\n\t\t\t}\n\t\t}else if(!strncasecmp(http_headers[i].name, \"Sec-WebSocket-Key\", http_headers[i].name_len)){\n\t\t\tclient_key = http_headers[i].value;\n\t\t\tclient_key_len = http_headers[i].value_len;\n\t\t}else if(!strncasecmp(http_headers[i].name, \"Sec-WebSocket-Version\", http_headers[i].name_len)){\n\t\t\t/* Check for \"13\" */\n\t\t\tif(http_headers[i].value_len != 2\n\t\t\t\t\t|| http_headers[i].value[0] != '1'\n\t\t\t\t\t|| http_headers[i].value[1] != '3'\n\t\t\t\t\t){\n\n\t\t\t\t/* FIXME - not supported */\n\t\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t\t\t}\n\t\t}else if(!strncasecmp(http_headers[i].name, \"Sec-WebSocket-Protocol\", http_headers[i].name_len)){\n\t\t\t/* Check for \"mqtt\" */\n\t\t\tif(!strncmp(http_headers[i].value, \"mqtt\", http_headers[i].value_len)\n\t\t\t\t\t|| !strncmp(http_headers[i].value, \"mqttv3.1\", http_headers[i].value_len)){\n\n\t\t\t\tsubprotocol = http_headers[i].value;\n\t\t\t\tsubprotocol_len = (int)http_headers[i].value_len;\n\t\t\t}\n\t\t}else if(!strncasecmp(http_headers[i].name, \"X-Forwarded-For\", http_headers[i].name_len)){\n\t\t\t/* Before implementing this, refer to:\n\t\t\t * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For\n\t\t\t *\n\t\t\t * At the very least, a trusted proxy count must be used. A trusted\n\t\t\t * proxy list would ideally be used.\n\t\t\t *\n\t\t\t * Problematic for us is that if the listener is directly\n\t\t\t * connectable, then the use of this header is insecure. We can't\n\t\t\t * control that, so we have to make it very clear to the end user\n\t\t\t * that this is the case.\n\t\t\t */\n\t\t}else if(!strncasecmp(http_headers[i].name, \"Origin\", http_headers[i].name_len)){\n\t\t\tif(mosq->listener){\n\t\t\t\tbool have_match = false;\n\t\t\t\tfor(int j=0; j<mosq->listener->ws_origin_count; j++){\n\t\t\t\t\tif(!strncmp(mosq->listener->ws_origins[j], http_headers[i].value, http_headers[i].value_len)){\n\t\t\t\t\t\thave_match = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(!have_match && mosq->listener->ws_origin_count > 0){\n\t\t\t\t\treturn MOSQ_ERR_HTTP_BAD_ORIGIN;\n\t\t\t\t}\n\t\t\t}\n\t\t}else{\n\t\t\t/* Unknown header */\n\t\t}\n\t}\n\n\tif(subprotocol == NULL){\n\t\t// FIXME ?\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(header_have_upgrade == false || header_have_connection == false\n\t\t\t|| client_key == NULL || client_key_len == 0){\n\n\t\t// FIXME - 404\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(ws__create_accept_key(client_key, client_key_len, &accept_key)){\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tpacket = mosquitto_calloc(1, sizeof(struct mosquitto__packet) + 1024 + WS_PACKET_OFFSET);\n\tif(!packet){\n\t\tSAFE_FREE(accept_key);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tpacket->packet_length = (uint32_t )snprintf((char *)&packet->payload[WS_PACKET_OFFSET], 1024,\n\t\t\t\"HTTP/1.1 101 Switching Protocols\\r\\n\"\n\t\t\t\"Upgrade: WebSocket\\r\\n\"\n\t\t\t\"Connection: Upgrade\\r\\n\"\n\t\t\t\"Sec-WebSocket-Accept: %s\\r\\n\"\n\t\t\t\"Sec-WebSocket-Protocol: %.*s\\r\\n\"\n\t\t\t\"\\r\\n\", accept_key, subprotocol_len, subprotocol) + WS_PACKET_OFFSET;\n\tSAFE_FREE(accept_key);\n\tpacket->to_process = packet->packet_length;\n\n\tmemset(mosq->in_packet.packet_buffer, 0, db.config->packet_buffer_size);\n\trc = packet__queue(mosq, packet);\n\thttp__context_cleanup(mosq);\n\tws__context_init(mosq);\n\treturn rc;\n}\n#endif\n"
  },
  {
    "path": "src/keepalive.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n#include <time.h>\n#include \"mosquitto_broker_internal.h\"\n#include <utlist.h>\n\n\n/* This contains code for checking whether clients have exceeded their keepalive timeouts.\n * There are two versions.\n *\n * The old version can be used by compiling with `make WITH_OLD_KEEPALIVE=yes`.\n * It will scan the entire list of connected clients every 5 seconds to see if\n * they have expired. Hence it scales with O(n) and with e.g. 60000 clients can\n * have a measurable effect on CPU usage in the low single digit percent range.\n *\n * The new version scales with O(1). It uses a ring buffer that contains\n * max_keepalive*1.5+1 entries. The current time in integer seconds, modulus\n * the number of entries, points to the head of the ring buffer. Any clients\n * will appear after this point at the position indexed by the time at which\n * they will expire if they do not send another message, assuming they do not\n * have keepalive==0 - in which case they are not part of this check. So a\n * client that connects with keepalive=60 will be added at `now + 60*1.5`.\n *\n * A client is added to an entry with a doubly linked list. When the client\n * sends a new message, it is removed from the old position and added to the\n * new.\n *\n * As time moves on, if the linked list at the current entry is not empty, all\n * of the clients are expired.\n *\n * The ring buffer size is determined by max_keepalive. At the default, it is\n * 65535*1.5+1=98303 entries long. On a 64-bit machine that is 786424 bytes.\n * If this is too big a burden and you do not need many clients connected, then\n * the old check is sufficient. You can reduce the number of entries by setting\n * a lower max_keepalive value. A value as low as 600 still gives a 10 minute\n * keepalive and reduces the memory for the ring buffer to 7208 bytes.\n *\n * *NOTE* It is likely that the old check routine will be removed in the\n * future, and max_keepalive set to a sensible default value. If this is a\n * problem for you please get in touch.\n */\n\nstatic time_t last_keepalive_check = 0;\n#ifndef WITH_OLD_KEEPALIVE\nstatic int keepalive_list_max = 0;\nstatic struct mosquitto **keepalive_list = NULL;\n#endif\n\n#ifndef WITH_OLD_KEEPALIVE\n\n\nstatic int calc_index(struct mosquitto *context)\n{\n\treturn (int)(context->last_msg_in + context->keepalive*3/2) % keepalive_list_max;\n}\n#endif\n\n\nint keepalive__init(void)\n{\n#ifndef WITH_OLD_KEEPALIVE\n\tstruct mosquitto *context, *ctxt_tmp;\n\n\tif(db.config->max_keepalive <= 0){\n\t\tkeepalive_list_max = (UINT16_MAX * 3)/2 + 1;\n\t}else{\n\t\tkeepalive_list_max = (db.config->max_keepalive * 3)/2 + 1;\n\t}\n\tkeepalive_list = mosquitto_calloc((size_t)keepalive_list_max, sizeof(struct mosquitto *));\n\tif(keepalive_list == NULL){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\tkeepalive_list_max = 0;\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t/* Add existing clients - should only be applicable on MOSQ_EVT_RELOAD */\n\tHASH_ITER(hh_sock, db.contexts_by_sock, context, ctxt_tmp){\n\t\tif(net__is_connected(context) && !context->bridge && context->keepalive > 0){\n\t\t\tkeepalive__add(context);\n\t\t}\n\t}\n#endif\n\tlast_keepalive_check = db.now_s;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid keepalive__cleanup(void)\n{\n#ifndef WITH_OLD_KEEPALIVE\n\tfor(int idx=0; idx<keepalive_list_max; idx++){\n\t\tstruct mosquitto *context, *ctxt_tmp;\n\t\tDL_FOREACH_SAFE2(keepalive_list[idx], context, ctxt_tmp, keepalive_next){\n\t\t\tDL_DELETE2(keepalive_list[idx], context, keepalive_prev, keepalive_next);\n\t\t}\n\t}\n\tmosquitto_free(keepalive_list);\n\tkeepalive_list = NULL;\n\tkeepalive_list_max = 0;\n#endif\n}\n\n\nint keepalive__add(struct mosquitto *context)\n{\n#ifndef WITH_OLD_KEEPALIVE\n\tif(context->keepalive <= 0 || !net__is_connected(context)){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n#ifdef WITH_BRIDGE\n\tif(context->bridge){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n#endif\n\n\tDL_APPEND2(keepalive_list[calc_index(context)], context, keepalive_prev, keepalive_next);\n\tcontext->keepalive_add_time = db.now_s;\n#else\n\tUNUSED(context);\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n#ifndef WITH_OLD_KEEPALIVE\n\n\nvoid keepalive__check(void)\n{\n\tstruct mosquitto *context, *ctxt_tmp;\n\ttime_t timeout;\n\n\tif(db.contexts_by_sock){\n\t\t/* Check the next 5 seconds for upcoming expiries */\n\t\t/* FIXME - find the actual next entry without having to iterate over\n\t\t * the whole list */\n\t\ttimeout = 5;\n\t\tfor(time_t i=5; i>0; i--){\n\t\t\tif(keepalive_list[(db.now_s + i) % keepalive_list_max]){\n\t\t\t\ttimeout = i;\n\t\t\t}\n\t\t}\n\t\tloop__update_next_event(timeout*1000);\n\t}\n\tfor(time_t i=last_keepalive_check; i<db.now_s; i++){\n\t\tint idx = (int)(i % keepalive_list_max);\n\t\tif(keepalive_list[idx]){\n\t\t\tDL_FOREACH_SAFE2(keepalive_list[idx], context, ctxt_tmp, keepalive_next){\n\t\t\t\t/* keepalive_add_time lets us account for the client adding itself to the keepalive\n\t\t\t\t * list when its last_msg_in value is greater than the last_keepalive_check.\n\t\t\t\t * Without this, the client would be expired if it has keepalive == max_keepalive.\n\t\t\t\t */\n\t\t\t\tif(context->keepalive_add_time <= last_keepalive_check && net__is_connected(context)){\n\t\t\t\t\t/* Client has exceeded keepalive*1.5 */\n\t\t\t\t\tdo_disconnect(context, MOSQ_ERR_KEEPALIVE);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tlast_keepalive_check = db.now_s;\n}\n#else\n\n\nvoid keepalive__check(void)\n{\n\tstruct mosquitto *context, *ctxt_tmp;\n\ttime_t timeout;\n\n\tif(db.contexts_by_sock){\n\t\ttimeout = (last_keepalive_check + 5 - db.now_s);\n\t\tif(timeout <= 0){\n\t\t\ttimeout = 5;\n\t\t}\n\t\tloop__update_next_event(timeout*1000);\n\t}\n\tif(last_keepalive_check + 5 <= db.now_s){\n\t\tlast_keepalive_check = db.now_s;\n\n\t\tHASH_ITER(hh_sock, db.contexts_by_sock, context, ctxt_tmp){\n\t\t\tif(net__is_connected(context)){\n\t\t\t\t/* Local bridges never time out in this fashion. */\n\t\t\t\tif(!(context->keepalive)\n\t\t\t\t\t\t|| context->bridge\n\t\t\t\t\t\t|| db.now_s - context->last_msg_in <= (time_t)(context->keepalive)*3/2){\n\n\t\t\t\t}else{\n\t\t\t\t\t/* Client has exceeded keepalive*1.5 */\n\t\t\t\t\tdo_disconnect(context, MOSQ_ERR_KEEPALIVE);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\n\nint keepalive__remove(struct mosquitto *context)\n{\n#ifndef WITH_OLD_KEEPALIVE\n\tint idx;\n\n\tif(context->keepalive <= 0 || context->keepalive_prev == NULL){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tidx = calc_index(context);\n\tif(keepalive_list[idx]){\n\t\tDL_DELETE2(keepalive_list[idx], context, keepalive_prev, keepalive_next);\n\t\tcontext->keepalive_next = NULL;\n\t\tcontext->keepalive_prev = NULL;\n\t}\n#else\n\tUNUSED(context);\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint keepalive__update(struct mosquitto *context)\n{\n#ifndef WITH_OLD_KEEPALIVE\n\tkeepalive__remove(context);\n\t/* coverity[missing_lock] - broker is single threaded, so no lock required */\n\tcontext->last_msg_in = db.now_s;\n\tkeepalive__add(context);\n#else\n\tUNUSED(context);\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "src/linker-aix.syms",
    "content": "mosquitto_apply_on_all_clients\nmosquitto_broker_node_id_set\nmosquitto_broker_publish\nmosquitto_broker_publish_copy\nmosquitto_callback_register\nmosquitto_callback_unregister\nmosquitto_calloc\nmosquitto_client\nmosquitto_client_address\nmosquitto_client_certificate\nmosquitto_client_clean_session\nmosquitto_client_id\nmosquitto_client_id_hashv\nmosquitto_client_keepalive\nmosquitto_client_port\nmosquitto_client_protocol\nmosquitto_client_protocol_version\nmosquitto_client_sub_count\nmosquitto_client_username\nmosquitto_complete_basic_auth\nmosquitto_control_command_reply\nmosquitto_control_generic_callback\nmosquitto_control_send_response\nmosquitto_free\nmosquitto_kick_client_by_clientid\nmosquitto_kick_client_by_username\nmosquitto_log_printf\nmosquitto_log_vprintf\nmosquitto_malloc\nmosquitto_persist_base_msg_add\nmosquitto_persist_base_msg_delete\nmosquitto_persist_client_add\nmosquitto_persist_client_delete\nmosquitto_persist_client_msg_add\nmosquitto_persist_client_msg_clear\nmosquitto_persist_client_msg_delete\nmosquitto_persist_client_msg_update\nmosquitto_persist_client_update\nmosquitto_persist_retain_msg_delete\nmosquitto_persist_retain_msg_set\nmosquitto_persistence_location\nmosquitto_plugin_set_info\nmosquitto_property_add_binary\nmosquitto_property_add_byte\nmosquitto_property_add_int16\nmosquitto_property_add_int32\nmosquitto_property_add_string\nmosquitto_property_add_string_pair\nmosquitto_property_add_varint\nmosquitto_property_check_all\nmosquitto_property_check_command\nmosquitto_property_copy_all\nmosquitto_property_free_all\nmosquitto_property_identifier\nmosquitto_property_identifier_to_string\nmosquitto_property_next\nmosquitto_property_read_binary\nmosquitto_property_read_byte\nmosquitto_property_read_int16\nmosquitto_property_read_int32\nmosquitto_property_read_string\nmosquitto_property_read_string_pair\nmosquitto_property_read_varint\nmosquitto_pub_topic_check2\nmosquitto_pub_topic_check\nmosquitto_realloc\nmosquitto_set_clientid\nmosquitto_set_username\nmosquitto_strdup\nmosquitto_string_to_property_info\nmosquitto_strndup\nmosquitto_sub_matches_acl\nmosquitto_sub_matches_acl_with_pattern\nmosquitto_sub_topic_check2\nmosquitto_sub_topic_check\nmosquitto_subscription_add\nmosquitto_subscription_delete\nmosquitto_topic_matches_sub2\nmosquitto_topic_matches_sub\nmosquitto_topic_matches_sub_with_pattern\nmosquitto_validate_utf8\n"
  },
  {
    "path": "src/linker-macosx.syms",
    "content": "_mosquitto_apply_on_all_clients\n_mosquitto_broker_node_id_set\n_mosquitto_broker_publish\n_mosquitto_broker_publish_copy\n_mosquitto_callback_register\n_mosquitto_callback_unregister\n_mosquitto_calloc\n_mosquitto_client\n_mosquitto_client_address\n_mosquitto_client_certificate\n_mosquitto_client_clean_session\n_mosquitto_client_id\n_mosquitto_client_id_hashv\n_mosquitto_client_keepalive\n_mosquitto_client_port\n_mosquitto_client_protocol\n_mosquitto_client_protocol_version\n_mosquitto_client_sub_count\n_mosquitto_client_username\n_mosquitto_client_will_set\n_mosquitto_complete_basic_auth\n_mosquitto_control_command_reply\n_mosquitto_control_generic_callback\n_mosquitto_control_send_response\n_mosquitto_free\n_mosquitto_kick_client_by_clientid\n_mosquitto_kick_client_by_username\n_mosquitto_log_printf\n_mosquitto_log_vprintf\n_mosquitto_malloc\n_mosquitto_persist_base_msg_add\n_mosquitto_persist_base_msg_delete\n_mosquitto_persist_client_add\n_mosquitto_persist_client_delete\n_mosquitto_persist_client_msg_add\n_mosquitto_persist_client_msg_clear\n_mosquitto_persist_client_msg_delete\n_mosquitto_persist_client_msg_update\n_mosquitto_persist_client_update\n_mosquitto_persist_retain_msg_delete\n_mosquitto_persist_retain_msg_set\n_mosquitto_persistence_location\n_mosquitto_plugin_set_info\n_mosquitto_property_add_binary\n_mosquitto_property_add_byte\n_mosquitto_property_add_int16\n_mosquitto_property_add_int32\n_mosquitto_property_add_string\n_mosquitto_property_add_string_pair\n_mosquitto_property_add_varint\n_mosquitto_property_check_all\n_mosquitto_property_check_command\n_mosquitto_property_copy_all\n_mosquitto_property_free_all\n_mosquitto_property_identifier\n_mosquitto_property_identifier_to_string\n_mosquitto_property_next\n_mosquitto_property_read_binary\n_mosquitto_property_read_byte\n_mosquitto_property_read_int16\n_mosquitto_property_read_int32\n_mosquitto_property_read_string\n_mosquitto_property_read_string_pair\n_mosquitto_property_read_varint\n_mosquitto_pub_topic_check\n_mosquitto_pub_topic_check2\n_mosquitto_realloc\n_mosquitto_set_clientid\n_mosquitto_set_username\n_mosquitto_strdup\n_mosquitto_string_to_property_info\n_mosquitto_strndup\n_mosquitto_sub_matches_acl\n_mosquitto_sub_matches_acl_with_pattern\n_mosquitto_sub_topic_check\n_mosquitto_sub_topic_check2\n_mosquitto_subscription_add\n_mosquitto_subscription_delete\n_mosquitto_topic_matches_sub\n_mosquitto_topic_matches_sub2\n_mosquitto_topic_matches_sub_with_pattern\n_mosquitto_validate_utf8\n"
  },
  {
    "path": "src/linker.syms",
    "content": "{\n\tmosquitto_apply_on_all_clients;\n\tmosquitto_broker_node_id_set;\n\tmosquitto_broker_publish;\n\tmosquitto_broker_publish_copy;\n\tmosquitto_callback_register;\n\tmosquitto_callback_unregister;\n\tmosquitto_calloc;\n\tmosquitto_client;\n\tmosquitto_client_address;\n\tmosquitto_client_certificate;\n\tmosquitto_client_clean_session;\n\tmosquitto_client_id;\n\tmosquitto_client_id_hashv;\n\tmosquitto_client_keepalive;\n\tmosquitto_client_port;\n\tmosquitto_client_protocol;\n\tmosquitto_client_protocol_version;\n\tmosquitto_client_sub_count;\n\tmosquitto_client_username;\n\tmosquitto_client_will_set;\n\tmosquitto_complete_basic_auth;\n\tmosquitto_control_command_reply;\n\tmosquitto_control_generic_callback;\n\tmosquitto_control_send_response;\n\tmosquitto_free;\n\tmosquitto_kick_client_by_clientid;\n\tmosquitto_kick_client_by_username;\n\tmosquitto_log_printf;\n\tmosquitto_log_vprintf;\n\tmosquitto_malloc;\n\tmosquitto_persist_base_msg_add;\n\tmosquitto_persist_base_msg_delete;\n\tmosquitto_persist_client_add;\n\tmosquitto_persist_client_delete;\n\tmosquitto_persist_client_msg_add;\n\tmosquitto_persist_client_msg_clear;\n\tmosquitto_persist_client_msg_delete;\n\tmosquitto_persist_client_msg_update;\n\tmosquitto_persist_client_update;\n\tmosquitto_persist_retain_msg_delete;\n\tmosquitto_persist_retain_msg_set;\n\tmosquitto_persistence_location;\n\tmosquitto_plugin_set_info;\n\tmosquitto_property_add_binary;\n\tmosquitto_property_add_byte;\n\tmosquitto_property_add_int16;\n\tmosquitto_property_add_int32;\n\tmosquitto_property_add_string;\n\tmosquitto_property_add_string_pair;\n\tmosquitto_property_add_varint;\n\tmosquitto_property_check_all;\n\tmosquitto_property_check_command;\n\tmosquitto_property_copy_all;\n\tmosquitto_property_free_all;\n\tmosquitto_property_identifier;\n\tmosquitto_property_identifier_to_string;\n\tmosquitto_property_next;\n\tmosquitto_property_read_binary;\n\tmosquitto_property_read_byte;\n\tmosquitto_property_read_int16;\n\tmosquitto_property_read_int32;\n\tmosquitto_property_read_string;\n\tmosquitto_property_read_string_pair;\n\tmosquitto_property_read_varint;\n\tmosquitto_pub_topic_check2;\n\tmosquitto_pub_topic_check;\n\tmosquitto_realloc;\n\tmosquitto_set_clientid;\n\tmosquitto_set_username;\n\tmosquitto_strdup;\n\tmosquitto_string_to_property_info;\n\tmosquitto_strndup;\n\tmosquitto_sub_matches_acl;\n\tmosquitto_sub_matches_acl_with_pattern;\n\tmosquitto_sub_topic_check2;\n\tmosquitto_sub_topic_check;\n\tmosquitto_subscription_add;\n\tmosquitto_subscription_delete;\n\tmosquitto_topic_matches_sub2;\n\tmosquitto_topic_matches_sub;\n\tmosquitto_topic_matches_sub_with_pattern;\n\tmosquitto_validate_utf8;\n};\n"
  },
  {
    "path": "src/listeners.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n#include \"net_mosq.h\"\n#include \"mosquitto_broker_internal.h\"\n\nstatic int listensock_index = 0;\nextern int g_run;\n\n\nvoid listener__set_defaults(struct mosquitto__listener *listener)\n{\n\tlistener->disable_protocol_v3 = false;\n\tlistener->disable_protocol_v4 = false;\n\tlistener->disable_protocol_v5 = false;\n\tlistener->max_connections = -1;\n\tlistener->max_qos = 2;\n\tlistener->max_topic_alias = 10;\n\tlistener->max_topic_alias_broker = 10;\n\tlistener->protocol = mp_mqtt;\n\tmosquitto_FREE(listener->mount_point);\n\n\tmosquitto_FREE(listener->security_options->acl_data.acl_file);\n\tmosquitto_FREE(listener->security_options->password_data.password_file);\n\tmosquitto_FREE(listener->security_options->psk_file);\n\tlistener->security_options->allow_anonymous = -1;\n\tlistener->security_options->allow_zero_length_clientid = true;\n\tmosquitto_FREE(listener->security_options->auto_id_prefix);\n\tlistener->security_options->auto_id_prefix_len = 0;\n#ifdef WITH_TLS\n\tlistener->require_certificate = false;\n\tlistener->use_identity_as_username = false;\n\tlistener->use_subject_as_username = false;\n\tlistener->use_username_as_clientid = false;\n\tlistener->disable_client_cert_date_checks = false;\n#endif\n\n#if defined(WITH_WEBSOCKETS) && (LWS_LIBRARY_VERSION_NUMBER >= 3001000 || WITH_WEBSOCKETS == WS_IS_BUILTIN)\n\tfor(int i=0; i<listener->ws_origin_count; i++){\n\t\tmosquitto_FREE(listener->ws_origins[i]);\n\t}\n\tmosquitto_FREE(listener->ws_origins);\n\tlistener->ws_origin_count = 0;\n#endif\n}\n\n\nvoid listeners__reload_all_certificates(void)\n{\n#ifdef WITH_TLS\n\tfor(int i=0; i<db.config->listener_count; i++){\n\t\tstruct mosquitto__listener *listener = &db.config->listeners[i];\n\t\tif(listener->ssl_ctx && listener->certfile && listener->keyfile){\n\t\t\tint rc = net__load_certificates(listener);\n\t\t\tif(rc){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error when reloading certificate '%s' or key '%s'.\",\n\t\t\t\t\t\tlistener->certfile, listener->keyfile);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n\n\nstatic int listeners__start_single_mqtt(struct mosquitto__listener *listener)\n{\n\tstruct mosquitto__listener_sock *listensock_new;\n\n\tif(net__socket_listen(listener)){\n\t\treturn 1;\n\t}\n\tg_listensock_count += listener->sock_count;\n\tlistensock_new = mosquitto_realloc(g_listensock, sizeof(struct mosquitto__listener_sock)*(size_t)g_listensock_count);\n\tif(!listensock_new){\n\t\treturn 1;\n\t}\n\tg_listensock = listensock_new;\n\n\tfor(int i=0; i<listener->sock_count; i++){\n\t\tif(listener->socks[i] == INVALID_SOCKET){\n\t\t\treturn 1;\n\t\t}\n\t\tg_listensock[listensock_index].sock = listener->socks[i];\n\t\tg_listensock[listensock_index].listener = listener;\n#if defined(WITH_EPOLL) || defined(WITH_KQUEUE)\n\t\tg_listensock[listensock_index].ident = id_listener;\n#endif\n\t\tlistensock_index++;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\n\nvoid listeners__add_websockets(struct lws_context *ws_context, mosq_sock_t fd)\n{\n\tstruct mosquitto__listener *listener = NULL;\n\tstruct mosquitto__listener_sock *listensock_new;\n\n\t/* Don't add more listeners after we've started the main loop */\n\tif(g_run || ws_context == NULL){\n\t\treturn;\n\t}\n\n\t/* Find context */\n\tfor(int i=0; i<db.config->listener_count; i++){\n\t\tif(db.config->listeners[i].ws_in_init){\n\t\t\tlistener = &db.config->listeners[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tif(listener == NULL){\n\t\treturn;\n\t}\n\n\tg_listensock_count++;\n\tlistensock_new = mosquitto_realloc(g_listensock, sizeof(struct mosquitto__listener_sock)*(size_t)g_listensock_count);\n\tif(!listensock_new){\n\t\treturn;\n\t}\n\tg_listensock = listensock_new;\n\n\tg_listensock[listensock_index].sock = fd;\n\tg_listensock[listensock_index].listener = listener;\n#if defined(WITH_EPOLL) || defined(WITH_KQUEUE)\n\tg_listensock[listensock_index].ident = id_listener_ws;\n#endif\n\tlistensock_index++;\n}\n#endif\n\n\nstatic int listeners__add_local(const char *host, uint16_t port)\n{\n\tstruct mosquitto__listener *listeners;\n\tbool allow_anonymous;\n\n\tlisteners = db.config->listeners;\n\tif(db.config->security_options.allow_anonymous == -1){\n\t\tallow_anonymous = true;\n\t}else{\n\t\tallow_anonymous = db.config->security_options.allow_anonymous;\n\t}\n\n\tlisteners[db.config->listener_count].security_options = mosquitto_calloc(1, sizeof(struct mosquitto__security_options));\n\tif(listeners[db.config->listener_count].security_options == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tlistener__set_defaults(&listeners[db.config->listener_count]);\n\tlisteners[db.config->listener_count].security_options->allow_anonymous = allow_anonymous;\n\tlisteners[db.config->listener_count].security_options->auto_id_prefix = mosquitto_strdup(\"auto-\");\n\tif(listeners[db.config->listener_count].security_options->auto_id_prefix == NULL){\n\t\tmosquitto_FREE(listeners[db.config->listener_count].security_options);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tlisteners[db.config->listener_count].security_options->auto_id_prefix_len = (int)strlen(\"auto-\");\n\tlisteners[db.config->listener_count].port = port;\n\tlisteners[db.config->listener_count].host = mosquitto_strdup(host);\n\tif(listeners[db.config->listener_count].host == NULL){\n\t\tmosquitto_FREE(listeners[db.config->listener_count].security_options->auto_id_prefix);\n\t\tmosquitto_FREE(listeners[db.config->listener_count].security_options);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tif(listeners__start_single_mqtt(&listeners[db.config->listener_count])){\n\t\tmosquitto_FREE(listeners[db.config->listener_count].security_options->auto_id_prefix);\n\t\tmosquitto_FREE(listeners[db.config->listener_count].security_options);\n\t\tmosquitto_FREE(listeners[db.config->listener_count].host);\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tdb.config->listener_count++;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int listeners__start_local_only(void)\n{\n\t/* Attempt to open listeners bound to 127.0.0.1 and ::1 only */\n\tint rc;\n\tstruct mosquitto__listener *listeners;\n\tsize_t count;\n\n\tif(db.config->cmd_port_count == 0){\n\t\tcount = 2;\n\t}else{\n\t\tcount = (size_t)(db.config->cmd_port_count*2);\n\t}\n\n#ifdef WITH_HTTP_API\n\tcount++;\n#endif\n\n\tlisteners = mosquitto_realloc(db.config->listeners, count*sizeof(struct mosquitto__listener));\n\tif(listeners == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tmemset(listeners, 0, count*sizeof(struct mosquitto__listener));\n\tdb.config->listener_count = 0;\n\tdb.config->listeners = listeners;\n\n\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Starting in local only mode. Connections will only be possible from clients running on this machine.\");\n\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Create a configuration file which defines a listener to allow remote access.\");\n\tlog__printf(NULL, MOSQ_LOG_WARNING, \"For more details see https://mosquitto.org/documentation/authentication-methods/\");\n\tif(db.config->cmd_port_count == 0){\n\t\trc = listeners__add_local(\"127.0.0.1\", 1883);\n\t\tif(rc == MOSQ_ERR_NOMEM){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\trc = listeners__add_local(\"::1\", 1883);\n\t\tif(rc == MOSQ_ERR_NOMEM){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}else{\n\t\tfor(int i=0; i<db.config->cmd_port_count; i++){\n\t\t\trc = listeners__add_local(\"127.0.0.1\", db.config->cmd_port[i]);\n\t\t\tif(rc == MOSQ_ERR_NOMEM){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\trc = listeners__add_local(\"::1\", db.config->cmd_port[i]);\n\t\t\tif(rc == MOSQ_ERR_NOMEM){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(db.config->listener_count > 0){\n#ifdef WITH_HTTP_API\n\t\tdb.config->listener_count++;\n\t\thttp_api__start_local(&db.config->listeners[db.config->listener_count-1]);\n#endif\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n}\n\n\nint listeners__start(void)\n{\n\tg_listensock_count = 0;\n\n\tif(db.config->local_only){\n\t\tif(listeners__start_local_only()){\n\t\t\tdb__close();\n\t\t\tif(db.config->pid_file){\n\t\t\t\t(void)remove(db.config->pid_file);\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\t\tmux__add_listeners(g_listensock, g_listensock_count);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tfor(int i=0; i<db.config->listener_count; i++){\n\t\tif(db.config->listeners[i].protocol == mp_mqtt){\n\t\t\tif(listeners__start_single_mqtt(&db.config->listeners[i])){\n\t\t\t\tdb__close();\n\t\t\t\tif(db.config->pid_file){\n\t\t\t\t\t(void)remove(db.config->pid_file);\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}else if(db.config->listeners[i].protocol == mp_websockets){\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\t\tmosq_websockets_init(&db.config->listeners[i], db.config);\n\t\t\tif(!db.config->listeners[i].ws_context){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to create websockets listener on port %d.\", db.config->listeners[i].port);\n\t\t\t\treturn 1;\n\t\t\t}\n#elif defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\tif(listeners__start_single_mqtt(&db.config->listeners[i])){\n\t\t\t\tdb__close();\n\t\t\t\tif(db.config->pid_file){\n\t\t\t\t\t(void)remove(db.config->pid_file);\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t\t}\n#endif\n#ifdef WITH_HTTP_API\n\t\t}else if(db.config->listeners[i].protocol == mp_http_api){\n\t\t\thttp_api__start(&db.config->listeners[i]);\n#endif\n\t\t}\n\t}\n\tif(g_listensock == NULL){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to start any listening sockets, exiting.\");\n\t\treturn 1;\n\t}\n\n\tmux__add_listeners(g_listensock, g_listensock_count);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid listeners__stop(void)\n{\n\tmux__delete_listeners(g_listensock, g_listensock_count);\n\n\tfor(int i=0; i<db.config->listener_count; i++){\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\tif(db.config->listeners[i].ws_context){\n\t\t\tlws_context_destroy(db.config->listeners[i].ws_context);\n\t\t}\n\t\tmosquitto_FREE(db.config->listeners[i].ws_protocol);\n#endif\n#ifdef WITH_UNIX_SOCKETS\n\t\tif(db.config->listeners[i].unix_socket_path != NULL){\n\t\t\tunlink(db.config->listeners[i].unix_socket_path);\n\t\t}\n#endif\n#ifdef WITH_HTTP_API\n\t\tif(db.config->listeners[i].mhd){\n\t\t\thttp_api__stop(&db.config->listeners[i]);\n\t\t}\n#endif\n\t}\n\n\tfor(int i=0; i<g_listensock_count; i++){\n\t\tif(g_listensock[i].sock != INVALID_SOCKET){\n\t\t\tCOMPAT_CLOSE(g_listensock[i].sock);\n\t\t}\n\t}\n\tmosquitto_FREE(g_listensock);\n\tg_listensock_count = 0;\n\tlistensock_index = 0;\n}\n"
  },
  {
    "path": "src/logging.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#include \"config.h\"\n\n#include <inttypes.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <string.h>\n#include <inttypes.h>\n#ifndef WIN32\n#include <syslog.h>\n#endif\n#include <time.h>\n\n#if defined(__APPLE__)\n#  include <sys/time.h>\n#endif\n\n#ifdef WITH_DLT\n#include <sys/stat.h>\n#include <dlt/dlt.h>\n#endif\n\n#include \"logging_mosq.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"util_mosq.h\"\n\n#ifdef WIN32\nHANDLE syslog_h;\n#endif\n\n#ifdef ANDROID\n#include <android/log.h>\nstatic const char *LOG_TAG = \"mosquitto\";\n#endif\n\nstatic char log_fptr_buffer[BUFSIZ];\nstatic void libcommon__vprintf(const char *fmt, va_list va);\n\n/* Options for logging should be:\n *\n * A combination of:\n * Via syslog\n * To a file\n * To stdout/stderr\n * To topics\n */\n\n/* Give option of logging timestamp.\n * Logging pid.\n */\nstatic unsigned int log_destinations = MQTT3_LOG_STDERR;\nstatic unsigned int log_priorities = MOSQ_LOG_ERR | MOSQ_LOG_WARNING | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO;\n\n#ifdef WITH_DLT\nstatic DltContext dltContext;\nstatic bool dlt_allowed = false;\n\n\nvoid dlt_fifo_check(void)\n{\n\tstruct stat statbuf;\n\n\t/* If we start DLT but the /tmp/dlt fifo doesn't exist, or isn't available\n\t * for writing then there is a big delay when we try and close the log\n\t * later, so check for it first. This has the side effect of not letting\n\t * people using DLT create the fifo after Mosquitto has started, but at the\n\t * benefit of not having a massive delay for everybody else. */\n\tmemset(&statbuf, 0, sizeof(statbuf));\n\tif(stat(\"/tmp/dlt\", &statbuf) == 0){\n\t\tif(S_ISFIFO(statbuf.st_mode)){\n\t\t\tint fd = open(\"/tmp/dlt\", O_NONBLOCK | O_WRONLY);\n\t\t\tif(fd != -1){\n\t\t\t\tdlt_allowed = true;\n\t\t\t\tclose(fd);\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\n\nstatic int get_time(struct tm **ti)\n{\n\ttime_t s;\n\n\ts = db.now_real_s;\n\n\t*ti = localtime(&s);\n\tif(!(*ti)){\n\t\tfprintf(stderr, \"Error obtaining system time.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\n\nint log__init(struct mosquitto__config *config)\n{\n\tint rc = 0;\n\n\tlibcommon_vprintf = libcommon__vprintf;\n\n\tlog_priorities = config->log_type;\n\tlog_destinations = config->log_dest;\n\n\tif(log_destinations & MQTT3_LOG_SYSLOG){\n#ifndef WIN32\n\t\topenlog(\"mosquitto\", LOG_PID|LOG_CONS, config->log_facility);\n#else\n\t\tsyslog_h = OpenEventLog(NULL, \"mosquitto\");\n#endif\n\t}\n\n\tif(log_destinations & MQTT3_LOG_FILE){\n\t\tconfig->log_fptr = mosquitto_fopen(config->log_file, \"at\", true);\n\t\tif(config->log_fptr){\n\t\t\tsetvbuf(config->log_fptr, log_fptr_buffer, _IOLBF, sizeof(log_fptr_buffer));\n\t\t}else{\n\t\t\tlog_destinations = MQTT3_LOG_STDERR;\n\t\t\tlog_priorities = MOSQ_LOG_ERR;\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to open log file %s for writing.\", config->log_file);\n\t\t}\n\t}\n#ifndef WIN32\n\tif(log_destinations & MQTT3_LOG_STDOUT){\n\t\tsetvbuf(stdout, NULL, _IOLBF, 0);\n\t}\n#endif\n#ifdef WITH_DLT\n\tif(log_destinations & MQTT3_LOG_DLT){\n\t\tdlt_fifo_check();\n\t\tif(dlt_allowed){\n\t\t\tDLT_REGISTER_APP(\"MQTT\", \"mosquitto log\");\n\t\t\tdlt_register_context(&dltContext, \"MQTT\", \"mosquitto DLT context\");\n\t\t}\n\t}\n#endif\n\treturn rc;\n}\n\n\nint log__close(struct mosquitto__config *config)\n{\n\tif(log_destinations & MQTT3_LOG_SYSLOG){\n#ifndef WIN32\n\t\tcloselog();\n#else\n\t\tCloseEventLog(syslog_h);\n#endif\n\t}\n\tif(log_destinations & MQTT3_LOG_FILE){\n\t\tif(config->log_fptr){\n\t\t\tfclose(config->log_fptr);\n\t\t\tconfig->log_fptr = NULL;\n\t\t}\n\t}\n\n#ifdef WITH_DLT\n\tif(dlt_allowed){\n\t\tdlt_unregister_context(&dltContext);\n\t\tDLT_UNREGISTER_APP();\n\t}\n#endif\n\t/* FIXME - do something for all destinations! */\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n#ifdef WITH_DLT\n\n\nDltLogLevelType get_dlt_level(unsigned int priority)\n{\n\tswitch(priority){\n\t\tcase MOSQ_LOG_ERR:\n\t\t\treturn DLT_LOG_ERROR;\n\t\tcase MOSQ_LOG_WARNING:\n\t\t\treturn DLT_LOG_WARN;\n\t\tcase MOSQ_LOG_INFO:\n\t\t\treturn DLT_LOG_INFO;\n\t\tcase MOSQ_LOG_DEBUG:\n\t\t\treturn DLT_LOG_DEBUG;\n\t\tcase MOSQ_LOG_NOTICE:\n\t\tcase MOSQ_LOG_SUBSCRIBE:\n\t\tcase MOSQ_LOG_UNSUBSCRIBE:\n\t\t\treturn DLT_LOG_VERBOSE;\n\t\tdefault:\n\t\t\treturn DLT_LOG_DEFAULT;\n\t}\n}\n#endif\n\n#ifdef ANDROID\n\n\nandroid_LogPriority get_android_level(unsigned int priority)\n{\n\tswitch(priority){\n\t\tcase MOSQ_LOG_ERR:\n\t\t\treturn ANDROID_LOG_ERROR;\n\t\tcase MOSQ_LOG_WARNING:\n\t\t\treturn ANDROID_LOG_WARN;\n\t\tcase MOSQ_LOG_INFO:\n\t\t\treturn ANDROID_LOG_INFO;\n\t\tcase MOSQ_LOG_DEBUG:\n\t\t\treturn ANDROID_LOG_DEBUG;\n\t\tcase MOSQ_LOG_NOTICE:\n\t\tcase MOSQ_LOG_SUBSCRIBE:\n\t\tcase MOSQ_LOG_UNSUBSCRIBE:\n\t\t\treturn ANDROID_LOG_VERBOSE;\n\t\tdefault:\n\t\t\treturn ANDROID_LOG_DEBUG;\n\t}\n}\n#endif\n\n\nstatic int log__vprintf(unsigned int priority, const char *fmt, va_list va)\n{\n\tconst char *topic;\n\tint syslog_priority;\n\tchar log_line[1000];\n\tsize_t log_line_pos;\n#ifdef WIN32\n\tchar *sp;\n#endif\n\tbool log_timestamp = true;\n\tchar *log_timestamp_format = NULL;\n\tFILE *log_fptr = NULL;\n\n\tif(db.config){\n\t\tlog_timestamp = db.config->log_timestamp;\n\t\tlog_timestamp_format = db.config->log_timestamp_format;\n\t\tlog_fptr = db.config->log_fptr;\n\t}\n\n\tif((log_priorities & priority) && log_destinations != MQTT3_LOG_NONE){\n\t\tswitch(priority){\n\t\t\tcase MOSQ_LOG_SUBSCRIBE:\n\t\t\t\ttopic = \"$SYS/broker/log/M/subscribe\";\n#ifndef WIN32\n\t\t\t\tsyslog_priority = LOG_NOTICE;\n#else\n\t\t\t\tsyslog_priority = EVENTLOG_INFORMATION_TYPE;\n#endif\n\t\t\t\tbreak;\n\t\t\tcase MOSQ_LOG_UNSUBSCRIBE:\n\t\t\t\ttopic = \"$SYS/broker/log/M/unsubscribe\";\n#ifndef WIN32\n\t\t\t\tsyslog_priority = LOG_NOTICE;\n#else\n\t\t\t\tsyslog_priority = EVENTLOG_INFORMATION_TYPE;\n#endif\n\t\t\t\tbreak;\n\t\t\tcase MOSQ_LOG_DEBUG:\n\t\t\t\ttopic = \"$SYS/broker/log/D\";\n#ifndef WIN32\n\t\t\t\tsyslog_priority = LOG_DEBUG;\n#else\n\t\t\t\tsyslog_priority = EVENTLOG_INFORMATION_TYPE;\n#endif\n\t\t\t\tbreak;\n\t\t\tcase MOSQ_LOG_ERR:\n\t\t\t\ttopic = \"$SYS/broker/log/E\";\n#ifndef WIN32\n\t\t\t\tsyslog_priority = LOG_ERR;\n#else\n\t\t\t\tsyslog_priority = EVENTLOG_ERROR_TYPE;\n#endif\n\t\t\t\tbreak;\n\t\t\tcase MOSQ_LOG_WARNING:\n\t\t\t\ttopic = \"$SYS/broker/log/W\";\n#ifndef WIN32\n\t\t\t\tsyslog_priority = LOG_WARNING;\n#else\n\t\t\t\tsyslog_priority = EVENTLOG_WARNING_TYPE;\n#endif\n\t\t\t\tbreak;\n\t\t\tcase MOSQ_LOG_NOTICE:\n\t\t\t\ttopic = \"$SYS/broker/log/N\";\n#ifndef WIN32\n\t\t\t\tsyslog_priority = LOG_NOTICE;\n#else\n\t\t\t\tsyslog_priority = EVENTLOG_INFORMATION_TYPE;\n#endif\n\t\t\t\tbreak;\n\t\t\tcase MOSQ_LOG_INFO:\n\t\t\t\ttopic = \"$SYS/broker/log/I\";\n#ifndef WIN32\n\t\t\t\tsyslog_priority = LOG_INFO;\n#else\n\t\t\t\tsyslog_priority = EVENTLOG_INFORMATION_TYPE;\n#endif\n\t\t\t\tbreak;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\t\tcase MOSQ_LOG_WEBSOCKETS:\n\t\t\t\ttopic = \"$SYS/broker/log/WS\";\n#ifndef WIN32\n\t\t\t\tsyslog_priority = LOG_DEBUG;\n#else\n\t\t\t\tsyslog_priority = EVENTLOG_INFORMATION_TYPE;\n#endif\n\t\t\t\tbreak;\n#endif\n\t\t\tdefault:\n\t\t\t\ttopic = \"$SYS/broker/log/E\";\n#ifndef WIN32\n\t\t\t\tsyslog_priority = LOG_ERR;\n#else\n\t\t\t\tsyslog_priority = EVENTLOG_ERROR_TYPE;\n#endif\n\t\t}\n\t\tif(log_timestamp){\n\t\t\tif(log_timestamp_format){\n\t\t\t\tstruct tm *ti = NULL;\n\t\t\t\tget_time(&ti);\n\t\t\t\tlog_line_pos = strftime(log_line, sizeof(log_line), log_timestamp_format, ti);\n\t\t\t\tif(log_line_pos == 0){\n\t\t\t\t\tlog_line_pos = (size_t)snprintf(log_line, sizeof(log_line), \"Time error\");\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tlog_line_pos = (size_t)snprintf(log_line, sizeof(log_line), \"%\" PRIu64, (uint64_t)db.now_real_s);\n\t\t\t}\n\t\t\tif(log_line_pos < sizeof(log_line)-3){\n\t\t\t\tlog_line[log_line_pos] = ':';\n\t\t\t\tlog_line[log_line_pos+1] = ' ';\n\t\t\t\tlog_line[log_line_pos+2] = '\\0';\n\t\t\t\tlog_line_pos += 2;\n\t\t\t}\n\t\t}else{\n\t\t\tlog_line_pos = 0;\n\t\t}\n\t\tvsnprintf(&log_line[log_line_pos], sizeof(log_line)-log_line_pos, fmt, va);\n\t\tlog_line[sizeof(log_line)-1] = '\\0'; /* Ensure string is null terminated. */\n\n\t\tif(log_destinations & MQTT3_LOG_STDOUT){\n\t\t\tfprintf(stdout, \"%s\\n\", log_line);\n\t\t}\n\t\tif(log_destinations & MQTT3_LOG_STDERR){\n\t\t\tfprintf(stderr, \"%s\\n\", log_line);\n\t\t}\n\t\tif(log_destinations & MQTT3_LOG_FILE && log_fptr){\n\t\t\tfprintf(log_fptr, \"%s\\n\", log_line);\n#ifdef WIN32\n\t\t\t/* Windows doesn't support line buffering, so flush. */\n\t\t\tfflush(log_fptr);\n#endif\n\t\t}\n\t\tif(log_destinations & MQTT3_LOG_SYSLOG){\n#ifndef WIN32\n\t\t\tsyslog(syslog_priority, \"%s\", log_line);\n#else\n\t\t\tsp = (char *)log_line;\n\t\t\tReportEvent(syslog_h, syslog_priority, 0, 0, NULL, 1, 0, &sp, NULL);\n#endif\n\t\t}\n\t\tif(log_destinations & MQTT3_LOG_TOPIC && priority != MOSQ_LOG_DEBUG && priority != MOSQ_LOG_INTERNAL){\n\t\t\tdb__messages_easy_queue(NULL, topic, 2, (uint32_t)strlen(log_line), log_line, 0, 20, NULL);\n\t\t}\n#ifdef WITH_DLT\n\t\tif(log_destinations & MQTT3_LOG_DLT && priority != MOSQ_LOG_INTERNAL){\n\t\t\tDLT_LOG_STRING(dltContext, get_dlt_level(priority), log_line);\n\t\t}\n#endif\n#ifdef ANDROID\n\t\tif(log_destinations & MQTT3_LOG_ANDROID && priority != MOSQ_LOG_INTERNAL){\n\t\t\t__android_log_write(get_android_level(priority), LOG_TAG, log_line);\n\t\t}\n#endif\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\tva_list va;\n\tint rc;\n\n\tUNUSED(mosq);\n\n\tva_start(va, fmt);\n\trc = log__vprintf(priority, fmt, va);\n\tva_end(va);\n\n\treturn rc;\n}\n\n\nvoid log__internal(const char *fmt, ...)\n{\n\tva_list va;\n\tchar buf[200];\n\tint len;\n\n\tva_start(va, fmt);\n\tlen = vsnprintf(buf, 200, fmt, va);\n\tva_end(va);\n\n\tif(len >= 200){\n\t\tlog__printf(NULL, MOSQ_LOG_INTERNAL, \"Internal log buffer too short (%d)\", len);\n\t\treturn;\n\t}\n\n#ifdef WIN32\n\tlog__printf(NULL, MOSQ_LOG_INTERNAL, \"%s\", buf);\n#else\n\tlog__printf(NULL, MOSQ_LOG_INTERNAL, \"%s%s%s\", \"\\e[32m\", buf, \"\\e[0m\");\n#endif\n}\n\n\nBROKER_EXPORT int mosquitto_log_vprintf(int level, const char *fmt, va_list va)\n{\n\treturn log__vprintf((unsigned int)level, fmt, va);\n}\n\n\nBROKER_EXPORT void mosquitto_log_printf(int level, const char *fmt, ...)\n{\n\tva_list va;\n\n\tva_start(va, fmt);\n\tlog__vprintf((unsigned int)level, fmt, va);\n\tva_end(va);\n}\n\n\nstatic void libcommon__vprintf(const char *fmt, va_list va)\n{\n\tlog__vprintf(MOSQ_LOG_INFO, fmt, va);\n}\n"
  },
  {
    "path": "src/loop.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n   Tatsuzo Osawa - Add epoll.\n*/\n\n#include \"config.h\"\n\n#ifndef WIN32\n#  define _GNU_SOURCE\n#endif\n\n#ifndef WIN32\n#include <unistd.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#endif\n\n#include <errno.h>\n#include <signal.h>\n#include <stdio.h>\n#include <string.h>\n#ifndef WIN32\n#  include <sys/socket.h>\n#endif\n#include <time.h>\n#include <utlist.h>\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n#  include <libwebsockets.h>\n#endif\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_common.h\"\n#include \"send_mosq.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n\nextern int g_run;\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS && LWS_LIBRARY_VERSION_NUMBER == 3002000\n\n\nvoid lws__sul_callback(struct lws_sorted_usec_list *l)\n{\n}\n\nstatic struct lws_sorted_usec_list sul;\n#endif\n\n\nstatic int single_publish(struct mosquitto *context, struct mosquitto__message_v5 *pub_msg, uint32_t message_expiry)\n{\n\tstruct mosquitto__base_msg *base_msg;\n\tuint16_t mid;\n\n\tbase_msg = mosquitto_calloc(1, sizeof(struct mosquitto__base_msg));\n\tif(base_msg == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tbase_msg->data.topic = pub_msg->topic;\n\tpub_msg->topic = NULL;\n\tbase_msg->data.retain = 0;\n\tbase_msg->data.payloadlen = (uint32_t)pub_msg->payloadlen;\n\tbase_msg->data.payload = mosquitto_malloc(base_msg->data.payloadlen+1);\n\tif(base_msg->data.payload == NULL){\n\t\tdb__msg_store_free(base_msg);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\t/* Ensure payload is always zero terminated, this is the reason for the extra byte above */\n\t((uint8_t *)base_msg->data.payload)[base_msg->data.payloadlen] = 0;\n\tmemcpy(base_msg->data.payload, pub_msg->payload, base_msg->data.payloadlen);\n\n\tif(pub_msg->properties){\n\t\tbase_msg->data.properties = pub_msg->properties;\n\t\tpub_msg->properties = NULL;\n\t}\n\n\tif(db__message_store(context, base_msg, &message_expiry, mosq_mo_broker)){\n\t\treturn 1;\n\t}\n\n\tif(pub_msg->qos){\n\t\tmid = mosquitto__mid_generate(context);\n\t}else{\n\t\tmid = 0;\n\t}\n\treturn db__message_insert_outgoing(context, 0, mid, (uint8_t)pub_msg->qos, 0, base_msg, 0, true, true);\n}\n\n\nstatic void read_message_expiry_interval(mosquitto_property **proplist, uint32_t *message_expiry)\n{\n\tmosquitto_property *p, *previous = NULL;\n\n\t*message_expiry = MSG_EXPIRY_INFINITE;\n\n\tif(!proplist){\n\t\treturn;\n\t}\n\n\tp = *proplist;\n\twhile(p){\n\t\tif(mosquitto_property_identifier(p) == MQTT_PROP_MESSAGE_EXPIRY_INTERVAL){\n\t\t\t*message_expiry = mosquitto_property_int32_value(p);\n\t\t\tif(p == *proplist){\n\t\t\t\t*proplist = mosquitto_property_next(p);\n\t\t\t}else{\n\t\t\t\tprevious->next = mosquitto_property_next(p);\n\t\t\t}\n\t\t\tmosquitto_property_free(&p);\n\t\t\treturn;\n\n\t\t}\n\t\tprevious = p;\n\t\tp = mosquitto_property_next(p);\n\t}\n}\n\n\nstatic void queue_plugin_msgs(void)\n{\n\tstruct mosquitto__message_v5 *msg, *tmp;\n\tstruct mosquitto *context;\n\tuint32_t message_expiry;\n\n\tDL_FOREACH_SAFE(db.plugin_msgs, msg, tmp){\n\t\tDL_DELETE(db.plugin_msgs, msg);\n\n\t\tread_message_expiry_interval(&msg->properties, &message_expiry);\n\n\t\tif(msg->clientid){\n\t\t\tHASH_FIND(hh_id, db.contexts_by_id, msg->clientid, strlen(msg->clientid), context);\n\t\t\tif(context){\n\t\t\t\tsingle_publish(context, msg, message_expiry);\n\t\t\t}\n\t\t}else{\n\t\t\tdb__messages_easy_queue(NULL, msg->topic, (uint8_t)msg->qos, (uint32_t)msg->payloadlen, msg->payload, msg->retain, message_expiry, &msg->properties);\n\t\t}\n\t\tmosquitto_FREE(msg->topic);\n\t\tmosquitto_FREE(msg->payload);\n\t\tmosquitto_property_free_all(&msg->properties);\n\t\tmosquitto_FREE(msg->clientid);\n\t\tmosquitto_FREE(msg);\n\t}\n}\n\n\nvoid loop__update_next_event(time_t new_ms)\n{\n\tif(new_ms > 0 && new_ms < db.next_event_ms){\n\t\tdb.next_event_ms = new_ms;\n\t}\n}\n\n\nint mosquitto_main_loop(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n#ifdef WITH_PERSISTENCE\n\ttime_t last_backup = mosquitto_time();\n#endif\n\tint rc;\n\n\n\twatchdog__init();\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS && LWS_LIBRARY_VERSION_NUMBER == 3002000\n\tmemset(&sul, 0, sizeof(struct lws_sorted_usec_list));\n#endif\n\n\tdb.now_s = mosquitto_time();\n\tdb.now_real_s = time(NULL);\n\n#ifdef WITH_BRIDGE\n\trc = bridge__register_local_connections();\n\tif(rc){\n\t\treturn rc;\n\t}\n#endif\n\n\twhile(g_run){\n\t\tretain__expiry_check();\n\t\tqueue_plugin_msgs();\n\t\tcontext__free_disused();\n\n\t\tdb.next_event_ms = 86400000;\n#ifdef WITH_SYS_TREE\n\t\tif(db.config->sys_interval > 0){\n\t\t\tsys_tree__update(false);\n\t\t}\n#endif\n\n\t\tkeepalive__check();\n\t\twatchdog__check();\n\n#ifdef WITH_BRIDGE\n\t\tbridge_check();\n#endif\n\t\tplugin__handle_tick();\n\t\tsession_expiry__check();\n\t\twill_delay__check();\n\n\t\trc = mux__handle(listensock, listensock_count);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n#ifdef WITH_PERSISTENCE\n\t\tif(db.config->persistence && db.config->autosave_interval){\n\t\t\tif(db.config->autosave_on_changes){\n\t\t\t\tif(db.persistence_changes >= db.config->autosave_interval){\n\t\t\t\t\tpersist__backup(false);\n\t\t\t\t\tdb.persistence_changes = 0;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tif(last_backup + db.config->autosave_interval < db.now_s){\n\t\t\t\t\tpersist__backup(false);\n\t\t\t\t\tlast_backup = db.now_s;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\n\t\trc = signal__flag_check();\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\tfor(int i=0; i<db.config->listener_count; i++){\n\t\t\t/* Extremely hacky, should be using the lws provided external poll\n\t\t\t * interface, but their interface has changed recently and ours\n\t\t\t * will soon, so for now websockets clients are second class\n\t\t\t * citizens. */\n\t\t\tif(db.config->listeners[i].ws_context){\n#if LWS_LIBRARY_VERSION_NUMBER > 3002000\n\t\t\t\tlws_service(db.config->listeners[i].ws_context, -1);\n#elif LWS_LIBRARY_VERSION_NUMBER == 3002000\n\t\t\t\tlws_sul_schedule(db.config->listeners[i].ws_context, 0, &sul, lws__sul_callback, 10);\n\t\t\t\tlws_service(db.config->listeners[i].ws_context, 0);\n#else\n\t\t\t\tlws_service(db.config->listeners[i].ws_context, 0);\n#endif\n\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid do_disconnect(struct mosquitto *context, int reason)\n{\n\tconst char *id;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tbool is_duplicate = false;\n#endif\n\n\tif(context->state == mosq_cs_disconnected){\n\t\treturn;\n\t}\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tif(context->wsi){\n\t\tif(context->state == mosq_cs_duplicate){\n\t\t\tis_duplicate = true;\n\t\t}\n\n\t\tif(context->state != mosq_cs_disconnecting && context->state != mosq_cs_disconnect_with_will){\n\t\t\tmosquitto__set_state(context, mosq_cs_disconnect_ws);\n\t\t}\n\t\tif(context->wsi){\n\t\t\tlws_callback_on_writable(context->wsi);\n\t\t}\n\t\tif(context->sock != INVALID_SOCKET){\n\t\t\tHASH_DELETE(hh_sock, db.contexts_by_sock, context);\n\t\t\tmux__delete(context);\n\t\t\tcontext->sock = INVALID_SOCKET;\n\t\t}\n\t\tif(is_duplicate){\n\t\t\t/* This occurs if another client is taking over the same client id.\n\t\t\t * It is important to remove this from the by_id hash here, so it\n\t\t\t * doesn't leave us with multiple clients in the hash with the same\n\t\t\t * id. Websockets doesn't actually close the connection here,\n\t\t\t * unlike for normal clients, which means there is extra time when\n\t\t\t * there could be two clients with the same id in the hash. */\n\t\t\tcontext__remove_from_by_id(context);\n\t\t}\n\t}else\n#endif\n\t{\n\t\tif(db.config->connection_messages == true){\n\t\t\tif(context->id){\n\t\t\t\tid = context->id;\n\t\t\t}else{\n\t\t\t\tid = context->address;\n\t\t\t}\n\t\t\tif(context->state != mosq_cs_disconnecting && context->state != mosq_cs_disconnect_with_will){\n\t\t\t\tswitch(reason){\n\t\t\t\t\tcase MOSQ_ERR_SUCCESS:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_MALFORMED_PACKET:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: malformed packet.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_PROTOCOL:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: protocol error.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_CONN_LOST:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: connection closed by client.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_AUTH:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: not authorised.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_KEEPALIVE:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: exceeded timeout.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_OVERSIZE_PACKET:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: oversize packet.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_PAYLOAD_SIZE:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: oversize payload.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_NOMEM:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: out of memory.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_NOT_SUPPORTED:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: used disallowed feature (QoS too high, retain not supported, or bad AUTH method).\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_ADMINISTRATIVE_ACTION:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: administrative action.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_ERRNO:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: %s.\", id, context->address, context->remote_port, strerror(errno));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_RECEIVE_MAXIMUM_EXCEEDED:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: receive maximum exceeded.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_IMPLEMENTATION_SPECIFIC:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: implementation specific error.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_CLIENT_IDENTIFIER_NOT_VALID:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: client identifier not valid.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_BAD_USERNAME_OR_PASSWORD:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: bad username or password.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_SERVER_UNAVAILABLE:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: server unavailable.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_SERVER_BUSY:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: server busy.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_BANNED:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: client banned.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_BAD_AUTHENTICATION_METHOD:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: bad authentication method.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_QUOTA_EXCEEDED:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: quota exceeded.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_CONNECTION_RATE_EXCEEDED:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: connection rate exceeded.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_SESSION_TAKEN_OVER:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: session taken over.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_TOPIC_ALIAS_INVALID:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: topic alias invalid.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_HTTP_BAD_ORIGIN:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: non-matching http origin.\", id, context->address, context->remote_port);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MOSQ_ERR_PROXY:\n\t\t\t\t\t\t/* This was a proxy v2 health check connection, so don't report */\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: bad socket read/write: %s\", id, context->address, context->remote_port, mosquitto_strerror(reason));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tif(reason == MOSQ_ERR_ADMINISTRATIVE_ACTION){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected: administrative action.\", id, context->address, context->remote_port);\n\t\t\t\t}else{\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client %s [%s:%d] disconnected.\", id, context->address, context->remote_port);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tmux__delete(context);\n\t\tcontext__disconnect(context, reason);\n\t}\n}\n"
  },
  {
    "path": "src/mosquitto.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifndef WIN32\n/* For initgroups() */\n#  include <unistd.h>\n#  include <grp.h>\n#  include <assert.h>\n/* For umask() */\n#  include <sys/stat.h>\n#endif\n\n#ifndef WIN32\n#include <pwd.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#endif\n\n#ifndef WIN32\n#  include <sys/time.h>\n#endif\n\n#include <errno.h>\n#include <limits.h>\n#include <signal.h>\n#include <stdio.h>\n#include <string.h>\n#ifdef WITH_SYSTEMD\n#  include <systemd/sd-daemon.h>\n#endif\n#ifdef WITH_WRAP\n#include <tcpd.h>\n#endif\n\n#include \"mosquitto_broker_internal.h\"\n#include \"util_mosq.h\"\n\nstruct mosquitto_db db;\n\nstruct mosquitto__listener_sock *g_listensock = NULL;\nint g_listensock_count = 0;\n\nint g_run = 0;\n#ifdef WITH_WRAP\n#include <syslog.h>\nint allow_severity = LOG_INFO;\nint deny_severity = LOG_INFO;\n#endif\n\n\nstatic int set_umask(void)\n{\n#if !defined(__CYGWIN__) && !defined(WIN32)\n\t/* This affects files that are written to, apart from those that are\n\t * created using mosquitto_fopen(..., restrict_read=true), which sets a\n\t * umask of 077. */\n\tconst char *mask_s;\n\tchar *endptr = NULL;\n\tlong mask;\n\n\n\tmask_s = getenv(\"UMASK_SET\");\n\tif(mask_s){\n\t\terrno = 0;\n\t\tmask = strtol(mask_s, &endptr, 8);\n\t\tif(errno || endptr == mask_s || *endptr != '\\0'){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: UMASK_SET environment variable not a valid octal number.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(mask < 000 || mask > 0777){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: UMASK_SET environment variable out of range.\");\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tumask((mode_t)mask);\n\t}\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* coverity[ +tainted_string_sanitize_content : arg-0 ] */\nstatic int check_uid(const char *s, const char *name)\n{\n\tchar *endptr = NULL;\n\tlong id;\n\n\terrno = 0;\n\tid = strtol(s, &endptr, 10);\n\tif(errno || endptr == s || *endptr != '\\0'){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s not a valid ID '%s'\", name, s);\n\t\treturn -1;\n\t}\n\tif(id < 0){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s must not be negative\", name);\n\t\treturn -1;\n\t}\n\tif(id > INT_MAX){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s must not be less than %d\", name, INT_MAX);\n\t\treturn -1;\n\t}\n\treturn (int)id;\n}\n\n\n/* Prints the name of the process user from its user id if not found\n * it simply prints out the user id\n */\nstatic void print_pwname(void)\n{\n#ifndef WIN32\n\tstruct passwd *pwd = getpwuid(geteuid());\n\tif(!pwd){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Info: running mosquitto as user id: %i.\", geteuid());\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Info: running mosquitto as user: %s.\", pwd->pw_name);\n\t}\n#endif\n}\n\n\n/* mosquitto shouldn't run as root.\n * This function will attempt to change to an unprivileged user and group if\n * running as root. The user is given in config->user.\n * Returns 1 on failure (unknown user, setuid/setgid failure)\n * Returns 0 on success.\n * Note that setting config->user to \"root\" does not produce an error, but it\n * strongly discouraged.\n */\n\n\nstatic int drop_privileges(struct mosquitto__config *config)\n{\n#if !defined(__CYGWIN__) && !defined(WIN32)\n\tstruct passwd *pwd;\n\tchar *err;\n\tint rc;\n\tconst char *puid_s, *pgid_s;\n\tint puid;\n\tint pgid;\n\n\tconst char *snap = getenv(\"SNAP_NAME\");\n\tif(snap && !strcmp(snap, \"mosquitto\")){\n\t\t/* Don't attempt to drop privileges if running as a snap */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\t/* PUID and PGID are docker custom user mappings */\n\tpuid_s = getenv(\"PUID\");\n\tpgid_s = getenv(\"PGID\");\n\n\tif(geteuid() == 0){\n\t\tif(puid_s || pgid_s){\n\t\t\tif(pgid_s){\n\t\t\t\tpgid = check_uid(pgid_s, \"PGID\");\n\t\t\t\tif(pgid < 0){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}else if(pgid > 0){\n\t\t\t\t\trc = setgid((gid_t)pgid);\n\t\t\t\t\tif(rc == -1){\n\t\t\t\t\t\terr = strerror(errno);\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error setting gid whilst dropping privileges: %s.\", err);\n\t\t\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(puid_s){\n\t\t\t\tpuid = check_uid(puid_s, \"PUID\");\n\t\t\t\tif(puid < 0){\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}else if(puid > 0){\n\t\t\t\t\trc = setuid((uid_t)puid);\n\t\t\t\t\tif(rc == -1){\n\t\t\t\t\t\terr = strerror(errno);\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error setting uid whilst dropping privileges: %s.\", err);\n\t\t\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}else if(config->user && strcmp(config->user, \"root\")){\n\t\t\tpwd = getpwnam(config->user);\n\t\t\tif(!pwd){\n\t\t\t\tif(strcmp(config->user, \"mosquitto\")){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to drop privileges to '%s' because this user does not exist.\", config->user);\n\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t}else{\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Warning: Unable to drop privileges to '%s' because this user does not exist. Trying 'nobody' instead.\", config->user);\n\t\t\t\t\tpwd = getpwnam(\"nobody\");\n\t\t\t\t\tif(!pwd){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to drop privileges to 'nobody'.\");\n\t\t\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(initgroups(config->user, pwd->pw_gid) == -1){\n\t\t\t\terr = strerror(errno);\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error setting groups whilst dropping privileges: %s.\", err);\n\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t}\n\t\t\trc = setgid(pwd->pw_gid);\n\t\t\tif(rc == -1){\n\t\t\t\terr = strerror(errno);\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error setting gid whilst dropping privileges: %s.\", err);\n\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t}\n\t\t\trc = setuid(pwd->pw_uid);\n\t\t\tif(rc == -1){\n\t\t\t\terr = strerror(errno);\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error setting uid whilst dropping privileges: %s.\", err);\n\t\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t\t}\n\t\t}\n\t\tprint_pwname();\n\t\tif(geteuid() == 0 || getegid() == 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Mosquitto should not be run as root/administrator.\");\n\t\t}\n\t}else{\n\t\tprint_pwname();\n\t}\n\n#else\n\tUNUSED(config);\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void mosquitto__daemonise(void)\n{\n#ifndef WIN32\n\tchar *err;\n\tpid_t pid;\n\n\tpid = fork();\n\tif(pid < 0){\n\t\terr = strerror(errno);\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in fork: %s\", err);\n\t\texit(1);\n\t}\n\tif(pid > 0){\n\t\texit(0);\n\t}\n\tif(setsid() < 0){\n\t\terr = strerror(errno);\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in setsid: %s\", err);\n\t\texit(1);\n\t}\n\n\tif(!freopen(\"/dev/null\", \"r\", stdin)){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error whilst daemonising (%s): %s\", \"stdin\", strerror(errno));\n\t\texit(1);\n\t}\n\tif(!freopen(\"/dev/null\", \"w\", stdout)){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error whilst daemonising (%s): %s\", \"stdout\", strerror(errno));\n\t\texit(1);\n\t}\n\tif(!freopen(\"/dev/null\", \"w\", stderr)){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error whilst daemonising (%s): %s\", \"stderr\", strerror(errno));\n\t\texit(1);\n\t}\n#else\n\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Can't start in daemon mode in Windows.\");\n#endif\n}\n\n\nstatic int pid__write(void)\n{\n\tFILE *pid;\n\n\tif(db.config->pid_file){\n\t\tpid = mosquitto_fopen(db.config->pid_file, \"wt\", false);\n\t\tif(pid){\n\t\t\tfprintf(pid, \"%d\", getpid());\n\t\t\tfclose(pid);\n\t\t}else{\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to write pid file.\");\n\t\t\treturn MOSQ_ERR_ERRNO;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void report_features(void)\n{\n#ifdef WITH_BRIDGE\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Bridge support available.\");\n#else\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Bridge support NOT available.\");\n#endif\n#ifdef WITH_PERSISTENCE\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Persistence support available.\");\n#else\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Persistence support NOT available.\");\n#endif\n#ifdef WITH_TLS\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"TLS support available.\");\n#else\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"TLS support NOT available.\");\n#endif\n#ifdef FINAL_WITH_TLS_PSK\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"TLS-PSK support available.\");\n#else\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"TLS-PSK support NOT available.\");\n#endif\n#ifdef WITH_WEBSOCKETS\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Websockets support available.\");\n#else\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Websockets support NOT available.\");\n#endif\n\tif(getenv(\"MOSQUITTO_UNSAFE_ALLOW_SYMLINKS\")){\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"MOSQUITTO_UNSAFE_ALLOW_SYMLINKS is set, loading of sensitive files through symbolic links is allowed.\");\n\t}\n}\n\n\nstatic void post_shutdown_cleanup(void)\n{\n\tstruct mosquitto *ctxt, *ctxt_tmp;\n\n\t/* FIXME - this isn't quite right, all wills with will delay zero should be\n\t * sent now, but those with positive will delay should be persisted and\n\t * restored, pending the client reconnecting in time. */\n\tHASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){\n\t\tcontext__send_will(ctxt);\n\t}\n\twill_delay__send_all();\n\n\t/* Set to true only after persistence events have been processed */\n\tdb.shutdown = true;\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"mosquitto version %s terminating\", VERSION);\n\n\tbroker_control__cleanup();\n\n#ifdef WITH_PERSISTENCE\n\tpersist__backup(true);\n#endif\n\tsession_expiry__remove_all();\n\n\tlisteners__stop();\n\n\tHASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\tif(!ctxt->wsi)\n#endif\n\t\t{\n\t\t\tctxt->is_persisted = false; /* prevent persistence removal */\n\t\t\tcontext__cleanup(ctxt, true);\n\t\t}\n\t}\n\tHASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){\n\t\tctxt->is_persisted = false; /* prevent persistence removal */\n\t\tcontext__cleanup(ctxt, true);\n\t}\n#ifdef WITH_BRIDGE\n\tbridge__db_cleanup();\n#endif\n\tcontext__free_disused();\n\tkeepalive__cleanup();\n\n#ifdef WITH_TLS\n\tmosquitto_FREE(db.tls_keylog);\n#endif\n\tdb__close();\n\n\tplugin__unload_all();\n\tmosquitto_security_cleanup(false);\n\n\tif(db.config->pid_file){\n\t\t(void)remove(db.config->pid_file);\n\t}\n\n\tmux__cleanup();\n\n\tlog__close(db.config);\n\tconfig__cleanup(db.config);\n\tnet__broker_cleanup();\n}\n\n\nstatic void cjson_init(void)\n{\n\tcJSON_Hooks hooks = {mosquitto_malloc, mosquitto_free};\n\tcJSON_InitHooks(&hooks);\n}\n\n#ifdef WITH_FUZZING\n\n\nint mosquitto_fuzz_main(int argc, char *argv[])\n#else\n\n\nint main(int argc, char *argv[])\n#endif\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmosquitto_time_init();\n\tcjson_init();\n\n#if defined(WIN32) || defined(__CYGWIN__)\n\tif(argc == 2){\n\t\tif(!strcmp(argv[1], \"run\")){\n\t\t\tservice_run(argv[0]);\n\t\t\treturn 0;\n\t\t}else if(!strcmp(argv[1], \"install\")){\n\t\t\tservice_install(argv[0]);\n\t\t\treturn 0;\n\t\t}else if(!strcmp(argv[1], \"uninstall\")){\n\t\t\tservice_uninstall(argv[0]);\n\t\t\treturn 0;\n\t\t}\n\t}\n#endif\n\n\n#ifdef WIN32\n\tif(_setmaxstdio(8192) != 8192){\n\t\t/* Old limit was 2048 */\n\t\tif(_setmaxstdio(2048) != 2048){\n\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Unable to increase maximum allowed connections. This session may be limited to 512 connections.\");\n\t\t}\n\t}\n#endif\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tdb.now_s = mosquitto_time();\n\tdb.now_real_s = time(NULL);\n\tmosquitto_broker_node_id_set(0);\n\n\tnet__broker_init();\n\n\tdb.config = &config;\n\tconfig__init(&config);\n\trc = config__parse_args(&config, argc, argv);\n\tif(rc == MOSQ_ERR_UNKNOWN){\n\t\tpost_shutdown_cleanup();\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\n\tif(config.test_configuration){\n\t\tif(!db.config_file){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Please provide a configuration file to test.\");\n\t\t\tpost_shutdown_cleanup();\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}else{\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Configuration file is OK.\");\n\t\t\tpost_shutdown_cleanup();\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\n\trc = keepalive__init();\n\tif(rc){\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\n\t/* Drop privileges permanently immediately after the config is loaded.\n\t * This requires the user to ensure that all certificates, log locations,\n\t * etc. are accessible my the `mosquitto` or other unprivileged user.\n\t */\n\trc = drop_privileges(&config);\n\tif(rc){\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\t/* Set umask based on environment variable */\n\trc = set_umask();\n\tif(rc){\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\n\n\tif(config.daemon){\n\t\tmosquitto__daemonise();\n\t}\n\n\trc = pid__write();\n\tif(rc){\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\n\trc = db__open(&config);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Couldn't open database.\");\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\n\t/* Initialise logging only after initialising the database in case we're\n\t * logging to topics */\n\trc = log__init(&config);\n\tif(rc){\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"mosquitto version %s starting\", VERSION);\n\tif(db.config_file){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Config loaded from %s.\", db.config_file);\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Using default config.\");\n\t}\n\treport_features();\n\n\trc = plugin__load_all();\n\tif(rc){\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\trc = mosquitto_security_init(false);\n\tif(rc){\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\n\tplugin_persist__handle_restore();\n\tsession_expiry__check();\n\tretain__expire(&db.retains);\n\tdb__msg_store_compact();\n\n#ifdef WITH_SYS_TREE\n\tsys_tree__init();\n#endif\n\n\trc = mux__init();\n\tif(rc){\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\n\trc = listeners__start();\n\tif(rc){\n\t\tpost_shutdown_cleanup();\n\t\treturn rc;\n\t}\n\n\tsignal__setup();\n\n#ifdef WITH_BRIDGE\n\tbridge__start_all();\n#endif\n\n\tbroker_control__init();\n\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"mosquitto version %s running\", VERSION);\n#ifdef WITH_SYSTEMD\n\tsd_notify(0, \"READY=1\");\n#endif\n\n\tg_run = 1;\n\trc = mosquitto_main_loop(g_listensock, g_listensock_count);\n\n\tpost_shutdown_cleanup();\n\n\treturn rc;\n}\n\n#ifdef WIN32\n\n\nint WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)\n{\n\tchar **argv;\n\tint argc = 1;\n\tchar *token;\n\tchar *saveptr = NULL;\n\tint rc;\n\n\tUNUSED(hInstance);\n\tUNUSED(hPrevInstance);\n\tUNUSED(nCmdShow);\n\n\targv = mosquitto_malloc(sizeof(char *)*1);\n\targv[0] = \"mosquitto\";\n\ttoken = strtok_r(lpCmdLine, \" \", &saveptr);\n\twhile(token){\n\t\targc++;\n\t\targv = mosquitto_realloc(argv, sizeof(char *)*argc);\n\t\tif(!argv){\n\t\t\tfprintf(stderr, \"Error: Out of memory.\\n\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\targv[argc-1] = token;\n\t\ttoken = strtok_r(NULL, \" \", &saveptr);\n\t}\n\trc = main(argc, argv);\n\tmosquitto_FREE(argv);\n\treturn rc;\n}\n#endif\n"
  },
  {
    "path": "src/mosquitto_broker_internal.h",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n   Tatsuzo Osawa - Add epoll.\n*/\n\n#ifndef MOSQUITTO_BROKER_INTERNAL_H\n#define MOSQUITTO_BROKER_INTERNAL_H\n\n#include \"config.h\"\n#include <stdio.h>\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n#  include <libwebsockets.h>\n#  if LWS_LIBRARY_VERSION_NUMBER >= 3002000 && !defined(LWS_WITH_EXTERNAL_POLL)\n#    warning \"libwebsockets is not compiled with LWS_WITH_EXTERNAL_POLL support. Websocket performance will be unusable.\"\n#  endif\n#endif\n\n#ifdef WITH_HTTP_API\n#  include <microhttpd.h>\n#endif\n\n#ifdef __linux__\n#define WITH_TCP_USER_TIMEOUT\n#endif\n\n#include \"mosquitto_internal.h\"\n#include \"mosquitto/broker.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"mosquitto.h\"\n#include \"logging_mosq.h\"\n#include \"tls_mosq.h\"\n#include \"uthash.h\"\n#include \"acl_file.h\"\n#include \"password_file.h\"\n\n#ifndef __GNUC__\n#define __attribute__(attrib)\n#endif\n\n/* Log destinations */\n#define MQTT3_LOG_NONE 0x00\n#define MQTT3_LOG_SYSLOG 0x01\n#define MQTT3_LOG_FILE 0x02\n#define MQTT3_LOG_STDOUT 0x04\n#define MQTT3_LOG_STDERR 0x08\n#define MQTT3_LOG_TOPIC 0x10\n#define MQTT3_LOG_DLT 0x20\n#ifdef ANDROID\n#define MQTT3_LOG_ANDROID 0x40\n#endif\n#define MQTT3_LOG_ALL 0xFF\n\n#define CMD_PORT_LIMIT 10\n\ntypedef uint64_t dbid_t;\n\ntypedef int (*FUNC_plugin_init_v5)(mosquitto_plugin_id_t *, void **, struct mosquitto_opt *, int);\ntypedef int (*FUNC_plugin_cleanup_v5)(void *, struct mosquitto_opt *, int);\n\ntypedef int (*FUNC_auth_plugin_init_v4)(void **, struct mosquitto_opt *, int);\ntypedef int (*FUNC_auth_plugin_cleanup_v4)(void *, struct mosquitto_opt *, int);\ntypedef int (*FUNC_auth_plugin_security_init_v4)(void *, struct mosquitto_opt *, int, bool);\ntypedef int (*FUNC_auth_plugin_security_cleanup_v4)(void *, struct mosquitto_opt *, int, bool);\ntypedef int (*FUNC_auth_plugin_acl_check_v4)(void *, int, struct mosquitto *, struct mosquitto_acl_msg *);\ntypedef int (*FUNC_auth_plugin_unpwd_check_v4)(void *, struct mosquitto *, const char *, const char *);\ntypedef int (*FUNC_auth_plugin_psk_key_get_v4)(void *, struct mosquitto *, const char *, const char *, char *, int);\ntypedef int (*FUNC_auth_plugin_auth_start_v4)(void *, struct mosquitto *, const char *, bool, const void *, uint16_t, void **, uint16_t *);\ntypedef int (*FUNC_auth_plugin_auth_continue_v4)(void *, struct mosquitto *, const char *, const void *, uint16_t, void **, uint16_t *);\n\ntypedef int (*FUNC_auth_plugin_init_v3)(void **, struct mosquitto_opt *, int);\ntypedef int (*FUNC_auth_plugin_cleanup_v3)(void *, struct mosquitto_opt *, int);\ntypedef int (*FUNC_auth_plugin_security_init_v3)(void *, struct mosquitto_opt *, int, bool);\ntypedef int (*FUNC_auth_plugin_security_cleanup_v3)(void *, struct mosquitto_opt *, int, bool);\ntypedef int (*FUNC_auth_plugin_acl_check_v3)(void *, int, const struct mosquitto *, struct mosquitto_acl_msg *);\ntypedef int (*FUNC_auth_plugin_unpwd_check_v3)(void *, const struct mosquitto *, const char *, const char *);\ntypedef int (*FUNC_auth_plugin_psk_key_get_v3)(void *, const struct mosquitto *, const char *, const char *, char *, int);\n\ntypedef int (*FUNC_auth_plugin_init_v2)(void **, struct mosquitto_auth_opt *, int);\ntypedef int (*FUNC_auth_plugin_cleanup_v2)(void *, struct mosquitto_auth_opt *, int);\ntypedef int (*FUNC_auth_plugin_security_init_v2)(void *, struct mosquitto_auth_opt *, int, bool);\ntypedef int (*FUNC_auth_plugin_security_cleanup_v2)(void *, struct mosquitto_auth_opt *, int, bool);\ntypedef int (*FUNC_auth_plugin_acl_check_v2)(void *, const char *, const char *, const char *, int);\ntypedef int (*FUNC_auth_plugin_unpwd_check_v2)(void *, const char *, const char *);\ntypedef int (*FUNC_auth_plugin_psk_key_get_v2)(void *, const char *, const char *, char *, int);\n\n\nenum mosquitto_msg_origin {\n\tmosq_mo_client = 0,\n\tmosq_mo_broker = 1,\n};\n\nstruct mosquitto__plugin_lib {\n\tvoid *lib;\n\tvoid *user_data;\n\tint (*plugin_version)(void);\n\tstruct mosquitto_plugin_id_t *identifier;\n\n\tFUNC_plugin_init_v5 plugin_init_v5;\n\tFUNC_plugin_cleanup_v5 plugin_cleanup_v5;\n\n\tFUNC_auth_plugin_init_v4 plugin_init_v4;\n\tFUNC_auth_plugin_cleanup_v4 plugin_cleanup_v4;\n\tFUNC_auth_plugin_security_init_v4 security_init_v4;\n\tFUNC_auth_plugin_security_cleanup_v4 security_cleanup_v4;\n\tFUNC_auth_plugin_acl_check_v4 acl_check_v4;\n\tFUNC_auth_plugin_unpwd_check_v4 unpwd_check_v4;\n\tFUNC_auth_plugin_psk_key_get_v4 psk_key_get_v4;\n\tFUNC_auth_plugin_auth_start_v4 auth_start_v4;\n\tFUNC_auth_plugin_auth_continue_v4 auth_continue_v4;\n\n\tFUNC_auth_plugin_init_v3 plugin_init_v3;\n\tFUNC_auth_plugin_cleanup_v3 plugin_cleanup_v3;\n\tFUNC_auth_plugin_security_init_v3 security_init_v3;\n\tFUNC_auth_plugin_security_cleanup_v3 security_cleanup_v3;\n\tFUNC_auth_plugin_acl_check_v3 acl_check_v3;\n\tFUNC_auth_plugin_unpwd_check_v3 unpwd_check_v3;\n\tFUNC_auth_plugin_psk_key_get_v3 psk_key_get_v3;\n\n\tFUNC_auth_plugin_init_v2 plugin_init_v2;\n\tFUNC_auth_plugin_cleanup_v2 plugin_cleanup_v2;\n\tFUNC_auth_plugin_security_init_v2 security_init_v2;\n\tFUNC_auth_plugin_security_cleanup_v2 security_cleanup_v2;\n\tFUNC_auth_plugin_acl_check_v2 acl_check_v2;\n\tFUNC_auth_plugin_unpwd_check_v2 unpwd_check_v2;\n\tFUNC_auth_plugin_psk_key_get_v2 psk_key_get_v2;\n\tint version;\n};\n\nstruct mosquitto__plugin_config {\n\tchar *path;\n\tchar *name;\n\tstruct mosquitto_opt *options;\n\tstruct mosquitto__security_options **security_options;\n\tint option_count;\n\tint security_option_count;\n\tbool deny_special_chars;\n};\n\nstruct mosquitto__callback {\n\tUT_hash_handle hh; /* For callbacks that register for e.g. a specific topic */\n\tstruct mosquitto__callback *next, *prev; /* For typical callbacks */\n\tMOSQ_FUNC_generic_callback cb;\n\tvoid *userdata;\n\tunion {\n\t\tchar *topic;\n\t\tstruct timespec next_tick;\n\t} data;\n\tmosquitto_plugin_id_t *identifier;\n};\n\nstruct plugin__callbacks {\n\tstruct mosquitto__callback *tick;\n\tstruct mosquitto__callback *acl_check;\n\tstruct mosquitto__callback *basic_auth;\n\tstruct mosquitto__callback *connect;\n\tstruct mosquitto__callback *control;\n\tstruct mosquitto__callback *disconnect;\n\tstruct mosquitto__callback *client_offline;\n\tstruct mosquitto__callback *ext_auth_continue;\n\tstruct mosquitto__callback *ext_auth_start;\n\tstruct mosquitto__callback *message_in;\n\tstruct mosquitto__callback *message_out;\n\tstruct mosquitto__callback *psk_key;\n\tstruct mosquitto__callback *reload;\n\tstruct mosquitto__callback *subscribe;\n\tstruct mosquitto__callback *unsubscribe;\n\tstruct mosquitto__callback *persist_restore;\n\tstruct mosquitto__callback *persist_client_add;\n\tstruct mosquitto__callback *persist_client_delete;\n\tstruct mosquitto__callback *persist_client_update;\n\tstruct mosquitto__callback *persist_subscription_add;\n\tstruct mosquitto__callback *persist_subscription_delete;\n\tstruct mosquitto__callback *persist_client_msg_add;\n\tstruct mosquitto__callback *persist_client_msg_delete;\n\tstruct mosquitto__callback *persist_client_msg_update;\n\tstruct mosquitto__callback *persist_base_msg_add;\n\tstruct mosquitto__callback *persist_base_msg_delete;\n\tstruct mosquitto__callback *persist_retain_msg_set;\n\tstruct mosquitto__callback *persist_retain_msg_delete;\n\tstruct mosquitto__callback *persist_will_add;\n\tstruct mosquitto__callback *persist_will_delete;\n};\n\n/* This is owned by mosquitto__config or mosquitto__listener, and only referred\n * to by other structs */\nstruct mosquitto__security_options {\n\t/* Any options that get added here also need considering\n\t * in config__read() with regards whether allow_anonymous\n\t * should be disabled when these options are set.\n\t */\n\tstruct mosquitto__unpwd *unpwd;\n\tstruct mosquitto__psk *psk_id;\n\tstruct acl_file_data acl_data;\n\tstruct password_file_data password_data;\n\tchar *psk_file;\n\tmosquitto_plugin_id_t **plugins;\n\tint plugin_count;\n\tint8_t allow_anonymous;\n\tbool allow_zero_length_clientid;\n\tchar *auto_id_prefix;\n\tuint16_t auto_id_prefix_len;\n\tstruct plugin__callbacks plugin_callbacks;\n\tmosquitto_plugin_id_t *pid; /* For registering as a \"plugin\" */\n};\n\n#if defined(WITH_EPOLL) || defined(WITH_KQUEUE)\nenum struct_ident {\n\tid_invalid = 0,\n\tid_listener = 1,\n\tid_client = 2,\n\tid_listener_ws = 3,\n};\n#endif\n\nstruct mosquitto__listener {\n\tuint16_t port;\n\tchar *host;\n\tchar *bind_interface;\n\tint max_connections;\n\tchar *mount_point;\n\tmosq_sock_t *socks;\n\tint sock_count;\n\tint client_count;\n\tenum mosquitto_protocol protocol;\n\tint socket_domain;\n\tbool use_username_as_clientid;\n\tuint8_t max_qos;\n\tuint16_t max_topic_alias;\n\tuint16_t max_topic_alias_broker;\n#ifdef WITH_TLS\n\tchar *cafile;\n\tchar *capath;\n\tchar *certfile;\n\tchar *keyfile;\n\tchar *tls_engine;\n\tchar *tls_engine_kpass_sha1;\n\tchar *ciphers;\n\tchar *ciphers_tls13;\n\tchar *psk_hint;\n\tSSL_CTX *ssl_ctx;\n\tchar *crlfile;\n\tchar *tls_version;\n\tbool use_identity_as_username;\n\tbool use_subject_as_username;\n\tbool require_certificate;\n\tbool disable_client_cert_date_checks;\n\tenum mosquitto__keyform tls_keyform;\n#endif\n#ifdef WITH_WEBSOCKETS\n#  if WITH_WEBSOCKETS == WS_IS_LWS\n\tstruct lws_context *ws_context;\n\tbool ws_in_init;\n\tstruct lws_protocols *ws_protocol;\n#  endif\n\tchar **ws_origins;\n\tint ws_origin_count;\n#endif\n#if defined(WITH_WEBSOCKETS) || defined(WITH_HTTP_API)\n\tchar *http_dir;\n#endif\n\tstruct mosquitto__security_options *security_options;\n#ifdef WITH_UNIX_SOCKETS\n\tchar *unix_socket_path;\n#endif\n\tbool disable_protocol_v3;\n\tbool disable_protocol_v4;\n\tbool disable_protocol_v5;\n\tint enable_proxy_protocol;\n\tbool proxy_protocol_v2_require_tls;\n#ifdef WITH_HTTP_API\n\tstruct MHD_Daemon *mhd;\n#endif\n};\n\n\nstruct mosquitto__listener_sock {\n#if defined(WITH_EPOLL) || defined(WITH_KQUEUE)\n\t/* This *must* be the first element in the struct. */\n\tint ident;\n#endif\n\tmosq_sock_t sock;\n\tstruct mosquitto__listener *listener;\n};\n\n\n/* Callbacks belonging to a specific plugin\n * Doesn't include MOSQ_EVT_CONTROL events.\n */\nstruct plugin_own_callback {\n\tstruct plugin_own_callback *next, *prev;\n\tMOSQ_FUNC_generic_callback cb_func;\n\tenum mosquitto_plugin_event event;\n};\n\nstruct mosquitto_plugin_id_t {\n\tstruct mosquitto__plugin_config config;\n\tstruct mosquitto__plugin_lib lib;\n\tstruct mosquitto__listener *listener;\n\tchar *plugin_name;\n\tchar *plugin_version;\n\tstruct control_endpoint *control_endpoints;\n\tstruct plugin_own_callback *own_callbacks;\n\tstruct timespec next_tick;\n};\n\nstruct mosquitto__config {\n\tbool allow_duplicate_messages;\n\tint autosave_interval;\n\tbool autosave_on_changes;\n\tbool check_retain_source;\n\tchar *clientid_prefixes;\n\tbool connection_messages;\n\tuint16_t cmd_port[CMD_PORT_LIMIT];\n\tint cmd_port_count;\n\tbool daemon;\n\tbool test_configuration;\n\tbool enable_control_api;\n\tint global_max_clients;\n\tint global_max_connections;\n\tstruct mosquitto__listener *default_listener; /* Points to one of `listeners` */\n\tstruct mosquitto__listener *listeners;\n\tint listener_count;\n\tbool local_only;\n\tunsigned int log_dest;\n\tint log_facility;\n\tunsigned int log_type;\n\tbool log_timestamp;\n\tchar *log_timestamp_format;\n\tchar *log_file;\n\tFILE *log_fptr;\n\tsize_t max_inflight_bytes;\n\tsize_t max_queued_bytes;\n\tint max_queued_messages;\n\tuint32_t max_packet_size;\n\tuint32_t message_size_limit;\n\tuint16_t max_inflight_messages;\n\tuint16_t max_keepalive;\n\tuint8_t max_qos;\n\tuint32_t packet_max_connect;\n\tuint32_t packet_max_simple;\n\tuint32_t packet_max_sub;\n\tuint32_t packet_max_auth;\n\tbool persistence;\n\tchar *persistence_location;\n\tchar *persistence_file;\n\tchar *persistence_filepath;\n\ttime_t persistent_client_expiration;\n\tchar *pid_file;\n\tbool queue_qos0_messages;\n\tbool per_listener_settings;\n\tbool retain_available;\n\tint retain_expiry_interval;\n\tbool set_tcp_nodelay;\n\tint sys_interval;\n\tbool upgrade_outgoing_qos;\n\tchar *user;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tint websockets_log_level;\n#endif\n\tuint16_t packet_buffer_size;\n#ifdef WITH_BRIDGE\n\tstruct mosquitto__bridge **bridges;\n\tint bridge_count;\n#endif\n\tstruct mosquitto__security_options security_options;\n};\n\n\nstruct mosquitto__subshared {\n\tUT_hash_handle hh;\n\tstruct mosquitto__subleaf *subs;\n\tchar name[];\n};\n\nstruct mosquitto__subhier {\n\tUT_hash_handle hh;\n\tstruct mosquitto__subhier *parent;\n\tstruct mosquitto__subhier *children;\n\tstruct mosquitto__subleaf *subs;\n\tstruct mosquitto__subshared *shared;\n\tuint16_t topic_len;\n\tchar topic[];\n};\n\nstruct mosquitto__subleaf {\n\tstruct mosquitto__subleaf *prev;\n\tstruct mosquitto__subleaf *next;\n\tstruct mosquitto *context;\n\tstruct mosquitto__subhier *hier;\n\tstruct mosquitto__subshared *shared;\n\tuint32_t identifier;\n\tuint8_t subscription_options;\n\tchar topic_filter[];\n};\n\nstruct sub__token {\n\tstruct sub__token *next;\n\tuint16_t topic_len;\n\tchar topic[];\n};\n\nstruct mosquitto__retainhier {\n\tUT_hash_handle hh;\n\tstruct mosquitto__retainhier *parent;\n\tstruct mosquitto__retainhier *children;\n\tstruct mosquitto__base_msg *retained;\n\tuint16_t topic_len;\n\tchar topic[];\n};\n\nstruct mosquitto__base_msg {\n\tUT_hash_handle hh;\n\tstruct mosquitto_base_msg data;\n\tstruct mosquitto__listener *source_listener;\n\tchar **dest_ids;\n\tint dest_id_count;\n\tint ref_count;\n\tenum mosquitto_msg_origin origin;\n\tbool stored;\n};\n\nstruct mosquitto__client_msg {\n\tstruct mosquitto_client_msg data;\n\tstruct mosquitto__client_msg *prev;\n\tstruct mosquitto__client_msg *next;\n\tstruct mosquitto__base_msg *base_msg;\n};\n\n\nstruct mosquitto__psk {\n\tUT_hash_handle hh;\n\tchar *username;\n\tchar *password;\n};\n\nstruct mosquitto__message_v5 {\n\tstruct mosquitto__message_v5 *next, *prev;\n\tchar *topic;\n\tvoid *payload;\n\tmosquitto_property *properties;\n\t/* clientid is used only by mosquitto_broker_publish*() to indicate\n\t   this message is for a specific client. */\n\tchar *clientid;\n\tint payloadlen;\n\tint qos;\n\tbool retain;\n};\n\nstruct mosquitto_db {\n\tdbid_t last_db_id;\n\tuint64_t node_id_shifted;\n\tstruct mosquitto__subhier *normal_subs;\n\tstruct mosquitto__subhier *shared_subs;\n\tstruct mosquitto__retainhier *retains;\n\tstruct mosquitto *contexts_by_id;\n\tstruct mosquitto *contexts_by_sock;\n\tstruct mosquitto *contexts_by_id_delayed_auth;\n\tstruct mosquitto *contexts_for_free;\n\tmosquitto_plugin_id_t **plugins;\n\tint plugin_count;\n#ifdef WITH_BRIDGE\n\tstruct mosquitto **bridges;\n\tint bridge_count;\n#endif\n\tstruct clientid__index_hash *clientid_index_hash;\n\tstruct mosquitto__base_msg *msg_store;\n\ttime_t now_s; /* Monotonic clock, where possible */\n\ttime_t now_real_s; /* Read clock, for measuring session/message expiry */\n\tuint64_t node_id; /* for unique db ids */\n\ttime_t next_event_ms; /* for mux timeout */\n\tint msg_store_count;\n\tunsigned long msg_store_bytes;\n\tchar *config_file;\n\tstruct mosquitto__config *config;\n\tbool quiet;\n\tbool verbose;\n#ifdef WITH_SYS_TREE\n\tint subscription_count;\n\tint shared_subscription_count;\n\tint retained_count;\n#endif\n\tint persistence_changes;\n\tstruct mosquitto *ll_for_free;\n#ifdef WITH_EPOLL\n\tint epollfd;\n#endif\n#ifdef WITH_KQUEUE\n\tint kqueuefd;\n#endif\n\tstruct mosquitto__message_v5 *plugin_msgs;\n#ifdef WITH_TLS\n\t/* tls_keylog can't be in the config struct because it is used\n\t   before the config is allocated. Config probably\n\t   shouldn't be separately allocated. */\n\tchar *tls_keylog;\n#endif\n\tbool shutdown;\n};\n\nenum mosquitto__bridge_direction {\n\tbd_out = 0,\n\tbd_in = 1,\n\tbd_both = 2,\n};\n\nenum mosquitto_bridge_start_type {\n\tbst_automatic = 0,\n\tbst_lazy = 1,\n\tbst_manual = 2,\n\tbst_once = 3,\n};\n\nenum mosquitto_bridge_reload_type {\n\tbrt_lazy = 0,\n\tbrt_immediate = 1,\n};\n\nstruct mosquitto__bridge_topic {\n\tstruct mosquitto__bridge_topic *next;\n\tchar *topic;\n\tchar *local_prefix;\n\tchar *remote_prefix;\n\tchar *local_topic; /* topic prefixed with local_prefix */\n\tchar *remote_topic; /* topic prefixed with remote_prefix */\n\tenum mosquitto__bridge_direction direction;\n\tuint8_t qos;\n};\n\nstruct bridge_address {\n\tchar *address;\n\tuint16_t port;\n};\n\nstruct mosquitto__bridge {\n\tchar *name;\n\tstruct bridge_address *addresses;\n\tint cur_address;\n\tint address_count;\n\ttime_t primary_retry;\n\tmosq_sock_t primary_retry_sock;\n\tbool round_robin;\n\tbool try_private;\n\tbool try_private_accepted;\n\tbool clean_start;\n\tint8_t clean_start_local;\n\tuint16_t keepalive;\n\tunsigned int tcp_keepalive_idle;\n\tunsigned int tcp_keepalive_interval;\n\tunsigned int tcp_keepalive_counter;\n#ifdef WITH_TCP_USER_TIMEOUT\n\tint tcp_user_timeout;\n#endif\n\tstruct mosquitto__bridge_topic *topics;\n\tint topic_count;\n\tbool topic_remapping;\n\tenum mosquitto__protocol protocol_version;\n\ttime_t restart_t;\n\ttime_t connected_at;\n\tchar *remote_clientid;\n\tchar *remote_username;\n\tchar *remote_password;\n\tchar *local_clientid;\n\tchar *local_username;\n\tchar *local_password;\n\tchar *notification_topic;\n\tchar *bind_address;\n\tbool notifications;\n\tbool notifications_local_only;\n\tenum mosquitto_bridge_start_type start_type;\n\tint idle_timeout;\n\tint restart_timeout;\n\tint backoff_base;\n\tint backoff_cap;\n\tint stable_connection_period;\n\tint threshold;\n\tuint32_t maximum_packet_size;\n\tuint32_t session_expiry_interval;\n\tuint16_t receive_maximum;\n\tbool lazy_reconnect;\n\tbool attempt_unsubscribe;\n\tbool initial_notification_done;\n\tbool outgoing_retain;\n\tenum mosquitto_bridge_reload_type reload_type;\n\tuint16_t max_topic_alias;\n#ifdef WITH_TLS\n\tbool tls_insecure;\n\tbool tls_ocsp_required;\n\tbool tls_use_os_certs;\n\tchar *tls_cafile;\n\tchar *tls_capath;\n\tchar *tls_certfile;\n\tchar *tls_keyfile;\n\tchar *tls_version;\n\tchar *tls_alpn;\n\tchar *tls_ciphers;\n\tchar *tls_13_ciphers;\n#  ifdef FINAL_WITH_TLS_PSK\n\tchar *tls_psk_identity;\n\tchar *tls_psk;\n#  endif\n#endif\n};\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\nstruct libws_mqtt_hack {\n\tchar *http_dir;\n\tstruct mosquitto__listener *listener;\n};\n\nstruct libws_mqtt_data {\n\tstruct mosquitto *mosq;\n};\n#endif\n\n#include <net_mosq.h>\n\n\nstruct control_endpoint {\n\tstruct control_endpoint *next, *prev;\n\tchar topic[];\n};\n\nextern struct mosquitto_db db;\n\n/* ============================================================\n * Main functions\n * ============================================================ */\nint mosquitto_main_loop(struct mosquitto__listener_sock *listensock, int listensock_count);\nvoid loop__update_next_event(time_t new_ms);\n\n/* ============================================================\n * Config functions\n * ============================================================ */\n/* Initialise config struct to default values. */\nvoid config__init(struct mosquitto__config *config);\n#ifdef WITH_BRIDGE\nvoid config__bridge_cleanup(struct mosquitto__bridge *bridge);\n#endif\n/* Parse command line options into config. */\nint config__parse_args(struct mosquitto__config *config, int argc, char *argv[]);\n/* Read configuration data from config->config_file into config.\n * If reload is true, don't process config options that shouldn't be reloaded (listeners etc)\n * Returns 0 on success, 1 if there is a configuration error or if a file cannot be opened.\n */\nint config__read(struct mosquitto__config *config, bool reload);\n/* Free all config data. */\nvoid config__cleanup(struct mosquitto__config *config);\nint config__get_dir_files(const char *include_dir, char ***files, int *file_count);\n\n/* ============================================================\n * Server send functions\n * ============================================================ */\nint send__connack(struct mosquitto *context, uint8_t ack, uint8_t reason_code, const mosquitto_property *properties);\nint send__suback(struct mosquitto *context, uint16_t mid, uint32_t payloadlen, const void *payload);\nint send__unsuback(struct mosquitto *context, uint16_t mid, int reason_code_count, uint8_t *reason_codes, const mosquitto_property *properties);\nint send__auth(struct mosquitto *context, uint8_t reason_code, const void *auth_data, uint16_t auth_data_len);\n\n/* ============================================================\n * Network functions\n * ============================================================ */\nvoid net__broker_init(void);\nvoid net__broker_cleanup(void);\nstruct mosquitto *net__socket_accept(struct mosquitto__listener_sock *listensock);\nint net__socket_listen(struct mosquitto__listener *listener);\nint net__socket_get_address(mosq_sock_t sock, char *buf, size_t len, uint16_t *remote_address);\nint net__tls_load_verify(struct mosquitto__listener *listener);\nint net__tls_server_ctx(struct mosquitto__listener *listener);\nint net__load_certificates(struct mosquitto__listener *listener);\n\n/* ============================================================\n * Read handling functions\n * ============================================================ */\nint handle__packet(struct mosquitto *context);\nint handle__connack(struct mosquitto *context);\nint handle__connect(struct mosquitto *context);\nint handle__disconnect(struct mosquitto *context);\nint handle__publish(struct mosquitto *context);\nint handle__subscribe(struct mosquitto *context);\nint handle__unsubscribe(struct mosquitto *context);\nint handle__auth(struct mosquitto *context);\n\n/* ============================================================\n * Database handling\n * ============================================================ */\nint db__open(struct mosquitto__config *config);\nint db__close(void);\n#ifdef WITH_PERSISTENCE\nint persist__backup(bool shutdown);\nint persist__restore(void);\n#endif\n/* Return the number of in-flight messages in count. */\nint db__message_count(int *count);\nint db__message_delete_outgoing(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_state expect_state, int qos);\nint db__message_insert_outgoing(struct mosquitto *context, uint64_t cmsg_id, uint16_t mid, uint8_t qos, bool retain, struct mosquitto__base_msg *base_msg, uint32_t subscription_identifier, bool update, bool persist);\nint db__message_insert_incoming(struct mosquitto *context, uint64_t cmsg_id, struct mosquitto__base_msg *base_msg, bool persist);\nint db__message_remove_incoming(struct mosquitto *context, uint16_t mid);\nint db__message_release_incoming(struct mosquitto *context, uint16_t mid);\nint db__message_update_outgoing(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_state state, int qos, bool persist);\nvoid db__message_dequeue_first(struct mosquitto *context, struct mosquitto_msg_data *msg_data);\nint db__messages_delete(struct mosquitto *context, bool force_free);\nint db__messages_delete_incoming(struct mosquitto *context);\nint db__messages_delete_outgoing(struct mosquitto *context);\nint db__messages_easy_queue(struct mosquitto *context, const char *topic, uint8_t qos, uint32_t payloadlen, const void *payload, int retain, uint32_t message_expiry_interval, mosquitto_property **properties);\nint db__message_store(const struct mosquitto *source, struct mosquitto__base_msg *base_msg, uint32_t *message_expiry_interval, enum mosquitto_msg_origin origin);\nint db__message_store_find(struct mosquitto *context, uint16_t mid, struct mosquitto__client_msg **client_msg);\nint db__msg_store_add(struct mosquitto__base_msg *base_msg);\nvoid db__msg_store_remove(struct mosquitto__base_msg *base_msg, bool notify);\nvoid db__msg_store_ref_inc(struct mosquitto__base_msg *base_msg);\nvoid db__msg_store_ref_dec(struct mosquitto__base_msg **base_msg);\nvoid db__msg_store_clean(void);\nvoid db__msg_store_compact(void);\nvoid db__msg_store_free(struct mosquitto__base_msg *base_msg);\nint db__message_reconnect_reset(struct mosquitto *context);\nbool db__ready_for_flight(struct mosquitto *context, enum mosquitto_msg_direction dir, int qos);\nbool db__ready_for_queue(struct mosquitto *context, int qos, struct mosquitto_msg_data *msg_data);\nvoid sys_tree__init(void);\nvoid sys_tree__update(bool force);\nint db__message_write_inflight_out_all(struct mosquitto *context);\nint db__message_write_inflight_out_latest(struct mosquitto *context);\nint db__message_write_queued_out(struct mosquitto *context);\nint db__message_write_queued_in(struct mosquitto *context);\nvoid db__msg_add_to_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *msg);\nvoid db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto__client_msg *msg);\nuint64_t db__new_msg_id(void);\nvoid db__expire_all_messages(struct mosquitto *context);\nvoid db__check_acl_of_all_messages(struct mosquitto *context);\n\n/* ============================================================\n * Subscription functions\n * ============================================================ */\nint sub__init(void);\nint sub__add(struct mosquitto *context, const struct mosquitto_subscription *sub);\nint sub__remove(struct mosquitto *context, const char *sub, uint8_t *reason);\nvoid sub__tree_print(struct mosquitto__subhier *root, int level);\nint sub__clean_session(struct mosquitto *context);\nint sub__messages_queue(const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto__base_msg **base_msg);\nint sub__topic_tokenise(const char *subtopic, char **local_sub, char ***topics, const char **sharename);\nvoid sub__topic_tokens_free(struct sub__token *tokens);\n\n/* ============================================================\n * Context functions\n * ============================================================ */\nstruct mosquitto *context__init(void);\nint context__init_sock(struct mosquitto *context, mosq_sock_t sock, bool get_address);\nvoid context__cleanup(struct mosquitto *context, bool force_free);\nvoid context__disconnect(struct mosquitto *context, int reason);\nvoid context__add_to_disused(struct mosquitto *context);\nvoid context__free_disused(void);\nvoid context__send_will(struct mosquitto *context);\nvoid context__add_to_by_id(struct mosquitto *context);\nvoid context__remove_from_by_id(struct mosquitto *context);\n\nint connect__on_authorised(struct mosquitto *context, void *auth_data_out, uint16_t auth_data_out_len);\n\n\n/* ============================================================\n * Control functions\n * ============================================================ */\n#ifdef WITH_CONTROL\nint control__process(struct mosquitto *context, struct mosquitto__base_msg *base_msg);\nvoid control__cleanup(void);\n#endif\nint control__register_callback(mosquitto_plugin_id_t *pid, MOSQ_FUNC_generic_callback cb_func, const char *topic, void *userdata);\nint control__unregister_callback(mosquitto_plugin_id_t *pid, MOSQ_FUNC_generic_callback cb_func, const char *topic);\nvoid control__unregister_all_callbacks(mosquitto_plugin_id_t *identifier);\n\n\n/* ============================================================\n * Logging functions\n * ============================================================ */\nint log__init(struct mosquitto__config *config);\nint log__close(struct mosquitto__config *config);\nvoid log__internal(const char *fmt, ...) __attribute__((format(printf, 1, 2)));\n\n/* ============================================================\n * Bridge functions\n * ============================================================ */\n#ifdef WITH_BRIDGE\nvoid bridge__start_all(void);\nvoid bridge__reload(void);\nvoid bridge__db_cleanup(void);\nvoid bridge__cleanup(struct mosquitto *context);\nint bridge__connect(struct mosquitto *context);\n#if defined(__GLIBC__) && defined(WITH_ADNS)\nint bridge__connect_step3(struct mosquitto *context);\n#endif\nint bridge__on_connect(struct mosquitto *context);\nvoid bridge_check(void);\nint bridge__register_local_connections(void);\nint bridge__add_topic(struct mosquitto__bridge *bridge, const char *topic, enum mosquitto__bridge_direction direction, uint8_t qos, const char *local_prefix, const char *remote_prefix);\nvoid bridge__cleanup_topics(struct mosquitto__bridge *bridge);\nint bridge__remap_topic_in(struct mosquitto *context, char **topic);\n#endif\n\n/* ============================================================\n * IO multiplex related functions\n * ============================================================ */\nint mux__init(void);\nint mux__add_listeners(struct mosquitto__listener_sock *listensock, int listensock_count);\nint mux__delete_listeners(struct mosquitto__listener_sock *listensock, int listensock_count);\nint mux__loop_prepare(void);\nint mux__new(struct mosquitto *context);\nint mux__add_out(struct mosquitto *context);\nint mux__remove_out(struct mosquitto *context);\nint mux__delete(struct mosquitto *context);\nint mux__wait(void);\nint mux__handle(struct mosquitto__listener_sock *listensock, int listensock_count);\nint mux__cleanup(void);\n\n/* ============================================================\n * Listener related functions\n * ============================================================ */\nextern struct mosquitto__listener_sock *g_listensock;\nextern int g_listensock_count;\n\nvoid listener__set_defaults(struct mosquitto__listener *listener);\nvoid listeners__reload_all_certificates(void);\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\nvoid listeners__add_websockets(struct lws_context *ws_context, mosq_sock_t fd);\n#endif\nint listeners__start(void);\nvoid listeners__stop(void);\n\n/* ============================================================\n * Plugin related functions\n * ============================================================ */\nint plugin__load_v5(mosquitto_plugin_id_t *plugin, void *lib);\nint plugin__load_v4(mosquitto_plugin_id_t *plugin, void *lib);\nint plugin__load_v3(mosquitto_plugin_id_t *plugin, void *lib);\nint plugin__load_v2(mosquitto_plugin_id_t *plugin, void *lib);\nint acl__pre_check(mosquitto_plugin_id_t *plugin, struct mosquitto *context, int access);\n\nvoid LIB_ERROR(void);\nvoid plugin__handle_connect(struct mosquitto *context);\nvoid plugin__handle_disconnect(struct mosquitto *context, int reason);\nvoid plugin__handle_client_offline(struct mosquitto *context, int reason);\nint plugin__handle_message_in(struct mosquitto *context, struct mosquitto_base_msg *base_msg);\nint plugin__handle_message_out(struct mosquitto *context, struct mosquitto_base_msg *base_msg);\nint plugin__handle_subscribe(struct mosquitto *context, struct mosquitto_subscription *sub);\nint plugin__handle_unsubscribe(struct mosquitto *context, struct mosquitto_subscription *sub);\nint plugin__handle_reload(void);\nvoid plugin__handle_tick(void);\nint plugin__callback_unregister_all(mosquitto_plugin_id_t *identifier);\nvoid plugin_persist__handle_restore(void);\nvoid plugin_persist__handle_client_add(struct mosquitto *context);\nvoid plugin_persist__handle_client_delete(struct mosquitto *context);\nvoid plugin_persist__handle_client_update(struct mosquitto *context);\nvoid plugin_persist__handle_subscription_add(struct mosquitto *context, const struct mosquitto_subscription *sub);\nvoid plugin_persist__handle_subscription_delete(struct mosquitto *context, char *sub);\nvoid plugin_persist__handle_client_msg_add(struct mosquitto *context, const struct mosquitto__client_msg *cmsg);\nvoid plugin_persist__handle_client_msg_delete(struct mosquitto *context, const struct mosquitto__client_msg *cmsg);\nvoid plugin_persist__handle_client_msg_update(struct mosquitto *context, const struct mosquitto__client_msg *cmsg);\nvoid plugin_persist__handle_base_msg_add(struct mosquitto__base_msg *base_msg);\nvoid plugin_persist__handle_base_msg_delete(struct mosquitto__base_msg *base_msg);\nvoid plugin_persist__handle_retain_msg_set(struct mosquitto__base_msg *base_msg);\nvoid plugin_persist__handle_retain_msg_delete(struct mosquitto__base_msg *base_msg);\nvoid plugin_persist__handle_will_add(struct mosquitto *context);\nvoid plugin_persist__handle_will_delete(struct mosquitto *context);\n\n/* ============================================================\n * Property related functions\n * ============================================================ */\nint keepalive__init(void);\nvoid keepalive__cleanup(void);\nint keepalive__add(struct mosquitto *context);\nvoid keepalive__check(void);\nint keepalive__remove(struct mosquitto *context);\nint keepalive__update(struct mosquitto *context);\n\n/* ============================================================\n * Property related functions\n * ============================================================ */\nint property__process_connect(struct mosquitto *context, mosquitto_property **props);\nint property__process_will(struct mosquitto *context, struct mosquitto_message_all *msg, mosquitto_property **props);\nint property__process_publish(struct mosquitto__base_msg *base_msg, mosquitto_property **props, int *topic_alias, uint32_t *message_expiry_interval, bool is_bridge);\nint property__process_disconnect(struct mosquitto *context, mosquitto_property **props);\n\n/* ============================================================\n * Retain tree related functions\n * ============================================================ */\nint retain__init(void);\nvoid retain__clean(struct mosquitto__retainhier **retainhier);\nint retain__queue(struct mosquitto *context, const struct mosquitto_subscription *sub);\nint retain__store(const char *topic, struct mosquitto__base_msg *base_msg, char **split_topics, bool persist);\nvoid retain__expiry_check(void);\nvoid retain__expire(struct mosquitto__retainhier **retainhier);\n\n/* ============================================================\n * Security related functions\n * ============================================================ */\nint acl__find_acls(struct mosquitto *context);\nint plugin__load_all(void);\nint plugin__unload_all(void);\nint config__plugin_add_secopt(mosquitto_plugin_id_t *plugin, struct mosquitto__security_options *security_options);\n\nint mosquitto_security_init(bool reload);\nint mosquitto_security_cleanup(bool reload);\nint mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void *payload, uint8_t qos, bool retain, mosquitto_property *properties, int access);\nint mosquitto_basic_auth(struct mosquitto *context);\nint mosquitto_psk_key_get(struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len);\n\nint mosquitto_security_init_default(void);\nint mosquitto_security_apply_default(void);\nint mosquitto_security_cleanup_default(void);\nint mosquitto_psk_key_get_default(struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len);\nint broker_acl_file__init(void);\nvoid broker_acl_file__cleanup(void);\nint broker_password_file__init(void);\nvoid broker_password_file__cleanup(void);\nint psk_file__init(void);\nint psk_file__cleanup(void);\n\nint mosquitto_security_auth_start(struct mosquitto *context, bool reauth, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len);\nint mosquitto_security_auth_continue(struct mosquitto *context, const void *data_in, uint16_t data_len, void **data_out, uint16_t *data_out_len);\n\nvoid unpwd__free_item(struct mosquitto__unpwd **unpwd, struct mosquitto__unpwd *item);\n\n/* ============================================================\n * Session expiry\n * ============================================================ */\nint session_expiry__add(struct mosquitto *context);\nint session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time);\nvoid session_expiry__remove(struct mosquitto *context);\nvoid session_expiry__remove_all(void);\nvoid session_expiry__check(void);\nvoid session_expiry__send_all(void);\n\n/* ============================================================\n * Signals\n * ============================================================ */\nvoid signal__setup(void);\nint signal__flag_check(void);\n\n/* ============================================================\n * Window service and signal related functions\n * ============================================================ */\n#if defined(WIN32) || defined(__CYGWIN__)\nvoid service_install(char *name);\nvoid service_uninstall(char *name);\nvoid service_run(char *name);\n\n#ifdef WIN32\nDWORD WINAPI SigThreadProc(void *data);\n#endif\n#endif\n\n/* ============================================================\n * Watchdog\n * ============================================================ */\nvoid watchdog__init(void);\nvoid watchdog__check(void);\n\n/* ============================================================\n * Websockets related functions\n * ============================================================ */\n#ifdef WITH_WEBSOCKETS\n#  if WITH_WEBSOCKETS == WS_IS_LWS\nvoid mosq_websockets_init(struct mosquitto__listener *listener, const struct mosquitto__config *conf);\n#  endif\nint http__context_init(struct mosquitto *context);\nint http__context_cleanup(struct mosquitto *context);\nint http__read(struct mosquitto *context);\n#endif\nvoid do_disconnect(struct mosquitto *context, int reason);\n\n/* ============================================================\n * PROXY related functions\n * ============================================================ */\nint proxy_v2__read(struct mosquitto *context);\nint proxy_v1__read(struct mosquitto *context);\n\n/* ============================================================\n * Will delay\n * ============================================================ */\nint will_delay__add(struct mosquitto *context);\nvoid will_delay__check(void);\nvoid will_delay__send_all(void);\nvoid will_delay__remove(struct mosquitto *mosq);\n\n/* ============================================================\n * HTTP Info\n * ============================================================ */\nint http_api__start_local(struct mosquitto__listener *listener);\nint http_api__start(struct mosquitto__listener *listener);\nvoid http_api__stop(struct mosquitto__listener *listener);\n\n\n/* ============================================================\n * Other\n * ============================================================ */\n#ifdef WITH_XTREPORT\nvoid xtreport(void);\n#endif\n\nvoid broker_control__init(void);\nvoid broker_control__cleanup(void);\nvoid broker_control__reload(void);\n\n#endif\n"
  },
  {
    "path": "src/mux.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n   Tatsuzo Osawa - Add epoll.\n*/\n\n#include \"mux.h\"\n\n\nint mux__init(void)\n{\n#ifdef WITH_EPOLL\n\treturn mux_epoll__init();\n#elif defined(WITH_KQUEUE)\n\treturn mux_kqueue__init();\n#else\n\treturn mux_poll__init();\n#endif\n}\n\n\nint mux__add_listeners(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n#ifdef WITH_EPOLL\n\treturn mux_epoll__add_listeners(listensock, listensock_count);\n#elif defined(WITH_KQUEUE)\n\treturn mux_kqueue__add_listeners(listensock, listensock_count);\n#else\n\treturn mux_poll__add_listeners(listensock, listensock_count);\n#endif\n}\n\n\nint mux__delete_listeners(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n#ifdef WITH_EPOLL\n\treturn mux_epoll__delete_listeners(listensock, listensock_count);\n#elif defined(WITH_KQUEUE)\n\treturn mux_kqueue__delete_listeners(listensock, listensock_count);\n#else\n\treturn mux_poll__delete_listeners(listensock, listensock_count);\n#endif\n}\n\n\nint mux__add_out(struct mosquitto *context)\n{\n#ifdef WITH_EPOLL\n\treturn mux_epoll__add_out(context);\n#elif defined(WITH_KQUEUE)\n\treturn mux_kqueue__add_out(context);\n#else\n\treturn mux_poll__add_out(context);\n#endif\n}\n\n\nint mux__remove_out(struct mosquitto *context)\n{\n#ifdef WITH_EPOLL\n\treturn mux_epoll__remove_out(context);\n#elif defined(WITH_KQUEUE)\n\treturn mux_kqueue__remove_out(context);\n#else\n\treturn mux_poll__remove_out(context);\n#endif\n}\n\n\nint mux__new(struct mosquitto *context)\n{\n#ifdef WITH_EPOLL\n\treturn mux_epoll__new(context);\n#elif defined(WITH_KQUEUE)\n\treturn mux_kqueue__new(context);\n#else\n\treturn mux_poll__new(context);\n#endif\n}\n\n\nint mux__delete(struct mosquitto *context)\n{\n#ifdef WITH_EPOLL\n\treturn mux_epoll__delete(context);\n#elif defined(WITH_KQUEUE)\n\treturn mux_kqueue__delete(context);\n#else\n\treturn mux_poll__delete(context);\n#endif\n}\n\n\nint mux__handle(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n#ifdef WITH_EPOLL\n\tUNUSED(listensock);\n\tUNUSED(listensock_count);\n\treturn mux_epoll__handle();\n#elif defined(WITH_KQUEUE)\n\tUNUSED(listensock);\n\tUNUSED(listensock_count);\n\treturn mux_kqueue__handle();\n#else\n\treturn mux_poll__handle(listensock, listensock_count);\n#endif\n}\n\n\nint mux__cleanup(void)\n{\n#ifdef WITH_EPOLL\n\treturn mux_epoll__cleanup();\n#elif defined(WITH_KQUEUE)\n\treturn mux_kqueue__cleanup();\n#else\n\treturn mux_poll__cleanup();\n#endif\n}\n"
  },
  {
    "path": "src/mux.h",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MUX_H\n#define MUX_H\n\n#include \"mosquitto_broker_internal.h\"\n\nint mux_epoll__init(void);\nint mux_epoll__add_listeners(struct mosquitto__listener_sock *listensock, int listensock_count);\nint mux_epoll__delete_listeners(struct mosquitto__listener_sock *listensock, int listensock_count);\nint mux_epoll__new(struct mosquitto *context);\nint mux_epoll__add_out(struct mosquitto *context);\nint mux_epoll__remove_out(struct mosquitto *context);\nint mux_epoll__delete(struct mosquitto *context);\nint mux_epoll__handle(void);\nint mux_epoll__cleanup(void);\n\nint mux_kqueue__init(void);\nint mux_kqueue__add_listeners(struct mosquitto__listener_sock *listensock, int listensock_count);\nint mux_kqueue__delete_listeners(struct mosquitto__listener_sock *listensock, int listensock_count);\nint mux_kqueue__new(struct mosquitto *context);\nint mux_kqueue__add_out(struct mosquitto *context);\nint mux_kqueue__remove_out(struct mosquitto *context);\nint mux_kqueue__delete(struct mosquitto *context);\nint mux_kqueue__handle(void);\nint mux_kqueue__cleanup(void);\n\nint mux_poll__init(void);\nint mux_poll__add_listeners(struct mosquitto__listener_sock *listensock, int listensock_count);\nint mux_poll__delete_listeners(struct mosquitto__listener_sock *listensock, int listensock_count);\nint mux_poll__new(struct mosquitto *context);\nint mux_poll__add_out(struct mosquitto *context);\nint mux_poll__remove_out(struct mosquitto *context);\nint mux_poll__delete(struct mosquitto *context);\nint mux_poll__handle(struct mosquitto__listener_sock *listensock, int listensock_count);\nint mux_poll__cleanup(void);\n\n#endif\n"
  },
  {
    "path": "src/mux_epoll.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n   Tatsuzo Osawa - Add epoll.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_EPOLL\n\n#ifndef WIN32\n#  define _GNU_SOURCE\n#endif\n\n#ifndef WIN32\n#  include <sys/epoll.h>\n#  define MAX_EVENTS 1000\n#endif\n\n#include <errno.h>\n#include <signal.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mux.h\"\n#include \"packet_mosq.h\"\n#include \"util_mosq.h\"\n\nstatic void loop_handle_reads_writes(struct mosquitto *context, uint32_t events);\n\nstatic struct epoll_event ep_events[MAX_EVENTS];\n\n\nint mux_epoll__init(void)\n{\n\tmemset(&ep_events, 0, sizeof(struct epoll_event)*MAX_EVENTS);\n\n\tdb.epollfd = 0;\n\tif((db.epollfd = epoll_create(MAX_EVENTS)) == -1){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in epoll creating: %s\", strerror(errno));\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_epoll__add_listeners(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n\tfor(int i=0; i<listensock_count; i++){\n\t\tstruct epoll_event ev;\n\n\t\tmemset(&ev, 0, sizeof(struct epoll_event));\n\t\tev.data.ptr = &listensock[i];\n\t\tev.events = EPOLLIN;\n\t\tif(epoll_ctl(db.epollfd, EPOLL_CTL_ADD, listensock[i].sock, &ev) == -1){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in epoll initial registering: %s\", strerror(errno));\n\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_epoll__delete_listeners(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n\tfor(int i=0; i<listensock_count; i++){\n\t\tif(epoll_ctl(db.epollfd, EPOLL_CTL_DEL, listensock[i].sock, NULL) == -1){\n\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_epoll__add_out(struct mosquitto *context)\n{\n\tif(!(context->events & EPOLLOUT)){\n\t\tstruct epoll_event ev;\n\n\t\tmemset(&ev, 0, sizeof(struct epoll_event));\n\t\tev.data.ptr = context;\n\t\tev.events = EPOLLIN | EPOLLOUT;\n\t\tif(epoll_ctl(db.epollfd, EPOLL_CTL_MOD, context->sock, &ev) == -1){\n\t\t\tif((errno != ENOENT)||(epoll_ctl(db.epollfd, EPOLL_CTL_ADD, context->sock, &ev) == -1)){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Error in epoll re-registering to EPOLLOUT: %s\", strerror(errno));\n\t\t\t}\n\t\t}\n\t\tcontext->events = EPOLLIN | EPOLLOUT;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_epoll__remove_out(struct mosquitto *context)\n{\n\tif(context->events & EPOLLOUT){\n\t\tstruct epoll_event ev;\n\n\t\tmemset(&ev, 0, sizeof(struct epoll_event));\n\t\tev.data.ptr = context;\n\t\tev.events = EPOLLIN;\n\t\tif(epoll_ctl(db.epollfd, EPOLL_CTL_MOD, context->sock, &ev) == -1){\n\t\t\tif((errno != ENOENT)||(epoll_ctl(db.epollfd, EPOLL_CTL_ADD, context->sock, &ev) == -1)){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Error in epoll re-registering to EPOLLIN: %s\", strerror(errno));\n\t\t\t}\n\t\t}\n\t\tcontext->events = EPOLLIN;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_epoll__new(struct mosquitto *context)\n{\n\tstruct epoll_event ev;\n\n\tmemset(&ev, 0, sizeof(struct epoll_event));\n\tev.events = EPOLLIN;\n\tev.data.ptr = context;\n\tif(epoll_ctl(db.epollfd, EPOLL_CTL_ADD, context->sock, &ev) == -1){\n\t\tif(errno != EEXIST){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in epoll accepting: %s\", strerror(errno));\n\t\t}\n\t}\n\tcontext->events = EPOLLIN;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_epoll__delete(struct mosquitto *context)\n{\n\tif(context->sock != INVALID_SOCKET){\n\t\tif(epoll_ctl(db.epollfd, EPOLL_CTL_DEL, context->sock, NULL) == -1){\n\t\t\treturn 1;\n\t\t}\n\t}\n\treturn 0;\n}\n\n\nint mux_epoll__handle(void)\n{\n\tstruct epoll_event ev;\n\tstruct mosquitto *context;\n\tstruct mosquitto__listener_sock *listensock;\n\tint event_count;\n\n\tmemset(&ev, 0, sizeof(struct epoll_event));\n#if defined(WITH_WEBSOCKETS)\n\tevent_count = epoll_wait(db.epollfd, ep_events, MAX_EVENTS, 100);\n#else\n\tevent_count = epoll_wait(db.epollfd, ep_events, MAX_EVENTS, db.next_event_ms);\n#endif\n\n\tdb.now_s = mosquitto_time();\n\tdb.now_real_s = time(NULL);\n\n\tswitch(event_count){\n\t\tcase -1:\n\t\t\tif(errno != EINTR){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in epoll waiting: %s.\", strerror(errno));\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfor(int i=0; i<event_count; i++){\n\t\t\t\tcontext = ep_events[i].data.ptr;\n\t\t\t\tif(context->ident == id_client){\n\t\t\t\t\tloop_handle_reads_writes(context, ep_events[i].events);\n\t\t\t\t}else if(context->ident == id_listener){\n\t\t\t\t\tlistensock = ep_events[i].data.ptr;\n\n\t\t\t\t\tif(ep_events[i].events & (EPOLLIN | EPOLLPRI)){\n\t\t\t\t\t\twhile((context = net__socket_accept(listensock)) != NULL){\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\t\t\t}else if(context->ident == id_listener_ws){\n\t\t\t\t\t/* Nothing needs to happen here, because we always call lws_service in the loop.\n\t\t\t\t\t * The important point is we've been woken up for this listener. */\n#endif\n\t\t\t\t}\n\t\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_epoll__cleanup(void)\n{\n\t(void)close(db.epollfd);\n\tdb.epollfd = 0;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void loop_handle_reads_writes(struct mosquitto *context, uint32_t events)\n{\n\tint err;\n\tsocklen_t len;\n\tint rc;\n\n\tif(context->sock == INVALID_SOCKET){\n\t\treturn;\n\t}\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tif(context->wsi){\n\t\tstruct lws_pollfd wspoll;\n\t\twspoll.fd = context->sock;\n\t\twspoll.events = (int16_t)context->events;\n\t\twspoll.revents = (int16_t)events;\n\t\tlws_service_fd(lws_get_context(context->wsi), &wspoll);\n\t\treturn;\n\t}\n#endif\n\n\tif(events & EPOLLOUT\n#ifdef WITH_TLS\n\t\t\t|| context->want_write\n\t\t\t|| (context->ssl && context->state == mosq_cs_new)\n#endif\n\t\t\t){\n\n\t\tif(context->state == mosq_cs_connect_pending){\n\t\t\tlen = sizeof(int);\n\t\t\tif(!getsockopt(context->sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len)){\n\t\t\t\tif(err == 0){\n\t\t\t\t\tmosquitto__set_state(context, mosq_cs_new);\n#if defined(WITH_ADNS) && defined(WITH_BRIDGE)\n\t\t\t\t\tif(context->bridge){\n\t\t\t\t\t\tbridge__connect_step3(context);\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tdo_disconnect(context, MOSQ_ERR_CONN_LOST);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\trc = packet__write(context);\n\t\tif(rc){\n\t\t\tdo_disconnect(context, rc);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif(events & EPOLLIN\n#ifdef WITH_TLS\n\t\t\t|| (context->ssl && context->state == mosq_cs_new)\n#endif\n\t\t\t){\n\n\t\tdo{\n\t\t\tswitch(context->transport){\n\t\t\t\tcase mosq_t_tcp:\n\t\t\t\tcase mosq_t_ws:\n\t\t\t\t\trc = packet__read(context);\n\t\t\t\t\tbreak;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\t\tcase mosq_t_http:\n\t\t\t\t\trc = http__read(context);\n\t\t\t\t\tbreak;\n#endif\n#if !defined(WITH_WEBSOCKETS) || WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\t\t/* Not supported with LWS */\n\t\t\t\tcase mosq_t_proxy_v2:\n\t\t\t\t\trc = proxy_v2__read(context);\n\t\t\t\t\tbreak;\n\t\t\t\tcase mosq_t_proxy_v1:\n\t\t\t\t\trc = proxy_v1__read(context);\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tdefault:\n\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif(rc){\n\t\t\t\tdo_disconnect(context, rc);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}while(SSL_DATA_PENDING(context));\n\t}else{\n\t\tif(events & (EPOLLERR | EPOLLHUP)){\n\t\t\tdo_disconnect(context, MOSQ_ERR_CONN_LOST);\n\t\t\treturn;\n\t\t}\n\t}\n}\n#endif\n"
  },
  {
    "path": "src/mux_kqueue.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR EDL-1.0\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_KQUEUE\n\n#define MAX_EVENTS 1000\n\n#include <errno.h>\n#include <signal.h>\n#include <sys/event.h>\n#include <sys/socket.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"packet_mosq.h\"\n#include \"util_mosq.h\"\n\nstatic void loop_handle_reads_writes(struct mosquitto *context, short events);\n\nstatic struct kevent event_list[MAX_EVENTS];\n\n\nint mux_kqueue__init(void)\n{\n\tmemset(&event_list, 0, sizeof(struct kevent)*MAX_EVENTS);\n\n\tdb.kqueuefd = 0;\n\tif((db.kqueuefd = kqueue()) == -1){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in kqueue creating: %s\", strerror(errno));\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_kqueue__add_listeners(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n\tstruct kevent ev;\n\n\tmemset(&ev, 0, sizeof(struct kevent));\n\n\tfor(int i=0; i<listensock_count; i++){\n\t\tEV_SET(&ev, listensock[i].sock, EVFILT_READ, EV_ADD, 0, 0, &listensock[i]);\n\t\tif(kevent(db.kqueuefd, &ev, 1, NULL, 0, NULL) == -1){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in kqueue initial registering: %s\", strerror(errno));\n\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_kqueue__delete_listeners(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n\tstruct kevent ev;\n\n\tfor(int i=0; i<listensock_count; i++){\n\t\tEV_SET(&ev, listensock[i].sock, EVFILT_READ, EV_DELETE, 0, 0, &listensock[i]);\n\t\tif(kevent(db.kqueuefd, &ev, 1, NULL, 0, NULL) == -1){\n\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_kqueue__loop_setup(void)\n{\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_kqueue__add_out(struct mosquitto *context)\n{\n\tstruct kevent ev;\n\n\tif(context->events != EVFILT_WRITE){\n\t\tEV_SET(&ev, context->sock, EVFILT_WRITE, EV_ADD, 0, 0, context);\n\t\tif(kevent(db.kqueuefd, &ev, 1, NULL, 0, NULL) == -1){\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Error in kqueue re-registering to EVFILT_WRITE: %s\", strerror(errno));\n\t\t}\n\n\t\tcontext->events = EVFILT_WRITE;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_kqueue__remove_out(struct mosquitto *context)\n{\n\tstruct kevent ev;\n\n\tif(context->events == EVFILT_WRITE){\n\t\tEV_SET(&ev, context->sock, EVFILT_WRITE, EV_DELETE, 0, 0, context);\n\t\tif(kevent(db.kqueuefd, &ev, 1, NULL, 0, NULL) == -1){\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Error in kqueue removing EVFILT_WRITE: %s\", strerror(errno));\n\t\t}\n\n\t\tcontext->events = 0;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_kqueue__new(struct mosquitto *context)\n{\n\tstruct kevent ev;\n\n\tEV_SET(&ev, context->sock, EVFILT_READ, EV_ADD, 0, 0, context);\n\tif(kevent(db.kqueuefd, &ev, 1, NULL, 0, NULL) == -1){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in kqueue accepting: %s\", strerror(errno));\n\t}\n\n\tcontext->events = 0;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_kqueue__delete(struct mosquitto *context)\n{\n\tstruct kevent ev[2];\n\n\tif(context->sock != INVALID_SOCKET){\n\t\tEV_SET(&ev[0], context->sock, EVFILT_READ, EV_DELETE, 0, 0, context);\n\t\tEV_SET(&ev[1], context->sock, EVFILT_WRITE, EV_DELETE, 0, 0, context);\n\t\tif(kevent(db.kqueuefd, ev, 2, NULL, 0, NULL) == -1){\n\t\t\treturn 1;\n\t\t}\n\t}\n\treturn 0;\n}\n\n\nint mux_kqueue__handle(void)\n{\n\tstruct mosquitto *context;\n\tstruct mosquitto__listener_sock *listensock;\n\tint event_count;\n\tstruct timespec timeout;\n\n#ifdef WITH_WEBSOCKETS\n\ttimeout.tv_sec = 0;\n\ttimeout.tv_nsec = 100000000; /* 100 ms */\n#else\n\ttimeout.tv_sec = db.next_event_ms/1000;\n\ttimeout.tv_nsec = (db.next_event_ms - timeout.tv_sec*1000) * 1000000;\n#endif\n\n\tevent_count = kevent(db.kqueuefd,\n\t\t\tNULL, 0,\n\t\t\tevent_list, MAX_EVENTS,\n\t\t\t&timeout);\n\n\tdb.now_s = mosquitto_time();\n\tdb.now_real_s = time(NULL);\n\n\tswitch(event_count){\n\t\tcase -1:\n\t\t\tif(errno != EINTR){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in kqueue waiting: %s.\", strerror(errno));\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfor(int i=0; i<event_count; i++){\n\t\t\t\tcontext = event_list[i].udata;\n\t\t\t\tif(context->ident == id_client){\n\t\t\t\t\tloop_handle_reads_writes(context, event_list[i].filter);\n\t\t\t\t\tif(event_list[i].flags & (EV_EOF | EV_ERROR)){\n\t\t\t\t\t\tdo_disconnect(context, MOSQ_ERR_CONN_LOST);\n\t\t\t\t\t}\n\t\t\t\t}else if(context->ident == id_listener){\n\t\t\t\t\tlistensock = event_list[i].udata;\n\n\t\t\t\t\tif(event_list[i].filter == EVFILT_READ){\n\t\t\t\t\t\twhile((context = net__socket_accept(listensock)) != NULL){\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#ifdef WITH_WEBSOCKETS\n\t\t\t\t}else if(context->ident == id_listener_ws){\n\t\t\t\t\t/* Nothing needs to happen here, because we always call lws_service in the loop.\n\t\t\t\t\t * The important point is we've been woken up for this listener. */\n#endif\n\t\t\t\t}\n\t\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_kqueue__cleanup(void)\n{\n\t(void)close(db.kqueuefd);\n\tdb.kqueuefd = 0;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void loop_handle_reads_writes(struct mosquitto *context, short event)\n{\n\tint err;\n\tsocklen_t len;\n\tint rc;\n\n\tif(context->sock == INVALID_SOCKET){\n\t\treturn;\n\t}\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tif(context->wsi){\n\t\tstruct lws_pollfd wspoll;\n\t\twspoll.fd = context->sock;\n\t\tint16_t lws_events;\n\t\tswitch(event){\n\t\t\tcase EVFILT_READ:\n\t\t\t\tlws_events = LWS_POLLIN;\n\t\t\t\tbreak;\n\t\t\tcase EVFILT_WRITE:\n\t\t\t\tlws_events = LWS_POLLOUT;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tlws_events = LWS_POLLHUP;\n\t\t\t\tbreak;\n\t\t}\n\t\twspoll.events = lws_events;\n\t\twspoll.revents = lws_events;\n\t\tlws_service_fd(lws_get_context(context->wsi), &wspoll);\n\t\treturn;\n\t}\n#endif\n\n\tif(event == EVFILT_WRITE\n#ifdef WITH_TLS\n\t\t\t|| context->want_write\n\t\t\t|| (context->ssl && context->state == mosq_cs_new)\n#endif\n\t\t\t){\n\n\t\tif(context->state == mosq_cs_connect_pending){\n\t\t\tlen = sizeof(int);\n\t\t\tif(!getsockopt(context->sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len)){\n\t\t\t\tif(err == 0){\n\t\t\t\t\tmosquitto__set_state(context, mosq_cs_new);\n#if defined(WITH_ADNS) && defined(WITH_BRIDGE)\n\t\t\t\t\tif(context->bridge){\n\t\t\t\t\t\tbridge__connect_step3(context);\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tdo_disconnect(context, MOSQ_ERR_CONN_LOST);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\trc = packet__write(context);\n\t\tif(rc){\n\t\t\tdo_disconnect(context, rc);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif(event == EVFILT_READ\n#ifdef WITH_TLS\n\t\t\t|| (context->ssl && context->state == mosq_cs_new)\n#endif\n\t\t\t){\n\n\t\tdo{\n\t\t\tswitch(context->transport){\n\t\t\t\tcase mosq_t_tcp:\n\t\t\t\tcase mosq_t_ws:\n\t\t\t\t\trc = packet__read(context);\n\t\t\t\t\tbreak;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\t\tcase mosq_t_http:\n\t\t\t\t\trc = http__read(context);\n\t\t\t\t\tbreak;\n#endif\n#if !defined(WITH_WEBSOCKETS) || WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\t\t/* Not supported with LWS */\n\t\t\t\tcase mosq_t_proxy_v2:\n\t\t\t\t\trc = proxy_v2__read(context);\n\t\t\t\t\tbreak;\n\t\t\t\tcase mosq_t_proxy_v1:\n\t\t\t\t\trc = proxy_v1__read(context);\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tdefault:\n\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif(rc){\n\t\t\t\tdo_disconnect(context, rc);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}while(SSL_DATA_PENDING(context));\n\t}\n}\n#endif\n"
  },
  {
    "path": "src/mux_poll.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#if !defined(WITH_EPOLL) && !defined(WITH_KQUEUE)\n\n#ifndef WIN32\n#  define _GNU_SOURCE\n#endif\n\n#include <assert.h>\n#ifndef WIN32\n#include <poll.h>\n#include <unistd.h>\n#else\n#include <process.h>\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#endif\n\n#include <errno.h>\n#include <signal.h>\n#include <stdio.h>\n#include <string.h>\n#ifndef WIN32\n#  include <sys/socket.h>\n#endif\n#include <time.h>\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n#  include <libwebsockets.h>\n#endif\n\n#include \"mosquitto_broker_internal.h\"\n#include \"packet_mosq.h\"\n#include \"send_mosq.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n#include \"mux.h\"\n\nstatic void loop_handle_reads_writes(void);\n\nstatic struct pollfd *pollfds = NULL;\nstatic size_t pollfd_max, pollfd_current_max = 0;\n\n\nint mux_poll__init(void)\n{\n#ifdef WIN32\n\tpollfd_max = (size_t)_getmaxstdio();\n#else\n\tpollfd_max = (size_t)sysconf(_SC_OPEN_MAX);\n#endif\n\n\tpollfds = mosquitto_calloc(pollfd_max, sizeof(struct pollfd));\n\tif(!pollfds){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tmemset(pollfds, 0, sizeof(struct pollfd)*pollfd_max);\n\tfor(size_t i=0; i<pollfd_max; i++){\n\t\tpollfds[i].fd = INVALID_SOCKET;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_poll__add_listeners(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n\tsize_t pollfd_index = 0;\n\n\tfor(size_t i=0; i<(size_t )listensock_count; i++){\n\t\tpollfds[pollfd_index].fd = listensock[i].sock;\n\t\tpollfds[pollfd_index].events = POLLIN;\n\t\tpollfds[pollfd_index].revents = 0;\n\t\tpollfd_index++;\n\t}\n\n\tif(pollfd_current_max == 0){\n\t\tpollfd_current_max = pollfd_index-1;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_poll__delete_listeners(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n\tsize_t pollfd_index = 0;\n\n\tUNUSED(listensock);\n\n\tfor(size_t i=0; i<(size_t )listensock_count; i++){\n\t\tpollfds[pollfd_index].fd = INVALID_SOCKET;\n\t\tpollfds[pollfd_index].events = 0;\n\t\tpollfds[pollfd_index].revents = 0;\n\t\tpollfd_index++;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int mux_poll__add(struct mosquitto *context, uint16_t evt)\n{\n\tif(context->events == evt){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(context->pollfd_index != -1){\n\t\tpollfds[context->pollfd_index].fd = context->sock;\n\t\tpollfds[context->pollfd_index].events = (short int)evt;\n\t\tpollfds[context->pollfd_index].revents = 0;\n\t}else{\n\t\tfor(size_t i=0; i<pollfd_max; i++){\n\t\t\tif(pollfds[i].fd == INVALID_SOCKET){\n\t\t\t\tpollfds[i].fd = context->sock;\n\t\t\t\tpollfds[i].events = POLLIN;\n\t\t\t\tpollfds[i].revents = 0;\n\t\t\t\tcontext->pollfd_index = (int)i;\n\t\t\t\tif(i > pollfd_current_max){\n\t\t\t\t\tpollfd_current_max = i;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tcontext->events = evt;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_poll__add_out(struct mosquitto *context)\n{\n\treturn mux_poll__add(context, POLLIN | POLLOUT);\n}\n\n\nint mux_poll__remove_out(struct mosquitto *context)\n{\n\tif(context->events & POLLOUT){\n\t\treturn mux_poll__new(context);\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint mux_poll__new(struct mosquitto *context)\n{\n\treturn mux_poll__add(context, POLLIN);\n}\n\n\nint mux_poll__delete(struct mosquitto *context)\n{\n\tsize_t pollfd_index;\n\n\tif(context->pollfd_index != -1){\n\t\tpollfds[context->pollfd_index].fd = INVALID_SOCKET;\n\t\tpollfds[context->pollfd_index].events = 0;\n\t\tpollfds[context->pollfd_index].revents = 0;\n\t\tpollfd_index = (size_t )context->pollfd_index;\n\t\tcontext->pollfd_index = -1;\n\n\t\t/* If this is the highest index, reduce the current max until we find\n\t\t * the next highest in use index. */\n\t\twhile(pollfd_index == pollfd_current_max\n\t\t\t\t&& pollfd_index > 0\n\t\t\t\t&& pollfds[pollfd_index].fd == INVALID_SOCKET){\n\n\t\t\tpollfd_index--;\n\t\t\tpollfd_current_max--;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_poll__handle(struct mosquitto__listener_sock *listensock, int listensock_count)\n{\n\tstruct mosquitto *context;\n\tint fdcount;\n\tint timeout;\n\n#ifdef WITH_WEBSOCKETS\n\ttimeout = 100;\n#else\n\ttimeout = db.next_event_ms;\n#endif\n\n#ifndef WIN32\n\tfdcount = poll(pollfds, pollfd_current_max+1, timeout);\n#else\n\tfdcount = WSAPoll(pollfds, pollfd_current_max+1, timeout);\n#endif\n\n\tdb.now_s = mosquitto_time();\n\tdb.now_real_s = time(NULL);\n\n\tif(fdcount == -1){\n#  ifdef WIN32\n\t\tif(WSAGetLastError() == WSAEINVAL){\n\t\t\t/* WSAPoll() immediately returns an error if it is not given\n\t\t\t * any sockets to wait on. This can happen if we only have\n\t\t\t * websockets listeners. Sleep a little to prevent a busy loop.\n\t\t\t */\n\t\t\tSleep(10);\n\t\t}else\n#  endif\n\t\t{\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error in poll: %s.\", strerror(errno));\n\t\t}\n\t}else{\n\t\tloop_handle_reads_writes();\n\n\t\tfor(int i=0; i<listensock_count; i++){\n\t\t\tif(pollfds[i].revents & POLLIN){\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\t\t\tif(listensock[i].listener->ws_context){\n\t\t\t\t\t/* Nothing needs to happen here, because we always call lws_service in the loop.\n\t\t\t\t\t * The important point is we've been woken up for this listener. */\n\t\t\t\t}else\n#endif\n\t\t\t\t{\n\t\t\t\t\twhile((context = net__socket_accept(&listensock[i])) != NULL){\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mux_poll__cleanup(void)\n{\n\tmosquitto_FREE(pollfds);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void loop_handle_reads_writes(void)\n{\n\tstruct mosquitto *context, *ctxt_tmp;\n\tint err;\n\tsocklen_t len;\n\tint rc;\n\n\tHASH_ITER(hh_sock, db.contexts_by_sock, context, ctxt_tmp){\n\t\tif(context->pollfd_index < 0){\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(pollfds[context->pollfd_index].fd == INVALID_SOCKET){\n\t\t\tcontinue;\n\t\t}\n\n\t\tassert(pollfds[context->pollfd_index].fd == context->sock);\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\tif(context->wsi){\n\t\t\tstruct lws_pollfd wspoll;\n\t\t\twspoll.fd = pollfds[context->pollfd_index].fd;\n\t\t\twspoll.events = pollfds[context->pollfd_index].events;\n\t\t\twspoll.revents = pollfds[context->pollfd_index].revents;\n\t\t\tlws_service_fd(lws_get_context(context->wsi), &wspoll);\n\t\t\tcontinue;\n\t\t}\n#endif\n\n#ifdef WITH_TLS\n\t\tif(pollfds[context->pollfd_index].revents & POLLOUT ||\n\t\t\t\tcontext->want_write ||\n\t\t\t\t(context->ssl && context->state == mosq_cs_new)){\n#else\n\t\tif(pollfds[context->pollfd_index].revents & POLLOUT){\n#endif\n\t\t\tif(context->state == mosq_cs_connect_pending){\n\t\t\t\tlen = sizeof(int);\n\t\t\t\tif(!getsockopt(context->sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len)){\n\t\t\t\t\tif(err == 0){\n\t\t\t\t\t\tmosquitto__set_state(context, mosq_cs_new);\n#if defined(WITH_ADNS) && defined(WITH_BRIDGE)\n\t\t\t\t\t\tif(context->bridge){\n\t\t\t\t\t\t\tbridge__connect_step3(context);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tdo_disconnect(context, MOSQ_ERR_CONN_LOST);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\trc = packet__write(context);\n\t\t\tif(rc){\n\t\t\t\tdo_disconnect(context, rc);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\n\tHASH_ITER(hh_sock, db.contexts_by_sock, context, ctxt_tmp){\n\t\tif(context->pollfd_index < 0){\n\t\t\tcontinue;\n\t\t}\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\t\tif(context->wsi){\n\t\t\t// Websocket are already handled above\n\t\t\tcontinue;\n\t\t}\n#endif\n\n#ifdef WITH_TLS\n\t\tif(pollfds[context->pollfd_index].revents & POLLIN ||\n\t\t\t\t(context->ssl && context->state == mosq_cs_new)){\n#else\n\t\tif(pollfds[context->pollfd_index].revents & POLLIN){\n#endif\n\t\t\tdo{\n\t\t\t\tswitch(context->transport){\n\t\t\t\t\tcase mosq_t_tcp:\n\t\t\t\t\tcase mosq_t_ws:\n\t\t\t\t\t\trc = packet__read(context);\n\t\t\t\t\t\tbreak;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\t\t\tcase mosq_t_http:\n\t\t\t\t\t\trc = http__read(context);\n\t\t\t\t\t\tbreak;\n#endif\n#if !defined(WITH_WEBSOCKETS) || WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\t\t\t/* Not supported with LWS */\n\t\t\t\t\tcase mosq_t_proxy_v2:\n\t\t\t\t\t\trc = proxy_v2__read(context);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase mosq_t_proxy_v1:\n\t\t\t\t\t\trc = proxy_v1__read(context);\n\t\t\t\t\t\tbreak;\n#endif\n\t\t\t\t\tdefault:\n\t\t\t\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif(rc){\n\t\t\t\t\tdo_disconnect(context, rc);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}while(SSL_DATA_PENDING(context));\n\t\t}else{\n\t\t\tif(context->pollfd_index >= 0 && pollfds[context->pollfd_index].revents & (POLLERR | POLLNVAL | POLLHUP)){\n\t\t\t\tdo_disconnect(context, MOSQ_ERR_CONN_LOST);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n#endif\n"
  },
  {
    "path": "src/net.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifndef WIN32\n#  include <arpa/inet.h>\n#  ifndef _AIX\n#    include <ifaddrs.h>\n#  endif\n#  include <netdb.h>\n#  include <netinet/tcp.h>\n#  include <strings.h>\n#  include <sys/socket.h>\n#  include <unistd.h>\n#else\n#  include <winsock2.h>\n#  include <ws2tcpip.h>\n#endif\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#ifdef WITH_WRAP\n#  include <tcpd.h>\n#endif\n\n#ifdef HAVE_NETINET_IN_H\n#  include <netinet/in.h>\n#endif\n\n#if defined(WITH_UNIX_SOCKETS) || defined(WITH_TLS)\n#  include \"sys/stat.h\"\n#endif\n\n#ifdef WITH_UNIX_SOCKETS\n#  ifdef WIN32\n#    include <afunix.h>\n#  else\n#    include <sys/un.h>\n#  endif\n#endif\n\n#ifdef __QNX__\n#  include <net/netbyte.h>\n#endif\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"net_mosq.h\"\n#include \"util_mosq.h\"\n\n#ifdef WITH_TLS\n#  include \"tls_mosq.h\"\n#  include <openssl/err.h>\nstatic int tls_ex_index_context = -1;\nstatic int tls_ex_index_listener = -1;\n#endif\n\n#include \"sys_tree.h\"\n\n/* For EMFILE handling */\nstatic mosq_sock_t spare_sock = INVALID_SOCKET;\n\n\nvoid net__broker_init(void)\n{\n\tspare_sock = socket(AF_INET, SOCK_STREAM, 0);\n\tnet__init();\n#ifdef WITH_TLS\n\tnet__init_tls();\n#endif\n}\n\n\nvoid net__broker_cleanup(void)\n{\n\tif(spare_sock != INVALID_SOCKET){\n\t\tCOMPAT_CLOSE(spare_sock);\n\t\tspare_sock = INVALID_SOCKET;\n\t}\n\tnet__cleanup();\n}\n\n\nstatic void net__print_error(unsigned int log, const char *format_str)\n{\n\tchar *buf;\n\n#ifdef WIN32\n\tFormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\n\t\t\tNULL, WSAGetLastError(), LANG_NEUTRAL, (LPTSTR)&buf, 0, NULL);\n\n\tlog__printf(NULL, log, format_str, buf);\n\tLocalFree(buf);\n#else\n\tbuf = strerror(errno);\n\tlog__printf(NULL, log, format_str, buf);\n#endif\n}\n\n\nstruct mosquitto *net__socket_accept(struct mosquitto__listener_sock *listensock)\n{\n\tmosq_sock_t new_sock = INVALID_SOCKET;\n\tstruct mosquitto *new_context;\n#ifdef WITH_TLS\n\tBIO *bio;\n#endif\n#ifdef WITH_WRAP\n\tstruct request_info wrap_req;\n\tchar address[1024];\n#endif\n\n\tnew_sock = accept(listensock->sock, NULL, 0);\n\tif(new_sock == INVALID_SOCKET){\n#ifdef WIN32\n\t\tWINDOWS_SET_ERRNO();\n\t\tif(errno == WSAEMFILE){\n#else\n\t\tif(errno == EMFILE || errno == ENFILE){\n#endif\n\t\t\t/* Close the spare socket, which means we should be able to accept\n\t\t\t * this connection. Accept it, then close it immediately and create\n\t\t\t * a new spare_sock. This prevents the situation of ever properly\n\t\t\t * running out of sockets.\n\t\t\t * It would be nice to send a \"server not available\" connack here,\n\t\t\t * but there are lots of reasons why this would be tricky (TLS\n\t\t\t * being the big one). */\n\t\t\tCOMPAT_CLOSE(spare_sock);\n\t\t\tnew_sock = accept(listensock->sock, NULL, 0);\n\t\t\tif(new_sock != INVALID_SOCKET){\n\t\t\t\tCOMPAT_CLOSE(new_sock);\n\t\t\t}\n\t\t\tspare_sock = socket(AF_INET, SOCK_STREAM, 0);\n\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING,\n\t\t\t\t\t\"Unable to accept new connection, system socket count has been exceeded. Try increasing \\\"ulimit -n\\\" or equivalent.\");\n\t\t}\n\t\treturn NULL;\n\t}\n\n\tmetrics__int_inc(mosq_counter_socket_connections, 1);\n\n\tif(net__socket_nonblock(&new_sock)){\n\t\treturn NULL;\n\t}\n\n#ifdef WITH_WRAP\n\t/* Use tcpd / libwrap to determine whether a connection is allowed. */\n\trequest_init(&wrap_req, RQ_FILE, new_sock, RQ_DAEMON, \"mosquitto\", 0);\n\tfromhost(&wrap_req);\n\tif(!hosts_access(&wrap_req)){\n\t\t/* Access is denied */\n\t\tif(db.config->connection_messages == true){\n\t\t\tif(!net__socket_get_address(new_sock, address, 1024, NULL)){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client connection from %s denied access by tcpd.\", address);\n\t\t\t}\n\t\t}\n\t\tCOMPAT_CLOSE(new_sock);\n\t\treturn NULL;\n\t}\n#endif\n\n\tif(db.config->set_tcp_nodelay && listensock->listener->port){\n\t\tint flag = 1;\n#ifdef WIN32\n\t\tif(setsockopt(new_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) != 0){\n#else\n\t\tif(setsockopt(new_sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)) != 0){\n#endif\n\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Unable to set TCP_NODELAY.\");\n\t\t}\n\t}\n\n\tnew_context = context__init();\n\tif(!new_context){\n\t\tCOMPAT_CLOSE(new_sock);\n\t\treturn NULL;\n\t}\n\tnew_context->listener = listensock->listener;\n\tif(!new_context->listener){\n\t\tcontext__cleanup(new_context, true);\n\t\treturn NULL;\n\t}\n\tnew_context->listener->client_count++;\n\n\tif(new_context->listener->enable_proxy_protocol){\n\t\tif(context__init_sock(new_context, new_sock, false) != MOSQ_ERR_SUCCESS){\n\t\t\tcontext__cleanup(new_context, true);\n\t\t\tCOMPAT_CLOSE(new_sock);\n\t\t\treturn NULL;\n\t\t}\n\t\tif(new_context->listener->enable_proxy_protocol == 2){\n\t\t\tnew_context->transport = mosq_t_proxy_v2;\n\t\t}else{\n\t\t\tnew_context->transport = mosq_t_proxy_v1;\n\t\t}\n\t\tnew_context->proxy.cmd = -1;\n\t}else{\n\t\tif(context__init_sock(new_context, new_sock, true) != MOSQ_ERR_SUCCESS){\n\t\t\tcontext__cleanup(new_context, true);\n\t\t\tCOMPAT_CLOSE(new_sock);\n\t\t\treturn NULL;\n\t\t}\n\t\tswitch(new_context->listener->protocol){\n\t\t\tcase mp_mqtt:\n\t\t\t\tnew_context->transport = mosq_t_tcp;\n\t\t\t\tbreak;\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\t\tcase mp_websockets:\n\t\t\t\tif(http__context_init(new_context)){\n\t\t\t\t\tcontext__cleanup(new_context, true);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tbreak;\n#endif\n\t\t\tdefault:\n\t\t\t\tcontext__cleanup(new_context, true);\n\t\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tif((new_context->listener->max_connections > 0 && new_context->listener->client_count > new_context->listener->max_connections)\n\t\t\t|| (db.config->global_max_connections > 0 && HASH_CNT(hh_sock, db.contexts_by_sock) > (unsigned int)db.config->global_max_connections)){\n\n\t\tif(db.config->connection_messages == true){\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client connection from %s denied: max_connections exceeded.\", new_context->address);\n\t\t}\n\t\tcontext__cleanup(new_context, true);\n\t\treturn NULL;\n\t}\n\n#ifdef WITH_TLS\n\t/* TLS init */\n\tif(new_context->listener->ssl_ctx){\n\t\tnew_context->ssl = SSL_new(new_context->listener->ssl_ctx);\n\t\tif(!new_context->ssl){\n\t\t\tcontext__cleanup(new_context, true);\n\t\t\treturn NULL;\n\t\t}\n\t\tif(!SSL_set_ex_data(new_context->ssl, tls_ex_index_context, new_context)\n\t\t\t\t|| !SSL_set_ex_data(new_context->ssl, tls_ex_index_listener, new_context->listener)){\n\t\t\tcontext__cleanup(new_context, true);\n\t\t\treturn NULL;\n\t\t}\n\t\tnew_context->want_write = true;\n\t\tbio = BIO_new_socket(new_sock, BIO_NOCLOSE);\n\t\tSSL_set_bio(new_context->ssl, bio, bio);\n\t\tERR_clear_error();\n\t\tSSL_set_accept_state(new_context->ssl);\n\t}\n#endif\n\n\tif(db.config->connection_messages == true\n\t\t\t&& !new_context->listener->enable_proxy_protocol){\n\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"New connection from %s:%d on port %d.\",\n\t\t\t\tnew_context->address, new_context->remote_port, new_context->listener->port);\n\t}\n\n\tmux__new(new_context);\n\tkeepalive__add(new_context);\n\n\treturn new_context;\n}\n\n#ifdef WITH_TLS\n\n\nstatic int client_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx)\n{\n\t/* Preverify should check expiry, revocation. */\n\tif(preverify_ok == 0){\n\t\tSSL *ssl;\n\t\tssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());\n\t\tif(ssl){\n\t\t\tstruct mosquitto__listener *listener;\n\t\t\tlistener = SSL_get_ex_data(ssl, tls_ex_index_listener);\n\n\t\t\tif(listener && listener->disable_client_cert_date_checks){\n\t\t\t\tint err = X509_STORE_CTX_get_error(ctx);\n\t\t\t\tif(err == X509_V_ERR_CERT_NOT_YET_VALID || err == X509_V_ERR_CERT_HAS_EXPIRED){\n\t\t\t\t\tpreverify_ok = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn preverify_ok;\n}\n#endif\n\n#ifdef FINAL_WITH_TLS_PSK\n\n\nstatic unsigned int psk_server_callback(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len)\n{\n\tstruct mosquitto *context;\n\tstruct mosquitto__listener *listener;\n\tchar *psk_key = NULL;\n\tint len;\n\tconst char *psk_hint;\n\n\tif(!identity){\n\t\treturn 0;\n\t}\n\n\tcontext = SSL_get_ex_data(ssl, tls_ex_index_context);\n\tif(!context){\n\t\treturn 0;\n\t}\n\n\tlistener = SSL_get_ex_data(ssl, tls_ex_index_listener);\n\tif(!listener){\n\t\treturn 0;\n\t}\n\n\tpsk_hint = listener->psk_hint;\n\n\t/* The hex to BN conversion results in the length halving, so we can pass\n\t * max_psk_len*2 as the max hex key here. */\n\tpsk_key = mosquitto_calloc(1, (size_t)max_psk_len*2 + 1);\n\tif(!psk_key){\n\t\treturn 0;\n\t}\n\n\tif(mosquitto_psk_key_get(context, psk_hint, identity, psk_key, (int)max_psk_len*2) != MOSQ_ERR_SUCCESS){\n\t\tmosquitto_FREE(psk_key);\n\t\treturn 0;\n\t}\n\n\tlen = mosquitto__hex2bin(psk_key, psk, (int)max_psk_len);\n\tif(len < 0){\n\t\tmosquitto_FREE(psk_key);\n\t\treturn 0;\n\t}\n\n\tif(listener->use_identity_as_username){\n\t\tif(mosquitto_validate_utf8(identity, (int)strlen(identity))){\n\t\t\tmosquitto_free(psk_key);\n\t\t\treturn 0;\n\t\t}\n\t\tcontext->username = mosquitto_strdup(identity);\n\t\tif(!context->username){\n\t\t\tmosquitto_FREE(psk_key);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tmosquitto_FREE(psk_key);\n\treturn (unsigned int)len;\n}\n#endif\n\n#ifdef WITH_TLS\n\n\nstatic void tls_keylog_callback(const SSL *ssl, const char *line)\n{\n\tUNUSED(ssl);\n\n\tif(db.tls_keylog){\n\t\tFILE *fptr;\n\t\tfptr = mosquitto_fopen(db.tls_keylog, \"at\", true);\n\t\tif(fptr){\n#ifndef WIN32\n\t\t\t/* Until mosquitto_fopen enforces file permissions on all files,\n\t\t\t * enforce it here. We can enforce it here, because it isn't a\n\t\t\t * change of behaviour. */\n\t\t\tstruct stat statbuf;\n\t\t\tif(fstat(fileno(fptr), &statbuf) < 0\n\t\t\t\t\t|| statbuf.st_mode & (S_IRWXG | S_IRWXO)){\n\n\t\t\t\tfclose(fptr);\n\t\t\t\treturn;\n\t\t\t}\n#endif\n\t\t\tfprintf(fptr, \"%s\\n\", line);\n\t\t\tfclose(fptr);\n\t\t}else{\n#ifndef WIN32\n\t\t\tif(errno == ELOOP){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Error: keylog file must not be a symbolic link\");\n\t\t\t}else\n#endif\n\t\t\t{\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Error: Unable to open keylog file: %s\", strerror(errno));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nint net__tls_server_ctx(struct mosquitto__listener *listener)\n{\n\tchar buf[256];\n\tint rc;\n\n\tif(tls_ex_index_context == -1){\n\t\ttls_ex_index_context = SSL_get_ex_new_index(0, \"client context\", NULL, NULL, NULL);\n\t}\n\tif(tls_ex_index_listener == -1){\n\t\ttls_ex_index_listener = SSL_get_ex_new_index(0, \"listener\", NULL, NULL, NULL);\n\t}\n\n\tif(listener->ssl_ctx){\n\t\tSSL_CTX_free(listener->ssl_ctx);\n\t\tlistener->ssl_ctx = NULL;\n\t}\n\n\tlistener->ssl_ctx = SSL_CTX_new(TLS_server_method());\n\n\tif(!listener->ssl_ctx){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to create TLS context.\");\n\t\treturn MOSQ_ERR_TLS;\n\t}\n\n#ifdef SSL_OP_NO_TLSv1_3\n\tif(db.config->per_listener_settings){\n\t\tif(listener->security_options->psk_file){\n\t\t\tSSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_TLSv1_3);\n\t\t}\n\t}else{\n\t\tif(db.config->security_options.psk_file){\n\t\t\tSSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_TLSv1_3);\n\t\t}\n\t}\n#endif\n\n\tif(listener->tls_version == NULL){\n\t\tSSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);\n#ifdef SSL_OP_NO_TLSv1_3\n\t}else if(!strcmp(listener->tls_version, \"tlsv1.3\")){\n\t\tSSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);\n#endif\n\t}else if(!strcmp(listener->tls_version, \"tlsv1.2\")){\n\t\tSSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);\n\t}else if(!strcmp(listener->tls_version, \"tlsv1.1\")){\n\t\tSSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1);\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: TLS v1.1 is insecure. It must only be used in the case \"\n\t\t\t\t\"where you have devices that cannot be upgraded to use a secure version of TLS. It is \"\n\t\t\t\t\"recommended to use as secure a set of ciphers as possible and that will restrict it to \"\n\t\t\t\t\"TLS v1.1 only, and to use a separate listener using TLS v1.2 and secure ciphers for your \"\n\t\t\t\t\"other devices.\");\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Please be aware that support for TLS v1.1 will go away eventually \"\n\t\t\t\t\"and that you should plan now to migrate away from it.\");\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unsupported tls_version \\\"%s\\\".\", listener->tls_version);\n\t\treturn MOSQ_ERR_TLS;\n\t}\n\n#ifdef SSL_OP_NO_COMPRESSION\n\t/* Disable compression */\n\tSSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_COMPRESSION);\n#endif\n#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE\n\t/* Server chooses cipher */\n\tSSL_CTX_set_options(listener->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);\n#endif\n\n#ifdef SSL_MODE_RELEASE_BUFFERS\n\t/* Use even less memory per SSL connection. */\n\tSSL_CTX_set_mode(listener->ssl_ctx, SSL_MODE_RELEASE_BUFFERS);\n#endif\n\n\tSSL_CTX_set_dh_auto(listener->ssl_ctx, 1);\n\n#ifdef SSL_OP_NO_RENEGOTIATION\n\tSSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_RENEGOTIATION);\n#endif\n\n\tif(db.tls_keylog){\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"TLS key logging to '%s' enabled for all listeners.\",\n\t\t\t\tdb.tls_keylog);\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"TLS key logging is for DEBUGGING only.\");\n\n\t\tSSL_CTX_set_keylog_callback(listener->ssl_ctx, tls_keylog_callback);\n\t}\n\n\tsnprintf(buf, 256, \"mosquitto-%d\", listener->port);\n\tSSL_CTX_set_session_id_context(listener->ssl_ctx, (unsigned char *)buf, (unsigned int)strlen(buf));\n\n\tif(listener->ciphers){\n\t\trc = SSL_CTX_set_cipher_list(listener->ssl_ctx, listener->ciphers);\n\t\tif(rc == 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to set TLS ciphers. Check cipher list \\\"%s\\\".\", listener->ciphers);\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t}else{\n\t\trc = SSL_CTX_set_cipher_list(listener->ssl_ctx, \"DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2:@STRENGTH\");\n\t\tif(rc == 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to set TLS ciphers. Check cipher list \\\"%s\\\".\", listener->ciphers);\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t}\n\tif(listener->ciphers_tls13){\n\t\trc = SSL_CTX_set_ciphersuites(listener->ssl_ctx, listener->ciphers_tls13);\n\t\tif(rc == 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to set TLS 1.3 ciphersuites. Check cipher_tls13 list \\\"%s\\\".\", listener->ciphers_tls13);\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\n#ifdef WITH_TLS\n\n\nstatic int net__load_crl_file(struct mosquitto__listener *listener)\n{\n\tX509_STORE *store;\n\tX509_LOOKUP *lookup;\n\tint rc;\n\n\tstore = SSL_CTX_get_cert_store(listener->ssl_ctx);\n\tif(!store){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to obtain TLS store.\");\n\t\tnet__print_error(MOSQ_LOG_ERR, \"Error: %s\");\n\t\treturn MOSQ_ERR_TLS;\n\t}\n\tlookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());\n\trc = X509_load_crl_file(lookup, listener->crlfile, X509_FILETYPE_PEM);\n\tif(rc < 1){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load certificate revocation file \\\"%s\\\". Check crlfile.\", listener->crlfile);\n\t\tnet__print_error(MOSQ_LOG_ERR, \"Error: %s\");\n\t\tnet__print_ssl_error(NULL, NULL);\n\t\treturn MOSQ_ERR_TLS;\n\t}\n\tX509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\nint net__load_certificates(struct mosquitto__listener *listener)\n{\n#ifdef WITH_TLS\n\tint rc;\n\n\tif(listener->require_certificate){\n\t\tSSL_CTX_set_verify(listener->ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, client_certificate_verify);\n\t}else{\n\t\tSSL_CTX_set_verify(listener->ssl_ctx, SSL_VERIFY_NONE, client_certificate_verify);\n\t}\n\trc = SSL_CTX_use_certificate_chain_file(listener->ssl_ctx, listener->certfile);\n\tif(rc != 1){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load server certificate \\\"%s\\\". Check certfile.\", listener->certfile);\n\t\tnet__print_ssl_error(NULL, NULL);\n\t\treturn MOSQ_ERR_TLS;\n\t}\n\tif(listener->tls_engine == NULL || listener->tls_keyform == mosq_k_pem){\n\t\trc = SSL_CTX_use_PrivateKey_file(listener->ssl_ctx, listener->keyfile, SSL_FILETYPE_PEM);\n\t\tif(rc != 1){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load server key file \\\"%s\\\". Check keyfile.\", listener->keyfile);\n\t\t\tnet__print_ssl_error(NULL, NULL);\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t}\n\trc = SSL_CTX_check_private_key(listener->ssl_ctx);\n\tif(rc != 1){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Server certificate/key are inconsistent.\");\n\t\tnet__print_ssl_error(NULL, NULL);\n\t\treturn MOSQ_ERR_TLS;\n\t}\n\t/* Load CRLs if they exist. */\n\tif(listener->crlfile){\n\t\trc = net__load_crl_file(listener);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n#else\n\tUNUSED(listener);\n#endif\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n#if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\n\nstatic int net__load_engine(struct mosquitto__listener *listener)\n{\n\tENGINE *engine = NULL;\n\tUI_METHOD *ui_method;\n\tEVP_PKEY *pkey;\n\n\tif(!listener->tls_engine){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tengine = ENGINE_by_id(listener->tls_engine);\n\tif(!engine){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error loading %s engine\\n\", listener->tls_engine);\n\t\tnet__print_ssl_error(NULL, NULL);\n\t\treturn MOSQ_ERR_TLS;\n\t}\n\tif(!ENGINE_init(engine)){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Failed engine initialisation\\n\");\n\t\tnet__print_ssl_error(NULL, NULL);\n\t\treturn MOSQ_ERR_TLS;\n\t}\n\tENGINE_set_default(engine, ENGINE_METHOD_ALL);\n\n\tif(listener->tls_keyform == mosq_k_engine){\n\t\tui_method = net__get_ui_method();\n\t\tif(listener->tls_engine_kpass_sha1){\n\t\t\tif(!ENGINE_ctrl_cmd(engine, ENGINE_SECRET_MODE, ENGINE_SECRET_MODE_SHA, NULL, NULL, 0)){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to set engine secret mode sha\");\n\t\t\t\tnet__print_ssl_error(NULL, NULL);\n\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t}\n\t\t\tif(!ENGINE_ctrl_cmd(engine, ENGINE_PIN, 0, listener->tls_engine_kpass_sha1, NULL, 0)){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to set engine pin\");\n\t\t\t\tnet__print_ssl_error(NULL, NULL);\n\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t}\n\t\t\tui_method = NULL;\n\t\t}\n\t\tpkey = ENGINE_load_private_key(engine, listener->keyfile, ui_method, NULL);\n\t\tif(!pkey){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load engine private key file \\\"%s\\\".\", listener->keyfile);\n\t\t\tnet__print_ssl_error(NULL, NULL);\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t\tif(SSL_CTX_use_PrivateKey(listener->ssl_ctx, pkey) <= 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to use engine private key file \\\"%s\\\".\", listener->keyfile);\n\t\t\tnet__print_ssl_error(NULL, NULL);\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\t}\n\tENGINE_free(engine); /* release the structural reference from ENGINE_by_id() */\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n\n\nint net__tls_load_verify(struct mosquitto__listener *listener)\n{\n#ifdef WITH_TLS\n\tint rc;\n\n#  if OPENSSL_VERSION_NUMBER < 0x30000000L\n\tif(listener->cafile || listener->capath){\n\t\trc = SSL_CTX_load_verify_locations(listener->ssl_ctx, listener->cafile, listener->capath);\n\t\tif(rc == 0){\n\t\t\tif(listener->cafile && listener->capath){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates. Check cafile \\\"%s\\\" and capath \\\"%s\\\".\", listener->cafile, listener->capath);\n\t\t\t}else if(listener->cafile){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates. Check cafile \\\"%s\\\".\", listener->cafile);\n\t\t\t}else{\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates. Check capath \\\"%s\\\".\", listener->capath);\n\t\t\t}\n\t\t}\n\t}\n#  else\n\tSTACK_OF(X509_NAME) *ca_names = NULL;\n\tif(listener->cafile || listener->capath){\n\t\tca_names = sk_X509_NAME_new_null();\n\t}\n\n\tif(listener->cafile){\n\t\trc = SSL_CTX_load_verify_file(listener->ssl_ctx, listener->cafile);\n\t\tif(rc == 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates. Check cafile \\\"%s\\\".\", listener->cafile);\n\t\t\tnet__print_ssl_error(NULL, NULL);\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\n\t\tSTACK_OF(X509_NAME) *cert_names = SSL_load_client_CA_file(listener->cafile);\n\t\tif(cert_names){\n\t\t\tfor(int i=0; i<sk_X509_NAME_num(cert_names); i++){\n\t\t\t\tsk_X509_NAME_push(ca_names, X509_NAME_dup(sk_X509_NAME_value(cert_names, i)));\n\t\t\t}\n\t\t\tsk_X509_NAME_pop_free(cert_names, X509_NAME_free);\n\t\t}\n\n\t}\n\tif(listener->capath){\n\t\trc = SSL_CTX_load_verify_dir(listener->ssl_ctx, listener->capath);\n\t\tif(rc == 0){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to load CA certificates. Check capath \\\"%s\\\".\", listener->capath);\n\t\t\tnet__print_ssl_error(NULL, NULL);\n\t\t\tsk_X509_NAME_pop_free(ca_names, X509_NAME_free);\n\t\t\treturn MOSQ_ERR_TLS;\n\t\t}\n\n\t\tSSL_add_dir_cert_subjects_to_stack(ca_names, listener->capath);\n\t}\n\tif(ca_names){\n\t\tSSL_CTX_set_client_CA_list(listener->ssl_ctx, ca_names);\n\t}\n#  endif\n\n#  if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000\n\tif(net__load_engine(listener)){\n\t\treturn MOSQ_ERR_TLS;\n\t}\n#  endif\n#endif\n\treturn net__load_certificates(listener);\n}\n\n\n#if !defined(WIN32) && !defined(_AIX)\n\n\nstatic int net__bind_interface(struct mosquitto__listener *listener, struct addrinfo *rp)\n{\n\t/*\n\t * This binds the listener sock to a network interface.\n\t * The use of SO_BINDTODEVICE requires root access, which we don't have, so instead\n\t * use getifaddrs to find the interface addresses, and use IP of the\n\t * matching interface in the later bind().\n\t */\n\tstruct ifaddrs *ifaddr;\n\tbool have_interface = false;\n\n\tif(getifaddrs(&ifaddr) < 0){\n\t\tnet__print_error(MOSQ_LOG_ERR, \"Error: %s\");\n\t\treturn MOSQ_ERR_ERRNO;\n\t}\n\n\tfor(struct ifaddrs *ifa=ifaddr; ifa!=NULL; ifa=ifa->ifa_next){\n\t\tif(ifa->ifa_addr == NULL){\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(!strcasecmp(listener->bind_interface, ifa->ifa_name)){\n\t\t\thave_interface = true;\n\n\t\t\tif(ifa->ifa_addr->sa_family == rp->ai_addr->sa_family){\n\t\t\t\tif(rp->ai_addr->sa_family == AF_INET){\n\t\t\t\t\tif(listener->host &&\n\t\t\t\t\t\t\tmemcmp(&((struct sockaddr_in *)rp->ai_addr)->sin_addr,\n\t\t\t\t\t\t\t&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,\n\t\t\t\t\t\t\tsizeof(struct in_addr))){\n\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Interface address for %s does not match specified listener address (%s).\",\n\t\t\t\t\t\t\t\tlistener->bind_interface, listener->host);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tmemcpy(&((struct sockaddr_in *)rp->ai_addr)->sin_addr,\n\t\t\t\t\t\t\t\t&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,\n\t\t\t\t\t\t\t\tsizeof(struct in_addr));\n\n\t\t\t\t\t\tfreeifaddrs(ifaddr);\n\t\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t\t}\n\t\t\t\t}else if(rp->ai_addr->sa_family == AF_INET6){\n\t\t\t\t\tif(listener->host &&\n\t\t\t\t\t\t\tmemcmp(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr,\n\t\t\t\t\t\t\t&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr,\n\t\t\t\t\t\t\tsizeof(struct in6_addr))){\n\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Interface address for %s does not match specified listener address (%s).\",\n\t\t\t\t\t\t\t\tlistener->bind_interface, listener->host);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tmemcpy(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr,\n\t\t\t\t\t\t\t\t&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr,\n\t\t\t\t\t\t\t\tsizeof(struct in6_addr));\n\n\t\t\t\t\t\t((struct sockaddr_in6 *)rp->ai_addr)->sin6_scope_id = ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_scope_id;\n\n\t\t\t\t\t\tfreeifaddrs(ifaddr);\n\t\t\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfreeifaddrs(ifaddr);\n\tif(have_interface){\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Interface %s does not support %s configuration.\",\n\t\t\t\tlistener->bind_interface, rp->ai_addr->sa_family == AF_INET ? \"IPv4\" : \"IPv6\");\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Interface %s does not exist.\", listener->bind_interface);\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n}\n#endif\n\n\nstatic int net__socket_listen_tcp(struct mosquitto__listener *listener)\n{\n\tmosq_sock_t sock = INVALID_SOCKET;\n\tstruct addrinfo hints;\n\tstruct addrinfo *ainfo, *rp;\n\tchar service[10];\n\tint rc;\n\tint ss_opt = 1;\n#ifndef WIN32\n\tbool interface_bound = false;\n#endif\n\n\tif(!listener){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tsnprintf(service, 10, \"%d\", listener->port);\n\tmemset(&hints, 0, sizeof(struct addrinfo));\n\tif(listener->socket_domain){\n\t\thints.ai_family = listener->socket_domain;\n\t}else{\n\t\thints.ai_family = AF_UNSPEC;\n\t}\n\thints.ai_flags = AI_PASSIVE;\n\thints.ai_socktype = SOCK_STREAM;\n\n\trc = getaddrinfo(listener->host, service, &hints, &ainfo);\n\tif(rc){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error creating listener: %s.\", gai_strerror(rc));\n\t\treturn INVALID_SOCKET;\n\t}\n\n\tlistener->sock_count = 0;\n\tlistener->socks = NULL;\n\n\tfor(rp = ainfo; rp; rp = rp->ai_next){\n\t\tif(rp->ai_family == AF_INET){\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Opening ipv4 listen socket on port %d.\", ntohs(((struct sockaddr_in *)rp->ai_addr)->sin_port));\n\t\t}else if(rp->ai_family == AF_INET6){\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Opening ipv6 listen socket on port %d.\", ntohs(((struct sockaddr_in6 *)rp->ai_addr)->sin6_port));\n\t\t}else{\n\t\t\tcontinue;\n\t\t}\n\n\t\tsock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n\t\tif(sock == INVALID_SOCKET){\n\t\t\tnet__print_error(MOSQ_LOG_WARNING, \"Warning: %s\");\n\t\t\tcontinue;\n\t\t}\n\t\tlistener->sock_count++;\n\t\tlistener->socks = mosquitto_realloc(listener->socks, sizeof(mosq_sock_t)*(size_t)listener->sock_count);\n\t\tif(!listener->socks){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\tfreeaddrinfo(ainfo);\n\t\t\tCOMPAT_CLOSE(sock);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tlistener->socks[listener->sock_count-1] = sock;\n\n#ifndef WIN32\n\t\tss_opt = 1;\n\t\t/* Unimportant if this fails */\n\t\t(void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &ss_opt, sizeof(ss_opt));\n#endif\n#ifdef IPV6_V6ONLY\n\t\tss_opt = 1;\n\t\t(void)setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ss_opt, sizeof(ss_opt));\n#endif\n\n\t\tif(net__socket_nonblock(&sock)){\n\t\t\tfreeaddrinfo(ainfo);\n\t\t\tmosquitto_FREE(listener->socks);\n\t\t\treturn 1;\n\t\t}\n\n#if !defined(WIN32) && !defined(_AIX)\n\t\tif(listener->bind_interface){\n\t\t\t/* It might be possible that an interface does not support all relevant sa_families.\n\t\t\t * We should successfully find at least one. */\n\t\t\trc = net__bind_interface(listener, rp);\n\t\t\tif(rc){\n\t\t\t\tCOMPAT_CLOSE(sock);\n\t\t\t\tlistener->sock_count--;\n\t\t\t\tif(rc == MOSQ_ERR_NOT_FOUND || rc == MOSQ_ERR_INVAL){\n\t\t\t\t\tfreeaddrinfo(ainfo);\n\t\t\t\t\treturn rc;\n\t\t\t\t}else{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tinterface_bound = true;\n\t\t}\n#endif\n\n\t\tif(bind(sock, rp->ai_addr, rp->ai_addrlen) == -1){\n#if defined(__linux__)\n\t\t\tif(errno == EACCES){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"If you are trying to bind to a privileged port (<1024), try using setcap and do not start the broker as root:\");\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"    sudo setcap 'CAP_NET_BIND_SERVICE=+ep /usr/sbin/mosquitto'\");\n\t\t\t}\n#endif\n\t\t\tnet__print_error(MOSQ_LOG_ERR, \"Error: %s\");\n\t\t\tCOMPAT_CLOSE(sock);\n\t\t\tfreeaddrinfo(ainfo);\n\t\t\tmosquitto_FREE(listener->socks);\n\t\t\treturn 1;\n\t\t}\n\n\t\tif(listen(sock, 100) == -1){\n\t\t\tnet__print_error(MOSQ_LOG_ERR, \"Error: %s\");\n\t\t\tfreeaddrinfo(ainfo);\n\t\t\tCOMPAT_CLOSE(sock);\n\t\t\tmosquitto_FREE(listener->socks);\n\t\t\treturn 1;\n\t\t}\n\t}\n\tfreeaddrinfo(ainfo);\n\n#ifndef WIN32\n\tif(listener->bind_interface && !interface_bound){\n\t\tmosquitto_FREE(listener->socks);\n\t\treturn 1;\n\t}\n#endif\n\n\treturn 0;\n}\n\n\n#ifdef WITH_UNIX_SOCKETS\n\n\nstatic int net__socket_listen_unix(struct mosquitto__listener *listener)\n{\n\tstruct sockaddr_un addr;\n\tmosq_sock_t sock = INVALID_SOCKET;\n\tint rc;\n#ifndef WIN32\n\tmode_t old_mask;\n#endif\n\n\tif(listener->unix_socket_path == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(strlen(listener->unix_socket_path) > sizeof(addr.sun_path)-1){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Path to unix socket is too long \\\"%s\\\".\", listener->unix_socket_path);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n#ifdef WIN32\n\tDeleteFile(listener->unix_socket_path);\n#else\n\tunlink(listener->unix_socket_path);\n#endif\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Opening unix listen socket on path %s.\", listener->unix_socket_path);\n\tmemset(&addr, 0, sizeof(struct sockaddr_un));\n\taddr.sun_family = AF_UNIX;\n\tstrncpy(addr.sun_path, listener->unix_socket_path, sizeof(addr.sun_path)-1);\n\n\tsock = socket(AF_UNIX, SOCK_STREAM, 0);\n\tif(sock == INVALID_SOCKET){\n\t\tnet__print_error(MOSQ_LOG_ERR, \"Error creating unix socket: %s\");\n\t\treturn 1;\n\t}\n\tlistener->sock_count++;\n\tlistener->socks = mosquitto_realloc(listener->socks, sizeof(mosq_sock_t)*(size_t)listener->sock_count);\n\tif(!listener->socks){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\tCOMPAT_CLOSE(sock);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tlistener->socks[listener->sock_count-1] = sock;\n\n\n#ifndef WIN32\n\told_mask = umask(0007);\n#endif\n\trc = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un));\n#ifndef WIN32\n\tumask(old_mask);\n#endif\n\n\tif(rc == -1){\n\t\tnet__print_error(MOSQ_LOG_ERR, \"Error binding unix socket: %s\");\n\t\treturn 1;\n\t}\n\n\tif(listen(sock, 10) == -1){\n\t\tnet__print_error(MOSQ_LOG_ERR, \"Error listening to unix socket: %s\");\n\t\treturn 1;\n\t}\n\n\tif(net__socket_nonblock(&sock)){\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n#endif\n\n\n/* Creates a socket and listens on port 'port'.\n * Returns 1 on failure\n * Returns 0 on success.\n */\nint net__socket_listen(struct mosquitto__listener *listener)\n{\n\tint rc;\n\n\tif(!listener){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n#ifdef WITH_UNIX_SOCKETS\n\tif(listener->port == 0 && listener->unix_socket_path != NULL){\n\t\trc = net__socket_listen_unix(listener);\n\t}else\n#endif\n\t{\n\t\trc = net__socket_listen_tcp(listener);\n\t}\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\t/* We need to have at least one working socket. */\n\tif(listener->sock_count > 0){\n#ifdef WITH_TLS\n\t\tif(listener->certfile && listener->keyfile){\n\t\t\tif(net__tls_server_ctx(listener)){\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\tif(net__tls_load_verify(listener)){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n#  ifdef FINAL_WITH_TLS_PSK\n\t\tif(listener->psk_hint){\n\t\t\tif(listener->certfile == NULL || listener->keyfile == NULL){\n\t\t\t\tif(net__tls_server_ctx(listener)){\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tSSL_CTX_set_psk_server_callback(listener->ssl_ctx, psk_server_callback);\n\t\t\tif(listener->psk_hint){\n\t\t\t\trc = SSL_CTX_use_psk_identity_hint(listener->ssl_ctx, listener->psk_hint);\n\t\t\t\tif(rc == 0){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to set TLS PSK hint.\");\n\t\t\t\t\tnet__print_ssl_error(NULL, NULL);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#  endif /* FINAL_WITH_TLS_PSK */\n\t\tif(tls_ex_index_context == -1){\n\t\t\ttls_ex_index_context = SSL_get_ex_new_index(0, \"client context\", NULL, NULL, NULL);\n\t\t}\n\t\tif(tls_ex_index_listener == -1){\n\t\t\ttls_ex_index_listener = SSL_get_ex_new_index(0, \"listener\", NULL, NULL, NULL);\n\t\t}\n#endif /* WITH_TLS */\n\t\treturn 0;\n\t}else{\n\t\treturn 1;\n\t}\n}\n\n\nint net__socket_get_address(mosq_sock_t sock, char *buf, size_t len, uint16_t *remote_port)\n{\n\tstruct sockaddr_storage addr;\n\tsocklen_t addrlen;\n\n\tmemset(&addr, 0, sizeof(struct sockaddr_storage));\n\taddrlen = sizeof(addr);\n\tif(!getpeername(sock, (struct sockaddr *)&addr, &addrlen)){\n\t\tif(addr.ss_family == AF_INET){\n\t\t\tif(remote_port){\n\t\t\t\t*remote_port = ntohs(((struct sockaddr_in *)&addr)->sin_port);\n\t\t\t}\n\t\t\tif(inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr.s_addr, buf, (socklen_t)len)){\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}else if(addr.ss_family == AF_INET6){\n\t\t\tif(remote_port){\n\t\t\t\t*remote_port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port);\n\t\t\t}\n\t\t\tif(inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, buf, (socklen_t)len)){\n\t\t\t\treturn 0;\n\t\t\t}\n#ifdef WITH_UNIX_SOCKETS\n\t\t}else if(addr.ss_family == AF_UNIX){\n\t\t\tstruct sockaddr_un un;\n\t\t\taddrlen = sizeof(struct sockaddr_un);\n\t\t\tif(!getsockname(sock, (struct sockaddr *)&un, &addrlen)){\n\t\t\t\tsnprintf(buf, len, \"%s\", un.sun_path);\n\t\t\t}else{\n\t\t\t\tsnprintf(buf, len, \"unix-socket\");\n\t\t\t}\n\t\t\treturn 0;\n#endif\n\t\t}\n\t}\n\treturn 1;\n}\n"
  },
  {
    "path": "src/password_file.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <ctype.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"password_file.h\"\n\n\nint broker_password_file__init(void)\n{\n\tint rc;\n\n\t/* Load username/password data if required. */\n\tif(db.config->per_listener_settings){\n\t\tfor(int i=0; i<db.config->listener_count; i++){\n\t\t\tif(db.config->listeners[i].security_options->password_data.password_file){\n\t\t\t\trc = password_file__parse(&db.config->listeners[i].security_options->password_data);\n\t\t\t\tif(rc){\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t\tif(db.config->listeners[i].security_options->plugin_count == 0){\n\t\t\t\t\tconfig__plugin_add_secopt(db.config->listeners[i].security_options->pid, db.config->listeners[i].security_options);\n\t\t\t\t}\n\n\t\t\t\tmosquitto_callback_register(db.config->listeners[i].security_options->pid,\n\t\t\t\t\t\tMOSQ_EVT_BASIC_AUTH, password_file__check, NULL, &db.config->listeners[i].security_options->password_data);\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(db.config->security_options.password_data.password_file){\n\t\t\trc = password_file__parse(&db.config->security_options.password_data);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tif(db.config->security_options.plugin_count == 0){\n\t\t\t\tconfig__plugin_add_secopt(db.config->security_options.pid, &db.config->security_options);\n\t\t\t}\n\n\t\t\tmosquitto_callback_register(db.config->security_options.pid,\n\t\t\t\t\tMOSQ_EVT_BASIC_AUTH, password_file__check, NULL, &db.config->security_options.password_data);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid broker_password_file__cleanup(void)\n{\n\tif(db.config->per_listener_settings){\n\t\tfor(int i=0; i<db.config->listener_count; i++){\n\t\t\tif(db.config->listeners[i].security_options->pid){\n\t\t\t\tmosquitto_callback_unregister(db.config->listeners[i].security_options->pid,\n\t\t\t\t\t\tMOSQ_EVT_BASIC_AUTH, password_file__check, NULL);\n\n\t\t\t\tpassword_file__cleanup(&db.config->listeners[i].security_options->password_data);\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(db.config->security_options.pid){\n\t\t\tmosquitto_callback_unregister(db.config->security_options.pid,\n\t\t\t\t\tMOSQ_EVT_BASIC_AUTH, password_file__check, NULL);\n\n\t\t\tpassword_file__cleanup(&db.config->security_options.password_data);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/password_file.h",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n#ifndef PASSWORD_FILE_H\n#define PASSWORD_FILE_H\n\n#include <uthash.h>\n\nstruct mosquitto__unpwd {\n\tUT_hash_handle hh;\n\tchar *username;\n\tchar *clientid;\n\tstruct mosquitto_pw *pw;\n};\n\nstruct password_file_data {\n\tstruct mosquitto__unpwd *unpwd;\n\tchar *password_file;\n};\n\nint password_file__parse(struct password_file_data *data);\nint password_file__check(int event, void *event_data, void *userdata);\nint password_file__reload(int event, void *event_data, void *userdata);\nvoid password_file__cleanup(struct password_file_data *data);\n\n#endif\n"
  },
  {
    "path": "src/persist.h",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef PERSIST_H\n#define PERSIST_H\n\n#include \"mosquitto_broker_internal.h\"\n\n#define MOSQ_DB_VERSION 6\n\n/* DB read/write */\nextern const unsigned char magic[15];\n#define DB_CHUNK_CFG 1\n#define DB_CHUNK_BASE_MSG 2\n#define DB_CHUNK_CLIENT_MSG 3\n#define DB_CHUNK_RETAIN 4\n#define DB_CHUNK_SUB 5\n#define DB_CHUNK_CLIENT 6\n/* End DB read/write */\n\n#define read_e(f, b, c) if(fread(b, 1, c, f) != c){ rc = MOSQ_ERR_UNKNOWN; goto error; }\n#define write_e(f, b, c) if(fwrite(b, 1, c, f) != c){ goto error; }\n\n/* COMPATIBILITY NOTES\n *\n * The P_* structs (persist structs) contain all of the data for a particular\n * data chunk. They are loaded in multiple parts, so can be rearranged without\n * updating the db format version.\n *\n * The PF_* structs (persist fixed structs) contain the fixed size data for a\n * particular data chunk. They are written to disk as is, so they must not be\n * rearranged without updating the db format version. When adding new members,\n * always use explicit sized datatypes (\"uint32_t\", not \"long\"), and check\n * whether what is being added can go in an existing hole in the struct.\n */\n\nstruct PF_header {\n\tuint32_t chunk;\n\tuint32_t length;\n};\n\n\nstruct PF_cfg {\n\tuint64_t last_db_id;\n\tuint8_t shutdown;\n\tuint8_t dbid_size;\n};\n\nstruct PF_client_v5 {\n\tint64_t session_expiry_time;\n\tuint32_t session_expiry_interval;\n\tuint16_t last_mid;\n\tuint16_t id_len;\n};\nstruct PF_client {\n\t/* struct PF_client_v5; */\n\tint64_t session_expiry_time;\n\tuint32_t session_expiry_interval;\n\tuint16_t last_mid;\n\tuint16_t id_len;\n\n\tuint16_t listener_port;\n\tuint16_t username_len;\n\t/* tail: 4 byte padding, because 64bit member\n\t * forces multiple of 8 for struct size */\n};\nstruct P_client {\n\tstruct PF_client F;\n\tchar *clientid;\n\tchar *username;\n};\n\n\nstruct PF_client_msg {\n\tdbid_t store_id;\n\tuint16_t mid;\n\tuint16_t id_len;\n\tuint8_t qos;\n\tuint8_t state;\n\tuint8_t retain_dup;\n\tuint8_t direction;\n};\nstruct P_client_msg {\n\tstruct PF_client_msg F;\n\tchar *clientid;\n\tuint32_t subscription_identifier;\n};\n\n\nstruct PF_base_msg {\n\tdbid_t store_id;\n\tint64_t expiry_time;\n\tuint32_t payloadlen;\n\tuint16_t source_mid;\n\tuint16_t source_id_len;\n\tuint16_t source_username_len;\n\tuint16_t topic_len;\n\tuint16_t source_port;\n\tuint8_t qos;\n\tuint8_t retain;\n};\nstruct P_base_msg {\n\tstruct PF_base_msg F;\n\tvoid *payload;\n\tstruct mosquitto source;\n\tchar *topic;\n\tmosquitto_property *properties;\n};\n\n\nstruct PF_sub {\n\tuint32_t identifier;\n\tuint16_t id_len;\n\tuint16_t topic_len;\n\tuint8_t qos;\n\tuint8_t options;\n};\nstruct P_sub {\n\tstruct PF_sub F;\n\tchar *clientid;\n\tchar *topic;\n};\n\n\nstruct PF_retain {\n\tdbid_t store_id;\n};\nstruct P_retain {\n\tstruct PF_retain F;\n};\n\n\nint persist__read_string_len(FILE *db_fptr, char **str, uint16_t len);\nint persist__read_string(FILE *db_fptr, char **str);\n\nint persist__chunk_header_read(FILE *db_fptr, uint32_t *chunk, uint32_t *length);\n\nint persist__chunk_header_read_v234(FILE *db_fptr, uint32_t *chunk, uint32_t *length);\nint persist__chunk_cfg_read_v234(FILE *db_fptr, struct PF_cfg *chunk);\nint persist__chunk_client_read_v234(FILE *db_fptr, struct P_client *chunk, uint32_t db_version);\nint persist__chunk_client_msg_read_v234(FILE *db_fptr, struct P_client_msg *chunk);\nint persist__chunk_base_msg_read_v234(FILE *db_fptr, struct P_base_msg *chunk, uint32_t db_version);\nint persist__chunk_retain_read_v234(FILE *db_fptr, struct P_retain *chunk);\nint persist__chunk_sub_read_v234(FILE *db_fptr, struct P_sub *chunk);\n\nint persist__chunk_header_read_v56(FILE *db_fptr, uint32_t *chunk, uint32_t *length);\nint persist__chunk_cfg_read_v56(FILE *db_fptr, struct PF_cfg *chunk);\nint persist__chunk_client_read_v56(FILE *db_fptr, struct P_client *chunk, uint32_t db_version);\nint persist__chunk_client_msg_read_v56(FILE *db_fptr, struct P_client_msg *chunk, uint32_t length);\nint persist__chunk_base_msg_read_v56(FILE *db_fptr, struct P_base_msg *chunk, uint32_t length);\nint persist__chunk_retain_read_v56(FILE *db_fptr, struct P_retain *chunk);\nint persist__chunk_sub_read_v56(FILE *db_fptr, struct P_sub *chunk);\n\nint persist__chunk_cfg_write_v6(FILE *db_fptr, struct PF_cfg *chunk);\nint persist__chunk_client_write_v6(FILE *db_fptr, struct P_client *chunk);\nint persist__chunk_client_msg_write_v6(FILE *db_fptr, struct P_client_msg *chunk);\nint persist__chunk_message_store_write_v6(FILE *db_fptr, struct P_base_msg *chunk);\nint persist__chunk_retain_write_v6(FILE *db_fptr, struct P_retain *chunk);\nint persist__chunk_sub_write_v6(FILE *db_fptr, struct P_sub *chunk);\n\n#endif\n"
  },
  {
    "path": "src/persist_read.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_PERSISTENCE\n\n#ifndef WIN32\n#include <arpa/inet.h>\n#endif\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <time.h>\n#include <utlist.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"persist.h\"\n#include \"util_mosq.h\"\n\nuint32_t db_version;\n\nconst unsigned char magic[15] = {0x00, 0xB5, 0x00, 'm', 'o', 's', 'q', 'u', 'i', 't', 't', 'o', ' ', 'd', 'b'};\nstatic long base_msg_count = 0;\nstatic long retained_count = 0;\nstatic long client_count = 0;\nstatic long subscription_count = 0;\nstatic long client_msg_count = 0;\n\nstatic int persist__restore_sub(const struct mosquitto_subscription *sub);\n\nstatic struct mosquitto *persist__find_or_add_context(const char *clientid, uint16_t last_mid)\n{\n\tstruct mosquitto *context;\n\n\tif(!clientid){\n\t\treturn NULL;\n\t}\n\n\tcontext = NULL;\n\tHASH_FIND(hh_id, db.contexts_by_id, clientid, strlen(clientid), context);\n\tif(!context){\n\t\tcontext = context__init();\n\t\tif(!context){\n\t\t\treturn NULL;\n\t\t}\n\t\tcontext->id = mosquitto_strdup(clientid);\n\t\tif(!context->id){\n\t\t\tmosquitto_FREE(context);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tcontext->clean_start = false;\n\n\t\tcontext__add_to_by_id(context);\n\t}\n\tif(last_mid){\n\t\tcontext->last_mid = last_mid;\n\t}\n\treturn context;\n}\n\n\nint persist__read_string_len(FILE *db_fptr, char **str, uint16_t len)\n{\n\tchar *s = NULL;\n\n\tif(len){\n\t\ts = mosquitto_malloc(len+1U);\n\t\tif(!s){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tif(fread(s, 1, len, db_fptr) != len){\n\t\t\tmosquitto_FREE(s);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\ts[len] = '\\0';\n\t}\n\n\t*str = s;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint persist__read_string(FILE *db_fptr, char **str)\n{\n\tuint16_t i16temp;\n\tuint16_t slen;\n\n\tif(fread(&i16temp, 1, sizeof(uint16_t), db_fptr) != sizeof(uint16_t)){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tslen = ntohs(i16temp);\n\treturn persist__read_string_len(db_fptr, str, slen);\n}\n\n\nstatic int persist__client_msg_restore(struct P_client_msg *chunk)\n{\n\tstruct mosquitto__client_msg *cmsg;\n\tstruct mosquitto__base_msg *msg;\n\tstruct mosquitto *context;\n\tstruct mosquitto_msg_data *msg_data;\n\n\tHASH_FIND(hh, db.msg_store, &chunk->F.store_id, sizeof(chunk->F.store_id), msg);\n\tif(!msg){\n\t\t/* Can't find message - probably expired */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tcontext = persist__find_or_add_context(chunk->clientid, 0);\n\tif(!context){\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Persistence file contains client message with no matching client. File may be corrupt.\");\n\t\treturn 0;\n\t}\n\n\tcmsg = mosquitto_calloc(1, sizeof(struct mosquitto__client_msg));\n\tif(!cmsg){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tcmsg->next = NULL;\n\tcmsg->base_msg = NULL;\n\tcmsg->data.cmsg_id = ++context->last_cmsg_id;\n\tcmsg->data.mid = chunk->F.mid;\n\tcmsg->data.qos = chunk->F.qos;\n\tcmsg->data.retain = (chunk->F.retain_dup&0xF0)>>4;\n\tcmsg->data.direction = chunk->F.direction;\n\tcmsg->data.state = chunk->F.state;\n\tcmsg->data.dup = chunk->F.retain_dup&0x0F;\n\tcmsg->data.subscription_identifier = chunk->subscription_identifier;\n\n\tcmsg->base_msg = msg;\n\tdb__msg_store_ref_inc(cmsg->base_msg);\n\n\tif(cmsg->data.direction == mosq_md_out){\n\t\tmsg_data = &context->msgs_out;\n\t}else{\n\t\tmsg_data = &context->msgs_in;\n\t}\n\n\tif(chunk->F.state == mosq_ms_queued || (chunk->F.qos > 0 && msg_data->inflight_quota == 0)){\n\t\tDL_APPEND(msg_data->queued, cmsg);\n\t\tdb__msg_add_to_queued_stats(msg_data, cmsg);\n\t}else{\n\t\tDL_APPEND(msg_data->inflight, cmsg);\n\t\tif(chunk->F.qos > 0 && msg_data->inflight_quota > 0){\n\t\t\tmsg_data->inflight_quota--;\n\t\t}\n\t\tdb__msg_add_to_inflight_stats(msg_data, cmsg);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int persist__client_chunk_restore(FILE *db_fptr)\n{\n\tint rc = 0;\n\tstruct mosquitto *context;\n\tstruct P_client chunk;\n\n\tmemset(&chunk, 0, sizeof(struct P_client));\n\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_client_read_v56(db_fptr, &chunk, db_version);\n\t}else{\n\t\trc = persist__chunk_client_read_v234(db_fptr, &chunk, db_version);\n\t}\n\tif(rc > 0){\n\t\treturn rc;\n\t}else if(rc < 0){\n\t\t/* Client not loaded, but otherwise not an error */\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Empty client entry found in persistence database, it may be corrupt.\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tcontext = persist__find_or_add_context(chunk.clientid, chunk.F.last_mid);\n\tif(context){\n\t\tcontext->session_expiry_time = chunk.F.session_expiry_time;\n\t\tcontext->session_expiry_interval = chunk.F.session_expiry_interval;\n\t\tif(chunk.username && !context->username){\n\t\t\t/* username is not freed here, it is now owned by context */\n\t\t\tcontext->username = chunk.username;\n\t\t\tchunk.username = NULL;\n\t\t}\n\t\t/* in per_listener_settings mode, try to find the listener by persisted port */\n\t\tif(db.config->per_listener_settings && !context->listener && chunk.F.listener_port > 0){\n\t\t\tfor(int i=0; i < db.config->listener_count; i++){\n\t\t\t\tif(db.config->listeners[i].port == chunk.F.listener_port){\n\t\t\t\t\tcontext->listener = &db.config->listeners[i];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsession_expiry__add_from_persistence(context, chunk.F.session_expiry_time);\n\t}else{\n\t\trc = 1;\n\t}\n\n\tmosquitto_FREE(chunk.clientid);\n\tmosquitto_FREE(chunk.username);\n\tif(rc == 0){\n\t\tclient_count++;\n\t}\n\treturn rc;\n}\n\n\nstatic int persist__client_msg_chunk_restore(FILE *db_fptr, uint32_t length)\n{\n\tstruct P_client_msg chunk;\n\tint rc;\n\n\tmemset(&chunk, 0, sizeof(struct P_client_msg));\n\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_client_msg_read_v56(db_fptr, &chunk, length);\n\t}else{\n\t\trc = persist__chunk_client_msg_read_v234(db_fptr, &chunk);\n\t}\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = persist__client_msg_restore(&chunk);\n\tmosquitto_FREE(chunk.clientid);\n\n\tif(rc == 0){\n\t\tclient_msg_count++;\n\t}\n\treturn rc;\n}\n\n\nstatic int persist__base_msg_chunk_restore(FILE *db_fptr, uint32_t length)\n{\n\tstruct P_base_msg chunk;\n\tstruct mosquitto__base_msg *base_msg = NULL;\n\tint64_t message_expiry_interval64;\n\tuint32_t message_expiry_interval;\n\tuint32_t *p_message_expiry_interval = NULL;\n\tint rc = 0;\n\n\tmemset(&chunk, 0, sizeof(struct P_base_msg));\n\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_base_msg_read_v56(db_fptr, &chunk, length);\n\t}else{\n\t\trc = persist__chunk_base_msg_read_v234(db_fptr, &chunk, db_version);\n\t}\n\tif(rc){\n\t\treturn rc;\n\t}\n\tif(chunk.F.topic_len == 0){\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto cleanup;\n\t}\n\n\tif(chunk.F.source_port){\n\t\tfor(int i=0; i<db.config->listener_count; i++){\n\t\t\tif(db.config->listeners[i].port == chunk.F.source_port){\n\t\t\t\tchunk.source.listener = &db.config->listeners[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(chunk.F.expiry_time > 0){\n\t\tmessage_expiry_interval64 = chunk.F.expiry_time - time(NULL);\n\t\tif(message_expiry_interval64 < 0 || message_expiry_interval64 > UINT32_MAX){\n\t\t\t/* Expired message */\n\t\t\trc = MOSQ_ERR_SUCCESS;\n\t\t\tgoto cleanup;\n\t\t}else{\n\t\t\tmessage_expiry_interval = (uint32_t)message_expiry_interval64;\n\t\t}\n\t\tp_message_expiry_interval = &message_expiry_interval;\n\t}\n\n\tbase_msg = mosquitto_calloc(1, sizeof(struct mosquitto__base_msg));\n\tif(base_msg == NULL){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\trc = MOSQ_ERR_NOMEM;\n\t\tgoto cleanup;\n\t}\n\n\tbase_msg->data.store_id = chunk.F.store_id;\n\tbase_msg->data.source_mid = chunk.F.source_mid;\n\tbase_msg->data.topic = chunk.topic;\n\tbase_msg->data.qos = chunk.F.qos;\n\tbase_msg->data.payloadlen = chunk.F.payloadlen;\n\tbase_msg->data.retain = chunk.F.retain;\n\tbase_msg->data.properties = chunk.properties;\n\tbase_msg->data.payload = chunk.payload;\n\tbase_msg->source_listener = chunk.source.listener;\n\n\trc = db__message_store(&chunk.source, base_msg, p_message_expiry_interval,\n\t\t\tmosq_mo_client);\n\n\tmosquitto_FREE(chunk.source.id);\n\tmosquitto_FREE(chunk.source.username);\n\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tbase_msg_count++;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn rc;\n\t}\ncleanup:\n\tmosquitto_FREE(chunk.source.id);\n\tmosquitto_FREE(chunk.source.username);\n\tmosquitto_FREE(chunk.topic);\n\tmosquitto_FREE(chunk.payload);\n\treturn rc;\n}\n\n\nstatic int persist__retain_chunk_restore(FILE *db_fptr)\n{\n\tstruct mosquitto__base_msg *base_msg;\n\tstruct P_retain chunk;\n\tint rc;\n\tchar **split_topics;\n\tchar *local_topic;\n\n\tmemset(&chunk, 0, sizeof(struct P_retain));\n\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_retain_read_v56(db_fptr, &chunk);\n\t}else{\n\t\trc = persist__chunk_retain_read_v234(db_fptr, &chunk);\n\t}\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tHASH_FIND(hh, db.msg_store, &chunk.F.store_id, sizeof(chunk.F.store_id), base_msg);\n\tif(base_msg){\n\t\trc = sub__topic_tokenise(base_msg->data.topic, &local_topic, &split_topics, NULL);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\tretain__store(base_msg->data.topic, base_msg, split_topics, true);\n\t\tmosquitto_FREE(local_topic);\n\t\tmosquitto_FREE(split_topics);\n\t\tretained_count++;\n\t}else{\n\t\t/* Can't find the message - probably expired */\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int persist__sub_chunk_restore(FILE *db_fptr)\n{\n\tstruct P_sub chunk;\n\tint rc;\n\tstruct mosquitto_subscription sub;\n\n\tmemset(&chunk, 0, sizeof(struct P_sub));\n\n\tif(db_version == 6 || db_version == 5){\n\t\trc = persist__chunk_sub_read_v56(db_fptr, &chunk);\n\t}else{\n\t\trc = persist__chunk_sub_read_v234(db_fptr, &chunk);\n\t}\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tsub.clientid = chunk.clientid;\n\tsub.topic_filter = chunk.topic;\n\tsub.options = chunk.F.qos | chunk.F.options;\n\tsub.identifier = chunk.F.identifier;\n\trc = persist__restore_sub(&sub);\n\n\tmosquitto_FREE(chunk.clientid);\n\tmosquitto_FREE(chunk.topic);\n\tif(rc == 0){\n\t\tsubscription_count++;\n\t}\n\n\treturn rc;\n}\n\n\nint persist__chunk_header_read(FILE *db_fptr, uint32_t *chunk, uint32_t *length)\n{\n\tif(db_version == 6 || db_version == 5){\n\t\treturn persist__chunk_header_read_v56(db_fptr, chunk, length);\n\t}else{\n\t\treturn persist__chunk_header_read_v234(db_fptr, chunk, length);\n\t}\n}\n\n\nint persist__restore(void)\n{\n\tFILE *fptr;\n\tchar header[15];\n\tint rc = 0;\n\tuint32_t crc;\n\tuint32_t i32temp;\n\tuint32_t chunk, length;\n\tsize_t rlen;\n\tchar *err;\n\tstruct PF_cfg cfg_chunk;\n\n\tassert(db.config);\n\n\tif(!db.config->persistence || db.config->persistence_filepath == NULL){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tdb.msg_store = NULL;\n\tbase_msg_count = 0;\n\tretained_count = 0;\n\tclient_count = 0;\n\tsubscription_count = 0;\n\tclient_msg_count = 0;\n\n\tfptr = mosquitto_fopen(db.config->persistence_filepath, \"rb\", true);\n\tif(fptr == NULL){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\trlen = fread(&header, 1, 15, fptr);\n\tif(rlen == 0){\n\t\tfclose(fptr);\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Persistence file is empty.\");\n\t\treturn 0;\n\t}else if(rlen != 15){\n\t\tgoto error;\n\t}\n\tif(!memcmp(header, magic, 15)){\n\t\t/* Restore DB as normal */\n\t\tread_e(fptr, &crc, sizeof(uint32_t));\n\t\tread_e(fptr, &i32temp, sizeof(uint32_t));\n\t\tdb_version = ntohl(i32temp);\n\t\t/* IMPORTANT - this is where compatibility checks are made.\n\t\t * Is your DB change still compatible with previous versions?\n\t\t */\n\t\tif(db_version != MOSQ_DB_VERSION){\n\t\t\tif(db_version == 5){\n\t\t\t\t/* Addition of username and listener_port to client chunk in v6 */\n\t\t\t}else if(db_version == 4){\n\t\t\t}else if(db_version == 3){\n\t\t\t\t/* Addition of source_username and source_port to msg_store chunk in v4, v1.5.6 */\n\t\t\t}else if(db_version == 2){\n\t\t\t\t/* Addition of disconnect_t to client chunk in v3. */\n\t\t\t}else{\n\t\t\t\tfclose(fptr);\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unsupported persistent database format version %d (need version %d).\", db_version, MOSQ_DB_VERSION);\n\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t}\n\t\t}\n\n\t\twhile(persist__chunk_header_read(fptr, &chunk, &length) == MOSQ_ERR_SUCCESS){\n\t\t\tswitch(chunk){\n\t\t\t\tcase DB_CHUNK_CFG:\n\t\t\t\t\tif(db_version == 6 || db_version == 5){\n\t\t\t\t\t\trc = persist__chunk_cfg_read_v56(fptr, &cfg_chunk);\n\t\t\t\t\t\tif(rc){\n\t\t\t\t\t\t\tfclose(fptr);\n\t\t\t\t\t\t\treturn rc;\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\trc = persist__chunk_cfg_read_v234(fptr, &cfg_chunk);\n\t\t\t\t\t\tif(rc){\n\t\t\t\t\t\t\tfclose(fptr);\n\t\t\t\t\t\t\treturn rc;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif(cfg_chunk.dbid_size != sizeof(dbid_t)){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Incompatible database configuration (dbid size is %d bytes, expected %lu)\",\n\t\t\t\t\t\t\t\tcfg_chunk.dbid_size, (unsigned long)sizeof(dbid_t));\n\t\t\t\t\t\tfclose(fptr);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tdb.last_db_id = cfg_chunk.last_db_id;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DB_CHUNK_BASE_MSG:\n\t\t\t\t\trc = persist__base_msg_chunk_restore(fptr, length);\n\t\t\t\t\tif(rc){\n\t\t\t\t\t\tfclose(fptr);\n\t\t\t\t\t\treturn rc;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DB_CHUNK_CLIENT_MSG:\n\t\t\t\t\trc = persist__client_msg_chunk_restore(fptr, length);\n\t\t\t\t\tif(rc){\n\t\t\t\t\t\tfclose(fptr);\n\t\t\t\t\t\treturn rc;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DB_CHUNK_RETAIN:\n\t\t\t\t\trc = persist__retain_chunk_restore(fptr);\n\t\t\t\t\tif(rc){\n\t\t\t\t\t\tfclose(fptr);\n\t\t\t\t\t\treturn rc;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DB_CHUNK_SUB:\n\t\t\t\t\trc = persist__sub_chunk_restore(fptr);\n\t\t\t\t\tif(rc){\n\t\t\t\t\t\tfclose(fptr);\n\t\t\t\t\t\treturn rc;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DB_CHUNK_CLIENT:\n\t\t\t\t\trc = persist__client_chunk_restore(fptr);\n\t\t\t\t\tif(rc){\n\t\t\t\t\t\tfclose(fptr);\n\t\t\t\t\t\treturn rc;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Unsupported chunk \\\"%d\\\" in persistent database file. Ignoring.\", chunk);\n\t\t\t\t\tif(fseek(fptr, length, SEEK_CUR) < 0){\n\t\t\t\t\t\tfclose(fptr);\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to restore persistent database. Unrecognised file format.\");\n\t\trc = 1;\n\t}\n\n\tfclose(fptr);\n\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Restored %ld base messages\", base_msg_count);\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Restored %ld retained messages\", retained_count);\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Restored %ld clients\", client_count);\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Restored %ld subscriptions\", subscription_count);\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Restored %ld client messages\", client_msg_count);\n\n\treturn rc;\nerror:\n\terr = strerror(errno);\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", err);\n\tif(fptr){\n\t\tfclose(fptr);\n\t}\n\treturn MOSQ_ERR_ERRNO;\n}\n\n\nstatic int persist__restore_sub(const struct mosquitto_subscription *sub)\n{\n\tstruct mosquitto *context;\n\n\tif(!sub->clientid){\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Persistence found a subscription with no client id, ignoring.\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tif(!sub->topic_filter){\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: Persistence found a subscription with no topic filter, ignoring.\");\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tcontext = persist__find_or_add_context(sub->clientid, 0);\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn sub__add(context, sub);\n}\n\n#endif\n"
  },
  {
    "path": "src/persist_read_v234.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_PERSISTENCE\n\n#ifndef WIN32\n#include <arpa/inet.h>\n#endif\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <time.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"persist.h\"\n#include \"util_mosq.h\"\n\n\nint persist__chunk_header_read_v234(FILE *db_fptr, uint32_t *chunk, uint32_t *length)\n{\n\tsize_t rlen;\n\tuint16_t i16temp;\n\tuint32_t i32temp;\n\n\trlen = fread(&i16temp, sizeof(uint16_t), 1, db_fptr);\n\tif(rlen != 1){\n\t\treturn 1;\n\t}\n\n\trlen = fread(&i32temp, sizeof(uint32_t), 1, db_fptr);\n\tif(rlen != 1){\n\t\treturn 1;\n\t}\n\n\t*chunk = ntohs(i16temp);\n\t*length = ntohl(i32temp);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint persist__chunk_cfg_read_v234(FILE *db_fptr, struct PF_cfg *chunk)\n{\n\tint rc = MOSQ_ERR_UNKNOWN;\n\n\tread_e(db_fptr, &chunk->shutdown, sizeof(uint8_t)); /* shutdown */\n\tread_e(db_fptr, &chunk->dbid_size, sizeof(uint8_t)); /* sizeof(dbid_t) */\n\tread_e(db_fptr, &chunk->last_db_id, sizeof(dbid_t));\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\treturn rc;\n}\n\n\nint persist__chunk_client_read_v234(FILE *db_fptr, struct P_client *chunk, uint32_t db_version)\n{\n\tuint16_t i16temp;\n\tint rc;\n\ttime_t temp;\n\n\trc = persist__read_string(db_fptr, &chunk->clientid);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tread_e(db_fptr, &i16temp, sizeof(uint16_t));\n\tchunk->F.last_mid = ntohs(i16temp);\n\tif(db_version != 2){\n\t\tread_e(db_fptr, &temp, sizeof(time_t));\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\tmosquitto_FREE(chunk->clientid);\n\treturn 1;\n}\n\n\nint persist__chunk_client_msg_read_v234(FILE *db_fptr, struct P_client_msg *chunk)\n{\n\tuint16_t i16temp;\n\tint rc;\n\tuint8_t retain, dup;\n\n\trc = persist__read_string(db_fptr, &chunk->clientid);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tread_e(db_fptr, &chunk->F.store_id, sizeof(dbid_t));\n\n\tread_e(db_fptr, &i16temp, sizeof(uint16_t));\n\tchunk->F.mid = ntohs(i16temp);\n\n\tread_e(db_fptr, &chunk->F.qos, sizeof(uint8_t));\n\tread_e(db_fptr, &retain, sizeof(uint8_t));\n\tread_e(db_fptr, &chunk->F.direction, sizeof(uint8_t));\n\tread_e(db_fptr, &chunk->F.state, sizeof(uint8_t));\n\tread_e(db_fptr, &dup, sizeof(uint8_t));\n\n\tchunk->F.retain_dup = (uint8_t)((retain&0x0F)<<4 | (dup&0x0F));\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\tmosquitto_FREE(chunk->clientid);\n\treturn 1;\n}\n\n\nint persist__chunk_base_msg_read_v234(FILE *db_fptr, struct P_base_msg *chunk, uint32_t db_version)\n{\n\tuint32_t i32temp;\n\tuint16_t i16temp;\n\tint rc = 0;\n\tsize_t slen;\n\n\tread_e(db_fptr, &chunk->F.store_id, sizeof(dbid_t));\n\n\trc = persist__read_string(db_fptr, &chunk->source.id);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(db_version == 4){\n\t\trc = persist__read_string(db_fptr, &chunk->source.username);\n\t\tif(rc){\n\t\t\tgoto error;\n\t\t}\n\t\tread_e(db_fptr, &i16temp, sizeof(uint16_t));\n\t\tchunk->F.source_port = ntohs(i16temp);\n\t}\n\n\tread_e(db_fptr, &i16temp, sizeof(uint16_t));\n\tchunk->F.source_mid = ntohs(i16temp);\n\n\t/* This is the mid - don't need it */\n\tread_e(db_fptr, &i16temp, sizeof(uint16_t));\n\n\trc = persist__read_string(db_fptr, &chunk->topic);\n\tif(rc){\n\t\tgoto error;\n\t}\n\tif(!chunk->topic){\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto error;\n\t}\n\tslen = strlen(chunk->topic);\n\tif(slen > UINT16_MAX){\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto error;\n\t}\n\tchunk->F.topic_len = (uint16_t)slen;\n\n\tread_e(db_fptr, &chunk->F.qos, sizeof(uint8_t));\n\tread_e(db_fptr, &chunk->F.retain, sizeof(uint8_t));\n\n\tread_e(db_fptr, &i32temp, sizeof(uint32_t));\n\tchunk->F.payloadlen = ntohl(i32temp);\n\n\tif(chunk->F.payloadlen){\n\t\tif(chunk->F.payloadlen > MQTT_MAX_PAYLOAD){\n\t\t\trc = MOSQ_ERR_INVAL;\n\t\t\tgoto error;\n\t\t}\n\t\tchunk->payload = mosquitto_malloc(chunk->F.payloadlen+1);\n\t\tif(chunk->payload == NULL){\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t\tread_e(db_fptr, chunk->payload, chunk->F.payloadlen);\n\t\t/* Ensure zero terminated regardless of contents */\n\t\t((uint8_t *)chunk->payload)[chunk->F.payloadlen] = 0;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tmosquitto_FREE(chunk->payload);\n\tmosquitto_FREE(chunk->source.id);\n\tmosquitto_FREE(chunk->source.username);\n\tmosquitto_FREE(chunk->topic);\n\treturn rc;\n}\n\n\nint persist__chunk_retain_read_v234(FILE *db_fptr, struct P_retain *chunk)\n{\n\tdbid_t i64temp;\n\n\tif(fread(&i64temp, sizeof(dbid_t), 1, db_fptr) != 1){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\t\treturn 1;\n\t}\n\tchunk->F.store_id = i64temp;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint persist__chunk_sub_read_v234(FILE *db_fptr, struct P_sub *chunk)\n{\n\tint rc;\n\n\trc = persist__read_string(db_fptr, &chunk->clientid);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\trc = persist__read_string(db_fptr, &chunk->topic);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\tread_e(db_fptr, &chunk->F.qos, sizeof(uint8_t));\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tmosquitto_FREE(chunk->clientid);\n\tmosquitto_FREE(chunk->topic);\n\treturn rc;\n}\n\n#endif\n"
  },
  {
    "path": "src/persist_read_v5.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_PERSISTENCE\n\n#ifndef WIN32\n#include <arpa/inet.h>\n#endif\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <time.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"persist.h\"\n#include \"property_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint persist__chunk_header_read_v56(FILE *db_fptr, uint32_t *chunk, uint32_t *length)\n{\n\tsize_t rlen;\n\tstruct PF_header header;\n\n\trlen = fread(&header, sizeof(struct PF_header), 1, db_fptr);\n\tif(rlen != 1){\n\t\treturn 1;\n\t}\n\n\t*chunk = ntohl(header.chunk);\n\t*length = ntohl(header.length);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint persist__chunk_cfg_read_v56(FILE *db_fptr, struct PF_cfg *chunk)\n{\n\tif(fread(chunk, sizeof(struct PF_cfg), 1, db_fptr) != 1){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\t\treturn 1;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint persist__chunk_client_read_v56(FILE *db_fptr, struct P_client *chunk, uint32_t db_version)\n{\n\tint rc;\n\n\tif(db_version == 6){\n\t\tread_e(db_fptr, &chunk->F, sizeof(struct PF_client));\n\t\tchunk->F.username_len = ntohs(chunk->F.username_len);\n\t\tchunk->F.listener_port = ntohs(chunk->F.listener_port);\n\t}else if(db_version == 5){\n\t\tread_e(db_fptr, &chunk->F, sizeof(struct PF_client_v5));\n\t}else{\n\t\treturn 1;\n\t}\n\n\tchunk->F.session_expiry_interval = ntohl(chunk->F.session_expiry_interval);\n\tchunk->F.last_mid = ntohs(chunk->F.last_mid);\n\tchunk->F.id_len = ntohs(chunk->F.id_len);\n\n\n\trc = persist__read_string_len(db_fptr, &chunk->clientid, chunk->F.id_len);\n\tif(rc){\n\t\treturn 1;\n\t}else if(chunk->clientid == NULL){\n\t\treturn -1;\n\t}\n\n\tif(chunk->F.username_len > 0){\n\t\trc = persist__read_string_len(db_fptr, &chunk->username, chunk->F.username_len);\n\t\tif(rc || !chunk->username){\n\t\t\tmosquitto_FREE(chunk->clientid);\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\treturn 1;\n}\n\n\nint persist__chunk_client_msg_read_v56(FILE *db_fptr, struct P_client_msg *chunk, uint32_t length)\n{\n\tmosquitto_property *properties = NULL, *p;\n\tstruct mosquitto__packet_in prop_packet;\n\tint rc;\n\n\tmemset(&prop_packet, 0, sizeof(struct mosquitto__packet_in));\n\n\tread_e(db_fptr, &chunk->F, sizeof(struct PF_client_msg));\n\tchunk->F.mid = ntohs(chunk->F.mid);\n\tchunk->F.id_len = ntohs(chunk->F.id_len);\n\n\tlength -= (uint32_t)(sizeof(struct PF_client_msg) + chunk->F.id_len);\n\tif(length > MQTT_MAX_PAYLOAD){\n\t\tgoto error;\n\t}\n\n\trc = persist__read_string_len(db_fptr, &chunk->clientid, chunk->F.id_len);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(length > 0){\n\t\tprop_packet.remaining_length = length;\n\t\tprop_packet.payload = mosquitto_malloc(length);\n\t\tif(!prop_packet.payload){\n\t\t\terrno = ENOMEM;\n\t\t\tgoto error;\n\t\t}\n\n\t\tread_e(db_fptr, prop_packet.payload, length);\n\t\trc = property__read_all(CMD_PUBLISH, &prop_packet, &properties);\n\t\tmosquitto_FREE(prop_packet.payload);\n\t\tif(rc){\n\t\t\tmosquitto_FREE(chunk->clientid);\n\t\t\treturn rc;\n\t\t}\n\n\t\tif(properties){\n\t\t\tp = properties;\n\t\t\twhile(p){\n\t\t\t\tif(mosquitto_property_identifier(p) == MQTT_PROP_SUBSCRIPTION_IDENTIFIER){\n\t\t\t\t\tchunk->subscription_identifier = mosquitto_property_varint_value(p);\n\t\t\t\t}\n\t\t\t\tp = mosquitto_property_next(p);\n\t\t\t}\n\t\t\tmosquitto_property_free_all(&properties);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tmosquitto_FREE(chunk->clientid);\n\tmosquitto_FREE(prop_packet.payload);\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\treturn 1;\n}\n\n\nint persist__chunk_base_msg_read_v56(FILE *db_fptr, struct P_base_msg *chunk, uint32_t length)\n{\n\tint rc = 0;\n\tmosquitto_property *properties = NULL;\n\tstruct mosquitto__packet_in prop_packet;\n\n\tmemset(&prop_packet, 0, sizeof(struct mosquitto__packet_in));\n\n\tread_e(db_fptr, &chunk->F, sizeof(struct PF_base_msg));\n\tchunk->F.payloadlen = ntohl(chunk->F.payloadlen);\n\tif(chunk->F.payloadlen > MQTT_MAX_PAYLOAD){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tchunk->F.source_mid = ntohs(chunk->F.source_mid);\n\tchunk->F.source_id_len = ntohs(chunk->F.source_id_len);\n\tchunk->F.source_username_len = ntohs(chunk->F.source_username_len);\n\tchunk->F.topic_len = ntohs(chunk->F.topic_len);\n\tchunk->F.source_port = ntohs(chunk->F.source_port);\n\n\tlength -= (uint32_t)(sizeof(struct PF_base_msg) + chunk->F.payloadlen + chunk->F.source_id_len + chunk->F.source_username_len + chunk->F.topic_len);\n\tif(length > MQTT_MAX_PAYLOAD){\n\t\tgoto error;\n\t}\n\n\tif(chunk->F.source_id_len){\n\t\trc = persist__read_string_len(db_fptr, &chunk->source.id, chunk->F.source_id_len);\n\t\tif(rc){\n\t\t\tgoto error;\n\t\t}\n\t}\n\tif(chunk->F.source_username_len){\n\t\trc = persist__read_string_len(db_fptr, &chunk->source.username, chunk->F.source_username_len);\n\t\tif(rc){\n\t\t\tgoto error;\n\t\t}\n\t}\n\trc = persist__read_string_len(db_fptr, &chunk->topic, chunk->F.topic_len);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\tif(chunk->F.payloadlen > 0){\n\t\tchunk->payload = mosquitto_malloc(chunk->F.payloadlen+1);\n\t\tif(chunk->payload == NULL){\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t\tread_e(db_fptr, chunk->payload, chunk->F.payloadlen);\n\t\t/* Ensure zero terminated regardless of contents */\n\t\t((uint8_t *)chunk->payload)[chunk->F.payloadlen] = 0;\n\t}\n\n\tif(length > 0){\n\t\tprop_packet.remaining_length = length;\n\t\tprop_packet.payload = mosquitto_malloc(length);\n\t\tif(!prop_packet.payload){\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t\tread_e(db_fptr, prop_packet.payload, length);\n\t\trc = property__read_all(CMD_PUBLISH, &prop_packet, &properties);\n\t\tmosquitto_FREE(prop_packet.payload);\n\t\tif(rc){\n\t\t\trc = MOSQ_ERR_NOMEM;\n\t\t\tgoto error;\n\t\t}\n\t}\n\tchunk->properties = properties;\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tmosquitto_FREE(chunk->payload);\n\tmosquitto_FREE(chunk->source.id);\n\tmosquitto_FREE(chunk->source.username);\n\tmosquitto_FREE(chunk->topic);\n\tmosquitto_FREE(prop_packet.payload);\n\treturn rc;\n}\n\n\nint persist__chunk_retain_read_v56(FILE *db_fptr, struct P_retain *chunk)\n{\n\tif(fread(&chunk->F, sizeof(struct P_retain), 1, db_fptr) != 1){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\t\treturn 1;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint persist__chunk_sub_read_v56(FILE *db_fptr, struct P_sub *chunk)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tread_e(db_fptr, &chunk->F, sizeof(struct PF_sub));\n\tchunk->F.identifier = ntohl(chunk->F.identifier);\n\tchunk->F.id_len = ntohs(chunk->F.id_len);\n\tchunk->F.topic_len = ntohs(chunk->F.topic_len);\n\n\trc = persist__read_string_len(db_fptr, &chunk->clientid, chunk->F.id_len);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\trc = persist__read_string_len(db_fptr, &chunk->topic, chunk->F.topic_len);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tmosquitto_FREE(chunk->clientid);\n\treturn rc;\n}\n\n#endif\n"
  },
  {
    "path": "src/persist_write.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_PERSISTENCE\n\n#ifndef WIN32\n#include <arpa/inet.h>\n#endif\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <time.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"persist.h\"\n#include \"util_mosq.h\"\n\n\nstatic int persist__client_messages_save(FILE *db_fptr, struct mosquitto *context, struct mosquitto__client_msg *queue)\n{\n\tstruct P_client_msg chunk;\n\tstruct mosquitto__client_msg *cmsg;\n\tint rc;\n\n\tassert(db_fptr);\n\tassert(context);\n\n\tcmsg = queue;\n\twhile(cmsg){\n\t\tif(!strncmp(cmsg->base_msg->data.topic, \"$SYS\", 4)\n\t\t\t\t&& cmsg->base_msg->ref_count <= 1\n\t\t\t\t&& cmsg->base_msg->dest_id_count == 0){\n\n\t\t\t/* This $SYS message won't have been persisted, so we can't persist\n\t\t\t * this client message. */\n\t\t\tcmsg = cmsg->next;\n\t\t\tcontinue;\n\t\t}\n\n\t\tmemset(&chunk, 0, sizeof(struct P_client_msg));\n\n\t\tchunk.F.store_id = cmsg->base_msg->data.store_id;\n\t\tchunk.F.mid = cmsg->data.mid;\n\t\tchunk.F.id_len = (uint16_t)strlen(context->id);\n\t\tchunk.F.qos = cmsg->data.qos;\n\t\tchunk.F.retain_dup = (uint8_t)((cmsg->data.retain&0x0F)<<4 | (cmsg->data.dup&0x0F));\n\t\tchunk.F.direction = (uint8_t)cmsg->data.direction;\n\t\tchunk.F.state = (uint8_t)cmsg->data.state;\n\t\tchunk.clientid = context->id;\n\t\tchunk.subscription_identifier = cmsg->data.subscription_identifier;\n\n\t\trc = persist__chunk_client_msg_write_v6(db_fptr, &chunk);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\n\t\tcmsg = cmsg->next;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int persist__message_store_save(FILE *db_fptr)\n{\n\tstruct P_base_msg chunk;\n\tstruct mosquitto__base_msg *base_msg, *base_msg_tmp;\n\tint rc;\n\n\tassert(db_fptr);\n\n\tbase_msg = db.msg_store;\n\tHASH_ITER(hh, db.msg_store, base_msg, base_msg_tmp){\n\t\tif(base_msg->ref_count < 1 || base_msg->data.topic == NULL){\n\t\t\tcontinue;\n\t\t}\n\n\t\tmemset(&chunk, 0, sizeof(struct P_base_msg));\n\n\t\tif(!strncmp(base_msg->data.topic, \"$SYS\", 4)){\n\t\t\tif(base_msg->ref_count <= 1 && base_msg->dest_id_count == 0){\n\t\t\t\t/* $SYS messages that are only retained shouldn't be persisted. */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t/* Don't save $SYS messages as retained otherwise they can give\n\t\t\t * misleading information when reloaded. They should still be saved\n\t\t\t * because a disconnected durable client may have them in their\n\t\t\t * queue. */\n\t\t\tchunk.F.retain = 0;\n\t\t}else{\n\t\t\tchunk.F.retain = (uint8_t)base_msg->data.retain;\n\t\t}\n\n\t\tchunk.F.store_id = base_msg->data.store_id;\n\t\tchunk.F.expiry_time = base_msg->data.expiry_time;\n\t\tchunk.F.payloadlen = base_msg->data.payloadlen;\n\t\tchunk.F.source_mid = base_msg->data.source_mid;\n\t\tif(base_msg->data.source_id){\n\t\t\tchunk.F.source_id_len = (uint16_t)strlen(base_msg->data.source_id);\n\t\t\tchunk.source.id = base_msg->data.source_id;\n\t\t}else{\n\t\t\tchunk.F.source_id_len = 0;\n\t\t\tchunk.source.id = NULL;\n\t\t}\n\t\tif(base_msg->data.source_username){\n\t\t\tchunk.F.source_username_len = (uint16_t)strlen(base_msg->data.source_username);\n\t\t\tchunk.source.username = base_msg->data.source_username;\n\t\t}else{\n\t\t\tchunk.F.source_username_len = 0;\n\t\t\tchunk.source.username = NULL;\n\t\t}\n\n\t\tchunk.F.topic_len = (uint16_t)strlen(base_msg->data.topic);\n\t\tchunk.topic = base_msg->data.topic;\n\n\t\tif(base_msg->source_listener){\n\t\t\tchunk.F.source_port = base_msg->source_listener->port;\n\t\t}else{\n\t\t\tchunk.F.source_port = 0;\n\t\t}\n\t\tchunk.F.qos = base_msg->data.qos;\n\t\tchunk.payload = base_msg->data.payload;\n\t\tchunk.properties = base_msg->data.properties;\n\n\t\trc = persist__chunk_message_store_write_v6(db_fptr, &chunk);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int persist__client_save(FILE *db_fptr)\n{\n\tstruct mosquitto *context, *ctxt_tmp;\n\tstruct P_client chunk;\n\tint rc;\n\n\tassert(db_fptr);\n\n\tHASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){\n\t\tmemset(&chunk, 0, sizeof(struct P_client));\n\n\t\tif(context &&\n\t\t\t\tcontext->session_expiry_interval != MQTT_SESSION_EXPIRY_IMMEDIATE &&\n#ifdef WITH_BRIDGE\n\t\t\t\t((!context->bridge && context->clean_start == false)\n\t\t\t\t|| (context->bridge && context->bridge->clean_start_local == false))\n#else\n\t\t\t\tcontext->clean_start == false\n#endif\n\t\t\t\t){\n\t\t\tchunk.F.session_expiry_time = context->session_expiry_time;\n\t\t\tif(context->session_expiry_interval != MQTT_SESSION_EXPIRY_NEVER\n\t\t\t\t\t&& context->session_expiry_time == 0){\n\n\t\t\t\tchunk.F.session_expiry_time = context->session_expiry_interval + db.now_real_s;\n\t\t\t}else{\n\t\t\t\tchunk.F.session_expiry_time = context->session_expiry_time;\n\t\t\t}\n\t\t\tchunk.F.session_expiry_interval = context->session_expiry_interval;\n\t\t\tchunk.F.last_mid = context->last_mid;\n\t\t\tchunk.F.id_len = (uint16_t)strlen(context->id);\n\t\t\tchunk.clientid = context->id;\n\t\t\tif(context->username){\n\t\t\t\tchunk.F.username_len = (uint16_t)strlen(context->username);\n\t\t\t\tchunk.username = context->username;\n\t\t\t}\n\t\t\tif(context->listener){\n\t\t\t\tchunk.F.listener_port = context->listener->port;\n\t\t\t}\n\n\t\t\tif(chunk.F.id_len == 0){\n\t\t\t\t/* This should never happen, but in case we have a client with\n\t\t\t\t * zero length ID, don't persist them. */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\trc = persist__chunk_client_write_v6(db_fptr, &chunk);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\n\t\t\tif(persist__client_messages_save(db_fptr, context, context->msgs_in.inflight)){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(persist__client_messages_save(db_fptr, context, context->msgs_in.queued)){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(persist__client_messages_save(db_fptr, context, context->msgs_out.inflight)){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(persist__client_messages_save(db_fptr, context, context->msgs_out.queued)){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int persist__subs_save(FILE *db_fptr, struct mosquitto__subhier *node, const char *topic, int level)\n{\n\tstruct mosquitto__subhier *subhier, *subhier_tmp;\n\tstruct mosquitto__subleaf *sub;\n\tstruct P_sub sub_chunk;\n\tchar *thistopic;\n\tsize_t slen;\n\tint rc;\n\n\tslen = strlen(topic) + node->topic_len + 2;\n\tthistopic = mosquitto_malloc(sizeof(char)*slen);\n\tif(!thistopic){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tif(level > 1 || strlen(topic)){\n\t\tsnprintf(thistopic, slen, \"%s/%s\", topic, node->topic);\n\t}else{\n\t\tsnprintf(thistopic, slen, \"%s\", node->topic);\n\t}\n\n\tsub = node->subs;\n\twhile(sub){\n\t\tif(sub->context->session_expiry_interval != MQTT_SESSION_EXPIRY_IMMEDIATE\n\t\t\t\t&& sub->context->clean_start == false\n\t\t\t\t&& sub->context->id){\n\n\t\t\tmemset(&sub_chunk, 0, sizeof(struct P_sub));\n\n\t\t\tsub_chunk.F.identifier = sub->identifier;\n\t\t\tsub_chunk.F.id_len = (uint16_t)strlen(sub->context->id);\n\t\t\tsub_chunk.F.topic_len = (uint16_t)strlen(thistopic);\n\t\t\tsub_chunk.F.qos = MQTT_SUB_OPT_GET_QOS(sub->subscription_options);\n\t\t\tsub_chunk.F.options = sub->subscription_options & 0xFC;\n\t\t\tsub_chunk.clientid = sub->context->id;\n\t\t\tsub_chunk.topic = thistopic;\n\n\t\t\trc = persist__chunk_sub_write_v6(db_fptr, &sub_chunk);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_FREE(thistopic);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t\tsub = sub->next;\n\t}\n\n\tHASH_ITER(hh, node->children, subhier, subhier_tmp){\n\t\tpersist__subs_save(db_fptr, subhier, thistopic, level+1);\n\t}\n\tmosquitto_FREE(thistopic);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int persist__subs_save_all(FILE *db_fptr)\n{\n\tstruct mosquitto__subhier *subhier, *subhier_tmp;\n\n\tHASH_ITER(hh, db.normal_subs, subhier, subhier_tmp){\n\t\tif(subhier->children){\n\t\t\tpersist__subs_save(db_fptr, subhier->children, \"\", 0);\n\t\t}\n\t}\n\n\tHASH_ITER(hh, db.shared_subs, subhier, subhier_tmp){\n\t\tif(subhier->children){\n\t\t\tpersist__subs_save(db_fptr, subhier->children, \"\", 0);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int persist__retain_save(FILE *db_fptr, struct mosquitto__retainhier *node, int level)\n{\n\tstruct mosquitto__retainhier *retainhier, *retainhier_tmp;\n\tstruct P_retain retain_chunk;\n\tint rc;\n\n\tif(node->retained && strncmp(node->retained->data.topic, \"$SYS\", 4)){\n\t\tmemset(&retain_chunk, 0, sizeof(struct P_retain));\n\n\t\t/* Don't save $SYS messages. */\n\t\tretain_chunk.F.store_id = node->retained->data.store_id;\n\t\trc = persist__chunk_retain_write_v6(db_fptr, &retain_chunk);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tHASH_ITER(hh, node->children, retainhier, retainhier_tmp){\n\t\tpersist__retain_save(db_fptr, retainhier, level+1);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int persist__retain_save_all(FILE *db_fptr)\n{\n\tstruct mosquitto__retainhier *retainhier, *retainhier_tmp;\n\n\tHASH_ITER(hh, db.retains, retainhier, retainhier_tmp){\n\t\tif(retainhier->children){\n\t\t\tpersist__retain_save(db_fptr, retainhier->children, 0);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\nstatic int persist__write_data(FILE *db_fptr, void *user_data);\n\n\nstatic void persist__log_write_error(const char *msg)\n{\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error saving in-memory database, %s\", msg);\n}\n\n\nint persist__backup(bool shutdown)\n{\n\tif(db.config == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(db.config->persistence == false){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\tif(db.config->persistence_filepath == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Saving in-memory database to %s.\", db.config->persistence_filepath);\n\n\treturn mosquitto_write_file(db.config->persistence_filepath, true, &persist__write_data, &shutdown, &persist__log_write_error);\n}\n\n\nstatic int persist__write_data(FILE *db_fptr, void *user_data)\n{\n\tbool shutdown = *(bool *)(user_data);\n\tuint32_t db_version_w = htonl(MOSQ_DB_VERSION);\n\tuint32_t crc = 0;\n\tconst char *err;\n\tstruct PF_cfg cfg_chunk;\n\tint rc = MOSQ_ERR_UNKNOWN;\n\n\t/* Header */\n\twrite_e(db_fptr, magic, 15);\n\twrite_e(db_fptr, &crc, sizeof(uint32_t));\n\twrite_e(db_fptr, &db_version_w, sizeof(uint32_t));\n\n\tmemset(&cfg_chunk, 0, sizeof(struct PF_cfg));\n\tcfg_chunk.last_db_id = db.last_db_id;\n\tcfg_chunk.shutdown = shutdown;\n\tcfg_chunk.dbid_size = sizeof(dbid_t);\n\tif(persist__chunk_cfg_write_v6(db_fptr, &cfg_chunk)){\n\t\tgoto error;\n\t}\n\n\tif(persist__message_store_save(db_fptr)){\n\t\tgoto error;\n\t}\n\n\tif(persist__client_save(db_fptr)\n\t\t\t|| persist__subs_save_all(db_fptr)\n\t\t\t|| persist__retain_save_all(db_fptr)){\n\t\tgoto error;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n\nerror:\n\terr = strerror(errno);\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error during saving in-memory database %s: %s.\", db.config->persistence_filepath, err);\n\tif(db_fptr){\n\t\tfclose(db_fptr);\n\t}\n\treturn rc;\n}\n\n\n#endif\n"
  },
  {
    "path": "src/persist_write_v5.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#ifdef WITH_PERSISTENCE\n\n#ifndef WIN32\n#include <arpa/inet.h>\n#endif\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <time.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"persist.h\"\n#include \"packet_mosq.h\"\n#include \"property_common.h\"\n#include \"property_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint persist__chunk_cfg_write_v6(FILE *db_fptr, struct PF_cfg *chunk)\n{\n\tstruct PF_header header;\n\n\theader.chunk = htonl(DB_CHUNK_CFG);\n\theader.length = htonl(sizeof(struct PF_cfg));\n\twrite_e(db_fptr, &header, sizeof(struct PF_header));\n\twrite_e(db_fptr, chunk, sizeof(struct PF_cfg));\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\treturn 1;\n}\n\n\nint persist__chunk_client_write_v6(FILE *db_fptr, struct P_client *chunk)\n{\n\tstruct PF_header header;\n\tuint16_t id_len = chunk->F.id_len;\n\tuint16_t username_len = chunk->F.username_len;\n\n\tchunk->F.session_expiry_interval = htonl(chunk->F.session_expiry_interval);\n\tchunk->F.last_mid = htons(chunk->F.last_mid);\n\tchunk->F.id_len = htons(chunk->F.id_len);\n\tchunk->F.username_len = htons(chunk->F.username_len);\n\tchunk->F.listener_port = htons(chunk->F.listener_port);\n\n\theader.chunk = htonl(DB_CHUNK_CLIENT);\n\theader.length = htonl((uint32_t)sizeof(struct PF_client)+id_len+username_len);\n\n\twrite_e(db_fptr, &header, sizeof(struct PF_header));\n\twrite_e(db_fptr, &chunk->F, sizeof(struct PF_client));\n\n\twrite_e(db_fptr, chunk->clientid, id_len);\n\tif(username_len > 0){\n\t\twrite_e(db_fptr, chunk->username, username_len);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\treturn 1;\n}\n\n\nint persist__chunk_client_msg_write_v6(FILE *db_fptr, struct P_client_msg *chunk)\n{\n\tstruct PF_header header;\n\tstruct mosquitto__packet *prop_packet = NULL;\n\tuint16_t id_len = chunk->F.id_len;\n\tuint32_t proplen = 0;\n\tint rc;\n\tmosquitto_property subscription_id_prop = {\n\t\t.next = NULL, .identifier = MQTT_PROP_SUBSCRIPTION_IDENTIFIER, .client_generated = true, .property_type = MQTT_PROP_TYPE_VARINT\n\t};\n\n\tif(chunk->subscription_identifier){\n\t\tsubscription_id_prop.value.varint = chunk->subscription_identifier;\n\t\tproplen += mosquitto_property_get_remaining_length(&subscription_id_prop);\n\t}\n\n\tchunk->F.mid = htons(chunk->F.mid);\n\tchunk->F.id_len = htons(chunk->F.id_len);\n\n\theader.chunk = htonl(DB_CHUNK_CLIENT_MSG);\n\theader.length = htonl((uint32_t)sizeof(struct PF_client_msg) + id_len + proplen);\n\n\twrite_e(db_fptr, &header, sizeof(struct PF_header));\n\twrite_e(db_fptr, &chunk->F, sizeof(struct PF_client_msg));\n\twrite_e(db_fptr, chunk->clientid, id_len);\n\tif(chunk->subscription_identifier){\n\t\tif(proplen > 0){\n\t\t\tprop_packet = mosquitto_calloc(1, sizeof(struct mosquitto__packet)+proplen);\n\t\t\tif(prop_packet == NULL){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tprop_packet->remaining_length = proplen;\n\t\t\tprop_packet->packet_length = proplen;\n\t\t\trc = property__write_all(prop_packet, &subscription_id_prop, true);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_FREE(prop_packet);\n\t\t\t\treturn rc;\n\t\t\t}\n\n\t\t\twrite_e(db_fptr, prop_packet->payload, proplen);\n\t\t\tmosquitto_FREE(prop_packet);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\tmosquitto_FREE(prop_packet);\n\treturn 1;\n}\n\n\nint persist__chunk_message_store_write_v6(FILE *db_fptr, struct P_base_msg *chunk)\n{\n\tstruct PF_header header;\n\tuint32_t payloadlen = chunk->F.payloadlen;\n\tuint16_t source_id_len = chunk->F.source_id_len;\n\tuint16_t source_username_len = chunk->F.source_username_len;\n\tuint16_t topic_len = chunk->F.topic_len;\n\tuint32_t proplen = 0;\n\tint rc;\n\n\tif(chunk->properties){\n\t\tproplen += mosquitto_property_get_remaining_length(chunk->properties);\n\t}\n\n\tchunk->F.payloadlen = htonl(chunk->F.payloadlen);\n\tchunk->F.source_mid = htons(chunk->F.source_mid);\n\tchunk->F.source_id_len = htons(chunk->F.source_id_len);\n\tchunk->F.source_username_len = htons(chunk->F.source_username_len);\n\tchunk->F.topic_len = htons(chunk->F.topic_len);\n\tchunk->F.source_port = htons(chunk->F.source_port);\n\n\theader.chunk = htonl(DB_CHUNK_BASE_MSG);\n\theader.length = htonl((uint32_t)sizeof(struct PF_base_msg) +\n\t\t\ttopic_len + payloadlen +\n\t\t\tsource_id_len + source_username_len + proplen);\n\n\twrite_e(db_fptr, &header, sizeof(struct PF_header));\n\twrite_e(db_fptr, &chunk->F, sizeof(struct PF_base_msg));\n\tif(source_id_len){\n\t\twrite_e(db_fptr, chunk->source.id, source_id_len);\n\t}\n\tif(source_username_len){\n\t\twrite_e(db_fptr, chunk->source.username, source_username_len);\n\t}\n\twrite_e(db_fptr, chunk->topic, topic_len);\n\tif(payloadlen){\n\t\twrite_e(db_fptr, chunk->payload, (unsigned int)payloadlen);\n\t}\n\tif(chunk->properties){\n\t\tif(proplen > 0){\n\t\t\tstruct mosquitto__packet *prop_packet = mosquitto_calloc(1, sizeof(struct mosquitto__packet)+proplen);\n\t\t\tif(prop_packet == NULL){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tprop_packet->remaining_length = proplen;\n\t\t\tprop_packet->packet_length = proplen;\n\t\t\trc = property__write_all(prop_packet, chunk->properties, true);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_FREE(prop_packet);\n\t\t\t\treturn rc;\n\t\t\t}\n\n\t\t\tif(fwrite(prop_packet->payload, 1, proplen, db_fptr) != proplen){\n\t\t\t\tmosquitto_FREE(prop_packet);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t\tmosquitto_FREE(prop_packet);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\treturn 1;\n}\n\n\nint persist__chunk_retain_write_v6(FILE *db_fptr, struct P_retain *chunk)\n{\n\tstruct PF_header header;\n\n\theader.chunk = htonl(DB_CHUNK_RETAIN);\n\theader.length = htonl((uint32_t)sizeof(struct PF_retain));\n\n\twrite_e(db_fptr, &header, sizeof(struct PF_header));\n\twrite_e(db_fptr, &chunk->F, sizeof(struct PF_retain));\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\treturn 1;\n}\n\n\nint persist__chunk_sub_write_v6(FILE *db_fptr, struct P_sub *chunk)\n{\n\tstruct PF_header header;\n\tuint16_t id_len = chunk->F.id_len;\n\tuint16_t topic_len = chunk->F.topic_len;\n\n\tchunk->F.identifier = htonl(chunk->F.identifier);\n\tchunk->F.id_len = htons(chunk->F.id_len);\n\tchunk->F.topic_len = htons(chunk->F.topic_len);\n\n\theader.chunk = htonl(DB_CHUNK_SUB);\n\theader.length = htonl((uint32_t)sizeof(struct PF_sub) +\n\t\t\tid_len + topic_len);\n\n\twrite_e(db_fptr, &header, sizeof(struct PF_header));\n\twrite_e(db_fptr, &chunk->F, sizeof(struct PF_sub));\n\twrite_e(db_fptr, chunk->clientid, id_len);\n\twrite_e(db_fptr, chunk->topic, topic_len);\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: %s.\", strerror(errno));\n\treturn 1;\n}\n#endif\n"
  },
  {
    "path": "src/plugin_acl_check.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto/broker.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"lib_load.h\"\n#include \"utlist.h\"\n\n\nint acl__pre_check(mosquitto_plugin_id_t *plugin, struct mosquitto *context, int access)\n{\n\tconst char *username;\n\n\tusername = mosquitto_client_username(context);\n\tif(plugin->config.deny_special_chars == true){\n\t\t/* Check whether the client id or username contains a +, # or / and if\n\t\t* so deny access.\n\t\t*\n\t\t* Do this check for every message regardless, we have to protect the\n\t\t* plugins against possible pattern based attacks.\n\t\t*/\n\t\tif(username && strpbrk(username, \"+#/\")){\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"ACL denying access to client with dangerous username \\\"%s\\\"\", username);\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\t\tif(context->id && strpbrk(context->id, \"+#/\")){\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"ACL denying access to client with dangerous client id \\\"%s\\\"\", context->id);\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\t}\n\n\tif(plugin->lib.version == 4){\n\t\tif(access == MOSQ_ACL_UNSUBSCRIBE){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}else if(plugin->lib.version == 3){\n\t\tif(access == MOSQ_ACL_UNSUBSCRIBE){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}else if(plugin->lib.version == 2){\n\t\tif(access == MOSQ_ACL_SUBSCRIBE || access == MOSQ_ACL_UNSUBSCRIBE){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n\n\nstatic int acl__check_dollar(const char *topic, int access)\n{\n\tint rc;\n\tbool match = false;\n\n\tif(topic[0] != '$'){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(!strncmp(topic, \"$SYS\", 4)){\n\t\tif(access == MOSQ_ACL_WRITE){\n\t\t\t/* Potentially allow write access for bridge status, otherwise explicitly deny. */\n\t\t\trc = mosquitto_topic_matches_sub(\"$SYS/broker/connection/+/state\", topic, &match);\n\t\t\tif(rc == MOSQ_ERR_SUCCESS && match == true){\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t\t}\n\t\t}else{\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}else if(!strncmp(topic, \"$share\", 6)){\n\t\t/* Only allow sub/unsub to shared subscriptions */\n\t\tif(access == MOSQ_ACL_SUBSCRIBE || access == MOSQ_ACL_UNSUBSCRIBE){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\t}else{\n\t\t/* This is an unknown $ topic, for the moment just defer to actual tests. */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nstatic int plugin__acl_check(struct mosquitto__security_options *opts, struct mosquitto *context, const char *topic, uint32_t payloadlen, void *payload, uint8_t qos, bool retain, mosquitto_property *properties, int access)\n{\n\tint rc = MOSQ_ERR_PLUGIN_DEFER;\n\tstruct mosquitto_acl_msg msg;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto_evt_acl_check event_data;\n\n\tmemset(&msg, 0, sizeof(msg));\n\tmsg.topic = topic;\n\tmsg.payloadlen = payloadlen;\n\tmsg.payload = payload;\n\tmsg.qos = qos;\n\tmsg.retain = retain;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.acl_check, cb_base, cb_next){\n\t\t/* FIXME - username deny special chars */\n\n\t\tmemset(&event_data, 0, sizeof(event_data));\n\t\tevent_data.client = context;\n\t\tevent_data.access = access;\n\t\tevent_data.topic = topic;\n\t\tevent_data.payloadlen = payloadlen;\n\t\tevent_data.payload = payload;\n\t\tevent_data.qos = qos;\n\t\tevent_data.retain = retain;\n\t\tevent_data.properties = properties;\n\t\trc = cb_base->cb(MOSQ_EVT_ACL_CHECK, &event_data, cb_base->userdata);\n\t\tif(rc != MOSQ_ERR_PLUGIN_DEFER && rc != MOSQ_ERR_PLUGIN_IGNORE){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\nint mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void *payload, uint8_t qos, bool retain, mosquitto_property *properties, int access)\n{\n\tint rc;\n\tint rc_final;\n\n\tif(!context->id){\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n\tif(context->bridge){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\trc = acl__check_dollar(topic, access);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\t/*\n\t * If no plugins exist we should accept at this point so set rc to success.\n\t */\n\trc_final = MOSQ_ERR_SUCCESS;\n\n\tif(db.config->security_options.plugin_callbacks.acl_check){\n\t\trc = plugin__acl_check(&db.config->security_options, context, topic, payloadlen,\n\t\t\t\tpayload, qos, retain, properties, access);\n\n\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE){\n\t\t\t/* Do nothing, this is as if the plugin doesn't exist */\n\t\t}else if(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\trc_final = MOSQ_ERR_PLUGIN_DEFER;\n\t\t}else{\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tif(context->listener){\n\t\tif(context->listener->security_options->plugin_callbacks.acl_check){\n\t\t\trc = plugin__acl_check(context->listener->security_options, context, topic, payloadlen,\n\t\t\t\t\tpayload, qos, retain, properties, access);\n\n\t\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE){\n\t\t\t\t/* Do nothing, this is as if the plugin doesn't exist */\n\t\t\t}else if(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\t\trc_final = MOSQ_ERR_PLUGIN_DEFER;\n\t\t\t}else{\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(db.config->per_listener_settings){\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\t}\n\n\t/* If all plugins deferred, this is a denial. If rc == MOSQ_ERR_SUCCESS\n\t * here, then no plugins were configured, or all plugins ignored. */\n\tif(rc_final == MOSQ_ERR_PLUGIN_DEFER){\n\t\trc_final = MOSQ_ERR_ACL_DENIED;\n\t}\n\treturn rc_final;\n}\n"
  },
  {
    "path": "src/plugin_basic_auth.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto/broker.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"lib_load.h\"\n#include \"utlist.h\"\n\n\nstatic int plugin__basic_auth(struct mosquitto__security_options *opts, struct mosquitto *context)\n{\n\tstruct mosquitto_evt_basic_auth event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tint rc;\n\tint rc_final = MOSQ_ERR_PLUGIN_IGNORE;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.basic_auth, cb_base, cb_next){\n\t\tmemset(&event_data, 0, sizeof(event_data));\n\t\tevent_data.client = context;\n\t\tevent_data.username = context->username;\n\t\tevent_data.password = context->password;\n\t\tevent_data.password_len = context->password_len;\n\t\trc = cb_base->cb(MOSQ_EVT_BASIC_AUTH, &event_data, cb_base->userdata);\n\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE){\n\t\t\t/* Do nothing, this is as if the plugin doesn't exist */\n\t\t}else if(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\trc_final = MOSQ_ERR_PLUGIN_DEFER;\n\t\t}else{\n\t\t\treturn rc;\n\t\t}\n\t}\n\treturn rc_final;\n}\n\n\nint mosquitto_basic_auth(struct mosquitto *context)\n{\n\tint rc;\n\tbool plugin_used = false;\n\n\t/* Global plugins */\n\tif(db.config->security_options.plugin_callbacks.basic_auth){\n\t\trc = plugin__basic_auth(&db.config->security_options, context);\n\n\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE){\n\t\t\t/* Do nothing */\n\t\t}else if(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\tplugin_used = true;\n\t\t}else{\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tif(context->listener && context->listener->security_options->plugin_callbacks.basic_auth){\n\t\trc = plugin__basic_auth(context->listener->security_options, context);\n\n\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE){\n\t\t\t/* Do nothing */\n\t\t}else if(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\tplugin_used = true;\n\t\t}else{\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\t/* If all plugins deferred, this is a denial. plugin_used == false\n\t * here, then no plugins were configured.\n\t * anonymous logins are allowed. */\n\tif(plugin_used == false){\n\t\tif((context->listener && context->listener->security_options->allow_anonymous == true)\n\t\t\t\t|| (!db.config->per_listener_settings && db.config->security_options.allow_anonymous == true\n\t\t\t\t&& context->listener && context->listener->security_options->allow_anonymous != false)){\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}else{\n\t\t/* Can't have got here without at least one plugin returning MOSQ_ERR_PLUGIN_DEFER.\n\t\t * This will now be a denial, unless it is anon and allow anon is true. */\n\t\tif(context->username == NULL &&\n\t\t\t\t((context->listener && context->listener->security_options->allow_anonymous == true)\n\t\t\t\t|| (!db.config->per_listener_settings && db.config->security_options.allow_anonymous == true\n\t\t\t\t&& context->listener && context->listener->security_options->allow_anonymous != false))){\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}\n\n\treturn rc;\n}\n"
  },
  {
    "path": "src/plugin_callbacks.c",
    "content": "/*\nCopyright (c) 2016-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"utlist.h\"\n#include \"lib_load.h\"\n\n\nstatic const char *get_event_name(enum mosquitto_plugin_event event)\n{\n\tswitch(event){\n\t\tcase MOSQ_EVT_RELOAD:\n\t\t\treturn \"reload\";\n\t\tcase MOSQ_EVT_ACL_CHECK:\n\t\t\treturn \"acl-check\";\n\t\tcase MOSQ_EVT_BASIC_AUTH:\n\t\t\treturn \"basic-auth\";\n\t\tcase MOSQ_EVT_PSK_KEY:\n\t\t\treturn \"psk-key\";\n\t\tcase MOSQ_EVT_EXT_AUTH_START:\n\t\t\treturn \"auth-start\";\n\t\tcase MOSQ_EVT_EXT_AUTH_CONTINUE:\n\t\t\treturn \"auth-continue\";\n\t\tcase MOSQ_EVT_CONTROL:\n\t\t\treturn \"control\";\n\t\tcase MOSQ_EVT_MESSAGE_IN:\n\t\t\treturn \"message-in\";\n\t\tcase MOSQ_EVT_MESSAGE_OUT:\n\t\t\treturn \"message-out\";\n\t\tcase MOSQ_EVT_TICK:\n\t\t\treturn \"tick\";\n\t\tcase MOSQ_EVT_DISCONNECT:\n\t\t\treturn \"disconnect\";\n\t\tcase MOSQ_EVT_CONNECT:\n\t\t\treturn \"connect\";\n\t\tcase MOSQ_EVT_CLIENT_OFFLINE:\n\t\t\treturn \"connect\";\n\t\tcase MOSQ_EVT_SUBSCRIBE:\n\t\t\treturn \"subscribe\";\n\t\tcase MOSQ_EVT_UNSUBSCRIBE:\n\t\t\treturn \"unsubscribe\";\n\t\tcase MOSQ_EVT_PERSIST_RESTORE:\n\t\t\treturn \"persist-restore\";\n\t\tcase MOSQ_EVT_PERSIST_BASE_MSG_ADD:\n\t\t\treturn \"persist-base-msg-add\";\n\t\tcase MOSQ_EVT_PERSIST_BASE_MSG_DELETE:\n\t\t\treturn \"persist-base-msg-delete\";\n\t\tcase MOSQ_EVT_PERSIST_RETAIN_MSG_SET:\n\t\t\treturn \"persist-retain-msg-set\";\n\t\tcase MOSQ_EVT_PERSIST_RETAIN_MSG_DELETE:\n\t\t\treturn \"persist-retain-msg-delete\";\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_ADD:\n\t\t\treturn \"persist-client-add\";\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_DELETE:\n\t\t\treturn \"persist-client-delete\";\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_UPDATE:\n\t\t\treturn \"persist-client-update\";\n\t\tcase MOSQ_EVT_PERSIST_SUBSCRIPTION_ADD:\n\t\t\treturn \"persist-subscription-add\";\n\t\tcase MOSQ_EVT_PERSIST_SUBSCRIPTION_DELETE:\n\t\t\treturn \"persist-subscription-delete\";\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_MSG_ADD:\n\t\t\treturn \"persist-client-msg-add\";\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE:\n\t\t\treturn \"persist-client-msg-delete\";\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE:\n\t\t\treturn \"persist-client-msg-update\";\n\t\tcase MOSQ_EVT_PERSIST_WILL_ADD:\n\t\t\treturn \"persist-will-add\";\n\t\tcase MOSQ_EVT_PERSIST_WILL_DELETE:\n\t\t\treturn \"persist-will-delete\";\n\t}\n\treturn \"\";\n}\n\n\nstatic bool check_callback_exists(struct mosquitto__callback *cb_base, mosquitto_plugin_id_t *identifier, MOSQ_FUNC_generic_callback cb_func)\n{\n\tstruct mosquitto__callback *tail, *tmp;\n\n\tDL_FOREACH_SAFE(cb_base, tail, tmp){\n\t\tif(tail->identifier == identifier && tail->cb == cb_func){\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic struct mosquitto__callback **plugin__get_callback_base(struct mosquitto__security_options *security_options, enum mosquitto_plugin_event event)\n{\n\tswitch(event){\n\t\tcase MOSQ_EVT_RELOAD:\n\t\t\treturn &security_options->plugin_callbacks.reload;\n\t\tcase MOSQ_EVT_ACL_CHECK:\n\t\t\treturn &security_options->plugin_callbacks.acl_check;\n\t\tcase MOSQ_EVT_BASIC_AUTH:\n\t\t\treturn &security_options->plugin_callbacks.basic_auth;\n\t\tcase MOSQ_EVT_PSK_KEY:\n\t\t\treturn &security_options->plugin_callbacks.psk_key;\n\t\tcase MOSQ_EVT_EXT_AUTH_START:\n\t\t\treturn &security_options->plugin_callbacks.ext_auth_start;\n\t\tcase MOSQ_EVT_EXT_AUTH_CONTINUE:\n\t\t\treturn &security_options->plugin_callbacks.ext_auth_continue;\n\t\tcase MOSQ_EVT_CONTROL:\n\t\t\treturn NULL;\n\t\tcase MOSQ_EVT_MESSAGE_IN: /* same as MOSQ_EVT_MESSAGE */\n\t\t\treturn &security_options->plugin_callbacks.message_in;\n\t\tcase MOSQ_EVT_TICK:\n\t\t\treturn &security_options->plugin_callbacks.tick;\n\t\tcase MOSQ_EVT_DISCONNECT:\n\t\t\treturn &security_options->plugin_callbacks.disconnect;\n\t\tcase MOSQ_EVT_CONNECT:\n\t\t\treturn &security_options->plugin_callbacks.connect;\n\t\tcase MOSQ_EVT_CLIENT_OFFLINE:\n\t\t\treturn &security_options->plugin_callbacks.client_offline;\n\t\tcase MOSQ_EVT_SUBSCRIBE:\n\t\t\treturn &security_options->plugin_callbacks.subscribe;\n\t\tcase MOSQ_EVT_UNSUBSCRIBE:\n\t\t\treturn &security_options->plugin_callbacks.unsubscribe;\n\t\tcase MOSQ_EVT_PERSIST_RESTORE:\n\t\t\treturn &security_options->plugin_callbacks.persist_restore;\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_ADD:\n\t\t\treturn &security_options->plugin_callbacks.persist_client_add;\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_DELETE:\n\t\t\treturn &security_options->plugin_callbacks.persist_client_delete;\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_UPDATE:\n\t\t\treturn &security_options->plugin_callbacks.persist_client_update;\n\t\tcase MOSQ_EVT_PERSIST_SUBSCRIPTION_ADD:\n\t\t\treturn &security_options->plugin_callbacks.persist_subscription_add;\n\t\tcase MOSQ_EVT_PERSIST_SUBSCRIPTION_DELETE:\n\t\t\treturn &security_options->plugin_callbacks.persist_subscription_delete;\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_MSG_ADD:\n\t\t\treturn &security_options->plugin_callbacks.persist_client_msg_add;\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE:\n\t\t\treturn &security_options->plugin_callbacks.persist_client_msg_delete;\n\t\tcase MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE:\n\t\t\treturn &security_options->plugin_callbacks.persist_client_msg_update;\n\t\tcase MOSQ_EVT_PERSIST_BASE_MSG_ADD:\n\t\t\treturn &security_options->plugin_callbacks.persist_base_msg_add;\n\t\tcase MOSQ_EVT_PERSIST_BASE_MSG_DELETE:\n\t\t\treturn &security_options->plugin_callbacks.persist_base_msg_delete;\n\t\tcase MOSQ_EVT_PERSIST_RETAIN_MSG_SET:\n\t\t\treturn &security_options->plugin_callbacks.persist_retain_msg_set;\n\t\tcase MOSQ_EVT_PERSIST_RETAIN_MSG_DELETE:\n\t\t\treturn &security_options->plugin_callbacks.persist_retain_msg_delete;\n\t\tcase MOSQ_EVT_MESSAGE_OUT:\n\t\t\treturn &security_options->plugin_callbacks.message_out;\n\t\tcase MOSQ_EVT_PERSIST_WILL_ADD:\n\t\t\treturn &security_options->plugin_callbacks.persist_will_add;\n\t\tcase MOSQ_EVT_PERSIST_WILL_DELETE:\n\t\t\treturn &security_options->plugin_callbacks.persist_will_delete;\n\t}\n\treturn NULL;\n}\n\n\nstatic int remove_callback(mosquitto_plugin_id_t *plugin, struct plugin_own_callback *own)\n{\n\tstruct mosquitto__security_options *security_options;\n\tstruct mosquitto__callback *tail, *tmp;\n\tstruct mosquitto__callback **cb_base = NULL;\n\n\tfor(int i=0; i<plugin->config.security_option_count; i++){\n\t\tsecurity_options = plugin->config.security_options[i];\n\n\t\tcb_base = plugin__get_callback_base(security_options, own->event);\n\t\tif(cb_base == NULL){\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t\t}\n\n\t\tDL_FOREACH_SAFE(*cb_base, tail, tmp){\n\t\t\tif(tail->identifier == plugin && tail->cb == own->cb_func){\n\t\t\t\tDL_DELETE(*cb_base, tail);\n\t\t\t\tmosquitto_FREE(tail);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tDL_DELETE(plugin->own_callbacks, own);\n\tmosquitto_FREE(own);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_callback_register(\n\t\tmosquitto_plugin_id_t *identifier,\n\t\tint event,\n\t\tMOSQ_FUNC_generic_callback cb_func,\n\t\tconst void *event_data,\n\t\tvoid *userdata)\n{\n\tstruct mosquitto__callback **cb_base = NULL, *cb_new;\n\tstruct mosquitto__security_options *security_options;\n\tstruct plugin_own_callback *own_callback;\n\n\tif(cb_func == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(db.config->persistence && (event == MOSQ_EVT_PERSIST_RESTORE\n\t\t\t|| event == MOSQ_EVT_PERSIST_BASE_MSG_ADD\n\t\t\t|| event == MOSQ_EVT_PERSIST_BASE_MSG_DELETE\n\t\t\t|| event == MOSQ_EVT_PERSIST_RETAIN_MSG_SET\n\t\t\t|| event == MOSQ_EVT_PERSIST_RETAIN_MSG_DELETE\n\t\t\t|| event == MOSQ_EVT_PERSIST_CLIENT_ADD\n\t\t\t|| event == MOSQ_EVT_PERSIST_CLIENT_DELETE\n\t\t\t|| event == MOSQ_EVT_PERSIST_CLIENT_UPDATE\n\t\t\t|| event == MOSQ_EVT_PERSIST_SUBSCRIPTION_ADD\n\t\t\t|| event == MOSQ_EVT_PERSIST_SUBSCRIPTION_DELETE\n\t\t\t|| event == MOSQ_EVT_PERSIST_CLIENT_MSG_ADD\n\t\t\t|| event == MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE\n\t\t\t|| event == MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE\n\t\t\t|| event == MOSQ_EVT_PERSIST_WILL_ADD\n\t\t\t|| event == MOSQ_EVT_PERSIST_WILL_DELETE\n\t\t\t)){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: `persistence true` cannot be used with a persistence plugin.\");\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(event == MOSQ_EVT_CONTROL){\n\t\treturn control__register_callback(identifier, cb_func, event_data, userdata);\n\t}\n\n\town_callback = mosquitto_calloc(1, sizeof(struct plugin_own_callback));\n\tif(own_callback == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\town_callback->event = (enum mosquitto_plugin_event)event;\n\town_callback->cb_func = cb_func;\n\tDL_APPEND(identifier->own_callbacks, own_callback);\n\n\tif(identifier->config.security_option_count == 0){\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Plugin could not register callback '%s'\",\n\t\t\t\tget_event_name((enum mosquitto_plugin_event)event));\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tfor(int i=0; i<identifier->config.security_option_count; i++){\n\t\tsecurity_options = identifier->config.security_options[i];\n\n\t\tcb_base = plugin__get_callback_base(security_options, (enum mosquitto_plugin_event)event);\n\t\tif(cb_base == NULL){\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t\t}\n\n\t\tif(check_callback_exists(*cb_base, identifier, cb_func)){\n\t\t\treturn MOSQ_ERR_ALREADY_EXISTS;\n\t\t}\n\n\t\tcb_new = mosquitto_calloc(1, sizeof(struct mosquitto__callback));\n\t\tif(cb_new == NULL){\n\t\t\tDL_DELETE(identifier->own_callbacks, own_callback);\n\t\t\tmosquitto_FREE(own_callback);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tDL_APPEND(*cb_base, cb_new);\n\t\tcb_new->identifier = identifier;\n\t\tcb_new->cb = cb_func;\n\t\tcb_new->userdata = userdata;\n\t}\n\n\tif(identifier->plugin_name){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Plugin %s has registered to receive '%s' events.\",\n\t\t\t\tidentifier->plugin_name, get_event_name((enum mosquitto_plugin_event)event));\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Plugin has registered to receive '%s' events.\",\n\t\t\t\tget_event_name((enum mosquitto_plugin_event)event));\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint plugin__callback_unregister_all(mosquitto_plugin_id_t *plugin)\n{\n\tstruct plugin_own_callback *own, *own_tmp;\n\n\tif(plugin == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcontrol__unregister_all_callbacks(plugin);\n\n\tDL_FOREACH_SAFE(plugin->own_callbacks, own, own_tmp){\n\t\tremove_callback(plugin, own);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_callback_unregister(\n\t\tmosquitto_plugin_id_t *identifier,\n\t\tint event,\n\t\tMOSQ_FUNC_generic_callback cb_func,\n\t\tconst void *event_data)\n{\n\tstruct plugin_own_callback *own, *own_tmp;\n\n\tif(identifier == NULL || cb_func == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(event == MOSQ_EVT_CONTROL){\n\t\treturn control__unregister_callback(identifier, cb_func, event_data);\n\t}\n\n\tDL_FOREACH_SAFE(identifier->own_callbacks, own, own_tmp){\n\t\tif(own->event == (enum mosquitto_plugin_event)event && own->cb_func == cb_func){\n\t\t\treturn remove_callback(identifier, own);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_NOT_FOUND;\n}\n"
  },
  {
    "path": "src/plugin_cleanup.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto/broker.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"lib_load.h\"\n#include \"utlist.h\"\n\nstatic int plugin__security_cleanup_single(mosquitto_plugin_id_t *plugin, bool reload);\n\n\nstatic void plugin__unload_single(mosquitto_plugin_id_t *plugin)\n{\n\tstruct control_endpoint *ep, *tmp;\n\n\t/* Run plugin cleanup function */\n\tif(plugin->lib.version == 5){\n\t\tif(plugin->lib.plugin_cleanup_v5){\n\t\t\tplugin->lib.plugin_cleanup_v5(\n\t\t\t\t\tplugin->lib.user_data,\n\t\t\t\t\tplugin->config.options,\n\t\t\t\t\tplugin->config.option_count);\n\t\t}\n\t}else if(plugin->lib.version == 4){\n\t\tif(plugin->lib.plugin_cleanup_v4){\n\t\t\tplugin->lib.plugin_cleanup_v4(\n\t\t\t\t\tplugin->lib.user_data,\n\t\t\t\t\tplugin->config.options,\n\t\t\t\t\tplugin->config.option_count);\n\t\t}\n\t}else if(plugin->lib.version == 3){\n\t\tif(plugin->lib.plugin_cleanup_v3){\n\t\t\tplugin->lib.plugin_cleanup_v3(\n\t\t\t\t\tplugin->lib.user_data,\n\t\t\t\t\tplugin->config.options,\n\t\t\t\t\tplugin->config.option_count);\n\t\t}\n\t}else if(plugin->lib.version == 2){\n\t\tif(plugin->lib.plugin_cleanup_v2){\n\t\t\tplugin->lib.plugin_cleanup_v2(\n\t\t\t\t\tplugin->lib.user_data,\n\t\t\t\t\t(struct mosquitto_auth_opt *)plugin->config.options,\n\t\t\t\t\tplugin->config.option_count);\n\t\t}\n\t}\n\n\tplugin__callback_unregister_all(plugin);\n\tmosquitto_FREE(plugin->plugin_name);\n\tmosquitto_FREE(plugin->plugin_version);\n\tDL_FOREACH_SAFE(plugin->control_endpoints, ep, tmp){\n\t\tDL_DELETE(plugin->control_endpoints, ep);\n\t\tmosquitto_FREE(ep);\n\t}\n\n\tif(plugin->lib.lib){\n\t\t//LIB_CLOSE(plugin->lib.lib);\n\t}\n\tmemset(&plugin->lib, 0, sizeof(struct mosquitto__plugin_lib));\n}\n\n\nint plugin__unload_all(void)\n{\n\tfor(int i=0; i<db.plugin_count; i++){\n\t\tplugin__unload_single(db.plugins[i]);\n\t}\n\n\tmosquitto_security_cleanup(false);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int plugin__security_cleanup_single(mosquitto_plugin_id_t *plugin, bool reload)\n{\n\tint rc;\n\n\tif(plugin->lib.version == 5){\n\t\trc = MOSQ_ERR_SUCCESS;\n\t}else if(plugin->lib.version == 4){\n\t\trc = plugin->lib.security_cleanup_v4(\n\t\t\t\tplugin->lib.user_data,\n\t\t\t\tplugin->config.options,\n\t\t\t\tplugin->config.option_count,\n\t\t\t\treload);\n\n\t}else if(plugin->lib.version == 3){\n\t\trc = plugin->lib.security_cleanup_v3(\n\t\t\t\tplugin->lib.user_data,\n\t\t\t\tplugin->config.options,\n\t\t\t\tplugin->config.option_count,\n\t\t\t\treload);\n\n\t}else if(plugin->lib.version == 2){\n\t\trc = plugin->lib.security_cleanup_v2(\n\t\t\t\tplugin->lib.user_data,\n\t\t\t\t(struct mosquitto_auth_opt *)plugin->config.options,\n\t\t\t\tplugin->config.option_count,\n\t\t\t\treload);\n\t}else{\n\t\trc = MOSQ_ERR_INVAL;\n\t}\n\n\treturn rc;\n}\n\n\nint mosquitto_security_cleanup(bool reload)\n{\n\tfor(int i=0; i<db.plugin_count; i++){\n\t\tplugin__security_cleanup_single(db.plugins[i], reload);\n\t}\n\n\treturn mosquitto_security_cleanup_default();\n}\n"
  },
  {
    "path": "src/plugin_client_offline.c",
    "content": "/*\nCopyright (c) 2023 Roger Light <roger@atchoo.org>\nCopyright (c) 2023 Cedalo Gmbh\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"utlist.h\"\n\n\nstatic void plugin__handle_client_offline_single(struct mosquitto__security_options *opts, struct mosquitto *context, int reason)\n{\n\tstruct mosquitto_evt_client_offline event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\n\tif(context->id == NULL){\n\t\treturn;\n\t}\n\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.client = context;\n\tevent_data.reason = reason;\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.client_offline, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_CLIENT_OFFLINE, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin__handle_client_offline(struct mosquitto *context, int reason)\n{\n\t/* Global plugins */\n\tplugin__handle_client_offline_single(&db.config->security_options, context, reason);\n\n\t/* Per listener plugins */\n\tif(context->listener){\n\t\tplugin__handle_client_offline_single(context->listener->security_options, context, reason);\n\t}\n}\n"
  },
  {
    "path": "src/plugin_connect.c",
    "content": "/*\nCopyright (c) 2016-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto_internal.h\"\n#include \"utlist.h\"\n\n\nstatic void plugin__handle_connect_single(struct mosquitto__security_options *opts, struct mosquitto *context)\n{\n\tstruct mosquitto_evt_connect event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.client = context;\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.connect, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_CONNECT, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin__handle_connect(struct mosquitto *context)\n{\n\t/* Global plugins */\n\tplugin__handle_connect_single(&db.config->security_options, context);\n\n\t/* Per listener plugins */\n\tif(context->listener){\n\t\tplugin__handle_connect_single(context->listener->security_options, context);\n\t}\n}\n"
  },
  {
    "path": "src/plugin_disconnect.c",
    "content": "/*\nCopyright (c) 2016-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"utlist.h\"\n\n\nstatic void plugin__handle_disconnect_single(struct mosquitto__security_options *opts, struct mosquitto *context, int reason)\n{\n\tstruct mosquitto_evt_disconnect event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\n\tif(context->id == NULL){\n\t\treturn;\n\t}\n\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.client = context;\n\tevent_data.reason = reason;\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.disconnect, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_DISCONNECT, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin__handle_disconnect(struct mosquitto *context, int reason)\n{\n\t/* Global plugins */\n\tplugin__handle_disconnect_single(&db.config->security_options, context, reason);\n\n\t/* Per listener plugins */\n\tif(context->listener){\n\t\tplugin__handle_disconnect_single(context->listener->security_options, context, reason);\n\t}\n}\n"
  },
  {
    "path": "src/plugin_extended_auth.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto/broker.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"lib_load.h\"\n#include \"utlist.h\"\n\n\nstatic int plugin__ext_auth_start(struct mosquitto__security_options *opts, struct mosquitto *context, bool reauth, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len)\n{\n\tstruct mosquitto_evt_extended_auth event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tint rc;\n\tint rc_final = MOSQ_ERR_PLUGIN_DEFER;\n\n\tUNUSED(reauth);\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.ext_auth_start, cb_base, cb_next){\n\t\tmemset(&event_data, 0, sizeof(event_data));\n\t\tevent_data.client = context;\n\t\tevent_data.auth_method = context->auth_method;\n\t\tevent_data.data_in = data_in;\n\t\tevent_data.data_out = NULL;\n\t\tevent_data.data_in_len = data_in_len;\n\t\tevent_data.data_out_len = 0;\n\t\trc = cb_base->cb(MOSQ_EVT_EXT_AUTH_START, &event_data, cb_base->userdata);\n\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE){\n\t\t\t/* Do nothing */\n\t\t}else if(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\trc_final = MOSQ_ERR_PLUGIN_DEFER;\n\t\t}else{\n\t\t\t*data_out = event_data.data_out;\n\t\t\t*data_out_len = event_data.data_out_len;\n\t\t\treturn rc;\n\t\t}\n\t}\n\treturn rc_final;\n}\n\n\nint mosquitto_security_auth_start(struct mosquitto *context, bool reauth, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len)\n{\n\tint rc;\n\n\tif(!context || !context->listener || !context->auth_method){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!data_out || !data_out_len){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* Global plugins */\n\tif(db.config->security_options.plugin_callbacks.ext_auth_start){\n\t\trc = plugin__ext_auth_start(&db.config->security_options, context,\n\t\t\t\treauth, data_in, data_in_len, data_out, data_out_len);\n\n\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE || rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\t/* Do nothing */\n\t\t}else{\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\t/* Per listener plugins */\n\tif(context->listener){\n\t\tif(context->listener->security_options->plugin_callbacks.ext_auth_start){\n\t\t\trc = plugin__ext_auth_start(context->listener->security_options, context,\n\t\t\t\t\treauth, data_in, data_in_len, data_out, data_out_len);\n\n\t\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE || rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\t\t/* Do nothing */\n\t\t\t}else{\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(db.config->per_listener_settings){\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n}\n\n\nstatic int plugin__ext_auth_continue(struct mosquitto__security_options *opts, struct mosquitto *context, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len)\n{\n\tint rc;\n\tstruct mosquitto_evt_extended_auth event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.ext_auth_continue, cb_base, cb_next){\n\t\tmemset(&event_data, 0, sizeof(event_data));\n\t\tevent_data.client = context;\n\t\tevent_data.auth_method = context->auth_method;\n\t\tevent_data.data_in = data_in;\n\t\tevent_data.data_out = NULL;\n\t\tevent_data.data_in_len = data_in_len;\n\t\tevent_data.data_out_len = 0;\n\t\trc = cb_base->cb(MOSQ_EVT_EXT_AUTH_CONTINUE, &event_data, cb_base->userdata);\n\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE || rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\t/* Do nothing */\n\t\t}else{\n\t\t\t*data_out = event_data.data_out;\n\t\t\t*data_out_len = event_data.data_out_len;\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n\n\nint mosquitto_security_auth_continue(struct mosquitto *context, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len)\n{\n\tint rc;\n\n\tif(!context || !context->listener || !context->auth_method){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(!data_out || !data_out_len){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* Global plugins */\n\tif(db.config->security_options.plugin_callbacks.ext_auth_continue){\n\t\trc = plugin__ext_auth_continue(&db.config->security_options, context,\n\t\t\t\tdata_in, data_in_len, data_out, data_out_len);\n\n\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE || rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\t/* Do nothing */\n\t\t}else{\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\t/* Per listener plugins */\n\tif(context->listener){\n\t\tif(context->listener->security_options->plugin_callbacks.ext_auth_continue){\n\t\t\trc = plugin__ext_auth_continue(context->listener->security_options, context,\n\t\t\t\t\tdata_in, data_in_len, data_out, data_out_len);\n\n\t\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE || rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\t\t/* Do nothing */\n\t\t\t}else{\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(db.config->per_listener_settings){\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n}\n"
  },
  {
    "path": "src/plugin_init.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto/broker.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"lib_load.h\"\n#include \"utlist.h\"\n\ntypedef int (*FUNC_auth_plugin_version)(void);\ntypedef int (*FUNC_plugin_version)(int, const int *);\n\n\nvoid LIB_ERROR(void)\n{\n#ifdef WIN32\n\tchar *buf;\n\tFormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\n\t\t\tNULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&buf, 0, NULL);\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Load error: %s\", buf);\n\tLocalFree(buf);\n#else\n\tlog__printf(NULL, MOSQ_LOG_ERR, \"Load error: %s\", dlerror());\n#endif\n}\n\n\nstatic int plugin__load_single(mosquitto_plugin_id_t *plugin)\n{\n\tvoid *lib;\n\tint (*plugin_version)(int, const int *) = NULL;\n\tint (*plugin_auth_version)(void) = NULL;\n\tint version;\n\tint rc;\n\tconst int plugin_versions[] = {5, 4, 3, 2};\n\tint plugin_version_count = sizeof(plugin_versions)/sizeof(int);\n\n\tif(plugin->config.security_option_count == 0){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tmemset(&plugin->lib, 0, sizeof(struct mosquitto__plugin_lib));\n\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Loading plugin: %s\", plugin->config.path);\n\n\tlib = LIB_LOAD(plugin->config.path);\n\tif(!lib){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load plugin \\\"%s\\\".\", plugin->config.path);\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tplugin->lib.lib = NULL;\n\tif((plugin_version = (FUNC_plugin_version)LIB_SYM(lib, \"mosquitto_plugin_version\"))){\n\t\tversion = plugin_version(plugin_version_count, plugin_versions);\n\t}else if((plugin_auth_version = (FUNC_auth_plugin_version)LIB_SYM(lib, \"mosquitto_auth_plugin_version\"))){\n\t\tversion = plugin_auth_version();\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_plugin_version() or mosquitto_plugin_version().\");\n\t\tLIB_ERROR();\n\t\tLIB_CLOSE(lib);\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tplugin->lib.version = version;\n\tif(version == 5){\n\t\trc = plugin__load_v5(plugin, lib);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}else if(version == 4){\n\t\trc = plugin__load_v4(plugin, lib);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}else if(version == 3){\n\t\trc = plugin__load_v3(plugin, lib);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}else if(version == 2){\n\t\trc = plugin__load_v2(plugin, lib);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unsupported auth plugin version (got %d, expected %d).\",\n\t\t\t\tversion, MOSQ_PLUGIN_VERSION);\n\t\tLIB_ERROR();\n\n\t\tLIB_CLOSE(lib);\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint plugin__load_all(void)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tfor(int i=0; i<db.plugin_count; i++){\n\t\trc = plugin__load_single(db.plugins[i]);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int plugin__security_init_single(mosquitto_plugin_id_t *plugin, bool reload)\n{\n\tint rc;\n\n\tif(plugin->lib.version == 5){\n\t\trc = MOSQ_ERR_SUCCESS;\n\t}else if(plugin->lib.version == 4){\n\t\trc = plugin->lib.security_init_v4(\n\t\t\t\tplugin->lib.user_data,\n\t\t\t\tplugin->config.options,\n\t\t\t\tplugin->config.option_count,\n\t\t\t\treload);\n\n\t}else if(plugin->lib.version == 3){\n\t\trc = plugin->lib.security_init_v3(\n\t\t\t\tplugin->lib.user_data,\n\t\t\t\tplugin->config.options,\n\t\t\t\tplugin->config.option_count,\n\t\t\t\treload);\n\n\t}else if(plugin->lib.version == 2){\n\t\trc = plugin->lib.security_init_v2(\n\t\t\t\tplugin->lib.user_data,\n\t\t\t\t(struct mosquitto_auth_opt *)plugin->config.options,\n\t\t\t\tplugin->config.option_count,\n\t\t\t\treload);\n\t}else{\n\t\trc = MOSQ_ERR_INVAL;\n\t}\n\n\treturn rc;\n}\n\n\nint mosquitto_security_init(bool reload)\n{\n\tint rc;\n\n\tfor(int i=0; i<db.plugin_count; i++){\n\t\trc = plugin__security_init_single(db.plugins[i], reload);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\trc = mosquitto_security_init_default();\n\treturn rc;\n}\n"
  },
  {
    "path": "src/plugin_message.c",
    "content": "/*\nCopyright (c) 2016-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"utlist.h\"\n\nstruct should_free {\n\tbool topic;\n\tbool payload;\n\tbool properties;\n};\n\n\nstatic int plugin__handle_message_single(struct mosquitto__callback *callbacks, enum mosquitto_plugin_event ev_type, struct should_free *to_free, struct mosquitto *context, struct mosquitto_base_msg *stored)\n{\n\tstruct mosquitto_evt_message event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.client = context;\n\tevent_data.topic = stored->topic;\n\tevent_data.payloadlen = stored->payloadlen;\n\tevent_data.payload = stored->payload;\n\tevent_data.qos = stored->qos;\n\tevent_data.retain = stored->retain;\n\tevent_data.properties = stored->properties;\n\n\tDL_FOREACH_SAFE(callbacks, cb_base, cb_next){\n\t\trc = cb_base->cb((int)ev_type, &event_data, cb_base->userdata);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tbreak;\n\t\t}\n\n\t\tif(stored->topic != event_data.topic){\n\t\t\tif(to_free->topic){\n\t\t\t\tmosquitto_FREE(stored->topic);\n\t\t\t}\n\t\t\tstored->topic = event_data.topic;\n\t\t\tto_free->topic = true;\n\t\t}\n\n\t\tif(stored->payload != event_data.payload){\n\t\t\tif(to_free->payload){\n\t\t\t\tmosquitto_FREE(stored->payload);\n\t\t\t}\n\t\t\tstored->payload = event_data.payload;\n\t\t\tstored->payloadlen = event_data.payloadlen;\n\t\t\tto_free->payload = true;\n\t\t}\n\n\t\tif(stored->properties != event_data.properties){\n\t\t\tif(to_free->properties){\n\t\t\t\tmosquitto_property_free_all(&stored->properties);\n\t\t\t}\n\t\t\tstored->properties = event_data.properties;\n\t\t\tto_free->properties = true;\n\t\t}\n\t}\n\n\tstored->retain = event_data.retain;\n\tif(ev_type == MOSQ_EVT_MESSAGE_OUT){\n\t\tstored->qos = event_data.qos;\n\t}\n\n\treturn rc;\n}\n\n\nint plugin__handle_message_out(struct mosquitto *context, struct mosquitto_base_msg *stored)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\tstruct should_free to_free = {false, false, false}; /* in msg_out, original data will be freed later */\n\n\t/* Global plugins */\n\trc = plugin__handle_message_single(db.config->security_options.plugin_callbacks.message_out,\n\t\t\tMOSQ_EVT_MESSAGE_OUT, &to_free, context, stored);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(context->listener){\n\t\trc = plugin__handle_message_single(context->listener->security_options->plugin_callbacks.message_out,\n\t\t\t\tMOSQ_EVT_MESSAGE_OUT, &to_free, context, stored);\n\t}\n\n\treturn rc;\n}\n\n\nint plugin__handle_message_in(struct mosquitto *context, struct mosquitto_base_msg *stored)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\tstruct should_free to_free = {true, true, true}; /* in msg_in, original data should be freed */\n\n\t/* Global plugins */\n\trc = plugin__handle_message_single(db.config->security_options.plugin_callbacks.message_in,\n\t\t\tMOSQ_EVT_MESSAGE_IN, &to_free, context, stored);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(context->listener){\n\t\trc = plugin__handle_message_single(context->listener->security_options->plugin_callbacks.message_in,\n\t\t\t\tMOSQ_EVT_MESSAGE_IN, &to_free, context, stored);\n\t}\n\n\treturn rc;\n}\n"
  },
  {
    "path": "src/plugin_persist.c",
    "content": "/*\nCopyright (c) 2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto_internal.h\"\n#include \"mosquitto/broker.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n#include \"utlist.h\"\n#include \"lib_load.h\"\n#include \"will_mosq.h\"\n#include <stdint.h>\n\n\nvoid plugin_persist__handle_restore(void)\n{\n\tstruct mosquitto_evt_persist_restore event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_restore, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_RESTORE, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin_persist__handle_client_add(struct mosquitto *context)\n{\n\tstruct mosquitto_evt_persist_client event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(db.shutdown || context->is_persisted){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.data.clientid = context->id;\n\tevent_data.data.username = context->username;\n\tevent_data.data.auth_method = context->auth_method;\n\tevent_data.data.will_delay_time = context->will_delay_time;\n\tevent_data.data.session_expiry_time = context->session_expiry_time;\n\tevent_data.data.will_delay_interval = context->will_delay_interval;\n\tevent_data.data.session_expiry_interval = context->session_expiry_interval;\n\tif(context->listener){\n\t\tevent_data.data.listener_port = context->listener->port;\n\t}else{\n\t\tevent_data.data.listener_port = 0;\n\t}\n\tevent_data.data.max_qos = context->max_qos;\n\tevent_data.data.retain_available = context->retain_available;\n\tevent_data.data.max_packet_size = context->maximum_packet_size;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_client_add, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_CLIENT_ADD, &event_data, cb_base->userdata);\n\t}\n\n\tif(context->will){\n\t\tplugin_persist__handle_will_add(context);\n\t}\n\n\tcontext->is_persisted = true;\n}\n\n\nvoid plugin_persist__handle_client_update(struct mosquitto *context)\n{\n\tstruct mosquitto_evt_persist_client event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\tstruct mosquitto_message_v5 will;\n\n\tUNUSED(will); /* FIXME */\n\n\tif(db.shutdown){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.data.clientid = context->id;\n\tevent_data.data.username = context->username;\n\tevent_data.data.auth_method = context->auth_method;\n\tevent_data.data.will_delay_time = context->will_delay_time;\n\tevent_data.data.session_expiry_time = context->session_expiry_time;\n\tevent_data.data.will_delay_interval = context->will_delay_interval;\n\tevent_data.data.session_expiry_interval = context->session_expiry_interval;\n\tif(context->listener){\n\t\tevent_data.data.listener_port = context->listener->port;\n\t}else{\n\t\tevent_data.data.listener_port = 0;\n\t}\n\tevent_data.data.max_qos = context->max_qos;\n\tevent_data.data.retain_available = context->retain_available;\n\tevent_data.data.max_packet_size = context->maximum_packet_size;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_client_update, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_CLIENT_UPDATE, &event_data, cb_base->userdata);\n\t}\n\n\tif(context->will){\n\t\tplugin_persist__handle_will_add(context);\n\t}else{\n\t\tplugin_persist__handle_will_delete(context);\n\t}\n}\n\n\nvoid plugin_persist__handle_client_delete(struct mosquitto *context)\n{\n\tstruct mosquitto_evt_persist_client event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(context->id == NULL\n\t\t\t|| context->state == mosq_cs_duplicate\n\t\t\t|| db.shutdown){\n\t\treturn;\n\t}\n\n\tplugin_persist__handle_will_delete(context);\n\n\tif(context->is_persisted == false\n\t\t\t|| context->session_expiry_interval != MQTT_SESSION_EXPIRY_IMMEDIATE){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.data.clientid = context->id;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_client_delete, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_CLIENT_DELETE, &event_data, cb_base->userdata);\n\t}\n\tcontext->is_persisted = false;\n}\n\n\nvoid plugin_persist__handle_subscription_add(struct mosquitto *context, const struct mosquitto_subscription *sub)\n{\n\tstruct mosquitto_evt_persist_subscription event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(db.shutdown || context->is_persisted == false){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.data.clientid = context->id;\n\tevent_data.data.topic_filter = sub->topic_filter;\n\tevent_data.data.identifier = sub->identifier;\n\tevent_data.data.options = sub->options;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_subscription_add, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_SUBSCRIPTION_ADD, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin_persist__handle_subscription_delete(struct mosquitto *context, char *sub)\n{\n\tstruct mosquitto_evt_persist_subscription event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(db.shutdown || context->is_persisted == false){\n\t\treturn;\n\t}\n\tif(!sub){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.data.clientid = context->id;\n\tevent_data.data.topic_filter = sub;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_subscription_delete, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_SUBSCRIPTION_DELETE, &event_data, cb_base->userdata);\n\t}\n}\n\n\nstatic inline void set_client_msg_event_data(struct mosquitto_evt_persist_client_msg *event_data, struct mosquitto *context, const struct mosquitto__client_msg *client_msg)\n{\n\tevent_data->data.clientid = context->id;\n\tevent_data->data.cmsg_id = client_msg->data.cmsg_id;\n\tevent_data->data.direction = (uint8_t)client_msg->data.direction;\n\tevent_data->data.dup = client_msg->data.dup;\n\tevent_data->data.mid = client_msg->data.mid;\n\tevent_data->data.qos = client_msg->data.qos;\n\tevent_data->data.retain = client_msg->data.retain;\n\tevent_data->data.state = (uint8_t)client_msg->data.state;\n\tevent_data->data.store_id = client_msg->base_msg->data.store_id;\n\tevent_data->data.subscription_identifier = client_msg->data.subscription_identifier;\n}\n\n\nvoid plugin_persist__handle_client_msg_add(struct mosquitto *context, const struct mosquitto__client_msg *client_msg)\n{\n\tstruct mosquitto_evt_persist_client_msg event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(context->is_persisted == false\n\t\t\t|| (client_msg->data.qos == 0 && db.config->queue_qos0_messages == false)\n\t\t\t|| db.shutdown){\n\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\n\tset_client_msg_event_data(&event_data, context, client_msg);\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_client_msg_add, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_CLIENT_MSG_ADD, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin_persist__handle_client_msg_delete(struct mosquitto *context, const struct mosquitto__client_msg *client_msg)\n{\n\tstruct mosquitto_evt_persist_client_msg event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(context->is_persisted == false\n\t\t\t|| (client_msg->data.qos == 0 && db.config->queue_qos0_messages == false)\n\t\t\t|| db.shutdown){\n\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\n\tset_client_msg_event_data(&event_data, context, client_msg);\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_client_msg_delete, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin_persist__handle_client_msg_update(struct mosquitto *context, const struct mosquitto__client_msg *client_msg)\n{\n\tstruct mosquitto_evt_persist_client_msg event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(context->is_persisted == false\n\t\t\t|| (client_msg->data.qos == 0 && db.config->queue_qos0_messages == false)\n\t\t\t|| db.shutdown){\n\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\n\tset_client_msg_event_data(&event_data, context, client_msg);\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_client_msg_update, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin_persist__handle_base_msg_add(struct mosquitto__base_msg *base_msg)\n{\n\tstruct mosquitto_evt_persist_base_msg event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(base_msg->stored || db.shutdown){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\n\tevent_data.data.store_id = base_msg->data.store_id;\n\tevent_data.data.expiry_time = base_msg->data.expiry_time;\n\tevent_data.data.topic = base_msg->data.topic;\n\tevent_data.data.payload = base_msg->data.payload;\n\tevent_data.data.source_id = base_msg->data.source_id;\n\tevent_data.data.source_username = base_msg->data.source_username;\n\tevent_data.data.properties = base_msg->data.properties;\n\tevent_data.data.payloadlen = base_msg->data.payloadlen;\n\tevent_data.data.source_mid = base_msg->data.source_mid;\n\tif(base_msg->source_listener){\n\t\tevent_data.data.source_port = base_msg->source_listener->port;\n\t}else{\n\t\tevent_data.data.source_port = 0;\n\t}\n\tevent_data.data.qos = base_msg->data.qos;\n\tevent_data.data.retain = base_msg->data.retain;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_base_msg_add, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_BASE_MSG_ADD, &event_data, cb_base->userdata);\n\t}\n\tbase_msg->stored = true;\n}\n\n\nvoid plugin_persist__handle_base_msg_delete(struct mosquitto__base_msg *base_msg)\n{\n\tstruct mosquitto_evt_persist_base_msg event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(base_msg->stored == false || db.shutdown){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\n\tevent_data.data.store_id = base_msg->data.store_id;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_base_msg_delete, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_BASE_MSG_DELETE, &event_data, cb_base->userdata);\n\t}\n\tbase_msg->stored = false;\n}\n\n\nvoid plugin_persist__handle_retain_msg_set(struct mosquitto__base_msg *base_msg)\n{\n\tstruct mosquitto_evt_persist_retain_msg event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(db.shutdown){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\n\tevent_data.store_id = base_msg->data.store_id;\n\tevent_data.topic = base_msg->data.topic;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_retain_msg_set, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_RETAIN_MSG_SET, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin_persist__handle_retain_msg_delete(struct mosquitto__base_msg *base_msg)\n{\n\tstruct mosquitto_evt_persist_retain_msg event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tif(db.shutdown){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tmemset(&event_data, 0, sizeof(event_data));\n\n\tevent_data.topic = base_msg->data.topic;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_retain_msg_delete, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_RETAIN_MSG_DELETE, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin_persist__handle_will_add(struct mosquitto *context)\n{\n\tstruct mosquitto_evt_persist_will_msg event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\tstruct mosquitto_message *will_msg;\n\n\tif(db.shutdown || !context->will){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\twill_msg = &context->will->msg;\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.data.clientid = context->id;\n\tevent_data.data.topic = will_msg->topic;\n\tevent_data.data.payload = will_msg->payload;\n\tevent_data.data.payloadlen = (uint32_t)will_msg->payloadlen;\n\tevent_data.data.qos = (uint8_t)will_msg->qos;\n\tevent_data.data.retain = will_msg->retain;\n\tevent_data.data.properties = context->will->properties;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_will_add, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_WILL_ADD, &event_data, cb_base->userdata);\n\t}\n}\n\n\nvoid plugin_persist__handle_will_delete(struct mosquitto *context)\n{\n\tstruct mosquitto_evt_persist_will_msg event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tstruct mosquitto__security_options *opts;\n\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.data.clientid = context->id;\n\n\tif(db.shutdown){\n\t\treturn;\n\t}\n\n\topts = &db.config->security_options;\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.persist_will_delete, cb_base, cb_next){\n\t\tcb_base->cb(MOSQ_EVT_PERSIST_WILL_ADD, &event_data, cb_base->userdata);\n\t}\n\n}\n"
  },
  {
    "path": "src/plugin_psk_key.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto/broker.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"lib_load.h\"\n#include \"utlist.h\"\n\n\nstatic int plugin__psk_key_get(struct mosquitto__security_options *opts, struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\tstruct mosquitto_evt_psk_key event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tint rc;\n\tint rc_final = MOSQ_ERR_SUCCESS;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.psk_key, cb_base, cb_next){\n\t\tmemset(&event_data, 0, sizeof(event_data));\n\t\tevent_data.client = context;\n\t\tevent_data.hint = hint;\n\t\tevent_data.identity = identity;\n\t\tevent_data.key = key;\n\t\tevent_data.max_key_len = max_key_len;\n\t\trc = cb_base->cb(MOSQ_EVT_PSK_KEY, &event_data, cb_base->userdata);\n\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE){\n\t\t\t/* Do nothing */\n\t\t}else if(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\trc_final = MOSQ_ERR_PLUGIN_DEFER;\n\t\t}else{\n\t\t\treturn rc;\n\t\t}\n\t}\n\treturn rc_final;\n}\n\n\nint mosquitto_psk_key_get(struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\tint rc;\n\tint rc_final = MOSQ_ERR_SUCCESS;\n\n\t/* Global plugins */\n\tif(db.config->security_options.plugin_callbacks.psk_key){\n\t\trc = plugin__psk_key_get(&db.config->security_options, context,\n\t\t\t\thint, identity, key, max_key_len);\n\n\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE){\n\t\t\t/* Do nothing */\n\t\t}else if(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\trc_final = MOSQ_ERR_PLUGIN_DEFER;\n\t\t}else{\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\t/* Per listener plugins */\n\tif(context->listener){\n\t\tif(context->listener->security_options->plugin_callbacks.psk_key){\n\t\t\trc = plugin__psk_key_get(context->listener->security_options, context,\n\t\t\t\t\thint, identity, key, max_key_len);\n\n\t\t\tif(rc == MOSQ_ERR_PLUGIN_IGNORE){\n\t\t\t\t/* Do nothing */\n\t\t\t}else if(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\t\t\trc_final = MOSQ_ERR_PLUGIN_DEFER;\n\t\t\t}else{\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(db.config->per_listener_settings){\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}\n\n\trc = mosquitto_psk_key_get_default(context, hint, identity, key, max_key_len);\n\tif(rc == MOSQ_ERR_PLUGIN_IGNORE){\n\t\t/* Do nothing */\n\t}else if(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\trc_final = MOSQ_ERR_PLUGIN_DEFER;\n\t}else{\n\t\treturn rc;\n\t}\n\n\n\t/* If all plugins deferred, this is a denial. If rc == MOSQ_ERR_SUCCESS\n\t * here, then no plugins were configured. */\n\tif(rc_final == MOSQ_ERR_PLUGIN_DEFER){\n\t\trc_final = MOSQ_ERR_AUTH;\n\t}\n\treturn rc_final;\n}\n"
  },
  {
    "path": "src/plugin_public.c",
    "content": "/*\nCopyright (c) 2016-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n#include \"will_mosq.h\"\n#include \"utlist.h\"\n#include \"will_mosq.h\"\n\n#ifdef WITH_TLS\n#  include <openssl/ssl.h>\n#endif\n\n\nBROKER_EXPORT int mosquitto_plugin_set_info(mosquitto_plugin_id_t *identifier,\n\t\tconst char *plugin_name,\n\t\tconst char *plugin_version)\n{\n\tif(identifier == NULL || plugin_name == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tidentifier->plugin_name = mosquitto_strdup(plugin_name);\n\tif(plugin_version){\n\t\tidentifier->plugin_version = mosquitto_strdup(plugin_version);\n\t}else{\n\t\tidentifier->plugin_version = NULL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT const char *mosquitto_client_address(const struct mosquitto *client)\n{\n\tif(client){\n\t\treturn client->address;\n\t}else{\n\t\treturn NULL;\n\t}\n}\n\n\nBROKER_EXPORT struct mosquitto *mosquitto_client(const char *clientid)\n{\n\tsize_t len;\n\tstruct mosquitto *context;\n\n\tif(!clientid){\n\t\treturn NULL;\n\t}\n\tlen = strlen(clientid);\n\tif(len == 0){\n\t\treturn NULL;\n\t}\n\n\tHASH_FIND(hh_id, db.contexts_by_id, clientid, strlen(clientid), context);\n\n\treturn context;\n}\n\n\nBROKER_EXPORT int mosquitto_client_port(const struct mosquitto *client)\n{\n\tif(client && client->listener){\n\t\treturn client->listener->port;\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\nBROKER_EXPORT bool mosquitto_client_clean_session(const struct mosquitto *client)\n{\n\tif(client){\n\t\treturn client->clean_start;\n\t}else{\n\t\treturn true;\n\t}\n}\n\n\nBROKER_EXPORT const char *mosquitto_client_id(const struct mosquitto *client)\n{\n\tif(client){\n\t\treturn client->id;\n\t}else{\n\t\treturn NULL;\n\t}\n}\n\n\nBROKER_EXPORT unsigned mosquitto_client_id_hashv(const struct mosquitto *client)\n{\n\tif(client){\n\t\treturn client->id_hashv;\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\nBROKER_EXPORT int mosquitto_client_keepalive(const struct mosquitto *client)\n{\n\tif(client){\n\t\treturn client->keepalive;\n\t}else{\n\t\treturn -1;\n\t}\n}\n\n\nBROKER_EXPORT void *mosquitto_client_certificate(const struct mosquitto *client)\n{\n#ifdef WITH_TLS\n\tif(client && client->ssl){\n\t\treturn SSL_get_peer_certificate(client->ssl);\n\t}else{\n\t\treturn NULL;\n\t}\n#else\n\tUNUSED(client);\n\n\treturn NULL;\n#endif\n}\n\n\nBROKER_EXPORT int mosquitto_client_protocol(const struct mosquitto *client)\n{\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n\tif(client && client->wsi){\n\t\treturn mp_websockets;\n\t}else\n#elif defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\tif(client && client->transport == mosq_t_ws){\n\t\treturn mp_websockets;\n\t}else\n#else\n\tUNUSED(client);\n#endif\n\t{\n\t\treturn mp_mqtt;\n\t}\n}\n\n\nBROKER_EXPORT int mosquitto_client_protocol_version(const struct mosquitto *client)\n{\n\tif(client){\n\t\tswitch(client->protocol){\n\t\t\tcase mosq_p_mqtt31:\n\t\t\t\treturn 3;\n\t\t\tcase mosq_p_mqtt311:\n\t\t\t\treturn 4;\n\t\t\tcase mosq_p_mqtt5:\n\t\t\t\treturn 5;\n\t\t\tdefault:\n\t\t\t\treturn 0;\n\t\t}\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\nBROKER_EXPORT int mosquitto_client_sub_count(const struct mosquitto *client)\n{\n\tif(client){\n\t\treturn client->subs_count;\n\t}else{\n\t\treturn 0;\n\t}\n}\n\n\nBROKER_EXPORT const char *mosquitto_client_username(const struct mosquitto *client)\n{\n\tif(client){\n#ifdef WITH_BRIDGE\n\t\tif(client->bridge){\n\t\t\treturn client->bridge->local_username;\n\t\t}else\n#endif\n\t\t{\n\t\t\treturn client->username;\n\t\t}\n\t}else{\n\t\treturn NULL;\n\t}\n}\n\n\nBROKER_EXPORT int mosquitto_broker_publish(\n\t\tconst char *clientid,\n\t\tconst char *topic,\n\t\tint payloadlen,\n\t\tvoid *payload,\n\t\tint qos,\n\t\tbool retain,\n\t\tmosquitto_property *properties)\n{\n\tstruct mosquitto__message_v5 *msg;\n\n\tif(topic == NULL\n\t\t\t|| payloadlen < 0\n\t\t\t|| (payloadlen > 0 && payload == NULL)\n\t\t\t|| qos < 0 || qos > 2){\n\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmsg = mosquitto_malloc(sizeof(struct mosquitto__message_v5));\n\tif(msg == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tmsg->next = NULL;\n\tmsg->prev = NULL;\n\tif(clientid){\n\t\tmsg->clientid = mosquitto_strdup(clientid);\n\t\tif(msg->clientid == NULL){\n\t\t\tmosquitto_FREE(msg);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}else{\n\t\tmsg->clientid = NULL;\n\t}\n\tmsg->topic = mosquitto_strdup(topic);\n\tif(msg->topic == NULL){\n\t\tmosquitto_FREE(msg->clientid);\n\t\tmosquitto_FREE(msg);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tmsg->payloadlen = payloadlen;\n\tmsg->payload = payload;\n\tmsg->qos = qos;\n\tmsg->retain = retain;\n\tmsg->properties = properties;\n\n\tDL_APPEND(db.plugin_msgs, msg);\n\n\tloop__update_next_event(1);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_broker_publish_copy(\n\t\tconst char *clientid,\n\t\tconst char *topic,\n\t\tint payloadlen,\n\t\tconst void *payload,\n\t\tint qos,\n\t\tbool retain,\n\t\tmosquitto_property *properties)\n{\n\tvoid *payload_out;\n\tint rc;\n\n\tif(topic == NULL\n\t\t\t|| payloadlen < 0\n\t\t\t|| (payloadlen > 0 && payload == NULL)\n\t\t\t|| qos < 0 || qos > 2){\n\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tpayload_out = mosquitto_calloc(1, (size_t)(payloadlen+1));\n\tif(payload_out == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tmemcpy(payload_out, payload, (size_t)payloadlen);\n\n\trc = mosquitto_broker_publish(\n\t\t\tclientid,\n\t\t\ttopic,\n\t\t\tpayloadlen,\n\t\t\tpayload_out,\n\t\t\tqos,\n\t\t\tretain,\n\t\t\tproperties);\n\n\tif(rc){\n\t\tmosquitto_FREE(payload_out);\n\t}\n\treturn rc;\n}\n\n\nBROKER_EXPORT int mosquitto_set_username(struct mosquitto *client, const char *username)\n{\n\tchar *u_dup;\n\tchar *old;\n\n\tif(!client){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(username){\n\t\tif(mosquitto_validate_utf8(username, (int)strlen(username))){\n\t\t\treturn MOSQ_ERR_MALFORMED_UTF8;\n\t\t}\n\t\tu_dup = mosquitto_strdup(username);\n\t\tif(!u_dup){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}else{\n\t\tu_dup = NULL;\n\t}\n\n\told = client->username;\n\tclient->username = u_dup;\n\n\tmosquitto_FREE(old);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_set_clientid(struct mosquitto *client, const char *clientid)\n{\n\tstruct mosquitto *found_client;\n\tchar *id_dup;\n\tbool in_by_id;\n\tint clientid_len;\n\n\tif(!client || !clientid){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tin_by_id = client->in_by_id;\n\t/* If in_by_id is true, then this client has already authenticated and\n\t * completed the connection flow. This means it *cannot* take over an\n\t * existing session, and we must remove/add it to the by_id hash table.\n\t *\n\t * If in_by_id is false, then this client is currently going through\n\t * authentication and so it is safe to change the client id to any value\n\t * because it will be checked after authentication.\n\t */\n\n\tif(in_by_id){\n\t\tHASH_FIND(hh_id, db.contexts_by_id, clientid, strlen(clientid), found_client);\n\t\tif(found_client){\n\t\t\treturn MOSQ_ERR_ALREADY_EXISTS;\n\t\t}\n\t}\n\n\tclientid_len = (int)strlen(clientid);\n\tif(mosquitto_validate_utf8(clientid, clientid_len)){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tid_dup = mosquitto_strdup(clientid);\n\tif(!id_dup){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif(in_by_id){\n\t\tcontext__remove_from_by_id(client);\n\t}\n\tmosquitto_free(client->id);\n\tclient->id = id_dup;\n\tif(in_by_id){\n\t\tcontext__add_to_by_id(client);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* Check to see whether durable clients still have rights to their subscriptions. */\nstatic void check_subscription_acls(struct mosquitto *context)\n{\n\tint rc;\n\tuint8_t reason;\n\n\tfor(int i=0; i<context->subs_capacity; i++){\n\t\tif(context->subs[i] == NULL){\n\t\t\tcontinue;\n\t\t}\n\t\trc = mosquitto_acl_check(context,\n\t\t\t\tcontext->subs[i]->topic_filter,\n\t\t\t\t0,\n\t\t\t\tNULL,\n\t\t\t\t0, /* FIXME */\n\t\t\t\tfalse,\n\t\t\t\tNULL,\n\t\t\t\tMOSQ_ACL_SUBSCRIBE);\n\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tsub__remove(context, context->subs[i]->topic_filter, &reason);\n\t\t}\n\t}\n}\n\n\nstatic void disconnect_client(struct mosquitto *context, bool with_will)\n{\n\tif(context->protocol == mosq_p_mqtt5){\n\t\tsend__disconnect(context, MQTT_RC_ADMINISTRATIVE_ACTION, NULL);\n\t}\n\tif(with_will == false){\n\t\tmosquitto__set_state(context, mosq_cs_disconnecting);\n\t}\n\tif(context->session_expiry_interval != MQTT_SESSION_EXPIRY_IMMEDIATE){\n\t\tcheck_subscription_acls(context);\n\t}\n\tdo_disconnect(context, MOSQ_ERR_ADMINISTRATIVE_ACTION);\n}\n\n\nBROKER_EXPORT int mosquitto_kick_client_by_clientid(const char *clientid, bool with_will)\n{\n\tstruct mosquitto *ctxt, *ctxt_tmp;\n\n\tif(clientid == NULL){\n\t\tHASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){\n\t\t\tdisconnect_client(ctxt, with_will);\n\t\t}\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tHASH_FIND(hh_id, db.contexts_by_id, clientid, strlen(clientid), ctxt);\n\t\tif(ctxt){\n\t\t\tdisconnect_client(ctxt, with_will);\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_NOT_FOUND;\n\t\t}\n\t}\n}\n\n\nBROKER_EXPORT int mosquitto_kick_client_by_username(const char *username, bool with_will)\n{\n\tstruct mosquitto *ctxt, *ctxt_tmp;\n\n\tif(username == NULL){\n\t\tHASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){\n\t\t\tif(ctxt->username == NULL){\n\t\t\t\tdisconnect_client(ctxt, with_will);\n\t\t\t}\n\t\t}\n\t}else{\n\t\tHASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){\n\t\t\tif(ctxt->username != NULL && !strcmp(ctxt->username, username)){\n\t\t\t\tdisconnect_client(ctxt, with_will);\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_apply_on_all_clients(int (*FUNC_client_functor)(const struct mosquitto *, void *), void *functor_context)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\tstruct mosquitto *ctxt, *ctxt_tmp;\n\n\tHASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){\n\t\trc = (*FUNC_client_functor)(ctxt, functor_context);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\nBROKER_EXPORT int mosquitto_persist_client_add(struct mosquitto_client *client)\n{\n\tstruct mosquitto *context;\n\tint rc;\n\n\tif(client == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(client->clientid == NULL){\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto error;\n\t}\n\n\tcontext = NULL;\n\tHASH_FIND(hh_id, db.contexts_by_id, client->clientid, strlen(client->clientid), context);\n\tif(context){\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto error;\n\t}\n\n\tcontext = context__init();\n\tif(!context){\n\t\trc = MOSQ_ERR_NOMEM;\n\t\tgoto error;\n\t}\n\n\tcontext->id = client->clientid;\n\tclient->clientid = NULL;\n\tcontext->username = client->username;\n\tclient->username = NULL;\n\tcontext->auth_method = client->auth_method;\n\tclient->auth_method = NULL;\n\n\tcontext->clean_start = false;\n\tcontext->will_delay_time = client->will_delay_time;\n\tcontext->session_expiry_time = client->session_expiry_time;\n\tcontext->will_delay_interval = client->will_delay_interval;\n\tcontext->session_expiry_interval = client->session_expiry_interval;\n\tcontext->max_qos = client->max_qos;\n\tcontext->maximum_packet_size = client->max_packet_size;\n\tcontext->retain_available = client->retain_available;\n\tcontext->is_persisted = true;\n\n\t/* in per_listener_settings mode, try to find the listener by persisted port */\n\tif(db.config->per_listener_settings && client->listener_port > 0){\n\t\tfor(int i=0; i < db.config->listener_count; i++){\n\t\t\tif(db.config->listeners[i].port == client->listener_port){\n\t\t\t\tcontext->listener = &db.config->listeners[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tcontext__add_to_by_id(context);\n\tsession_expiry__add_from_persistence(context, context->session_expiry_time);\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tSAFE_FREE(client->clientid);\n\tSAFE_FREE(client->username);\n\tSAFE_FREE(client->auth_method);\n\treturn rc;\n}\n\n\nBROKER_EXPORT int mosquitto_persist_client_update(struct mosquitto_client *client)\n{\n\tstruct mosquitto *context;\n\tint rc;\n\n\tif(client == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(client->clientid == NULL){\n\t\trc = MOSQ_ERR_INVAL;\n\t\tgoto error;\n\t}\n\n\tcontext = NULL;\n\tHASH_FIND(hh_id, db.contexts_by_id, client->clientid, strlen(client->clientid), context);\n\tif(context == NULL){\n\t\trc = MOSQ_ERR_NOT_FOUND;\n\t\tgoto error;\n\t}\n\n\tmosquitto_free(context->username);\n\tcontext->username = client->username;\n\tclient->username = NULL;\n\n\tcontext->clean_start = false;\n\tcontext->will_delay_time = client->will_delay_time;\n\tcontext->session_expiry_time = client->session_expiry_time;\n\tcontext->will_delay_interval = client->will_delay_interval;\n\tcontext->session_expiry_interval = client->session_expiry_interval;\n\tcontext->max_qos = client->max_qos;\n\tcontext->maximum_packet_size = client->max_packet_size;\n\tcontext->retain_available = client->retain_available;\n\n\t/* in per_listener_settings mode, try to find the listener by persisted port */\n\tif(db.config->per_listener_settings && client->listener_port > 0){\n\t\tfor(int i=0; i < db.config->listener_count; i++){\n\t\t\tif(db.config->listeners[i].port == client->listener_port){\n\t\t\t\tcontext->listener = &db.config->listeners[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\nerror:\n\tSAFE_FREE(client->username);\n\treturn rc;\n}\n\n\nBROKER_EXPORT int mosquitto_persist_client_delete(const char *clientid)\n{\n\tstruct mosquitto *context;\n\n\tif(clientid == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcontext = NULL;\n\tHASH_FIND(hh_id, db.contexts_by_id, clientid, strlen(clientid), context);\n\tif(context == NULL){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tsession_expiry__remove(context);\n\twill_delay__remove(context);\n\twill__clear(context);\n\n\tcontext->clean_start = true;\n\tcontext->session_expiry_interval = MQTT_SESSION_EXPIRY_IMMEDIATE;\n\tcontext->is_persisted = false;\n\tmosquitto__set_state(context, mosq_cs_duplicate);\n\tdo_disconnect(context, MOSQ_ERR_SUCCESS);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic struct mosquitto__base_msg *find_store_msg(uint64_t store_id)\n{\n\tstruct mosquitto__base_msg *base_msg;\n\n\tHASH_FIND(hh, db.msg_store, &store_id, sizeof(store_id), base_msg);\n\treturn base_msg;\n}\n\n\nBROKER_EXPORT int mosquitto_persist_client_msg_add(struct mosquitto_client_msg *client_msg)\n{\n\tstruct mosquitto *context;\n\tstruct mosquitto__base_msg *base_msg;\n\n\tif(client_msg == NULL || client_msg->clientid == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_FIND(hh_id, db.contexts_by_id, client_msg->clientid, strlen(client_msg->clientid), context);\n\tif(context == NULL){\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n\tbase_msg = find_store_msg(client_msg->store_id);\n\tif(base_msg == NULL){\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n\n\tif(client_msg->direction == mosq_md_out){\n\t\tif(client_msg->qos > 0){\n\t\t\tcontext->last_mid = client_msg->mid;\n\t\t}\n\t\treturn db__message_insert_outgoing(context, client_msg->cmsg_id, client_msg->mid,\n\t\t\t\tclient_msg->qos, client_msg->retain,\n\t\t\t\tbase_msg, client_msg->subscription_identifier, false, false);\n\t}else if(client_msg->direction == mosq_md_in){\n\t\treturn db__message_insert_incoming(context, client_msg->cmsg_id, base_msg, false);\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_persist_client_msg_delete(struct mosquitto_client_msg *client_msg)\n{\n\tstruct mosquitto *context;\n\n\tif(client_msg == NULL || client_msg->clientid == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_FIND(hh_id, db.contexts_by_id, client_msg->clientid, strlen(client_msg->clientid), context);\n\tif(context == NULL){\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n\n\n\tint rc = MOSQ_ERR_INVAL;\n\tif(client_msg->direction == mosq_md_out){\n\t\trc = db__message_delete_outgoing(context, client_msg->mid, mosq_ms_any, client_msg->qos);\n\t}else if(client_msg->direction == mosq_md_in){\n\t\trc = db__message_remove_incoming(context, client_msg->mid);\n\t}\n\treturn rc;\n}\n\n\nBROKER_EXPORT int mosquitto_persist_client_msg_update(struct mosquitto_client_msg *client_msg)\n{\n\tstruct mosquitto *context;\n\n\tif(client_msg == NULL || client_msg->clientid == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_FIND(hh_id, db.contexts_by_id, client_msg->clientid, strlen(client_msg->clientid), context);\n\tif(context == NULL){\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n\n\tif(client_msg->direction == mosq_md_out){\n\t\tdb__message_update_outgoing(context, client_msg->mid, client_msg->state, client_msg->qos, false);\n\t}else if(client_msg->direction == mosq_md_in){\n\t\t// FIXME db__message_update_incoming(context, client_msg->mid, client_msg->state, client_msg->qos, false);\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_persist_client_msg_clear(struct mosquitto_client_msg *client_msg)\n{\n\tstruct mosquitto *context;\n\n\tif(client_msg == NULL || client_msg->clientid == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_FIND(hh_id, db.contexts_by_id, client_msg->clientid, strlen(client_msg->clientid), context);\n\tif(context == NULL){\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n\n\tif(client_msg->direction == mosq_bmd_in || client_msg->direction == mosq_bmd_all){\n\t\tdb__messages_delete_incoming(context);\n\t}else if(client_msg->direction == mosq_bmd_out || client_msg->direction == mosq_bmd_all){\n\t\tdb__messages_delete_outgoing(context);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_subscription_add(const struct mosquitto_subscription *sub)\n{\n\tstruct mosquitto *context;\n\n\tif(sub == NULL || sub->clientid == NULL || sub->topic_filter == NULL || sub->clientid[0] == '\\0' || sub->topic_filter[0] == '\\0'){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_FIND(hh_id, db.contexts_by_id, sub->clientid, strlen(sub->clientid), context);\n\n\tif(context){\n\t\treturn sub__add(context, sub);\n\t}else{\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n}\n\n\nBROKER_EXPORT int mosquitto_subscription_delete(const char *clientid, const char *topic)\n{\n\tstruct mosquitto *context;\n\tuint8_t reason;\n\n\tif(clientid == NULL || topic == NULL || clientid[0] == '\\0' || topic[0] == '\\0'){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_FIND(hh_id, db.contexts_by_id, clientid, strlen(clientid), context);\n\n\tif(context){\n\t\treturn sub__remove(context, topic, &reason);\n\t}else{\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n}\n\n\nBROKER_EXPORT int mosquitto_persist_base_msg_add(struct mosquitto_base_msg *msg_add)\n{\n\tstruct mosquitto context;\n\tstruct mosquitto__base_msg *base_msg;\n\tint rc;\n\n\tmemset(&context, 0, sizeof(context));\n\n\tif(msg_add->payloadlen > MQTT_MAX_PAYLOAD){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* db__message_store only takes a copy of .id and .username, so it is reasonably safe\n\t * to cast the const char * to char * */\n\tcontext.id = (char *)msg_add->source_id;\n\tcontext.username = (char *)msg_add->source_username;\n\n\tbase_msg = mosquitto_calloc(1, sizeof(struct mosquitto__base_msg));\n\tif(base_msg == NULL){\n\t\tgoto error;\n\t}\n\tbase_msg->data.store_id = msg_add->store_id;\n\tbase_msg->data.expiry_time = msg_add->expiry_time;\n\tbase_msg->data.payloadlen = msg_add->payloadlen;\n\tbase_msg->data.source_mid = msg_add->source_mid;\n\tbase_msg->data.qos = msg_add->qos;\n\tbase_msg->data.retain = msg_add->retain;\n\n\tbase_msg->data.payload = msg_add->payload;\n\tmsg_add->payload = NULL;\n\tbase_msg->data.topic = msg_add->topic;\n\tmsg_add->topic = NULL;\n\tbase_msg->data.properties = msg_add->properties;\n\tmsg_add->properties = NULL;\n\n\tif(msg_add->source_port){\n\t\tfor(int i=0; i<db.config->listener_count; i++){\n\t\t\tif(db.config->listeners[i].port == msg_add->source_port){\n\t\t\t\tbase_msg->source_listener = &db.config->listeners[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tbase_msg->stored = true;\n\trc = db__message_store(&context, base_msg, NULL, mosq_mo_broker);\n\treturn rc;\n\nerror:\n\tmosquitto_property_free_all(&msg_add->properties);\n\tmosquitto_free(msg_add->topic);\n\tmosquitto_free(msg_add->payload);\n\tmosquitto_free(base_msg);\n\n\treturn MOSQ_ERR_NOMEM;\n}\n\n\nBROKER_EXPORT int mosquitto_persist_base_msg_delete(uint64_t store_id)\n{\n\tstruct mosquitto__base_msg *base_msg;\n\n\tbase_msg = find_store_msg(store_id);\n\tif(base_msg && base_msg->ref_count == 0){\n\t\t/* If ref count is zero, then we should delete this. It might seem\n\t\t * surprising that the ref count is zero already, but it can be. If ref\n\t\t * count is greater than zero then there may be e.g. a retained message\n\t\t * still referring to this and the retained message persist update is\n\t\t * coming later. If we delete the message now in that case, then when\n\t\t * the retain changes there will be use after free errors. All messages\n\t\t * will eventually hit ref count 0 and be removed in some way or other.\n\t\t */\n\t\tdb__msg_store_remove(base_msg, false);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT void mosquitto_complete_basic_auth(const char *clientid, int result)\n{\n\tstruct mosquitto *context;\n\n\tif(clientid == NULL){\n\t\treturn;\n\t}\n\n\tHASH_FIND(hh_id, db.contexts_by_id_delayed_auth, clientid, strlen(clientid), context);\n\tif(context){\n\t\tHASH_DELETE(hh_id, db.contexts_by_id_delayed_auth, context);\n\t\tif(result == MOSQ_ERR_SUCCESS){\n\t\t\tconnect__on_authorised(context, NULL, 0);\n\t\t}else{\n\t\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\t\tsend__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL);\n\t\t\t}else{\n\t\t\t\tsend__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL);\n\t\t\t}\n\t\t\tcontext->clean_start = true;\n\t\t\tcontext->session_expiry_interval = MQTT_SESSION_EXPIRY_IMMEDIATE;\n\t\t\twill__clear(context);\n\t\t\tdo_disconnect(context, MOSQ_ERR_AUTH);\n\t\t}\n\t}\n}\n\n\nBROKER_EXPORT int mosquitto_broker_node_id_set(uint16_t id)\n{\n\tif(id > 1023){\n\t\treturn MOSQ_ERR_INVAL;\n\t}else{\n\t\tdb.node_id = id;\n\t\tdb.node_id_shifted = ((uint64_t)id) << 54;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nBROKER_EXPORT const char *mosquitto_persistence_location(void)\n{\n\treturn db.config->persistence_location;\n}\n\n\nBROKER_EXPORT int mosquitto_client_will_set(const char *clientid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties)\n{\n\tstruct mosquitto *mosq = mosquitto_client(clientid);\n\tif(!mosq){\n\t\treturn MOSQ_ERR_NOT_FOUND;\n\t}\n\tif(properties && mosq->protocol != mosq_p_mqtt5){\n\t\tif(net__is_connected(mosq)){\n\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t\t}\n\t\tmosq->protocol = mosq_p_mqtt5;\n\t}\n\treturn will__set(mosq, topic, payloadlen, payload, qos, retain, properties);\n}\n"
  },
  {
    "path": "src/plugin_reload.c",
    "content": "/*\nCopyright (c) 2016-2025 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker.h\"\n#include \"utlist.h\"\n\n\nstatic int plugin__handle_reload_single(struct mosquitto__security_options *opts)\n{\n\tstruct mosquitto_evt_reload event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\n\tmemset(&event_data, 0, sizeof(event_data));\n\n\t// Using DL_FOREACH_SAFE here, as reload callbacks might unregister themself\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.reload, cb_base, cb_next){\n\t\tint rc = cb_base->cb(MOSQ_EVT_RELOAD, &event_data, cb_base->userdata);\n\t\tif(rc){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Plugin %s produced error on reload: %s\",\n\t\t\t\t\tcb_base->identifier->plugin_name?cb_base->identifier->plugin_name:\"\",\n\t\t\t\t\tmosquitto_strerror(rc));\n\n\t\t\treturn rc;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint plugin__handle_reload(void)\n{\n\tstruct mosquitto__security_options *opts;\n\tint rc;\n\n\t/* Global plugins */\n\trc = plugin__handle_reload_single(&db.config->security_options);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tfor(int i=0; i<db.config->listener_count; i++){\n\t\topts = db.config->listeners[i].security_options;\n\t\tif(opts && opts->plugin_callbacks.reload){\n\t\t\trc = plugin__handle_reload_single(opts);\n\t\t\tif(rc){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "src/plugin_subscribe.c",
    "content": "/*\nCopyright (c) 2016-2022 Roger Light <roger@atchoo.org>\nCopyright (c) 2022 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"utlist.h\"\n\n\nstatic int plugin__handle_subscribe_single(struct mosquitto__security_options *opts, struct mosquitto *context, struct mosquitto_subscription *sub)\n{\n\tstruct mosquitto_evt_subscribe event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.client = context;\n\tevent_data.data.topic_filter = sub->topic_filter;\n\tevent_data.data.options = sub->options;\n\tevent_data.data.identifier = sub->identifier;\n\tevent_data.data.properties = sub->properties;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.subscribe, cb_base, cb_next){\n\t\trc = cb_base->cb(MOSQ_EVT_SUBSCRIBE, &event_data, cb_base->userdata);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tbreak;\n\t\t}\n\n\t\tif(sub->topic_filter != event_data.data.topic_filter){\n\t\t\tmosquitto_free(sub->topic_filter);\n\t\t\tsub->topic_filter = event_data.data.topic_filter;\n\t\t}\n\t}\n\tsub->options = event_data.data.options;\n\n\treturn rc;\n}\n\n\nint plugin__handle_subscribe(struct mosquitto *context, struct mosquitto_subscription *sub)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\t/* Global plugins */\n\trc = plugin__handle_subscribe_single(&db.config->security_options, context, sub);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(context->listener){\n\t\trc = plugin__handle_subscribe_single(context->listener->security_options, context, sub);\n\t}\n\n\treturn rc;\n}\n"
  },
  {
    "path": "src/plugin_tick.c",
    "content": "/*\nCopyright (c) 2016-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker.h\"\n#include \"utlist.h\"\n\n\nstatic void plugin__handle_tick_single(struct mosquitto__security_options *opts)\n{\n\tstruct mosquitto_evt_tick event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\n\tmemset(&event_data, 0, sizeof(event_data));\n\n\t// Using DL_FOREACH_SAFE here, as tick callbacks might unregister themself\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.tick, cb_base, cb_next){\n\t\tmosquitto_time_ns(&event_data.now_s, &event_data.now_ns);\n\n\t\tif(mosquitto_time_cmp(event_data.now_s, event_data.now_ns,\n\t\t\t\tcb_base->data.next_tick.tv_sec, cb_base->data.next_tick.tv_nsec) > 0){\n\n\t\t\tevent_data.next_s = 0;\n\t\t\tevent_data.next_ms = 0;\n\t\t\tcb_base->cb(MOSQ_EVT_TICK, &event_data, cb_base->userdata);\n\t\t\tloop__update_next_event(event_data.next_s * 1000 + event_data.next_ms);\n\n\t\t\tcb_base->data.next_tick.tv_sec = event_data.now_s + event_data.next_s;\n\t\t\tcb_base->data.next_tick.tv_nsec = event_data.now_ns + 1000000*event_data.next_ms;\n\t\t\tif(cb_base->data.next_tick.tv_nsec > 1000000000){\n\t\t\t\tcb_base->data.next_tick.tv_nsec -= 1000000000;\n\t\t\t\tcb_base->data.next_tick.tv_sec += 1;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nvoid plugin__handle_tick(void)\n{\n\tstruct mosquitto__security_options *opts;\n\n\t/* Global plugins */\n\tplugin__handle_tick_single(&db.config->security_options);\n\n\tfor(int i=0; i<db.config->listener_count; i++){\n\t\topts = db.config->listeners[i].security_options;\n\t\tif(opts && opts->plugin_callbacks.tick){\n\t\t\tplugin__handle_tick_single(opts);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/plugin_unsubscribe.c",
    "content": "/*\nCopyright (c) 2016-2022 Roger Light <roger@atchoo.org>\nCopyright (c) 2022 Cedalo GmbH\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"utlist.h\"\n\n\nstatic int plugin__handle_unsubscribe_single(struct mosquitto__security_options *opts, struct mosquitto *context, struct mosquitto_subscription *sub)\n{\n\tstruct mosquitto_evt_unsubscribe event_data;\n\tstruct mosquitto__callback *cb_base, *cb_next;\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\tmemset(&event_data, 0, sizeof(event_data));\n\tevent_data.client = context;\n\tevent_data.data.topic_filter = sub->topic_filter;\n\tevent_data.data.properties = sub->properties;\n\n\tDL_FOREACH_SAFE(opts->plugin_callbacks.unsubscribe, cb_base, cb_next){\n\t\trc = cb_base->cb(MOSQ_EVT_UNSUBSCRIBE, &event_data, cb_base->userdata);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tbreak;\n\t\t}\n\n\t\tif(sub->topic_filter != event_data.data.topic_filter){\n\t\t\tmosquitto_free(sub->topic_filter);\n\t\t\tsub->topic_filter = event_data.data.topic_filter;\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\nint plugin__handle_unsubscribe(struct mosquitto *context, struct mosquitto_subscription *sub)\n{\n\tint rc = MOSQ_ERR_SUCCESS;\n\n\t/* Global plugins */\n\trc = plugin__handle_unsubscribe_single(&db.config->security_options,\n\t\t\tcontext, sub);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(context->listener){\n\t\trc = plugin__handle_unsubscribe_single(context->listener->security_options,\n\t\t\t\tcontext, sub);\n\t}\n\n\treturn rc;\n}\n"
  },
  {
    "path": "src/plugin_v2.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/* This loads v2 plugins in a v5 wrapper to make the core code cleaner */\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto/broker.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"lib_load.h\"\n#include \"utlist.h\"\n\ntypedef int (*FUNC_auth_plugin_version)(void);\ntypedef int (*FUNC_plugin_version)(int, const int *);\n\n\nstatic int plugin_v2_basic_auth(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.unpwd_check_v2 == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn plugin->lib.unpwd_check_v2(\n\t\t\tplugin->lib.user_data,\n\t\t\ted->username,\n\t\t\ted->password);\n}\n\n\nstatic int plugin_v2_acl_check(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\tint rc;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.acl_check_v2 == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\trc = acl__pre_check(plugin, ed->client, ed->access);\n\tif(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\treturn plugin->lib.acl_check_v2(\n\t\t\t\tplugin->lib.user_data,\n\t\t\t\ted->client->id,\n\t\t\t\ted->client->username,\n\t\t\t\ted->topic,\n\t\t\t\ted->access);\n\t}else{\n\t\treturn rc;\n\t}\n}\n\n\nstatic int plugin_v2_psk_key_get(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_psk_key *ed = event_data;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.psk_key_get_v2 == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn plugin->lib.psk_key_get_v2(\n\t\t\tplugin->lib.user_data,\n\t\t\ted->hint,\n\t\t\ted->identity,\n\t\t\ted->key,\n\t\t\ted->max_key_len);\n}\n\n\nstatic int plugin_v2_reload(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tint rc;\n\n\tUNUSED(event);\n\tUNUSED(event_data);\n\n\trc = plugin->lib.security_cleanup_v2(\n\t\t\tplugin->lib.user_data,\n\t\t\t(struct mosquitto_auth_opt *)plugin->config.options,\n\t\t\tplugin->config.option_count,\n\t\t\ttrue);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = plugin->lib.security_init_v2(\n\t\t\tplugin->lib.user_data,\n\t\t\t(struct mosquitto_auth_opt *)plugin->config.options,\n\t\t\tplugin->config.option_count,\n\t\t\ttrue);\n\treturn rc;\n}\n\n\nint plugin__load_v2(mosquitto_plugin_id_t *plugin, void *lib)\n{\n\tint rc;\n\n\tif(!(plugin->lib.plugin_init_v2 = (FUNC_auth_plugin_init_v2)LIB_SYM(lib, \"mosquitto_auth_plugin_init\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_plugin_init().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tif(!(plugin->lib.plugin_cleanup_v2 = (FUNC_auth_plugin_cleanup_v2)LIB_SYM(lib, \"mosquitto_auth_plugin_cleanup\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_plugin_cleanup().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.security_init_v2 = (FUNC_auth_plugin_security_init_v2)LIB_SYM(lib, \"mosquitto_auth_security_init\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_security_init().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.security_cleanup_v2 = (FUNC_auth_plugin_security_cleanup_v2)LIB_SYM(lib, \"mosquitto_auth_security_cleanup\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_security_cleanup().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.acl_check_v2 = (FUNC_auth_plugin_acl_check_v2)LIB_SYM(lib, \"mosquitto_auth_acl_check\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_acl_check().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.unpwd_check_v2 = (FUNC_auth_plugin_unpwd_check_v2)LIB_SYM(lib, \"mosquitto_auth_unpwd_check\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_unpwd_check().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.psk_key_get_v2 = (FUNC_auth_plugin_psk_key_get_v2)LIB_SYM(lib, \"mosquitto_auth_psk_key_get\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_psk_key_get().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tplugin->lib.lib = lib;\n\tplugin->lib.user_data = NULL;\n\tplugin->lib.identifier = plugin;\n\n\tif(plugin->lib.plugin_init_v2){\n\t\trc = plugin->lib.plugin_init_v2(\n\t\t\t\t&plugin->lib.user_data,\n\t\t\t\t(struct mosquitto_auth_opt *)plugin->config.options,\n\t\t\t\tplugin->config.option_count);\n\t\tif(rc){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\t\"Error: Authentication plugin returned %d when initialising.\", rc);\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tmosquitto_callback_register(plugin, MOSQ_EVT_RELOAD, plugin_v2_reload, NULL, plugin);\n\n\tif(plugin->lib.unpwd_check_v2){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_BASIC_AUTH, plugin_v2_basic_auth, NULL, plugin);\n\t}\n\tif(plugin->lib.acl_check_v2){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_ACL_CHECK, plugin_v2_acl_check, NULL, plugin);\n\t}\n\tif(plugin->lib.psk_key_get_v2){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_PSK_KEY, plugin_v2_psk_key_get, NULL, plugin);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/plugin_v3.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/* This loads v3 plugins in a v5 wrapper to make the core code cleaner */\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto/broker.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"lib_load.h\"\n#include \"utlist.h\"\n\ntypedef int (*FUNC_auth_plugin_version)(void);\ntypedef int (*FUNC_plugin_version)(int, const int *);\n\n\nstatic int plugin_v3_basic_auth(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.unpwd_check_v3 == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn plugin->lib.unpwd_check_v3(\n\t\t\tplugin->lib.user_data,\n\t\t\ted->client,\n\t\t\ted->username,\n\t\t\ted->password);\n}\n\n\nstatic int plugin_v3_acl_check(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\tstruct mosquitto_acl_msg msg;\n\tint rc;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.acl_check_v3 == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmemset(&msg, 0, sizeof(msg));\n\tmsg.topic = ed->topic;\n\tmsg.payloadlen = ed->payloadlen;\n\tmsg.payload = ed->payload;\n\tmsg.qos = ed->qos;\n\tmsg.retain = ed->retain;\n\n\trc = acl__pre_check(plugin, ed->client, ed->access);\n\tif(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\treturn plugin->lib.acl_check_v3(\n\t\t\t\tplugin->lib.user_data,\n\t\t\t\ted->access,\n\t\t\t\ted->client,\n\t\t\t\t&msg);\n\t}else{\n\t\treturn rc;\n\t}\n}\n\n\nstatic int plugin_v3_psk_key_get(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_psk_key *ed = event_data;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.psk_key_get_v3 == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn plugin->lib.psk_key_get_v3(\n\t\t\tplugin->lib.user_data,\n\t\t\ted->client,\n\t\t\ted->hint,\n\t\t\ted->identity,\n\t\t\ted->key,\n\t\t\ted->max_key_len);\n}\n\n\nstatic int plugin_v3_reload(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tint rc;\n\n\tUNUSED(event);\n\tUNUSED(event_data);\n\n\trc = plugin->lib.security_cleanup_v3(\n\t\t\tplugin->lib.user_data,\n\t\t\tplugin->config.options,\n\t\t\tplugin->config.option_count,\n\t\t\ttrue);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = plugin->lib.security_init_v3(\n\t\t\tplugin->lib.user_data,\n\t\t\tplugin->config.options,\n\t\t\tplugin->config.option_count,\n\t\t\ttrue);\n\treturn rc;\n}\n\n\nint plugin__load_v3(mosquitto_plugin_id_t *plugin, void *lib)\n{\n\tint rc;\n\n\tif(!(plugin->lib.plugin_init_v3 = (FUNC_auth_plugin_init_v3)LIB_SYM(lib, \"mosquitto_auth_plugin_init\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_plugin_init().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tif(!(plugin->lib.plugin_cleanup_v3 = (FUNC_auth_plugin_cleanup_v3)LIB_SYM(lib, \"mosquitto_auth_plugin_cleanup\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_plugin_cleanup().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.security_init_v3 = (FUNC_auth_plugin_security_init_v3)LIB_SYM(lib, \"mosquitto_auth_security_init\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_security_init().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.security_cleanup_v3 = (FUNC_auth_plugin_security_cleanup_v3)LIB_SYM(lib, \"mosquitto_auth_security_cleanup\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_security_cleanup().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.acl_check_v3 = (FUNC_auth_plugin_acl_check_v3)LIB_SYM(lib, \"mosquitto_auth_acl_check\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_acl_check().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.unpwd_check_v3 = (FUNC_auth_plugin_unpwd_check_v3)LIB_SYM(lib, \"mosquitto_auth_unpwd_check\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_unpwd_check().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.psk_key_get_v3 = (FUNC_auth_plugin_psk_key_get_v3)LIB_SYM(lib, \"mosquitto_auth_psk_key_get\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_psk_key_get().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tplugin->lib.lib = lib;\n\tplugin->lib.user_data = NULL;\n\tplugin->lib.identifier = plugin;\n\tif(plugin->lib.plugin_init_v3){\n\t\trc = plugin->lib.plugin_init_v3(&plugin->lib.user_data, plugin->config.options, plugin->config.option_count);\n\t\tif(rc){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\t\"Error: Authentication plugin returned %d when initialising.\", rc);\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tmosquitto_callback_register(plugin, MOSQ_EVT_RELOAD, plugin_v3_reload, NULL, plugin);\n\n\tif(plugin->lib.unpwd_check_v3){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_BASIC_AUTH, plugin_v3_basic_auth, NULL, plugin);\n\t}\n\tif(plugin->lib.acl_check_v3){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_ACL_CHECK, plugin_v3_acl_check, NULL, plugin);\n\t}\n\tif(plugin->lib.psk_key_get_v3){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_PSK_KEY, plugin_v3_psk_key_get, NULL, plugin);\n\t}\n\n\treturn 0;\n}\n\n\n"
  },
  {
    "path": "src/plugin_v4.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/* This loads v4 plugins in a v5 wrapper to make the core code cleaner */\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto/broker.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/broker_plugin.h\"\n#include \"lib_load.h\"\n#include \"utlist.h\"\n\ntypedef int (*FUNC_auth_plugin_version)(void);\ntypedef int (*FUNC_plugin_version)(int, const int *);\n\n\nstatic int plugin_v4_basic_auth(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.unpwd_check_v4 == NULL){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}\n\n\treturn plugin->lib.unpwd_check_v4(\n\t\t\tplugin->lib.user_data,\n\t\t\ted->client,\n\t\t\ted->username,\n\t\t\ted->password);\n}\n\n\nstatic int plugin_v4_acl_check(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\tstruct mosquitto_acl_msg msg;\n\tint rc;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.acl_check_v4 == NULL){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}\n\n\tmemset(&msg, 0, sizeof(msg));\n\tmsg.topic = ed->topic;\n\tmsg.payloadlen = ed->payloadlen;\n\tmsg.payload = ed->payload;\n\tmsg.qos = ed->qos;\n\tmsg.retain = ed->retain;\n\n\trc = acl__pre_check(plugin, ed->client, ed->access);\n\tif(rc == MOSQ_ERR_PLUGIN_DEFER){\n\t\treturn plugin->lib.acl_check_v4(\n\t\t\t\tplugin->lib.user_data,\n\t\t\t\ted->access,\n\t\t\t\ted->client,\n\t\t\t\t&msg);\n\t}else{\n\t\treturn rc;\n\t}\n}\n\n\nstatic int plugin_v4_auth_start(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_extended_auth *ed = event_data;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.auth_start_v4 == NULL){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}\n\n\treturn plugin->lib.auth_start_v4(\n\t\t\tplugin->lib.user_data,\n\t\t\ted->client,\n\t\t\ted->client->auth_method,\n\t\t\tfalse,\n\t\t\ted->data_in, ed->data_in_len,\n\t\t\t&ed->data_out, &ed->data_out_len);\n}\n\n\nstatic int plugin_v4_auth_continue(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_extended_auth *ed = event_data;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.auth_continue_v4 == NULL){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}\n\n\treturn plugin->lib.auth_continue_v4(\n\t\t\tplugin->lib.user_data,\n\t\t\ted->client,\n\t\t\ted->client->auth_method,\n\t\t\ted->data_in, ed->data_in_len,\n\t\t\t&ed->data_out, &ed->data_out_len);\n}\n\n\nstatic int plugin_v4_psk_key_get(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tstruct mosquitto_evt_psk_key *ed = event_data;\n\n\tUNUSED(event);\n\n\tif(plugin->lib.psk_key_get_v4 == NULL){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}\n\n\treturn plugin->lib.psk_key_get_v4(\n\t\t\tplugin->lib.user_data,\n\t\t\ted->client,\n\t\t\ted->hint,\n\t\t\ted->identity,\n\t\t\ted->key,\n\t\t\ted->max_key_len);\n}\n\n\nstatic int plugin_v4_reload(int event, void *event_data, void *userdata)\n{\n\tmosquitto_plugin_id_t *plugin = userdata;\n\tint rc;\n\n\tUNUSED(event);\n\tUNUSED(event_data);\n\n\trc = plugin->lib.security_cleanup_v4(\n\t\t\tplugin->lib.user_data,\n\t\t\tplugin->config.options,\n\t\t\tplugin->config.option_count,\n\t\t\ttrue);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = plugin->lib.security_init_v4(\n\t\t\tplugin->lib.user_data,\n\t\t\tplugin->config.options,\n\t\t\tplugin->config.option_count,\n\t\t\ttrue);\n\treturn rc;\n}\n\n\nint plugin__load_v4(mosquitto_plugin_id_t *plugin, void *lib)\n{\n\tint rc;\n\n\tif(!(plugin->lib.plugin_init_v4 = (FUNC_auth_plugin_init_v4)LIB_SYM(lib, \"mosquitto_auth_plugin_init\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_plugin_init().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\tif(!(plugin->lib.plugin_cleanup_v4 = (FUNC_auth_plugin_cleanup_v4)LIB_SYM(lib, \"mosquitto_auth_plugin_cleanup\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_plugin_cleanup().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.security_init_v4 = (FUNC_auth_plugin_security_init_v4)LIB_SYM(lib, \"mosquitto_auth_security_init\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_security_init().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.security_cleanup_v4 = (FUNC_auth_plugin_security_cleanup_v4)LIB_SYM(lib, \"mosquitto_auth_security_cleanup\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_security_cleanup().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tif(!(plugin->lib.acl_check_v4 = (FUNC_auth_plugin_acl_check_v4)LIB_SYM(lib, \"mosquitto_auth_acl_check\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load auth plugin function mosquitto_auth_acl_check().\");\n\t\tLIB_ERROR();\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\tplugin->lib.unpwd_check_v4 = (FUNC_auth_plugin_unpwd_check_v4)LIB_SYM(lib, \"mosquitto_auth_unpwd_check\");\n\tif(plugin->lib.unpwd_check_v4){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\" ├── Username/password checking enabled.\");\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\" ├── Username/password checking not enabled.\");\n\t}\n\n\tplugin->lib.psk_key_get_v4 = (FUNC_auth_plugin_psk_key_get_v4)LIB_SYM(lib, \"mosquitto_auth_psk_key_get\");\n\tif(plugin->lib.psk_key_get_v4){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\" ├── TLS-PSK checking enabled.\");\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\" ├── TLS-PSK checking not enabled.\");\n\t}\n\n\tplugin->lib.auth_start_v4 = (FUNC_auth_plugin_auth_start_v4)LIB_SYM(lib, \"mosquitto_auth_start\");\n\tplugin->lib.auth_continue_v4 = (FUNC_auth_plugin_auth_continue_v4)LIB_SYM(lib, \"mosquitto_auth_continue\");\n\n\tif(plugin->lib.auth_start_v4){\n\t\tif(plugin->lib.auth_continue_v4){\n\t\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\t\" └── Extended authentication enabled.\");\n\t\t}else{\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\t\"Error: Plugin has missing mosquitto_auth_continue() function.\");\n\t\t\treturn MOSQ_ERR_UNKNOWN;\n\t\t}\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\" └── Extended authentication not enabled.\");\n\t}\n\n\tplugin->lib.lib = lib;\n\tplugin->lib.user_data = NULL;\n\tplugin->lib.identifier = plugin;\n\n\tif(plugin->lib.plugin_init_v4){\n\t\trc = plugin->lib.plugin_init_v4(&plugin->lib.user_data, plugin->config.options, plugin->config.option_count);\n\t\tif(rc){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\t\"Error: Authentication plugin returned %d when initialising.\", rc);\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tmosquitto_callback_register(plugin, MOSQ_EVT_RELOAD, plugin_v4_reload, NULL, plugin);\n\n\tif(plugin->lib.unpwd_check_v4){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_BASIC_AUTH, plugin_v4_basic_auth, NULL, plugin);\n\t}\n\tif(plugin->lib.acl_check_v4){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_ACL_CHECK, plugin_v4_acl_check, NULL, plugin);\n\t}\n\tif(plugin->lib.auth_start_v4){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_EXT_AUTH_START, plugin_v4_auth_start, NULL, plugin);\n\t}\n\tif(plugin->lib.auth_continue_v4){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_EXT_AUTH_CONTINUE, plugin_v4_auth_continue, NULL, plugin);\n\t}\n\tif(plugin->lib.psk_key_get_v4){\n\t\tmosquitto_callback_register(plugin, MOSQ_EVT_PSK_KEY, plugin_v4_psk_key_get, NULL, plugin);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/plugin_v5.c",
    "content": "/*\nCopyright (c) 2016-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"utlist.h\"\n#include \"lib_load.h\"\n\n\nint plugin__load_v5(mosquitto_plugin_id_t *plugin, void *lib)\n{\n\tint rc;\n\n\tif(!(plugin->lib.plugin_init_v5 = (FUNC_plugin_init_v5)LIB_SYM(lib, \"mosquitto_plugin_init\"))){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\"Error: Unable to load plugin function mosquitto_plugin_init().\");\n\t\tLIB_ERROR();\n\t\tLIB_CLOSE(lib);\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\t/* Optional function */\n\tplugin->lib.plugin_cleanup_v5 = (FUNC_plugin_cleanup_v5)LIB_SYM(lib, \"mosquitto_plugin_cleanup\");\n\n\tplugin->lib.lib = lib;\n\tplugin->lib.user_data = NULL;\n\tplugin->lib.identifier = plugin;\n\n\tif(plugin->lib.plugin_init_v5){\n\t\trc = plugin->lib.plugin_init_v5(plugin, &plugin->lib.user_data, plugin->config.options, plugin->config.option_count);\n\t\tif(rc){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR,\n\t\t\t\t\t\"Error: Plugin returned %d when initialising.\", rc);\n\t\t\treturn rc;\n\t\t}\n\t}\n\tif(plugin->plugin_name && plugin->plugin_version){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\"Plugin %s version %s loaded.\", plugin->plugin_name, plugin->plugin_version);\n\t}else if(plugin->plugin_name){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO,\n\t\t\t\t\"Plugin %s loaded.\", plugin->plugin_name);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/property_broker.c",
    "content": "/*\nCopyright (c) 2018-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"property_mosq.h\"\n#include \"property_common.h\"\n\n\n/* Process the incoming properties, we should be able to assume that only valid\n * properties for CONNECT are present here. */\nint property__process_connect(struct mosquitto *context, mosquitto_property **props)\n{\n\tmosquitto_property *p;\n\n\tp = *props;\n\n\twhile(p){\n\t\tswitch(mosquitto_property_identifier(p)){\n\t\t\tcase MQTT_PROP_SESSION_EXPIRY_INTERVAL:\n\t\t\t\tcontext->session_expiry_interval = mosquitto_property_int32_value(p);\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_RECEIVE_MAXIMUM:\n\t\t\t\tcontext->msgs_out.inflight_maximum = mosquitto_property_int16_value(p);\n\t\t\t\tif(context->msgs_out.inflight_maximum == 0){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: CONNECT packet with receive-maximum = 0.\", context->id);\n\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t}\n\t\t\t\tcontext->msgs_out.inflight_quota = context->msgs_out.inflight_maximum;\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_MAXIMUM_PACKET_SIZE:\n\t\t\t\tcontext->maximum_packet_size = mosquitto_property_int32_value(p);\n\t\t\t\tif(context->maximum_packet_size == 0){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: CONNECT packet with maximum-packet-size = 0.\", context->id);\n\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_TOPIC_ALIAS_MAXIMUM:\n\t\t\t\tcontext->alias_max_l2r = mosquitto_property_int16_value(p);\n\t\t\t\tif(context->alias_max_l2r > context->listener->max_topic_alias_broker){\n\t\t\t\t\tcontext->alias_max_l2r = context->listener->max_topic_alias_broker;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t\tp = mosquitto_property_next(p);\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint property__process_will(struct mosquitto *context, struct mosquitto_message_all *msg, mosquitto_property **props)\n{\n\tmosquitto_property *p, *p_prev;\n\tmosquitto_property *msg_properties, *msg_properties_last;\n\n\tp = *props;\n\tp_prev = NULL;\n\tmsg_properties = NULL;\n\tmsg_properties_last = NULL;\n\tmsg->expiry_interval = MSG_EXPIRY_INFINITE;\n\twhile(p){\n\t\tswitch(mosquitto_property_identifier(p)){\n\t\t\tcase MQTT_PROP_CONTENT_TYPE:\n\t\t\tcase MQTT_PROP_CORRELATION_DATA:\n\t\t\tcase MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:\n\t\t\tcase MQTT_PROP_RESPONSE_TOPIC:\n\t\t\tcase MQTT_PROP_USER_PROPERTY:\n\t\t\t\t/* We save these properties for transmission with the PUBLISH */\n\n\t\t\t\t/* Add this property to the end of the list */\n\t\t\t\tif(msg_properties){\n\t\t\t\t\tmsg_properties_last->next = p;\n\t\t\t\t\tmsg_properties_last = p;\n\t\t\t\t}else{\n\t\t\t\t\tmsg_properties = p;\n\t\t\t\t\tmsg_properties_last = p;\n\t\t\t\t}\n\n\t\t\t\t/* And remove it from *props */\n\t\t\t\tif(p_prev){\n\t\t\t\t\tp_prev->next = mosquitto_property_next(p);\n\t\t\t\t\tp = mosquitto_property_next(p_prev);\n\t\t\t\t}else{\n\t\t\t\t\t*props = mosquitto_property_next(p);\n\t\t\t\t\tp = *props;\n\t\t\t\t}\n\t\t\t\tmsg_properties_last->next = NULL;\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_WILL_DELAY_INTERVAL:\n\t\t\t\t/* Leave this in *props, to be freed */\n\t\t\t\tcontext->will_delay_interval = mosquitto_property_int32_value(p);\n\t\t\t\tp_prev = p;\n\t\t\t\tp = mosquitto_property_next(p);\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:\n\t\t\t\t/* Leave this in *props, to be freed */\n\t\t\t\tmsg->expiry_interval = mosquitto_property_int32_value(p);\n\t\t\t\tp_prev = p;\n\t\t\t\tp = mosquitto_property_next(p);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tmsg->properties = msg_properties;\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: CONNECT packet invalid property (%d).\", context->id, p->identifier);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tmsg->properties = msg_properties;\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint property__process_publish(struct mosquitto__base_msg *base_msg, mosquitto_property **props, int *topic_alias, uint32_t *message_expiry_interval, bool is_bridge)\n{\n\tmosquitto_property *p, *p_prev;\n\tmosquitto_property *msg_properties_last;\n\n\tp = *props;\n\tp_prev = NULL;\n\tbase_msg->data.properties = NULL;\n\tmsg_properties_last = NULL;\n\twhile(p){\n\t\tswitch(mosquitto_property_identifier(p)){\n\t\t\tcase MQTT_PROP_CONTENT_TYPE:\n\t\t\tcase MQTT_PROP_CORRELATION_DATA:\n\t\t\tcase MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:\n\t\t\tcase MQTT_PROP_RESPONSE_TOPIC:\n\t\t\tcase MQTT_PROP_USER_PROPERTY:\n\t\t\t\tif(base_msg->data.properties){\n\t\t\t\t\tmsg_properties_last->next = p;\n\t\t\t\t\tmsg_properties_last = p;\n\t\t\t\t}else{\n\t\t\t\t\tbase_msg->data.properties = p;\n\t\t\t\t\tmsg_properties_last = p;\n\t\t\t\t}\n\t\t\t\tif(p_prev){\n\t\t\t\t\tp_prev->next = mosquitto_property_next(p);\n\t\t\t\t\tp = mosquitto_property_next(p_prev);\n\t\t\t\t}else{\n\t\t\t\t\t*props = mosquitto_property_next(p);\n\t\t\t\t\tp = *props;\n\t\t\t\t}\n\t\t\t\tmsg_properties_last->next = NULL;\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_TOPIC_ALIAS:\n\t\t\t\t*topic_alias = mosquitto_property_int16_value(p);\n\t\t\t\tp_prev = p;\n\t\t\t\tp = mosquitto_property_next(p);\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:\n\t\t\t\t*message_expiry_interval = mosquitto_property_int32_value(p);\n\t\t\t\tp_prev = p;\n\t\t\t\tp = mosquitto_property_next(p);\n\t\t\t\tbreak;\n\n\t\t\tcase MQTT_PROP_SUBSCRIPTION_IDENTIFIER:\n\t\t\t\tif(!is_bridge || mosquitto_property_varint_value(p) == 0){\n\t\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t\t}\n\t\t\t\tp_prev = p;\n\t\t\t\tp = mosquitto_property_next(p);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tp = mosquitto_property_next(p);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* Process the incoming properties, we should be able to assume that only valid\n * properties for DISCONNECT are present here. */\nint property__process_disconnect(struct mosquitto *context, mosquitto_property **props)\n{\n\tmosquitto_property *p;\n\n\tp = *props;\n\n\twhile(p){\n\t\tif(mosquitto_property_identifier(p) == MQTT_PROP_SESSION_EXPIRY_INTERVAL){\n\t\t\tuint32_t session_expiry_interval = mosquitto_property_int32_value(p);\n\t\t\tif(context->session_expiry_interval == MQTT_SESSION_EXPIRY_IMMEDIATE\n\t\t\t\t\t&& session_expiry_interval != MQTT_SESSION_EXPIRY_IMMEDIATE){\n\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: DISCONNECT packet with mismatched session-expiry-interval (%d:%d).\",\n\t\t\t\t\t\tcontext->id, context->session_expiry_interval, p->value.i32);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\tcontext->session_expiry_interval = session_expiry_interval;\n\t\t}\n\t\tp = mosquitto_property_next(p);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "src/proxy_v1.c",
    "content": "#ifdef WIN32\n#  include <winsock2.h>\n#  include <ws2tcpip.h>\n#else\n#  include <arpa/inet.h>\n#  include <netinet/in.h>\n#endif\n#include <stdint.h>\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto_internal.h\"\n#include \"net_mosq.h\"\n\n#if !defined(WITH_WEBSOCKETS) || WITH_WEBSOCKETS == WS_IS_BUILTIN\n\n#define PROXY_V1_PACKET_LIMIT 108\n\nconst uint8_t signature4[11] = {'P', 'R', 'O', 'X', 'Y', ' ', 'T', 'C', 'P', '4', ' '};\nconst uint8_t signature6[11] = {'P', 'R', 'O', 'X', 'Y', ' ', 'T', 'C', 'P', '6', ' '};\nconst uint8_t signatureU[14] = {'P', 'R', 'O', 'X', 'Y', ' ', 'U', 'N', 'K', 'N', 'O', 'W', 'N', ' '};\n\n\nstatic void proxy_cleanup(struct mosquitto *context)\n{\n\tmosquitto_FREE(context->proxy.buf);\n}\n\n\nstatic int update_transport(struct mosquitto *context)\n{\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\tif(context->listener->protocol == mp_websockets){\n\t\treturn http__context_init(context);\n\t}else\n#endif\n\t{\n\t\tcontext->transport = mosq_t_tcp;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int get_address_for_unknown(struct mosquitto *context)\n{\n\tchar address[1024];\n\n\tproxy_cleanup(context);\n\n\tif(!net__socket_get_address(context->sock, address, sizeof(address), &context->remote_port)){\n\t\tcontext->address = mosquitto_strdup(address);\n\t}\n\tif(!context->address){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\treturn update_transport(context);\n}\n\n\nstatic int proxy_v1__decode(struct mosquitto *context)\n{\n\tchar *saddr_s, *daddr_s, *sport_s, *dport_s;\n\tchar *saveptr = NULL;\n\tint sport, dport;\n\tstruct in6_addr addr;\n\n\tif(context->proxy.pos >= sizeof(signatureU) && !memcmp(context->proxy.buf, signatureU, sizeof(signatureU))){\n\t\treturn get_address_for_unknown(context);\n\t}else if(context->proxy.pos >= sizeof(signature4) && !memcmp(context->proxy.buf, signature4, sizeof(signature4))){\n\t\tcontext->proxy.fam = AF_INET;\n\t}else if(context->proxy.pos >= sizeof(signature6) && !memcmp(context->proxy.buf, signature6, sizeof(signature6))){\n\t\tcontext->proxy.fam = AF_INET6;\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connection rejected, corrupt PROXY header.\");\n\t\tproxy_cleanup(context);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tcontext->proxy.buf[context->proxy.pos-1] = '\\0';\n\tcontext->proxy.buf[context->proxy.pos-2] = '\\0';\n\tsaddr_s = strtok_r((char *)&context->proxy.buf[sizeof(signature4)], \" \", &saveptr);\n\tdaddr_s = strtok_r(NULL, \" \", &saveptr);\n\tsport_s = strtok_r(NULL, \" \", &saveptr);\n\tdport_s = strtok_r(NULL, \" \", &saveptr);\n\n\n\tif(!saddr_s || !daddr_s || !sport_s || !dport_s || (saveptr && strlen(saveptr) > 0)){\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connection rejected, corrupt PROXY header.\");\n\t\tproxy_cleanup(context);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* Verify ports */\n\tsport = atoi(sport_s);\n\tdport = atoi(dport_s);\n\tif(sport < 1 || sport > 65535 || dport < 1 || dport > 65535){\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connection rejected, corrupt PROXY header.\");\n\t\tproxy_cleanup(context);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* Verify addresses */\n\tif(context->proxy.fam == AF_INET){\n\t\tif(inet_pton(AF_INET, saddr_s, &addr) != 1\n\t\t\t\t|| inet_pton(AF_INET, daddr_s, &addr) != 1){\n\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connection rejected, corrupt PROXY header.\");\n\t\t\tproxy_cleanup(context);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}else if(context->proxy.fam == AF_INET6){\n\t\tif(inet_pton(AF_INET6, saddr_s, &addr) != 1\n\t\t\t\t|| inet_pton(AF_INET6, daddr_s, &addr) != 1){\n\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connection rejected, corrupt PROXY header.\");\n\t\t\tproxy_cleanup(context);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\n\tcontext->address = mosquitto_strdup(saddr_s);\n\tif(!context->address){\n\t\tproxy_cleanup(context);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tcontext->remote_port = (uint16_t )sport;\n\tproxy_cleanup(context);\n\treturn update_transport(context);\n}\n\n\nint proxy_v1__read(struct mosquitto *context)\n{\n\tif(context->proxy.buf == NULL){\n\t\tcontext->proxy.buf = mosquitto_calloc(1, PROXY_V1_PACKET_LIMIT);\n\t\tif(!context->proxy.buf){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tcontext->proxy.pos = 0;\n\t}\n\n\twhile(context->proxy.pos < PROXY_V1_PACKET_LIMIT){\n\t\tif(net__read(context, &(context->proxy.buf[context->proxy.pos]), 1) != 1){\n\t\t\tproxy_cleanup(context);\n\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t}\n\t\tcontext->proxy.pos++;\n\t\tif(context->proxy.pos > 2){ /* FIXME: Figure out better limit */\n\t\t\tif(context->proxy.buf[context->proxy.pos-1] == '\\n'\n\t\t\t\t\t&& context->proxy.buf[context->proxy.pos-2] == '\\r'){\n\n\t\t\t\t/* Line received, now decode */\n\t\t\t\treturn proxy_v1__decode(context);\n\t\t\t}\n\t\t}\n\t}\n\tif(context->proxy.pos == PROXY_V1_PACKET_LIMIT){\n\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connection rejected, corrupt PROXY header.\");\n\t\tproxy_cleanup(context);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n"
  },
  {
    "path": "src/proxy_v2.c",
    "content": "#ifdef WIN32\n#  include <winsock2.h>\n#  include <ws2tcpip.h>\n#else\n#  include <arpa/inet.h>\n#  include <netinet/in.h>\n#endif\n#include <stdint.h>\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto_internal.h\"\n#include \"net_mosq.h\"\n\n#if !defined(WITH_WEBSOCKETS) || WITH_WEBSOCKETS == WS_IS_BUILTIN\n\n#define PROXY_CMD_LOCAL 0x00\n#define PROXY_CMD_PROXY 0x01\n\n#define PROXY_TCP_IPV4 0x11\n#define PROXY_TCP_IPV6 0x21\n#define PROXY_TCP_UNIX 0x31\n\n#define PP2_TYPE_ALPN           0x01\n#define PP2_TYPE_AUTHORITY      0x02\n#define PP2_TYPE_CRC32C         0x03\n#define PP2_TYPE_NOOP           0x04\n#define PP2_TYPE_UNIQUE_ID      0x05\n#define PP2_TYPE_SSL            0x20\n#define PP2_SUBTYPE_SSL_VERSION 0x21\n#define PP2_SUBTYPE_SSL_CN      0x22\n#define PP2_SUBTYPE_SSL_CIPHER  0x23\n#define PP2_SUBTYPE_SSL_SIG_ALG 0x24\n#define PP2_SUBTYPE_SSL_KEY_ALG 0x25\n#define PP2_TYPE_NETNS          0x30\n\n#define PP2_CLIENT_SSL           0x01\n#define PP2_CLIENT_CERT_CONN     0x02\n#define PP2_CLIENT_CERT_SESS     0x04\n\n#define PROXY_PACKET_LIMIT 500\n\nstruct proxy_hdr_v2 {\n\tuint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */\n\tuint8_t ver_cmd; /* protocol version and command */\n\tuint8_t fam; /* protocol family and address */\n\tuint16_t len; /* number of following bytes part of the header */\n};\n\nunion proxy_addr {\n\tstruct { /* for TCP/UDP over IPv4, len = 12 */\n\t\tuint32_t src_addr;\n\t\tuint32_t dst_addr;\n\t\tuint16_t src_port;\n\t\tuint16_t dst_port;\n\t} ipv4_addr;\n\tstruct { /* for TCP/UDP over IPv6, len = 36 */\n\t\tuint8_t src_addr[16];\n\t\tuint8_t dst_addr[16];\n\t\tuint16_t src_port;\n\t\tuint16_t dst_port;\n\t} ipv6_addr;\n\tstruct { /* for AF_UNIX sockets, len = 216 */\n\t\tuint8_t src_addr[108];\n\t\tuint8_t dst_addr[108];\n\t} unix_addr;\n};\n\nstruct pp2_tlv {\n\tuint8_t type;\n\tuint8_t length_h;\n\tuint8_t length_l;\n};\n\nstruct pp2_tlv_ssl {\n\tuint8_t client;\n\tuint32_t verify;\n};\n\nconst uint8_t signature[12] = {0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A};\n\n\nstatic void proxy_cleanup(struct mosquitto *context)\n{\n\tmosquitto_FREE(context->proxy.buf);\n\tmosquitto_FREE(context->proxy.tls_version);\n\tmosquitto_FREE(context->proxy.cipher);\n}\n\n\nstatic int read_tlv_ssl(struct mosquitto *context, uint16_t len, bool *have_certificate)\n{\n\tstruct pp2_tlv_ssl ssl = {0};\n\n\tif(len < sizeof(uint8_t) + sizeof(uint32_t)){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tssl.client = context->proxy.buf[context->proxy.pos];\n\tssl.verify = ntohl(*(uint32_t *)(&context->proxy.buf[context->proxy.pos + sizeof(uint8_t)]));\n\tcontext->proxy.pos = (uint16_t)(context->proxy.pos + sizeof(uint8_t) + sizeof(uint32_t));\n\tlen = (uint16_t)(len - (sizeof(uint8_t) + sizeof(uint32_t)));\n\n\tif(ssl.client & PP2_CLIENT_SSL && ssl.client & PP2_CLIENT_CERT_SESS && !ssl.verify){\n\t\t*have_certificate = true;\n\t}\n\n\twhile(len > 0){\n\t\tif(context->proxy.len - context->proxy.pos < (int)sizeof(struct pp2_tlv)){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tstruct pp2_tlv *tlv = (struct pp2_tlv *)(&context->proxy.buf[context->proxy.pos]);\n\t\tuint16_t tlv_len = (uint16_t)((tlv->length_h<<8) + tlv->length_l);\n\t\tcontext->proxy.pos = (uint16_t)(context->proxy.pos + sizeof(struct pp2_tlv));\n\n\t\tif(tlv_len > context->proxy.len - context->proxy.pos){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\tswitch(tlv->type){\n\t\t\tcase PP2_SUBTYPE_SSL_VERSION:\n#ifdef WITH_TLS\n\t\t\t\tmosquitto_free(context->proxy.tls_version);\n\t\t\t\tcontext->proxy.tls_version = mosquitto_strndup((const char *)&context->proxy.buf[context->proxy.pos], tlv_len);\n#else\n\t\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\t\t\tbreak;\n\n\t\t\tcase PP2_SUBTYPE_SSL_CIPHER:\n#ifdef WITH_TLS\n\t\t\t\tmosquitto_free(context->proxy.cipher);\n\t\t\t\tcontext->proxy.cipher = mosquitto_strndup((const char *)&context->proxy.buf[context->proxy.pos], tlv_len);\n#else\n\t\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\t\t\tbreak;\n\n\t\t\tcase PP2_SUBTYPE_SSL_CN:\n#ifdef WITH_TLS\n\t\t\t\tif(context->listener->use_identity_as_username){\n\t\t\t\t\tmosquitto_free(context->username);\n\t\t\t\t\tcontext->username = mosquitto_strndup((const char *)&context->proxy.buf[context->proxy.pos], tlv_len);\n\t\t\t\t\tif(!context->username){\n\t\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t\t}\n\t\t\t\t}\n#else\n\t\t\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n#endif\n\t\t\t\tbreak;\n\t\t}\n\t\tlen = (uint16_t)(len - (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + tlv_len));\n\t\tcontext->proxy.pos = (uint16_t)(context->proxy.pos + tlv_len);\n\t}\n\tcontext->proxy.have_tls = true;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int read_tlv(struct mosquitto *context, bool *have_certificate)\n{\n\twhile(context->proxy.pos < context->proxy.len){\n\t\tif(context->proxy.len - context->proxy.pos < (int)sizeof(struct pp2_tlv)){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tstruct pp2_tlv *tlv = (struct pp2_tlv *)(&context->proxy.buf[context->proxy.pos]);\n\t\tuint16_t tlv_len = (uint16_t)((tlv->length_h<<8) + tlv->length_l);\n\t\tcontext->proxy.pos = (uint16_t)(context->proxy.pos + sizeof(struct pp2_tlv));\n\n\t\tif(tlv_len > context->proxy.len - context->proxy.pos){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\n\t\tswitch(tlv->type){\n\t\t\tcase PP2_TYPE_SSL:\n\t\t\t\t{\n\t\t\t\t\tint rc = read_tlv_ssl(context, tlv_len, have_certificate);\n\t\t\t\t\tif(rc){\n\t\t\t\t\t\treturn rc;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontext->proxy.pos = (uint16_t)(context->proxy.pos + tlv_len);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint proxy_v2__read(struct mosquitto *context)\n{\n\tstruct proxy_hdr_v2 hdr;\n\n\tif(context->proxy.cmd == -1){\n\t\tcontext->proxy.buf = NULL;\n\t\tif(net__read(context, &hdr, sizeof(hdr)) != sizeof(hdr)){\n\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t}\n\t\tif(memcmp(hdr.sig, signature, sizeof(signature))){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif((hdr.ver_cmd & 0xF0) != 0x20){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tcontext->proxy.cmd = hdr.ver_cmd & 0x0F;\n\t\tif(context->proxy.cmd != 0x00 && context->proxy.cmd != 0x01){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tcontext->proxy.fam = hdr.fam;\n\t\tif((hdr.fam != 0x00 || context->proxy.cmd != PROXY_CMD_LOCAL)\n\t\t\t\t&& hdr.fam != PROXY_TCP_IPV4\n\t\t\t\t&& hdr.fam != PROXY_TCP_IPV6\n\t\t\t\t&& hdr.fam != PROXY_TCP_UNIX){\n\n\t\t\treturn 1;\n\t\t}\n\t\tcontext->proxy.pos = 0;\n\t\tcontext->proxy.len = ntohs(hdr.len);\n\t\tif(context->proxy.len > 0){\n\t\t\t/* PROXY_PACKET_LIMIT=500 bytes, arbitrary upper limit */\n\t\t\tswitch(context->proxy.fam){\n\t\t\t\tcase PROXY_TCP_IPV4:\n\t\t\t\t\tif(context->proxy.len < 12 || context->proxy.len > PROXY_PACKET_LIMIT){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PROXY_TCP_IPV6:\n\t\t\t\t\tif(context->proxy.len < 36 || context->proxy.len > PROXY_PACKET_LIMIT){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PROXY_TCP_UNIX:\n\t\t\t\t\tif(context->proxy.len > PROXY_PACKET_LIMIT){\n\t\t\t\t\t\treturn MOSQ_ERR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcontext->proxy.buf = mosquitto_calloc(1, (size_t)(context->proxy.len+1));\n\t\t\tif(!context->proxy.buf){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}else{\n\t\t\tif(context->proxy.cmd != PROXY_CMD_LOCAL || context->proxy.fam != 0x00){\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tif(context->proxy.pos < context->proxy.len){\n\t\tssize_t rc = net__read(context, context->proxy.buf, (size_t)(context->proxy.len - context->proxy.pos));\n\t\tif(rc > 0){\n\t\t\tcontext->proxy.pos = (uint16_t)(context->proxy.pos + rc);\n\t\t}else{\n\t\t\tproxy_cleanup(context);\n\t\t\treturn MOSQ_ERR_CONN_LOST;\n\t\t}\n\t}\n\tif(context->proxy.pos == context->proxy.len){\n\t\tif(context->proxy.fam == PROXY_TCP_IPV4){\n\t\t\tchar address[100];\n\t\t\tunion proxy_addr *addr = (union proxy_addr *)context->proxy.buf;\n\n\t\t\tinet_ntop(AF_INET, &addr->ipv4_addr.src_addr, address, sizeof(address));\n\t\t\tcontext->address = mosquitto_strdup(address);\n\t\t\tcontext->remote_port = ntohs(addr->ipv4_addr.src_port);\n\t\t\tcontext->proxy.pos = 4+4+2+2;\n\t\t}else if(context->proxy.fam == PROXY_TCP_IPV6){\n\t\t\tchar address[100];\n\t\t\tunion proxy_addr *addr = (union proxy_addr *)context->proxy.buf;\n\n\t\t\tinet_ntop(AF_INET6, addr->ipv6_addr.src_addr, address, sizeof(address));\n\t\t\tcontext->address = mosquitto_strdup(address);\n\t\t\tcontext->remote_port = ntohs(addr->ipv6_addr.src_port);\n\t\t\tcontext->proxy.pos = 16+16+2+2;\n\t\t}else if(context->proxy.fam == PROXY_TCP_UNIX){\n\t\t\tunion proxy_addr *addr = (union proxy_addr *)context->proxy.buf;\n\t\t\tcontext->address = mosquitto_strndup((char *)addr->unix_addr.src_addr, sizeof(addr->unix_addr.src_addr));\n\t\t\tcontext->remote_port = 0;\n\t\t\tcontext->proxy.pos = (uint16_t)(sizeof(addr->unix_addr.src_addr) + 1);\n\t\t}else{\n\t\t\t/* Must be LOCAL */\n\t\t\t/* Ignore address */\n\t\t\tcontext->address = NULL;\n\t\t\tcontext->remote_port = 0;\n\t\t\tproxy_cleanup(context);\n\t\t\treturn MOSQ_ERR_PROXY;\n\t\t}\n\t\tif(!context->address){\n\t\t\tproxy_cleanup(context);\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\tbool have_certificate = false;\n\n\t\tint rc = read_tlv(context, &have_certificate);\n\t\tif(rc){\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connection from %s:%d rejected, corrupt PROXY header.\",\n\t\t\t\t\tcontext->address, context->remote_port);\n\t\t\tproxy_cleanup(context);\n\t\t\treturn MOSQ_ERR_PROXY;\n\t\t}\n\t\tmosquitto_FREE(context->proxy.buf);\n\n\t\tif(context->listener->proxy_protocol_v2_require_tls && !context->proxy.have_tls){\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connection from %s:%d rejected, client did not connect using TLS.\",\n\t\t\t\t\tcontext->address, context->remote_port);\n\t\t\tproxy_cleanup(context);\n\t\t\treturn MOSQ_ERR_PROXY;\n\t\t}\n\n#ifdef WITH_TLS\n\t\tif(context->listener->require_certificate){\n\t\t\tif(!have_certificate){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connection from %s:%d rejected, client did not provide a certificate.\",\n\t\t\t\t\t\tcontext->address, context->remote_port);\n\t\t\t\tproxy_cleanup(context);\n\t\t\t\treturn MOSQ_ERR_PROXY;\n\t\t\t}\n\t\t}\n\n\t\tif(context->proxy.tls_version && context->proxy.cipher){\n\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Connection from %s:%d negotiated %s cipher %s\",\n\t\t\t\t\tcontext->address, context->remote_port, context->proxy.tls_version, context->proxy.cipher);\n\t\t}\n#endif\n\t\tproxy_cleanup(context);\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN\n\t\tif(context->listener->protocol == mp_websockets){\n\t\t\treturn http__context_init(context);\n\t\t}else\n#endif\n\t\t{\n\t\t\tcontext->transport = mosq_t_tcp;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n#endif\n"
  },
  {
    "path": "src/psk_file.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <ctype.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\nstatic int psk__cleanup(struct mosquitto__psk **psk);\nstatic int psk__file_parse(struct mosquitto__psk **psk_id, const char *psk_file);\n\n\nstatic void psk__free_item(struct mosquitto__psk *psk)\n{\n\tmosquitto_FREE(psk->username);\n\tmosquitto_FREE(psk->password);\n\tmosquitto_FREE(psk);\n}\n\n\nint psk_file__init(void)\n{\n\tint rc;\n\tchar *pskf = NULL;\n\n\t/* Load psk data if required. */\n\tif(db.config->per_listener_settings){\n\t\tfor(int i=0; i<db.config->listener_count; i++){\n\t\t\tpskf = db.config->listeners[i].security_options->psk_file;\n\t\t\tif(pskf){\n\t\t\t\trc = psk__file_parse(&db.config->listeners[i].security_options->psk_id, pskf);\n\t\t\t\tif(rc){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error opening psk file \\\"%s\\\".\", pskf);\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}else{\n\t\tpskf = db.config->security_options.psk_file;\n\t\tif(pskf){\n\t\t\trc = psk__file_parse(&db.config->security_options.psk_id, pskf);\n\t\t\tif(rc){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error opening psk file \\\"%s\\\".\", pskf);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint psk_file__cleanup(void)\n{\n\tint rc;\n\n\trc = psk__cleanup(&db.config->security_options.psk_id);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\tfor(int i=0; i<db.config->listener_count; i++){\n\t\tif(db.config->listeners[i].security_options->psk_id){\n\t\t\trc = psk__cleanup(&db.config->listeners[i].security_options->psk_id);\n\t\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int pwfile__parse(const char *file, struct mosquitto__psk **root)\n{\n\tFILE *pwfile;\n\tstruct mosquitto__psk *psk;\n\tchar *username, *password;\n\tchar *saveptr = NULL;\n\tchar *buf;\n\tint buflen = 256;\n\n\tbuf = mosquitto_malloc((size_t)buflen);\n\tif(buf == NULL){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tpwfile = mosquitto_fopen(file, \"rt\", true);\n\tif(!pwfile){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to open pwfile \\\"%s\\\".\", file);\n\t\tmosquitto_FREE(buf);\n\t\treturn MOSQ_ERR_UNKNOWN;\n\t}\n\n\twhile(!feof(pwfile)){\n\t\tif(mosquitto_fgets(&buf, &buflen, pwfile)){\n\t\t\tif(buf[0] == '#'){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(!strchr(buf, ':')){\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tusername = strtok_r(buf, \":\", &saveptr);\n\t\t\tif(username){\n\t\t\t\tusername = mosquitto_trimblanks(username);\n\t\t\t\tif(strlen(username) > 65535){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Warning: Invalid line in password file '%s', username too long.\", file);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(strlen(username) <= 0){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Warning: Empty username in password file '%s', ingoring.\", file);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tHASH_FIND(hh, *root, username, strlen(username), psk);\n\t\t\t\tif(psk){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Error: Duplicate user '%s' in password file '%s', ignoring.\", username, file);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tpsk = mosquitto_calloc(1, sizeof(struct mosquitto__psk));\n\t\t\t\tif(!psk){\n\t\t\t\t\tfclose(pwfile);\n\t\t\t\t\tmosquitto_FREE(buf);\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\n\t\t\t\tpsk->username = mosquitto_strdup(username);\n\t\t\t\tif(!psk->username){\n\t\t\t\t\tpsk__free_item(psk);\n\t\t\t\t\tmosquitto_FREE(buf);\n\t\t\t\t\tfclose(pwfile);\n\t\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t\t}\n\t\t\t\tpassword = strtok_r(NULL, \":\", &saveptr);\n\t\t\t\tif(password){\n\t\t\t\t\tpassword = mosquitto_trimblanks(password);\n\n\t\t\t\t\tif(strlen(password) > 65535){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Warning: Invalid line in password file '%s', password too long.\", file);\n\t\t\t\t\t\tpsk__free_item(psk);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tpsk->password = mosquitto_strdup(password);\n\t\t\t\t\tif(!psk->password){\n\t\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Warning: Unable to decode line in password file '%s'.\", file);\n\t\t\t\t\t\tpsk__free_item(psk);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tHASH_ADD_KEYPTR(hh, *root, psk->username, strlen(psk->username), psk);\n\t\t\t\t}else{\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Warning: Invalid line in psk file '%s': %s\", file, buf);\n\t\t\t\t\tpsk__free_item(psk);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfclose(pwfile);\n\tmosquitto_FREE(buf);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int psk__file_parse(struct mosquitto__psk **psk_id, const char *psk_file)\n{\n\tint rc;\n\tstruct mosquitto__psk *psk, *tmp = NULL;\n\n\tif(!db.config || !psk_id){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t/* We haven't been asked to parse a psk file. */\n\tif(!psk_file){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\trc = pwfile__parse(psk_file, psk_id);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tHASH_ITER(hh, (*psk_id), psk, tmp){\n\t\t/* Check for hex only digits */\n\t\tif(!psk->password){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Empty psk for identity \\\"%s\\\".\", psk->username);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(strspn(psk->password, \"0123456789abcdefABCDEF\") < strlen(psk->password)){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: psk for identity \\\"%s\\\" contains non-hexadecimal characters.\", psk->username);\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int psk__cleanup(struct mosquitto__psk **root)\n{\n\tstruct mosquitto__psk *psk, *tmp = NULL;\n\n\tif(!root){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_ITER(hh, *root, psk, tmp){\n\t\tHASH_DEL(*root, psk);\n\t\tpsk__free_item(psk);\n\t}\n\n\t*root = NULL;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_psk_key_get_default(struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\tstruct mosquitto__psk *psk;\n\tstruct mosquitto__psk *psk_id_ref = NULL;\n\n\tif(!hint || !identity || !key){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(db.config->per_listener_settings){\n\t\tif(!context->listener){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tpsk_id_ref = context->listener->security_options->psk_id;\n\t}else{\n\t\tpsk_id_ref = db.config->security_options.psk_id;\n\t}\n\tif(!psk_id_ref){\n\t\treturn MOSQ_ERR_PLUGIN_IGNORE;\n\t}\n\n\tHASH_FIND(hh, psk_id_ref, identity, strlen(identity), psk);\n\tif(psk){\n\t\tstrncpy(key, psk->password, (size_t)max_key_len);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\treturn MOSQ_ERR_AUTH;\n}\n"
  },
  {
    "path": "src/read_handle.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n\n\nint handle__packet(struct mosquitto *context)\n{\n\tint rc = MOSQ_ERR_INVAL;\n\n\tif(!context){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tswitch((context->in_packet.command)&0xF0){\n\t\tcase CMD_PINGREQ:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_pingreq_received, 1);\n\t\t\trc = handle__pingreq(context);\n\t\t\tbreak;\n\t\tcase CMD_PINGRESP:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_pingresp_received, 1);\n\t\t\trc = handle__pingresp(context);\n\t\t\tbreak;\n\t\tcase CMD_PUBACK:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_puback_received, 1);\n\t\t\trc = handle__pubackcomp(context, \"PUBACK\");\n\t\t\tbreak;\n\t\tcase CMD_PUBCOMP:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_pubcomp_received, 1);\n\t\t\trc = handle__pubackcomp(context, \"PUBCOMP\");\n\t\t\tbreak;\n\t\tcase CMD_PUBLISH:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_publish_received, 1);\n\t\t\trc = handle__publish(context);\n\t\t\tbreak;\n\t\tcase CMD_PUBREC:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_pubrec_received, 1);\n\t\t\trc = handle__pubrec(context);\n\t\t\tbreak;\n\t\tcase CMD_PUBREL:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_pubrel_received, 1);\n\t\t\trc = handle__pubrel(context);\n\t\t\tbreak;\n\t\tcase CMD_CONNECT:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_connect_received, 1);\n\t\t\treturn handle__connect(context);\n\t\tcase CMD_DISCONNECT:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_disconnect_received, 1);\n\t\t\trc = handle__disconnect(context);\n\t\t\tbreak;\n\t\tcase CMD_SUBSCRIBE:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_subscribe_received, 1);\n\t\t\trc = handle__subscribe(context);\n\t\t\tbreak;\n\t\tcase CMD_UNSUBSCRIBE:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_unsubscribe_received, 1);\n\t\t\trc = handle__unsubscribe(context);\n\t\t\tbreak;\n#ifdef WITH_BRIDGE\n\t\tcase CMD_CONNACK:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_connack_received, 1);\n\t\t\trc = handle__connack(context);\n\t\t\tbreak;\n\t\tcase CMD_SUBACK:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_suback_received, 1);\n\t\t\trc = handle__suback(context);\n\t\t\tbreak;\n\t\tcase CMD_UNSUBACK:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_unsuback_received, 1);\n\t\t\trc = handle__unsuback(context);\n\t\t\tbreak;\n#endif\n\t\tcase CMD_AUTH:\n\t\t\tmetrics__int_inc(mosq_counter_mqtt_auth_received, 1);\n\t\t\trc = handle__auth(context);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\trc = MOSQ_ERR_PROTOCOL;\n\t}\n\n\tif(context->protocol == mosq_p_mqtt5){\n\t\tif(rc == MOSQ_ERR_PROTOCOL || rc == MOSQ_ERR_DUPLICATE_PROPERTY){\n\t\t\tsend__disconnect(context, MQTT_RC_PROTOCOL_ERROR, NULL);\n\t\t}else if(rc == MOSQ_ERR_MALFORMED_PACKET){\n\t\t\tsend__disconnect(context, MQTT_RC_MALFORMED_PACKET, NULL);\n\t\t}else if(rc == MOSQ_ERR_QOS_NOT_SUPPORTED){\n\t\t\tsend__disconnect(context, MQTT_RC_QOS_NOT_SUPPORTED, NULL);\n\t\t}else if(rc == MOSQ_ERR_RETAIN_NOT_SUPPORTED){\n\t\t\tsend__disconnect(context, MQTT_RC_RETAIN_NOT_SUPPORTED, NULL);\n\t\t}else if(rc == MOSQ_ERR_TOPIC_ALIAS_INVALID){\n\t\t\tsend__disconnect(context, MQTT_RC_TOPIC_ALIAS_INVALID, NULL);\n\t\t}else if(rc == MOSQ_ERR_RECEIVE_MAXIMUM_EXCEEDED){\n\t\t\tsend__disconnect(context, MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED, NULL);\n\t\t}else if(rc == MOSQ_ERR_UNKNOWN || rc == MOSQ_ERR_NOMEM){\n\t\t\tsend__disconnect(context, MQTT_RC_UNSPECIFIED, NULL);\n\t\t}\n\t}\n\treturn rc;\n}\n"
  },
  {
    "path": "src/retain.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"util_mosq.h\"\n\n#include \"utlist.h\"\n\nstatic time_t next_expire_check = 0;\n\nstatic struct mosquitto__retainhier *retain__add_hier_entry(struct mosquitto__retainhier *parent, struct mosquitto__retainhier **sibling, const char *topic, uint16_t len)\n{\n\tstruct mosquitto__retainhier *child;\n\n\tassert(sibling);\n\n\tchild = mosquitto_calloc(1, sizeof(struct mosquitto__retainhier) + len + 1);\n\tif(!child){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn NULL;\n\t}\n\tchild->parent = parent;\n\tchild->topic_len = len;\n\tif(len > 0){\n\t\tstrncpy(child->topic, topic, (size_t)(len+1));\n\t}\n\n\tHASH_ADD(hh, *sibling, topic, child->topic_len, child);\n\n\treturn child;\n}\n\n\nint retain__init(void)\n{\n\tstruct mosquitto__retainhier *retainhier;\n\n\tretainhier = retain__add_hier_entry(NULL, &db.retains, \"\", 0);\n\tif(!retainhier){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tretainhier = retain__add_hier_entry(NULL, &db.retains, \"$SYS\", (uint16_t)strlen(\"$SYS\"));\n\tif(!retainhier){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nBROKER_EXPORT int mosquitto_persist_retain_msg_set(const char *topic, uint64_t base_msg_id)\n{\n\tstruct mosquitto__base_msg *base_msg;\n\tint rc = MOSQ_ERR_UNKNOWN;\n\tchar **split_topics = NULL;\n\tchar *local_topic = NULL;\n\n\tif(topic == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tHASH_FIND(hh, db.msg_store, &base_msg_id, sizeof(base_msg_id), base_msg);\n\tif(base_msg){\n\t\tif(sub__topic_tokenise(topic, &local_topic, &split_topics, NULL)){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\n\t\trc = retain__store(topic, base_msg, split_topics, false);\n\t\tmosquitto_free(split_topics);\n\t\tmosquitto_free(local_topic);\n\t}\n\n\treturn rc;\n}\n\n\nBROKER_EXPORT int mosquitto_persist_retain_msg_delete(const char *topic)\n{\n\tstruct mosquitto__base_msg base_msg;\n\tint rc = MOSQ_ERR_UNKNOWN;\n\tchar **split_topics = NULL;\n\tchar *local_topic = NULL;\n\n\tif(topic == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tmemset(&base_msg, 0, sizeof(base_msg));\n\tbase_msg.ref_count = 10; /* Ensure this isn't freed */\n\n\tif(sub__topic_tokenise(topic, &local_topic, &split_topics, NULL)){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\t/* With stored->payloadlen == 0, this means the message will be removed */\n\trc = retain__store(topic, &base_msg, split_topics, false);\n\tmosquitto_FREE(split_topics);\n\tmosquitto_FREE(local_topic);\n\n\treturn rc;\n}\n\n\nvoid retain__clean_empty_hierarchy(struct mosquitto__retainhier *retainhier)\n{\n\twhile(retainhier){\n\t\tif(retainhier->children || retainhier->retained || retainhier->parent == NULL){\n\t\t\t/* Entry is being used */\n\t\t\treturn;\n\t\t}else{\n\t\t\tHASH_DELETE(hh, retainhier->parent->children, retainhier);\n\n\t\t\tstruct mosquitto__retainhier *parent = retainhier->parent;\n\t\t\tmosquitto_FREE(retainhier);\n\t\t\tretainhier = parent;\n\t\t}\n\t}\n}\n\n\nint retain__store(const char *topic, struct mosquitto__base_msg *base_msg, char **split_topics, bool persist)\n{\n\tstruct mosquitto__retainhier *retainhier;\n\tstruct mosquitto__retainhier *branch;\n\tsize_t slen;\n\n\tassert(base_msg);\n\tassert(split_topics);\n\n\tHASH_FIND(hh, db.retains, split_topics[0], strlen(split_topics[0]), retainhier);\n\tif(retainhier == NULL){\n\t\tretainhier = retain__add_hier_entry(NULL, &db.retains, split_topics[0], (uint16_t)strlen(split_topics[0]));\n\t\tif(!retainhier){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t}\n\n\tfor(int i=0; split_topics[i] != NULL; i++){\n\t\tslen = strlen(split_topics[i]);\n\t\tHASH_FIND(hh, retainhier->children, split_topics[i], slen, branch);\n\t\tif(branch == NULL){\n\t\t\tbranch = retain__add_hier_entry(retainhier, &retainhier->children, split_topics[i], (uint16_t)slen);\n\t\t\tif(branch == NULL){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t\tretainhier = branch;\n\t}\n\n#ifdef WITH_PERSISTENCE\n\tif(strncmp(topic, \"$SYS\", 4)){\n\t\t/* Retained messages count as a persistence change, but only if\n\t\t * they aren't for $SYS. */\n\t\tdb.persistence_changes++;\n\t}\n#else\n\tUNUSED(topic);\n#endif\n\n\tif(retainhier->retained){\n\t\tif(retainhier->retained == base_msg){\n\t\t\t/* This may occur if multiple persistence providers are used */\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\n\t\tif(persist && retainhier->retained->data.topic[0] != '$' && base_msg->data.payloadlen == 0){\n\t\t\t/* Only delete if another retained message isn't replacing this one */\n\t\t\tplugin_persist__handle_retain_msg_delete(retainhier->retained);\n\t\t}\n\t\tdb__msg_store_ref_dec(&retainhier->retained);\n#ifdef WITH_SYS_TREE\n\t\tdb.retained_count--;\n#endif\n\t\tif(base_msg->data.payloadlen == 0){\n\t\t\tretainhier->retained = NULL;\n\t\t\tretain__clean_empty_hierarchy(retainhier);\n\t\t}\n\t}\n\tif(base_msg->data.payloadlen){\n\t\tretainhier->retained = base_msg;\n\t\tdb__msg_store_ref_inc(retainhier->retained);\n\t\tif(persist && retainhier->retained->data.topic[0] != '$'){\n\t\t\tplugin_persist__handle_base_msg_add(retainhier->retained);\n\t\t\tplugin_persist__handle_retain_msg_set(retainhier->retained);\n\t\t}\n#ifdef WITH_SYS_TREE\n\t\tdb.retained_count++;\n#endif\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic bool retain__delete_expired_msg(struct mosquitto__retainhier *branch)\n{\n\tif(branch->retained && branch->retained->data.expiry_time > 0 && db.now_real_s >= branch->retained->data.expiry_time){\n\t\tplugin_persist__handle_retain_msg_delete(branch->retained);\n\t\tdb__msg_store_ref_dec(&branch->retained);\n\t\tbranch->retained = NULL;\n#ifdef WITH_SYS_TREE\n\t\tdb.retained_count--;\n#endif\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\nstatic int retain__process(struct mosquitto__retainhier *branch, struct mosquitto *context, const struct mosquitto_subscription *sub)\n{\n\tint rc = 0;\n\tuint8_t qos, sub_qos;\n\tuint16_t mid;\n\tstruct mosquitto__base_msg *retained;\n\n\tif(retain__delete_expired_msg(branch)){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tretained = branch->retained;\n\n\trc = mosquitto_acl_check(context, retained->data.topic, retained->data.payloadlen, retained->data.payload,\n\t\t\tretained->data.qos, retained->data.retain, retained->data.properties, MOSQ_ACL_READ);\n\tif(rc == MOSQ_ERR_ACL_DENIED){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\t/* Check for original source access */\n\tif(db.config->check_retain_source && retained->origin != mosq_mo_broker && retained->data.source_id){\n\t\tstruct mosquitto retain_ctxt;\n\t\tmemset(&retain_ctxt, 0, sizeof(struct mosquitto));\n\n\t\tretain_ctxt.id = retained->data.source_id;\n\t\tretain_ctxt.username = retained->data.source_username;\n\t\tretain_ctxt.listener = retained->source_listener;\n\n\t\trc = mosquitto_acl_check(&retain_ctxt, retained->data.topic, retained->data.payloadlen, retained->data.payload,\n\t\t\t\tretained->data.qos, retained->data.retain, retained->data.properties, MOSQ_ACL_WRITE);\n\t\tif(rc == MOSQ_ERR_ACL_DENIED){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else if(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tsub_qos = sub->options & 0x03;\n\tif(db.config->upgrade_outgoing_qos){\n\t\tqos = sub_qos;\n\t}else{\n\t\tqos = retained->data.qos;\n\t\tif(qos > sub_qos){\n\t\t\tqos = sub_qos;\n\t\t}\n\t}\n\tif(qos > 0){\n\t\tmid = mosquitto__mid_generate(context);\n\t}else{\n\t\tmid = 0;\n\t}\n\treturn db__message_insert_outgoing(context, 0, mid, qos, true, retained, sub->identifier, false, true);\n}\n\n\nstatic int retain__search(struct mosquitto__retainhier *retainhier, char **split_topics, struct mosquitto *context, const struct mosquitto_subscription *sub, int level)\n{\n\tstruct mosquitto__retainhier *branch, *branch_tmp;\n\tint flag = 0;\n\n\tif(!strcmp(split_topics[0], \"#\") && split_topics[1] == NULL){\n\t\tHASH_ITER(hh, retainhier->children, branch, branch_tmp){\n\t\t\t/* Set flag to indicate that we should check for retained messages\n\t\t\t * on \"foo\" when we are subscribing to e.g. \"foo/#\" and then exit\n\t\t\t * this function and return to an earlier retain__search().\n\t\t\t */\n\t\t\tflag = -1;\n\t\t\tif(branch->retained){\n\t\t\t\tretain__process(branch, context, sub);\n\t\t\t}\n\t\t\tif(branch->children){\n\t\t\t\tretain__search(branch, split_topics, context, sub, level+1);\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(!strcmp(split_topics[0], \"+\")){\n\t\t\tHASH_ITER(hh, retainhier->children, branch, branch_tmp){\n\t\t\t\tif(split_topics[1] != NULL){\n\t\t\t\t\tif(retain__search(branch, &(split_topics[1]), context, sub, level+1) == -1\n\t\t\t\t\t\t\t|| (split_topics[1] != NULL && !strcmp(split_topics[1], \"#\") && level>0)){\n\n\t\t\t\t\t\tif(branch->retained){\n\t\t\t\t\t\t\tretain__process(branch, context, sub);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tif(branch->retained){\n\t\t\t\t\t\tretain__process(branch, context, sub);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}else{\n\t\t\tHASH_FIND(hh, retainhier->children, split_topics[0], strlen(split_topics[0]), branch);\n\t\t\tif(branch){\n\t\t\t\tif(split_topics[1] != NULL){\n\t\t\t\t\tif(retain__search(branch, &(split_topics[1]), context, sub, level+1) == -1\n\t\t\t\t\t\t\t|| (split_topics[1] != NULL && !strcmp(split_topics[1], \"#\") && level>0)){\n\n\t\t\t\t\t\tif(branch->retained){\n\t\t\t\t\t\t\tretain__process(branch, context, sub);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tif(branch->retained){\n\t\t\t\t\t\tretain__process(branch, context, sub);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn flag;\n}\n\n\nint retain__queue(struct mosquitto *context, const struct mosquitto_subscription *sub)\n{\n\tstruct mosquitto__retainhier *retainhier;\n\tchar *local_sub;\n\tchar **split_topics;\n\tint rc;\n\n\tassert(context);\n\tassert(sub);\n\n\tif(!strncmp(sub->topic_filter, \"$share/\", strlen(\"$share/\"))){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\trc = sub__topic_tokenise(sub->topic_filter, &local_sub, &split_topics, NULL);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tHASH_FIND(hh, db.retains, split_topics[0], strlen(split_topics[0]), retainhier);\n\n\tif(retainhier){\n\t\tretain__search(retainhier, split_topics, context, sub, 0);\n\t}\n\tmosquitto_FREE(local_sub);\n\tmosquitto_FREE(split_topics);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid retain__expire(struct mosquitto__retainhier **retainhier)\n{\n\tstruct mosquitto__retainhier *peer, *retainhier_tmp;\n\n\tHASH_ITER(hh, *retainhier, peer, retainhier_tmp){\n\t\tretain__expire(&peer->children);\n\n\t\tstruct mosquitto__retainhier *parent = peer->parent;\n\t\tif(retain__delete_expired_msg(peer)){\n\t\t\tretain__clean_empty_hierarchy(parent);\n\t\t}\n\t}\n}\n\n\nvoid retain__clean(struct mosquitto__retainhier **retainhier)\n{\n\tstruct mosquitto__retainhier *peer, *retainhier_tmp;\n\n\tHASH_ITER(hh, *retainhier, peer, retainhier_tmp){\n\t\tif(peer->retained){\n\t\t\tdb__msg_store_ref_dec(&peer->retained);\n\t\t}\n\t\tretain__clean(&peer->children);\n\n\t\tHASH_DELETE(hh, *retainhier, peer);\n\t\tmosquitto_FREE(peer);\n\t}\n}\n\n\nvoid retain__expiry_check(void)\n{\n\tif(db.config->retain_expiry_interval > 0 && db.now_s > next_expire_check){\n\t\tretain__expire(&db.retains);\n\t\tnext_expire_check = db.now_s + db.config->retain_expiry_interval;\n\t}\n}\n"
  },
  {
    "path": "src/security_default.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <ctype.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"password_file.h\"\n#include \"send_mosq.h\"\n#include \"util_mosq.h\"\n\n\nint mosquitto_security_init_default(void)\n{\n\tint rc;\n\n\t/* Configure plugin identifier */\n\tif(db.config->per_listener_settings){\n\t\tfor(int i=0; i<db.config->listener_count; i++){\n\t\t\tdb.config->listeners[i].security_options->pid = mosquitto_calloc(1, sizeof(mosquitto_plugin_id_t));\n\t\t\tif(db.config->listeners[i].security_options->pid == NULL){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tdb.config->listeners[i].security_options->pid->plugin_name = mosquitto_strdup(\"builtin-security\");\n\t\t\tdb.config->listeners[i].security_options->pid->listener = &db.config->listeners[i];\n\t\t\tconfig__plugin_add_secopt(db.config->listeners[i].security_options->pid, db.config->listeners[i].security_options);\n\t\t}\n\t}else{\n\t\tdb.config->security_options.pid = mosquitto_calloc(1, sizeof(mosquitto_plugin_id_t));\n\t\tif(db.config->security_options.pid == NULL){\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tdb.config->security_options.pid->plugin_name = mosquitto_strdup(\"builtin-security\");\n\t\tconfig__plugin_add_secopt(db.config->security_options.pid, &db.config->security_options);\n\t}\n\n\trc = broker_password_file__init();\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = broker_acl_file__init();\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\trc = psk_file__init();\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_security_cleanup_default(void)\n{\n\tint rc = 0;\n\n\tbroker_password_file__cleanup();\n\tbroker_acl_file__cleanup();\n\n\trc = psk_file__cleanup();\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\tif(db.config->per_listener_settings){\n\t\tfor(int i=0; i<db.config->listener_count; i++){\n\t\t\tif(db.config->listeners[i].security_options->pid){\n\t\t\t\tmosquitto_FREE(db.config->listeners[i].security_options->pid->plugin_name);\n\t\t\t\tmosquitto_FREE(db.config->listeners[i].security_options->pid->config.security_options);\n\t\t\t\tmosquitto_FREE(db.config->listeners[i].security_options->pid);\n\t\t\t}\n\t\t}\n\t}else{\n\t\tif(db.config->security_options.pid){\n\t\t\tmosquitto_FREE(db.config->security_options.pid->plugin_name);\n\t\t\tmosquitto_FREE(db.config->security_options.pid->config.security_options);\n\t\t\tmosquitto_FREE(db.config->security_options.pid);\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n#ifdef WITH_TLS\n\n\nstatic void security__disconnect_auth(struct mosquitto *context)\n{\n\tif(context->protocol == mosq_p_mqtt5){\n\t\tsend__disconnect(context, MQTT_RC_ADMINISTRATIVE_ACTION, NULL);\n\t}\n\tmosquitto__set_state(context, mosq_cs_disconnecting);\n\tdo_disconnect(context, MOSQ_ERR_AUTH);\n}\n#endif\n\n\n/* Apply security settings after a reload.\n * Includes:\n * - Disconnecting anonymous users if appropriate\n * - Disconnecting users with invalid passwords\n * - Reapplying ACLs\n */\nint mosquitto_security_apply_default(void)\n{\n\tstruct mosquitto *context, *ctxt_tmp = NULL;\n\tbool allow_anonymous;\n#ifdef WITH_TLS\n\tX509_NAME *name;\n\tX509_NAME_ENTRY *name_entry;\n\tASN1_STRING *name_asn1 = NULL;\n\tstruct mosquitto__listener *listener;\n\tBIO *subject_bio;\n\tchar *data_start;\n\tsize_t name_length;\n\tchar *subject;\n#endif\n\n#ifdef WITH_TLS\n\tfor(int i=0; i<db.config->listener_count; i++){\n\t\tlistener = &db.config->listeners[i];\n\t\tif(listener && listener->ssl_ctx && listener->certfile && listener->keyfile && listener->crlfile && listener->require_certificate){\n\t\t\tif(net__tls_server_ctx(listener)){\n\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t}\n\n\t\t\tif(net__tls_load_verify(listener)){\n\t\t\t\treturn MOSQ_ERR_TLS;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tHASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){\n\t\tif(context->bridge){\n\t\t\tcontinue;\n\t\t}\n\n\t\tif((context->listener && context->listener->security_options->allow_anonymous == true)\n\t\t\t\t|| (!db.config->per_listener_settings && db.config->security_options.allow_anonymous == true\n\t\t\t\t&& context->listener && context->listener->security_options->allow_anonymous != false)){\n\t\t\tallow_anonymous = true;\n\t\t}else{\n\t\t\tallow_anonymous = false;\n\t\t}\n\n\t\tif(!allow_anonymous && !context->username){\n\t\t\tmosquitto__set_state(context, mosq_cs_disconnecting);\n\t\t\tdo_disconnect(context, MOSQ_ERR_AUTH);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* Check for connected clients that are no longer authorised */\n#ifdef WITH_TLS\n\t\tif(context->listener && context->listener->ssl_ctx && (context->listener->use_identity_as_username || context->listener->use_subject_as_username)){\n\t\t\t/* Client must have either a valid certificate, or valid PSK used as a username. */\n\t\t\tif(!context->ssl){\n\t\t\t\tif(context->protocol == mosq_p_mqtt5){\n\t\t\t\t\tsend__disconnect(context, MQTT_RC_ADMINISTRATIVE_ACTION, NULL);\n\t\t\t\t}\n\t\t\t\tmosquitto__set_state(context, mosq_cs_disconnecting);\n\t\t\t\tdo_disconnect(context, MOSQ_ERR_AUTH);\n\t\t\t\tcontinue;\n\t\t\t}\n#ifdef FINAL_WITH_TLS_PSK\n\t\t\tif(context->listener->psk_hint){\n\t\t\t\t/* Client should have provided an identity to get this far. */\n\t\t\t\tif(!context->username){\n\t\t\t\t\tsecurity__disconnect_auth(context);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}else\n#endif /* FINAL_WITH_TLS_PSK */\n\t\t\t{\n\t\t\t\t/* Free existing credentials and then recover them. */\n\t\t\t\tmosquitto_FREE(context->username);\n\t\t\t\tmosquitto_FREE(context->password);\n\n\t\t\t\tX509 *client_cert = SSL_get_peer_certificate(context->ssl);\n\t\t\t\tif(!client_cert){\n\t\t\t\t\tsecurity__disconnect_auth(context);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tname = X509_get_subject_name(client_cert);\n\t\t\t\tif(!name){\n\t\t\t\t\tX509_free(client_cert);\n\t\t\t\t\tsecurity__disconnect_auth(context);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(context->listener->use_identity_as_username){   /* use_identity_as_username */\n\t\t\t\t\tint i = X509_NAME_get_index_by_NID(name, NID_commonName, -1);\n\t\t\t\t\tif(i == -1){\n\t\t\t\t\t\tX509_free(client_cert);\n\t\t\t\t\t\tsecurity__disconnect_auth(context);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tname_entry = X509_NAME_get_entry(name, i);\n\t\t\t\t\tif(name_entry){\n\t\t\t\t\t\tname_asn1 = X509_NAME_ENTRY_get_data(name_entry);\n\t\t\t\t\t\tif(name_asn1 == NULL){\n\t\t\t\t\t\t\tX509_free(client_cert);\n\t\t\t\t\t\t\tsecurity__disconnect_auth(context);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst char *username = (const char *)ASN1_STRING_get0_data(name_asn1);\n\t\t\t\t\t\tif(!username){\n\t\t\t\t\t\t\tX509_free(client_cert);\n\t\t\t\t\t\t\tclient_cert = NULL;\n\t\t\t\t\t\t\tsecurity__disconnect_auth(context);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontext->username = mosquitto_strdup(username);\n\t\t\t\t\t\tif(!context->username){\n\t\t\t\t\t\t\tX509_free(client_cert);\n\t\t\t\t\t\t\tsecurity__disconnect_auth(context);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* Make sure there isn't an embedded NUL character in the CN */\n\t\t\t\t\t\tif((size_t)ASN1_STRING_length(name_asn1) != strlen(context->username)){\n\t\t\t\t\t\t\tX509_free(client_cert);\n\t\t\t\t\t\t\tsecurity__disconnect_auth(context);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}else{   /* use_subject_as_username */\n\t\t\t\t\tsubject_bio = BIO_new(BIO_s_mem());\n\t\t\t\t\tX509_NAME_print_ex(subject_bio, X509_get_subject_name(client_cert), 0, XN_FLAG_RFC2253);\n\t\t\t\t\tdata_start = NULL;\n\t\t\t\t\tname_length = (size_t)BIO_get_mem_data(subject_bio, &data_start);\n\t\t\t\t\tsubject = mosquitto_malloc(sizeof(char)*name_length+1);\n\t\t\t\t\tif(!subject){\n\t\t\t\t\t\tBIO_free(subject_bio);\n\t\t\t\t\t\tX509_free(client_cert);\n\t\t\t\t\t\tsecurity__disconnect_auth(context);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tmemcpy(subject, data_start, name_length);\n\t\t\t\t\tsubject[name_length] = '\\0';\n\t\t\t\t\tBIO_free(subject_bio);\n\t\t\t\t\tcontext->username = subject;\n\t\t\t\t}\n\t\t\t\tif(!context->username){\n\t\t\t\t\tX509_free(client_cert);\n\t\t\t\t\tsecurity__disconnect_auth(context);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tX509_free(client_cert);\n\t\t\t}\n\t\t}else\n#endif\n\t\t{\n\t\t\t/* Username/password check only if the identity/subject check not used */\n\t\t\tif(mosquitto_basic_auth(context) != MOSQ_ERR_SUCCESS){\n\t\t\t\tmosquitto__set_state(context, mosq_cs_disconnecting);\n\t\t\t\tdo_disconnect(context, MOSQ_ERR_AUTH);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "src/send_auth.c",
    "content": "/*\nCopyright (c) 2019-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n\n\nint send__auth(struct mosquitto *context, uint8_t reason_code, const void *auth_data, uint16_t auth_data_len)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tint rc;\n\tmosquitto_property *properties = NULL;\n\tuint32_t remaining_length;\n\n\tif(context->auth_method == NULL){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\tif(context->protocol != mosq_p_mqtt5){\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Protocol error from %s: Sending AUTH packet when session not MQTT v5.0.\", context->id);\n\t\treturn MOSQ_ERR_PROTOCOL;\n\t}\n\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending AUTH to %s (rc%d, %s)\", context->id, reason_code, context->auth_method);\n\n\tremaining_length = 1;\n\n\trc = mosquitto_property_add_string(&properties, MQTT_PROP_AUTHENTICATION_METHOD, context->auth_method);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\tif(auth_data != NULL && auth_data_len > 0){\n\t\trc = mosquitto_property_add_binary(&properties, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len);\n\t\tif(rc){\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\tremaining_length += mosquitto_property_get_remaining_length(properties);\n\n\trc = packet__check_oversize(context, remaining_length);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\trc = packet__alloc(&packet, CMD_AUTH, remaining_length);\n\tif(rc){\n\t\tgoto error;\n\t}\n\n\tpacket__write_byte(packet, reason_code);\n\tproperty__write_all(packet, properties, true);\n\tmosquitto_property_free_all(&properties);\n\n\tmetrics__int_inc(mosq_counter_mqtt_auth_sent, 1);\n\treturn packet__queue(context, packet);\nerror:\n\tmosquitto_property_free_all(&properties);\n\treturn rc;\n}\n"
  },
  {
    "path": "src/send_connack.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n\n\nint send__connack(struct mosquitto *context, uint8_t ack, uint8_t reason_code, const mosquitto_property *properties)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tint rc;\n\tmosquitto_property *connack_props = NULL;\n\tuint32_t remaining_length;\n\n\trc = mosquitto_property_copy_all(&connack_props, properties);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(context->id){\n\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending CONNACK to %s (%d, %d)\", context->id, ack, reason_code);\n\t}else{\n\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending CONNACK to %s (%d, %d)\", context->address, ack, reason_code);\n\t}\n\n\tremaining_length = 2;\n\n\tif(context->protocol == mosq_p_mqtt5){\n\t\tif(reason_code < 128 && db.config->retain_available == false){\n\t\t\trc = mosquitto_property_add_byte(&connack_props, MQTT_PROP_RETAIN_AVAILABLE, 0);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_property_free_all(&connack_props);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t\tif(reason_code < 128 && db.config->max_packet_size > 0){\n\t\t\trc = mosquitto_property_add_int32(&connack_props, MQTT_PROP_MAXIMUM_PACKET_SIZE, db.config->max_packet_size);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_property_free_all(&connack_props);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t\tif(reason_code < 128 && db.config->max_inflight_messages > 0){\n\t\t\trc = mosquitto_property_add_int16(&connack_props, MQTT_PROP_RECEIVE_MAXIMUM, db.config->max_inflight_messages);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_property_free_all(&connack_props);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t\tif(context->listener->max_qos != 2){\n\t\t\trc = mosquitto_property_add_byte(&connack_props, MQTT_PROP_MAXIMUM_QOS, context->listener->max_qos);\n\t\t\tif(rc){\n\t\t\t\tmosquitto_property_free_all(&connack_props);\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\n\t\tremaining_length += mosquitto_property_get_remaining_length(connack_props);\n\t}\n\n\tif(packet__check_oversize(context, remaining_length)){\n\t\tmosquitto_property_free_all(&connack_props);\n\t\treturn MOSQ_ERR_OVERSIZE_PACKET;\n\t}\n\n\trc = packet__alloc(&packet, CMD_CONNACK, remaining_length);\n\tif(rc){\n\t\tmosquitto_property_free_all(&connack_props);\n\t\treturn rc;\n\t}\n\tpacket__write_byte(packet, ack);\n\tpacket__write_byte(packet, reason_code);\n\tif(context->protocol == mosq_p_mqtt5){\n\t\tproperty__write_all(packet, connack_props, true);\n\t}\n\tmosquitto_property_free_all(&connack_props);\n\n\tmetrics__int_inc(mosq_counter_mqtt_connack_sent, 1);\n\treturn packet__queue(context, packet);\n}\n\n"
  },
  {
    "path": "src/send_suback.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n\n\nint send__suback(struct mosquitto *context, uint16_t mid, uint32_t payloadlen, const void *payload)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tint rc;\n\tmosquitto_property *properties = NULL;\n\tuint32_t remaining_length;\n\n\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"Sending SUBACK to %s\", context->id);\n\n\tremaining_length = 2+payloadlen;\n\tif(context->protocol == mosq_p_mqtt5){\n\t\tremaining_length += mosquitto_property_get_remaining_length(properties);\n\t}\n\trc = packet__alloc(&packet, CMD_SUBACK, remaining_length);\n\tif(rc){\n\t\treturn rc;\n\t}\n\tpacket__write_uint16(packet, mid);\n\n\tif(context->protocol == mosq_p_mqtt5){\n\t\t/* We don't use Reason String or User Property yet. */\n\t\tproperty__write_all(packet, properties, true);\n\t}\n\n\tif(payloadlen){\n\t\tpacket__write_bytes(packet, payload, payloadlen);\n\t}\n\n\tmetrics__int_inc(mosq_counter_mqtt_suback_sent, 1);\n\treturn packet__queue(context, packet);\n}\n"
  },
  {
    "path": "src/send_unsuback.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <assert.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"property_mosq.h\"\n#include \"sys_tree.h\"\n\n\nint send__unsuback(struct mosquitto *mosq, uint16_t mid, int reason_code_count, uint8_t *reason_codes, const mosquitto_property *properties)\n{\n\tstruct mosquitto__packet *packet = NULL;\n\tint rc;\n\tuint32_t remaining_length;\n\n\tassert(mosq);\n\n\tremaining_length = 2;\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tremaining_length += mosquitto_property_get_remaining_length(properties);\n\t\tremaining_length += (uint32_t)reason_code_count;\n\t}\n\n\trc = packet__alloc(&packet, CMD_UNSUBACK, remaining_length);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tpacket__write_uint16(packet, mid);\n\n\tif(mosq->protocol == mosq_p_mqtt5){\n\t\tproperty__write_all(packet, properties, true);\n\t\tpacket__write_bytes(packet, reason_codes, (uint32_t)reason_code_count);\n\t}\n\n\tmetrics__int_inc(mosq_counter_mqtt_unsuback_sent, 1);\n\treturn packet__queue(mosq, packet);\n}\n"
  },
  {
    "path": "src/service.c",
    "content": "/*\nCopyright (c) 2011-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#if defined(WIN32) || defined(__CYGWIN__)\n\n#include \"config.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include <windows.h>\n\nextern int g_run;\nSERVICE_STATUS_HANDLE service_handle = 0;\nstatic SERVICE_STATUS service_status;\nint main(int argc, char *argv[]);\n\n\nstatic char *fix_name(char *name)\n{\n\tsize_t len;\n\n\tif(strrchr(name, '\\\\')){\n\t\tname = strrchr(name, '\\\\') + 1;\n\t}\n\tlen = strlen(name);\n\tif(!strcasecmp(&name[len-4], \".exe\")){\n\t\tname[len-4] = '\\0';\n\t}\n\n\treturn name;\n}\n\n\nstatic void print_error(void)\n{\n\tchar *buf = NULL;\n\n\tFormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\n\t\t\tNULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&buf, 0, NULL);\n\n\tfprintf(stderr, \"Error: %s\\n\", buf);\n\tLocalFree(buf);\n}\n\n\n/* Service control callback */\nvoid __stdcall service_handler(DWORD fdwControl)\n{\n\tswitch(fdwControl){\n\t\tcase SERVICE_CONTROL_CONTINUE:\n\t\t\t/* Continue from Paused state. */\n\t\t\tbreak;\n\t\tcase SERVICE_CONTROL_PAUSE:\n\t\t\t/* Pause service. */\n\t\t\tbreak;\n\t\tcase SERVICE_CONTROL_SHUTDOWN:\n\t\t/* System is shutting down. */\n\t\tcase SERVICE_CONTROL_STOP:\n\t\t\t/* Service should stop. */\n\t\t\tservice_status.dwCurrentState = SERVICE_STOP_PENDING;\n\t\t\tSetServiceStatus(service_handle, &service_status);\n\t\t\tg_run = 0;\n\t\t\tbreak;\n\t}\n}\n\n\n/* Function called when started as a service. */\nvoid __stdcall service_main(DWORD dwArgc, LPTSTR *lpszArgv)\n{\n\tchar **argv;\n\tint argc = 1;\n\tchar conf_path[MAX_PATH + 20];\n\tint rc;\n\tchar *name;\n\tchar env_name[MAX_PATH];\n\n\tUNUSED(dwArgc);\n\n\tname = fix_name(lpszArgv[0]);\n\tsnprintf(env_name, sizeof(env_name), \"%s_DIR\", name);\n\tfor(int i=0; i<strlen(env_name); i++){\n\t\tif(env_name[i]>='A' && env_name[i]<='Z'){\n\t\t\t/* Keep upper case letter */\n\t\t}else if(env_name[i]>='0' && env_name[i]<='9'){\n\t\t\t/* Keep number */\n\t\t}else if(env_name[i]>='a' && env_name[i]<='z'){\n\t\t\t/* Convert lower case to upper case */\n\t\t\tenv_name[i] -= 32;\n\t\t}else{\n\t\t\tenv_name[i] = '_';\n\t\t}\n\t}\n\n\tservice_handle = RegisterServiceCtrlHandler(name, service_handler);\n\tif(service_handle){\n\t\tmemset(conf_path, 0, sizeof(conf_path));\n\t\trc = GetEnvironmentVariable(env_name, conf_path, MAX_PATH);\n\t\tif(!rc || rc == MAX_PATH){\n\t\t\tservice_status.dwCurrentState = SERVICE_STOPPED;\n\t\t\tSetServiceStatus(service_handle, &service_status);\n\t\t\treturn;\n\t\t}\n\t\tstrcat(conf_path, \"\\\\mosquitto.conf\");\n\n\t\targv = mosquitto_malloc(sizeof(char *)*3);\n\t\targv[0] = name;\n\t\targv[1] = \"-c\";\n\t\targv[2] = conf_path;\n\t\targc = 3;\n\n\t\tservice_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;\n\t\tservice_status.dwCurrentState = SERVICE_RUNNING;\n\t\tservice_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;\n\t\tservice_status.dwWin32ExitCode = NO_ERROR;\n\t\tservice_status.dwCheckPoint = 0;\n\t\tSetServiceStatus(service_handle, &service_status);\n\n\t\tmain(argc, argv);\n\t\tmosquitto_FREE(argv);\n\n\t\tservice_status.dwCurrentState = SERVICE_STOPPED;\n\t\tSetServiceStatus(service_handle, &service_status);\n\t}\n}\n\n\nvoid service_install(char *name)\n{\n\tSC_HANDLE sc_manager, svc_handle;\n\tchar service_string[MAX_PATH + 20];\n\tchar exe_path[MAX_PATH + 1];\n\tchar display_name[MAX_PATH+1];\n\tSERVICE_DESCRIPTION svc_desc;\n\n\tmemset(exe_path, 0, sizeof(exe_path));\n\tif(GetModuleFileName(NULL, exe_path, MAX_PATH) == MAX_PATH){\n\t\tfprintf(stderr, \"Error: Path too long.\\n\");\n\t\treturn;\n\t}\n\tsnprintf(service_string, sizeof(service_string), \"\\\"%s\\\" run\", exe_path);\n\n\tname = fix_name(name);\n\n\tsc_manager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);\n\tif(sc_manager){\n\t\tif(!strcmp(name, \"mosquitto\")){\n\t\t\tsnprintf(display_name, sizeof(display_name), \"Mosquitto Broker\");\n\t\t}else{\n\t\t\tsnprintf(display_name, sizeof(display_name), \"Mosquitto Broker (%s.exe)\", name);\n\t\t}\n\n\t\tsvc_handle = CreateService(sc_manager, name, display_name,\n\t\t\t\tSERVICE_START | SERVICE_STOP | SERVICE_CHANGE_CONFIG,\n\t\t\t\tSERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,\n\t\t\t\tservice_string, NULL, NULL, NULL, NULL, NULL);\n\n\t\tif(svc_handle){\n\t\t\tmemset(&svc_desc, 0, sizeof(svc_desc));\n\t\t\tsvc_desc.lpDescription = \"Eclipse Mosquitto MQTT v5/v3.1.1 broker\";\n\t\t\tChangeServiceConfig2(svc_handle, SERVICE_CONFIG_DESCRIPTION, &svc_desc);\n\t\t\tCloseServiceHandle(svc_handle);\n\t\t}else{\n\t\t\tprint_error();\n\t\t}\n\t\tCloseServiceHandle(sc_manager);\n\t}else{\n\t\tprint_error();\n\t}\n}\n\n\nvoid service_uninstall(char *name)\n{\n\tSC_HANDLE sc_manager, svc_handle;\n\tSERVICE_STATUS status;\n\n\tname = fix_name(name);\n\n\tsc_manager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);\n\tif(sc_manager){\n\t\tsvc_handle = OpenService(sc_manager, name, SERVICE_QUERY_STATUS | DELETE);\n\t\tif(svc_handle){\n\t\t\tif(QueryServiceStatus(svc_handle, &status)){\n\t\t\t\tif(status.dwCurrentState == SERVICE_STOPPED){\n\t\t\t\t\tDeleteService(svc_handle);\n\t\t\t\t}\n\t\t\t}\n\t\t\tCloseServiceHandle(svc_handle);\n\t\t}else{\n\t\t\tprint_error();\n\t\t}\n\t\tCloseServiceHandle(sc_manager);\n\t}else{\n\t\tprint_error();\n\t}\n}\n\n\nvoid service_run(char *name)\n{\n\tSERVICE_TABLE_ENTRY ste[] = {\n\t\t{ name, service_main },\n\t\t{ NULL, NULL }\n\t};\n\tname = fix_name(name);\n\tste[0].lpServiceName = name;\n\tStartServiceCtrlDispatcher(ste);\n}\n\n#endif\n"
  },
  {
    "path": "src/session_expiry.c",
    "content": "/*\nCopyright (c) 2019-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <math.h>\n#include <stdio.h>\n#include <utlist.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"sys_tree.h\"\n\nstatic struct session_expiry_list *expiry_list = NULL;\nstatic time_t last_check = 0;\n\n\nstatic int session_expiry__cmp(struct session_expiry_list *i1, struct session_expiry_list *i2)\n{\n\tif(i1->context->session_expiry_time == i2->context->session_expiry_time){\n\t\treturn 0;\n\t}else if(i1->context->session_expiry_time > i2->context->session_expiry_time){\n\t\treturn 1;\n\t}else{\n\t\treturn -1;\n\t}\n}\n\n\nstatic void set_session_expiry_time(struct mosquitto *context)\n{\n\tcontext->session_expiry_time = db.now_real_s;\n\n\tif(db.config->persistent_client_expiration == 0){\n\t\t/* No global expiry, so use the client expiration interval */\n\t\tcontext->session_expiry_time += context->session_expiry_interval;\n\t}else{\n\t\t/* We have a global expiry interval */\n\t\tif(db.config->persistent_client_expiration < context->session_expiry_interval){\n\t\t\t/* The client expiry is longer than the global expiry, so use the global */\n\t\t\tcontext->session_expiry_time += db.config->persistent_client_expiration;\n\t\t}else{\n\t\t\t/* The global expiry is longer than the client expiry, so use the client */\n\t\t\tcontext->session_expiry_time += context->session_expiry_interval;\n\t\t}\n\t}\n}\n\n\nint session_expiry__add(struct mosquitto *context)\n{\n\tstruct session_expiry_list *item;\n\n\tif(context->expiry_list_item){\n\t\t/* Already added, may happen in cases like a client disconnecting then\n\t\t * being kicked by a plugin. */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(db.config->persistent_client_expiration == 0){\n\t\tif(context->session_expiry_interval == MQTT_SESSION_EXPIRY_NEVER){\n\t\t\t/* There isn't a global expiry set, and the client has asked to\n\t\t\t * never expire, so we don't add it to the list. */\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\n\titem = mosquitto_calloc(1, sizeof(struct session_expiry_list));\n\tif(!item){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\titem->context = context;\n\tset_session_expiry_time(item->context);\n\tcontext->expiry_list_item = item;\n\n\tDL_INSERT_INORDER(expiry_list, item, session_expiry__cmp);\n\n\tplugin_persist__handle_client_update(context);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)\n{\n\tstruct session_expiry_list *item;\n\n\tif(context->expiry_list_item){\n\t\t/* Already added, may happen in cases like a client disconnecting then\n\t\t * being kicked by a plugin. */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(db.config->persistent_client_expiration == 0){\n\t\tif(context->session_expiry_interval == MQTT_SESSION_EXPIRY_NEVER){\n\t\t\t/* There isn't a global expiry set, and the client has asked to\n\t\t\t * never expire, so we don't add it to the list. */\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t}\n\n\titem = mosquitto_calloc(1, sizeof(struct session_expiry_list));\n\tif(!item){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\titem->context = context;\n\tif(expiry_time){\n\t\titem->context->session_expiry_time = expiry_time;\n\t}else{\n\t\tset_session_expiry_time(item->context);\n\n\t}\n\tcontext->expiry_list_item = item;\n\n\tDL_INSERT_INORDER(expiry_list, item, session_expiry__cmp);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid session_expiry__remove(struct mosquitto *context)\n{\n\tif(context->expiry_list_item){\n\t\tDL_DELETE(expiry_list, context->expiry_list_item);\n\t\tmosquitto_FREE(context->expiry_list_item);\n\t}\n}\n\n\n/* Call on broker shutdown only */\nvoid session_expiry__remove_all(void)\n{\n\tstruct session_expiry_list *item, *tmp;\n\tstruct mosquitto *context;\n\n\tDL_FOREACH_SAFE(expiry_list, item, tmp){\n\t\tcontext = item->context;\n\t\tsession_expiry__remove(context);\n\t\tcontext->session_expiry_interval = MQTT_SESSION_EXPIRY_IMMEDIATE;\n\t\tcontext->will_delay_interval = 0;\n\t\twill_delay__remove(context);\n\t\tcontext__disconnect(context, -1);\n\t}\n}\n\n\nvoid session_expiry__check(void)\n{\n\tstruct session_expiry_list *item, *tmp;\n\tstruct mosquitto *context;\n\ttime_t timeout;\n\n\tif(last_check != 0 && db.now_real_s <= last_check){\n\t\tif(expiry_list){\n\t\t\t/* Next event is the first item of the list, we must set the timeout even if we aren't\n\t\t\t * checking the full list */\n\t\t\ttimeout = (expiry_list->context->session_expiry_time - db.now_real_s) * 1000;\n\t\t\tif(timeout <= 0){\n\t\t\t\ttimeout = 100;\n\t\t\t}\n\t\t\tloop__update_next_event(timeout);\n\t\t}\n\t\treturn;\n\t}\n\n\tlast_check = db.now_real_s;\n\n\tDL_FOREACH_SAFE(expiry_list, item, tmp){\n\t\tif(item->context->session_expiry_time < db.now_real_s){\n\n\t\t\tcontext = item->context;\n\t\t\tsession_expiry__remove(context);\n\n\t\t\tif(context->id){\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Expiring client %s due to timeout.\", context->id);\n\t\t\t}\n\t\t\tmetrics__int_inc(mosq_counter_clients_expired, 1);\n\n\t\t\t/* Session has now expired, so clear interval */\n\t\t\tcontext->session_expiry_interval = MQTT_SESSION_EXPIRY_IMMEDIATE;\n\t\t\t/* Session has expired, so will delay should be cleared. */\n\t\t\tcontext->will_delay_interval = 0;\n\t\t\twill_delay__remove(context);\n\t\t\tcontext__send_will(context);\n\t\t\tplugin_persist__handle_client_delete(context);\n\t\t\tcontext__add_to_disused(context);\n\t\t}else{\n\t\t\ttimeout = (item->context->session_expiry_time - db.now_real_s + 1) * 1000;\n\t\t\tif(timeout <= 0){\n\t\t\t\ttimeout = 100;\n\t\t\t}\n\t\t\tloop__update_next_event(timeout);\n\t\t\treturn;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/signals.c",
    "content": "/*\nCopyright (c) 2016-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n   Dmitry Kaukov - windows named events implementation.\n*/\n#ifdef WIN32\n#  ifndef WIN32_LEAN_AND_MEAN\n#    define WIN32_LEAN_AND_MEAN\n#  endif\n#  include <windows.h>\n#endif\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <stdbool.h>\n#include <signal.h>\n\n#include \"mosquitto_broker_internal.h\"\n\nextern int g_run;\n\nstatic bool flag_reload = false;\nstatic bool flag_log_rotate = false;\n#ifdef WITH_PERSISTENCE\nstatic bool flag_db_backup = false;\n#endif\nstatic bool flag_tree_print = false;\nstatic bool flag_xtreport = false;\n\n\nstatic void handle_signal(int signal)\n{\n\tUNUSED(signal);\n\n\tif(signal == SIGINT || signal == SIGTERM){\n\t\tg_run = 0;\n#ifdef SIGHUP\n\t}else if(signal == SIGHUP){\n\t\tflag_reload = true;\n#endif\n#ifdef SIGUSR1\n\t}else if(signal == SIGUSR1){\n#ifdef WITH_PERSISTENCE\n\t\tflag_db_backup = true;\n#endif\n#endif\n#ifdef SIGUSR2\n\t}else if(signal == SIGUSR2){\n\t\tflag_tree_print = true;\n\t\tflag_xtreport = true;\n#endif\n#ifdef SIGRTMIN\n\t}else if(signal == SIGRTMIN){\n\t\tflag_log_rotate = true;\n#endif\n\t}\n}\n\n\nvoid signal__setup(void)\n{\n\tsignal(SIGINT, handle_signal);\n\tsignal(SIGTERM, handle_signal);\n#ifdef SIGHUP\n\tsignal(SIGHUP, handle_signal);\n#endif\n#ifndef WIN32\n\tsignal(SIGUSR1, handle_signal);\n\tsignal(SIGUSR2, handle_signal);\n\tsignal(SIGPIPE, SIG_IGN);\n#endif\n#ifdef SIGRTMIN\n\tsignal(SIGRTMIN, handle_signal);\n#endif\n#ifdef WIN32\n\tCreateThread(NULL, 0, SigThreadProc, NULL, 0, NULL);\n#endif\n}\n\n\nint signal__flag_check(void)\n{\n#ifdef WITH_PERSISTENCE\n\tif(flag_db_backup){\n\t\tpersist__backup(false);\n\t\tflag_db_backup = false;\n\t}\n#endif\n\tif(flag_log_rotate){\n\t\tlog__close(db.config);\n\t\tlog__init(db.config);\n\t\tflag_log_rotate = false;\n\t}\n\tif(flag_reload){\n\t\tint rc;\n\t\tlog__printf(NULL, MOSQ_LOG_INFO, \"Reloading config.\");\n\t\tconfig__read(db.config, true);\n\t\tlisteners__reload_all_certificates();\n\t\trc = plugin__handle_reload();\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\tmosquitto_security_cleanup(true);\n\t\trc = mosquitto_security_init(true);\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\trc = mosquitto_security_apply_default();\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\tlog__close(db.config);\n\t\tlog__init(db.config);\n\t\tkeepalive__cleanup();\n\t\trc = keepalive__init();\n\t\tif(rc){\n\t\t\treturn rc;\n\t\t}\n\t\tbroker_control__reload();\n#ifdef WITH_BRIDGE\n\t\tbridge__reload();\n#endif\n\t\tflag_reload = false;\n\t}\n\tif(flag_tree_print){\n\t\tsub__tree_print(db.normal_subs, 0);\n\t\tsub__tree_print(db.shared_subs, 0);\n\t\tflag_tree_print = false;\n\t}\n#ifdef WITH_XTREPORT\n\tif(flag_xtreport){\n\t\txtreport();\n\t\tflag_xtreport = false;\n\t}\n#endif\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n/*\n *\n * Signalling mosquitto process on Win32.\n *\n *  On Windows we can use named events to pass signals to the mosquitto process.\n *  List of events :\n *\n *    mosqPID_shutdown\n *    mosqPID_reload\n *    mosqPID_backup\n *    mosqPID_log_rotate\n *    mosqPID_tree_print\n *    mosqPID_xtreport\n *\n * (where PID is the PID of the mosquitto process).\n */\n#ifdef WIN32\n\n#define MOSQ_MAX_EVTS 6\n\n\nDWORD WINAPI SigThreadProc(void *data)\n{\n\tTCHAR evt_name[MAX_PATH];\n\tstatic HANDLE evt[MOSQ_MAX_EVTS];\n\tint pid = GetCurrentProcessId();\n\tconst char *evt_names[MOSQ_MAX_EVTS] = {\n\t\t\"shutdown\",\n\t\t\"reload\",\n\t\t\"backup\",\n\t\t\"log_rotate\",\n\t\t\"tree_print\",\n\t\t\"xtreport\"\n\t};\n\n\tUNUSED(data);\n\n\tfor(int i=0; i<MOSQ_MAX_EVTS; i++){\n\t\tsprintf_s(evt_name, MAX_PATH, \"mosq%d_%s\", pid, evt_names[i]);\n\t\tevt[i] = CreateEvent(NULL, TRUE, FALSE, evt_name);\n\t}\n\n\twhile(g_run){\n\t\tint wr = WaitForMultipleObjects(MOSQ_MAX_EVTS, evt, FALSE, INFINITE);\n\t\tswitch(wr){\n\t\t\tcase WAIT_OBJECT_0 + 0:\n\t\t\t\thandle_signal(SIGINT);\n\t\t\t\tbreak;\n\t\t\tcase WAIT_OBJECT_0 + 1:\n\t\t\t\tflag_reload = true;\n\t\t\t\tcontinue;\n\t\t\tcase WAIT_OBJECT_0 + 2:\n#ifdef WITH_PERSISTENCE\n\t\t\t\tflag_db_backup = true;\n#endif\n\t\t\t\tcontinue;\n\t\t\t\tbreak;\n\t\t\tcase WAIT_OBJECT_0 + 3:\n\t\t\t\tflag_log_rotate = true;\n\t\t\t\tcontinue;\n\t\t\tcase WAIT_OBJECT_0 + 4:\n\t\t\t\tflag_tree_print = true;\n\t\t\t\tcontinue;\n\t\t\tcase WAIT_OBJECT_0 + 5:\n\t\t\t\tflag_xtreport = true;\n\t\t\t\tcontinue;\n\t\t}\n\t}\n\tfor(int i=0; i<MOSQ_MAX_EVTS; i++){\n\t\tif(evt[i]){\n\t\t\tCloseHandle(evt[i]);\n\t\t}\n\t}\n\treturn 0;\n}\n#undef MOSQ_MAX_EVTS\n#endif\n"
  },
  {
    "path": "src/subs.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n/* A note on matching topic subscriptions.\n *\n * Topics can be up to 32767 characters in length. The / character is used as a\n * hierarchy delimiter. Messages are published to a particular topic.\n * Clients may subscribe to particular topics directly, but may also use\n * wildcards in subscriptions.  The + and # characters are used as wildcards.\n * The # wildcard can be used at the end of a subscription only, and is a\n * wildcard for the level of hierarchy at which it is placed and all subsequent\n * levels.\n * The + wildcard may be used at any point within the subscription and is a\n * wildcard for only the level of hierarchy at which it is placed.\n * Neither wildcard may be used as part of a substring.\n * Valid:\n * \ta/b/+\n * \ta/+/c\n * \ta/#\n * \ta/b/#\n * \t#\n * \t+/b/c\n * \t+/+/+\n * Invalid:\n *\ta/#/c\n *\ta+/b/c\n * Valid but non-matching:\n *\ta/b\n *\ta/+\n *\t+/b\n *\tb/c/a\n *\ta/b/d\n */\n\n#include \"config.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"util_mosq.h\"\n\n#include \"utlist.h\"\n\nstatic struct mosquitto__subhier *sub__add_hier_entry(struct mosquitto__subhier *parent, struct mosquitto__subhier **sibling, const char *topic, uint16_t len);\n\nstatic unsigned int hashv_plus = 0;\nstatic unsigned int hashv_hash = 0;\n\n\nstatic int subs__send(struct mosquitto__subleaf *leaf, const char *topic, uint8_t qos, int retain, struct mosquitto__base_msg *stored)\n{\n\tbool client_retain;\n\tuint16_t mid;\n\tuint8_t client_qos, msg_qos;\n\tint rc2;\n\n\t/* Check for ACL topic access. */\n\trc2 = mosquitto_acl_check(leaf->context, topic, stored->data.payloadlen, stored->data.payload, stored->data.qos, stored->data.retain, stored->data.properties, MOSQ_ACL_READ);\n\tif(rc2 == MOSQ_ERR_ACL_DENIED){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(rc2 == MOSQ_ERR_SUCCESS){\n\t\tclient_qos = MQTT_SUB_OPT_GET_QOS(leaf->subscription_options);\n\n\t\tif(db.config->upgrade_outgoing_qos){\n\t\t\tmsg_qos = client_qos;\n\t\t}else{\n\t\t\tif(qos > client_qos){\n\t\t\t\tmsg_qos = client_qos;\n\t\t\t}else{\n\t\t\t\tmsg_qos = qos;\n\t\t\t}\n\t\t}\n\t\tif(msg_qos){\n\t\t\tmid = mosquitto__mid_generate(leaf->context);\n\t\t}else{\n\t\t\tmid = 0;\n\t\t}\n\t\tif(MQTT_SUB_OPT_GET_RETAIN_AS_PUBLISHED(leaf->subscription_options)){\n\t\t\tclient_retain = retain;\n\t\t}else{\n\t\t\tclient_retain = false;\n\t\t}\n\t\tif(db__message_insert_outgoing(leaf->context, 0, mid, msg_qos, client_retain, stored, leaf->identifier, true, true) == 1){\n\t\t\treturn 1;\n\t\t}\n\t}else{\n\t\treturn 1; /* Application error */\n\t}\n\treturn 0;\n}\n\n\nstatic int subs__shared_process(struct mosquitto__subhier *hier, const char *topic, uint8_t qos, int retain, struct mosquitto__base_msg *stored)\n{\n\tint rc = 0, rc2;\n\tstruct mosquitto__subshared *shared, *shared_tmp;\n\tstruct mosquitto__subleaf *leaf;\n\n\tHASH_ITER(hh, hier->shared, shared, shared_tmp){\n\t\tleaf = shared->subs;\n\t\trc2 = subs__send(leaf, topic, qos, retain, stored);\n\t\t/* Remove current from the top, add back to the bottom */\n\t\tDL_DELETE(shared->subs, leaf);\n\t\tDL_APPEND(shared->subs, leaf);\n\n\t\tif(rc2){\n\t\t\trc = 1;\n\t\t}\n\t}\n\n\treturn rc;\n}\n\n\nstatic int subs__process(struct mosquitto__subhier *hier, const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto__base_msg *stored)\n{\n\tint rc = 0;\n\tint rc2;\n\tstruct mosquitto__subleaf *leaf;\n\n\trc = subs__shared_process(hier, topic, qos, retain, stored);\n\n\tleaf = hier->subs;\n\twhile(source_id && leaf){\n\t\tif(!leaf->context->id || (MQTT_SUB_OPT_GET_NO_LOCAL(leaf->subscription_options) && !strcmp(leaf->context->id, source_id))){\n\t\t\tleaf = leaf->next;\n\t\t\tcontinue;\n\t\t}\n\t\trc2 = subs__send(leaf, topic, qos, retain, stored);\n\t\tif(rc2){\n\t\t\trc = 1;\n\t\t}\n\t\tleaf = leaf->next;\n\t}\n\tif(hier->subs || hier->shared){\n\t\treturn rc;\n\t}else{\n\t\treturn MOSQ_ERR_NO_SUBSCRIBERS;\n\t}\n}\n\n\nstatic int sub__add_leaf(struct mosquitto *context, const struct mosquitto_subscription *sub, struct mosquitto__subleaf **head, struct mosquitto__subleaf **newleaf)\n{\n\tstruct mosquitto__subleaf *leaf;\n\n\t*newleaf = NULL;\n\tleaf = *head;\n\n\twhile(leaf){\n\t\tif(leaf->context && leaf->context->id && !strcmp(leaf->context->id, context->id)){\n\t\t\t/* Client making a second subscription to same topic. Only\n\t\t\t * need to update QoS. Return MOSQ_ERR_SUB_EXISTS to\n\t\t\t * indicate this to the calling function. */\n\t\t\tleaf->identifier = sub->identifier;\n\t\t\tleaf->subscription_options = sub->options;\n\t\t\treturn MOSQ_ERR_SUB_EXISTS;\n\t\t}\n\t\tleaf = leaf->next;\n\t}\n\tleaf = mosquitto_calloc(1, sizeof(struct mosquitto__subleaf) + strlen(sub->topic_filter) + 1);\n\tif(!leaf){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\tleaf->context = context;\n\tleaf->identifier = sub->identifier;\n\tleaf->subscription_options = sub->options;\n\tstrcpy(leaf->topic_filter, sub->topic_filter);\n\n\tDL_APPEND(*head, leaf);\n\t*newleaf = leaf;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic void sub__remove_shared_leaf(struct mosquitto__subhier *subhier, struct mosquitto__subshared *shared, struct mosquitto__subleaf *leaf)\n{\n\tDL_DELETE(shared->subs, leaf);\n\tif(shared->subs == NULL){\n\t\tHASH_DELETE(hh, subhier->shared, shared);\n\t\tmosquitto_FREE(shared);\n\t}\n}\n\n\nstatic int sub__add_shared(struct mosquitto *context, const struct mosquitto_subscription *sub, struct mosquitto__subhier *subhier, const char *sharename)\n{\n\tstruct mosquitto__subleaf *newleaf;\n\tstruct mosquitto__subshared *shared = NULL;\n\tstruct mosquitto__subleaf **subs;\n\tsize_t slen;\n\tint rc;\n\tunsigned hashv;\n\n\tslen = strlen(sharename);\n\n\tHASH_VALUE(sharename, slen, hashv);\n\n\tHASH_FIND_BYHASHVALUE(hh, subhier->shared, sharename, slen, hashv, shared);\n\tif(shared == NULL){\n\t\tshared = mosquitto_calloc(1, sizeof(struct mosquitto__subshared) + slen + 1);\n\t\tif(!shared){\n\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t}\n\t\tstrncpy(shared->name, sharename, slen+1);\n\n\t\tHASH_ADD_BYHASHVALUE(hh, subhier->shared, name, slen, hashv, shared);\n\t}\n\n\trc = sub__add_leaf(context, sub, &shared->subs, &newleaf);\n\tif(rc > 0){\n\t\tif(shared->subs == NULL){\n\t\t\tHASH_DELETE(hh, subhier->shared, shared);\n\t\t\tmosquitto_FREE(shared);\n\t\t}\n\t\treturn rc;\n\t}\n\n\tif(rc != MOSQ_ERR_SUB_EXISTS){\n\t\tnewleaf->hier = subhier;\n\t\tnewleaf->shared = shared;\n\n\t\tbool assigned = false;\n\t\tfor(int i=0; i<context->subs_capacity; i++){\n\t\t\tif(!context->subs[i]){\n\t\t\t\tcontext->subs[i] = newleaf;\n\t\t\t\tcontext->subs_count++;\n\t\t\t\tassigned = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(assigned == false){\n\t\t\tsubs = mosquitto_realloc(context->subs, sizeof(struct mosquitto__subleaf *)*(size_t)(context->subs_capacity + 1));\n\t\t\tif(!subs){\n\t\t\t\tsub__remove_shared_leaf(subhier, shared, newleaf);\n\t\t\t\tmosquitto_FREE(newleaf);\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tcontext->subs = subs;\n\t\t\tcontext->subs_capacity++;\n\t\t\tcontext->subs_count++;\n\t\t\tcontext->subs[context->subs_capacity-1] = newleaf;\n\t\t}\n#ifdef WITH_SYS_TREE\n\t\tdb.shared_subscription_count++;\n#endif\n\t}\n\n\tif(context->protocol == mosq_p_mqtt31 || context->protocol == mosq_p_mqtt5){\n\t\treturn rc;\n\t}else{\n\t\t/* mqttv311/mqttv5 requires retained messages are resent on\n\t\t * resubscribe. */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nstatic int sub__add_normal(struct mosquitto *context, const struct mosquitto_subscription *sub, struct mosquitto__subhier *subhier)\n{\n\tstruct mosquitto__subleaf *newleaf = NULL;\n\tstruct mosquitto__subleaf **subs;\n\tint rc;\n\n\trc = sub__add_leaf(context, sub, &subhier->subs, &newleaf);\n\tif(rc > 0){\n\t\treturn rc;\n\t}\n\n\tif(rc != MOSQ_ERR_SUB_EXISTS){\n\t\tnewleaf->hier = subhier;\n\t\tnewleaf->shared = NULL;\n\n\t\tbool assigned = false;\n\t\tfor(int i=0; i<context->subs_capacity; i++){\n\t\t\tif(!context->subs[i]){\n\t\t\t\tcontext->subs[i] = newleaf;\n\t\t\t\tcontext->subs_count++;\n\t\t\t\tassigned = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(assigned == false){\n\t\t\tsubs = mosquitto_realloc(context->subs, sizeof(struct mosquitto__subleaf *)*(size_t)(context->subs_capacity + 1));\n\t\t\tif(!subs){\n\t\t\t\tDL_DELETE(subhier->subs, newleaf);\n\t\t\t\tmosquitto_FREE(newleaf);\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tcontext->subs = subs;\n\t\t\tcontext->subs_capacity++;\n\t\t\tcontext->subs_count++;\n\t\t\tcontext->subs[context->subs_capacity-1] = newleaf;\n\t\t}\n#ifdef WITH_SYS_TREE\n\t\tdb.subscription_count++;\n#endif\n\t}\n\n\tif(context->protocol == mosq_p_mqtt31 || context->protocol == mosq_p_mqtt5){\n\t\treturn rc;\n\t}else{\n\t\t/* mqttv311/mqttv5 requires retained messages are resent on\n\t\t * resubscribe. */\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nstatic int sub__add_context(struct mosquitto *context, const struct mosquitto_subscription *sub, struct mosquitto__subhier *subhier, char *const *const topics, const char *sharename)\n{\n\tstruct mosquitto__subhier *branch;\n\tint topic_index = 0;\n\tsize_t topiclen;\n\n\t/* Find leaf node */\n\twhile(topics && topics[topic_index] != NULL){\n\t\ttopiclen = strlen(topics[topic_index]);\n\t\tif(topiclen > UINT16_MAX){\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t\tHASH_FIND(hh, subhier->children, topics[topic_index], topiclen, branch);\n\t\tif(!branch){\n\t\t\t/* Not found */\n\t\t\tbranch = sub__add_hier_entry(subhier, &subhier->children, topics[topic_index], (uint16_t)topiclen);\n\t\t\tif(!branch){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t\tsubhier = branch;\n\t\ttopic_index++;\n\t}\n\n\t/* Add add our context */\n\tif(context && context->id){\n\t\tif(sharename){\n\t\t\treturn sub__add_shared(context, sub, subhier, sharename);\n\t\t}else{\n\t\t\treturn sub__add_normal(context, sub, subhier);\n\t\t}\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nstatic int sub__remove_normal(struct mosquitto *context, struct mosquitto__subhier *subhier, uint8_t *reason)\n{\n\tstruct mosquitto__subleaf *leaf;\n\n\tleaf = subhier->subs;\n\twhile(leaf){\n\t\tif(leaf->context==context){\n#ifdef WITH_SYS_TREE\n\t\t\tdb.subscription_count--;\n#endif\n\t\t\tDL_DELETE(subhier->subs, leaf);\n\n\t\t\t/* Remove the reference to the sub that the client is keeping.\n\t\t\t * It would be nice to be able to use the reference directly,\n\t\t\t * but that would involve keeping a copy of the topic string in\n\t\t\t * each subleaf. Might be worth considering though. */\n\t\t\tfor(int i=0; i<context->subs_capacity; i++){\n\t\t\t\tif(context->subs[i] && context->subs[i]->hier == subhier){\n\t\t\t\t\tcontext->subs_count--;\n\t\t\t\t\tmosquitto_free(context->subs[i]);\n\t\t\t\t\tcontext->subs[i] = NULL;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t*reason = 0;\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}\n\t\tleaf = leaf->next;\n\t}\n\treturn MOSQ_ERR_NO_SUBSCRIBERS;\n}\n\n\nstatic int sub__remove_shared(struct mosquitto *context, struct mosquitto__subhier *subhier, uint8_t *reason, const char *sharename)\n{\n\tstruct mosquitto__subshared *shared;\n\n\tHASH_FIND(hh, subhier->shared, sharename, strlen(sharename), shared);\n\tif(shared){\n\t\tstruct mosquitto__subleaf *leaf = shared->subs;\n\t\twhile(leaf){\n\t\t\tif(leaf->context==context){\n#ifdef WITH_SYS_TREE\n\t\t\t\tdb.shared_subscription_count--;\n#endif\n\t\t\t\tDL_DELETE(shared->subs, leaf);\n\n\t\t\t\t/* Remove the reference to the sub that the client is keeping.\n\t\t\t\t* It would be nice to be able to use the reference directly,\n\t\t\t\t* but that would involve keeping a copy of the topic string in\n\t\t\t\t* each subleaf. Might be worth considering though. */\n\t\t\t\tfor(int i=0; i<context->subs_capacity; i++){\n\t\t\t\t\tif(context->subs[i]\n\t\t\t\t\t\t\t&& context->subs[i]->hier == subhier\n\t\t\t\t\t\t\t&& context->subs[i]->shared == shared){\n\n\t\t\t\t\t\tmosquitto_free(context->subs[i]);\n\t\t\t\t\t\tcontext->subs[i] = NULL;\n\t\t\t\t\t\tcontext->subs_count--;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif(shared->subs == NULL){\n\t\t\t\t\tHASH_DELETE(hh, subhier->shared, shared);\n\t\t\t\t\tmosquitto_FREE(shared);\n\t\t\t\t}\n\n\t\t\t\t*reason = 0;\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}\n\t\t\tleaf = leaf->next;\n\t\t}\n\t\treturn MOSQ_ERR_NO_SUBSCRIBERS;\n\t}else{\n\t\treturn MOSQ_ERR_NO_SUBSCRIBERS;\n\t}\n}\n\n\nstatic int sub__remove_recurse(struct mosquitto *context, struct mosquitto__subhier *subhier, char **topics, uint8_t *reason, const char *sharename)\n{\n\tstruct mosquitto__subhier *branch;\n\n\tif(topics == NULL || topics[0] == NULL){\n\t\tif(sharename){\n\t\t\treturn sub__remove_shared(context, subhier, reason, sharename);\n\t\t}else{\n\t\t\treturn sub__remove_normal(context, subhier, reason);\n\t\t}\n\t}\n\n\tHASH_FIND(hh, subhier->children, topics[0], strlen(topics[0]), branch);\n\tif(branch){\n\t\tsub__remove_recurse(context, branch, &(topics[1]), reason, sharename);\n\t\tif(!branch->children && !branch->subs && !branch->shared){\n\t\t\tHASH_DELETE(hh, subhier->children, branch);\n\t\t\tmosquitto_FREE(branch);\n\t\t}\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int sub__search(struct mosquitto__subhier *subhier, char **split_topics, const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto__base_msg *stored)\n{\n\t/* FIXME - need to take into account source_id if the client is a bridge */\n\tstruct mosquitto__subhier *branch;\n\tint rc;\n\tbool have_subscribers = false;\n\n\tif(split_topics && split_topics[0]){\n\t\t/* Check for literal match */\n\t\tHASH_FIND(hh, subhier->children, split_topics[0], strlen(split_topics[0]), branch);\n\n\t\tif(branch){\n\t\t\trc = sub__search(branch, &(split_topics[1]), source_id, topic, qos, retain, stored);\n\t\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\t\thave_subscribers = true;\n\t\t\t}else if(rc != MOSQ_ERR_NO_SUBSCRIBERS){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tif(split_topics[1] == NULL){ /* End of list */\n\t\t\t\trc = subs__process(branch, source_id, topic, qos, retain, stored);\n\t\t\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\t\t\thave_subscribers = true;\n\t\t\t\t}else if(rc != MOSQ_ERR_NO_SUBSCRIBERS){\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/* Check for + match */\n\t\tHASH_FIND_BYHASHVALUE(hh, subhier->children, \"+\", 1, hashv_plus, branch);\n\n\t\tif(branch){\n\t\t\trc = sub__search(branch, &(split_topics[1]), source_id, topic, qos, retain, stored);\n\t\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\t\thave_subscribers = true;\n\t\t\t}else if(rc != MOSQ_ERR_NO_SUBSCRIBERS){\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t\tif(split_topics[1] == NULL){ /* End of list */\n\t\t\t\trc = subs__process(branch, source_id, topic, qos, retain, stored);\n\t\t\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\t\t\thave_subscribers = true;\n\t\t\t\t}else if(rc != MOSQ_ERR_NO_SUBSCRIBERS){\n\t\t\t\t\treturn rc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Check for # match */\n\tHASH_FIND_BYHASHVALUE(hh, subhier->children, \"#\", 1, hashv_hash, branch);\n\tif(branch && !branch->children){\n\t\t/* The topic matches due to a # wildcard - process the\n\t\t * subscriptions but *don't* return. Although this branch has ended\n\t\t * there may still be other subscriptions to deal with.\n\t\t */\n\t\trc = subs__process(branch, source_id, topic, qos, retain, stored);\n\t\tif(rc == MOSQ_ERR_SUCCESS){\n\t\t\thave_subscribers = true;\n\t\t}else if(rc != MOSQ_ERR_NO_SUBSCRIBERS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tif(have_subscribers){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_NO_SUBSCRIBERS;\n\t}\n}\n\n\nstatic struct mosquitto__subhier *sub__add_hier_entry(struct mosquitto__subhier *parent, struct mosquitto__subhier **sibling, const char *topic, uint16_t len)\n{\n\tstruct mosquitto__subhier *child;\n\n\tassert(sibling);\n\n\tchild = mosquitto_calloc(1, sizeof(struct mosquitto__subhier) + len + 1);\n\tif(!child){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\treturn NULL;\n\t}\n\tchild->parent = parent;\n\tchild->topic_len = len;\n\tif(len > 0){\n\t\tstrncpy(child->topic, topic, (size_t)(len+1));\n\t}\n\n\tHASH_ADD(hh, *sibling, topic, child->topic_len, child);\n\n\treturn child;\n}\n\n\nint sub__add(struct mosquitto *context, const struct mosquitto_subscription *sub)\n{\n\tint rc = 0;\n\tstruct mosquitto__subhier *subhier;\n\tconst char *sharename = NULL;\n\tchar *local_sub;\n\tchar **topics;\n\tsize_t topiclen;\n\n\tassert(sub);\n\tassert(sub->topic_filter);\n\n\trc = sub__topic_tokenise(sub->topic_filter, &local_sub, &topics, &sharename);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\ttopiclen = strlen(topics[0]);\n\tif(topiclen > UINT16_MAX){\n\t\tmosquitto_FREE(local_sub);\n\t\tmosquitto_FREE(topics);\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tif(sharename){\n\t\tHASH_FIND(hh, db.shared_subs, topics[0], topiclen, subhier);\n\t\tif(!subhier){\n\t\t\tsubhier = sub__add_hier_entry(NULL, &db.shared_subs, topics[0], (uint16_t)topiclen);\n\t\t\tif(!subhier){\n\t\t\t\tmosquitto_FREE(local_sub);\n\t\t\t\tmosquitto_FREE(topics);\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}else{\n\t\tHASH_FIND(hh, db.normal_subs, topics[0], topiclen, subhier);\n\t\tif(!subhier){\n\t\t\tsubhier = sub__add_hier_entry(NULL, &db.normal_subs, topics[0], (uint16_t)topiclen);\n\t\t\tif(!subhier){\n\t\t\t\tmosquitto_FREE(local_sub);\n\t\t\t\tmosquitto_FREE(topics);\n\t\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Out of memory.\");\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t}\n\t}\n\trc = sub__add_context(context, sub, subhier, topics, sharename);\n\n\tmosquitto_FREE(local_sub);\n\tmosquitto_FREE(topics);\n\n\treturn rc;\n}\n\n\nint sub__remove(struct mosquitto *context, const char *sub, uint8_t *reason)\n{\n\tint rc = 0;\n\tstruct mosquitto__subhier *subhier;\n\tconst char *sharename = NULL;\n\tchar *local_sub = NULL;\n\tchar **topics = NULL;\n\n\tassert(sub);\n\n\trc = sub__topic_tokenise(sub, &local_sub, &topics, &sharename);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tif(sharename){\n\t\tHASH_FIND(hh, db.shared_subs, topics[0], strlen(topics[0]), subhier);\n\t}else{\n\t\tHASH_FIND(hh, db.normal_subs, topics[0], strlen(topics[0]), subhier);\n\t}\n\tif(subhier){\n\t\t*reason = MQTT_RC_NO_SUBSCRIPTION_EXISTED;\n\t\trc = sub__remove_recurse(context, subhier, topics, reason, sharename);\n\t}\n\n\tmosquitto_FREE(local_sub);\n\tmosquitto_FREE(topics);\n\n\treturn rc;\n}\n\n\nint sub__messages_queue(const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto__base_msg **stored)\n{\n\tint rc = MOSQ_ERR_SUCCESS, rc2;\n\tint rc_normal = MOSQ_ERR_NO_SUBSCRIBERS, rc_shared = MOSQ_ERR_NO_SUBSCRIBERS;\n\tstruct mosquitto__subhier *subhier;\n\tchar **split_topics = NULL;\n\tchar *local_topic = NULL;\n\tunsigned hashv;\n\tsize_t topiclen;\n\n\tassert(topic);\n\n\tif(sub__topic_tokenise(topic, &local_topic, &split_topics, NULL)){\n\t\treturn 1;\n\t}\n\n\t/* Protect this message until we have sent it to all\n\tclients - this is required because websockets client calls\n\tdb__message_write(), which could remove the message if ref_count==0.\n\t*/\n\tdb__msg_store_ref_inc(*stored);\n\n\ttopiclen = strlen(split_topics[0]);\n\tHASH_VALUE(split_topics[0], topiclen, hashv);\n\tHASH_FIND_BYHASHVALUE(hh, db.normal_subs, split_topics[0], topiclen, hashv, subhier);\n\tif(subhier){\n\t\trc_normal = sub__search(subhier, split_topics, source_id, topic, qos, retain, *stored);\n\t\tif(rc_normal > 0){\n\t\t\trc = rc_normal;\n\t\t\tgoto end;\n\t\t}\n\t}\n\n\tHASH_FIND_BYHASHVALUE(hh, db.shared_subs, split_topics[0], topiclen, hashv, subhier);\n\tif(subhier){\n\t\trc_shared = sub__search(subhier, split_topics, source_id, topic, qos, retain, *stored);\n\t\tif(rc_shared > 0){\n\t\t\trc = rc_shared;\n\t\t\tgoto end;\n\t\t}\n\t}\n\n\tif(rc_normal == MOSQ_ERR_NO_SUBSCRIBERS && rc_shared == MOSQ_ERR_NO_SUBSCRIBERS){\n\t\trc = MOSQ_ERR_NO_SUBSCRIBERS;\n\t}\n\n\tif(retain){\n\t\trc2 = retain__store(topic, *stored, split_topics, true);\n\t\tif(rc2){\n\t\t\trc = rc2;\n\t\t}\n\t}\n\nend:\n\tmosquitto_FREE(split_topics);\n\tmosquitto_FREE(local_topic);\n\t/* Remove our reference and free if needed. */\n\tdb__msg_store_ref_dec(stored);\n\n\treturn rc;\n}\n\n\n/* Remove a subhier element, and return its parent if that needs freeing as well. */\nstatic struct mosquitto__subhier *tmp_remove_subs(struct mosquitto__subhier *sub)\n{\n\tstruct mosquitto__subhier *parent;\n\n\tif(!sub || !sub->parent){\n\t\treturn NULL;\n\t}\n\n\tif(sub->children || sub->subs){\n\t\treturn NULL;\n\t}\n\n\tparent = sub->parent;\n\tHASH_DELETE(hh, parent->children, sub);\n\tmosquitto_FREE(sub);\n\n\tif(parent->subs == NULL\n\t\t\t&& parent->children == NULL\n\t\t\t&& parent->shared == NULL\n\t\t\t&& parent->parent){\n\n\t\treturn parent;\n\t}else{\n\t\treturn NULL;\n\t}\n}\n\n\n/* Remove all subscriptions for a client.\n */\nint sub__clean_session(struct mosquitto *context)\n{\n\tfor(int i=0; i<context->subs_capacity; i++){\n\t\tif(context->subs[i] == NULL || context->subs[i]->hier == NULL){\n\t\t\tcontinue;\n\t\t}\n\n\t\tstruct mosquitto__subhier *hier = context->subs[i]->hier;\n\t\tstruct mosquitto__subleaf *leaf;\n\n\t\tplugin_persist__handle_subscription_delete(context, context->subs[i]->topic_filter);\n\t\tif(context->subs[i]->shared){\n\t\t\tleaf = context->subs[i]->shared->subs;\n\t\t\twhile(leaf){\n\t\t\t\tif(leaf->context==context){\n#ifdef WITH_SYS_TREE\n\t\t\t\t\tdb.shared_subscription_count--;\n#endif\n\t\t\t\t\tsub__remove_shared_leaf(context->subs[i]->hier, context->subs[i]->shared, leaf);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tleaf = leaf->next;\n\t\t\t}\n\t\t}else{\n\t\t\tleaf = hier->subs;\n\t\t\twhile(leaf){\n\t\t\t\tif(leaf->context==context){\n#ifdef WITH_SYS_TREE\n\t\t\t\t\tdb.subscription_count--;\n#endif\n\t\t\t\t\tDL_DELETE(hier->subs, leaf);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tleaf = leaf->next;\n\t\t\t}\n\t\t}\n\t\tmosquitto_FREE(context->subs[i]);\n\n\t\tif(hier->subs == NULL\n\t\t\t\t&& hier->children == NULL\n\t\t\t\t&& hier->shared == NULL\n\t\t\t\t&& hier->parent){\n\n\t\t\tdo{\n\t\t\t\thier = tmp_remove_subs(hier);\n\t\t\t}while(hier);\n\t\t}\n\t}\n\tmosquitto_FREE(context->subs);\n\tcontext->subs_capacity = 0;\n\tcontext->subs_count = 0;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid sub__tree_print(struct mosquitto__subhier *root, int level)\n{\n\tint i;\n\tstruct mosquitto__subhier *branch, *branch_tmp;\n\tstruct mosquitto__subleaf *leaf;\n\n\tHASH_ITER(hh, root, branch, branch_tmp){\n\t\tif(level > -1){\n\t\t\tfor(i=0; i<(level+2)*2; i++){\n\t\t\t\tprintf(\" \");\n\t\t\t}\n\t\t\tprintf(\"%s\", branch->topic);\n\t\t\tleaf = branch->subs;\n\t\t\twhile(leaf){\n\t\t\t\tif(leaf->context){\n\t\t\t\t\tprintf(\" (%s, %d)\", leaf->context->id, MQTT_SUB_OPT_GET_QOS(leaf->subscription_options));\n\t\t\t\t}else{\n\t\t\t\t\tprintf(\" (%s, %d)\", \"\", MQTT_SUB_OPT_GET_QOS(leaf->subscription_options));\n\t\t\t\t}\n\t\t\t\tleaf = leaf->next;\n\t\t\t}\n\t\t\tprintf(\"\\n\");\n\t\t}\n\n\t\tsub__tree_print(branch->children, level+1);\n\t}\n}\n\n\nint sub__init(void)\n{\n\tHASH_VALUE(\"+\", 1, hashv_plus);\n\tHASH_VALUE(\"#\", 1, hashv_hash);\n\n\tif(sub__add_hier_entry(NULL, &db.shared_subs, \"\", 0) == NULL\n\t\t\t|| sub__add_hier_entry(NULL, &db.normal_subs, \"\", 0) == NULL\n\t\t\t|| sub__add_hier_entry(NULL, &db.normal_subs, \"$SYS\", (uint16_t)strlen(\"$SYS\")) == NULL\n\t\t\t){\n\n\t\treturn MOSQ_ERR_NOMEM;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n"
  },
  {
    "path": "src/sys_tree.c",
    "content": "/*\nCopyright (c) 2009-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifdef WITH_SYS_TREE\n\n#include \"config.h\"\n\n#include <math.h>\n#include <stdio.h>\n#include <inttypes.h>\n#include <limits.h>\n#include <inttypes.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"sys_tree.h\"\n\n#define BUFLEN 100\n\n#define SYS_TREE_QOS 2\n\n#define METRIC_LOAD_1MIN 1\n#define METRIC_LOAD_5MIN 2\n#define METRIC_LOAD_15MIN 3\n\nstruct metric {\n\tint64_t current;\n\tint64_t next;\n\tconst char *topic, *topic_alias;\n\tbool is_max;\n};\n\nstruct metric_load {\n\tdouble current;\n\tconst char *topic;\n\tint load_ref;\n\tuint8_t interval;\n};\n\nstruct metric metrics[mosq_metric_max] = {\n\t{ 1, 0, \"$SYS/broker/clients/total\", NULL, false }, /* mosq_gauge_clients_total */\n\t{ 1, 0, \"$SYS/broker/clients/maximum\", NULL, true }, /* metric_clients_maximum */\n\t{ 1, 0, \"$SYS/broker/clients/disconnected\", \"$SYS/broker/clients/inactive\", false }, /* mosq_gauge_clients_disconnected */\n\t{ 1, 0, \"$SYS/broker/clients/connected\", \"$SYS/broker/clients/active\", false }, /* mosq_gauge_clients_connected */\n\t{ 1, 0, \"$SYS/broker/clients/expired\", NULL, false }, /* mosq_counter_clients_expired */\n\t{ 1, 0, \"$SYS/broker/messages/stored\", \"$SYS/broker/store/messages/count\", false }, /* mosq_gauge_message_store_count */\n\t{ 1, 0, \"$SYS/broker/store/messages/bytes\", NULL, false }, /* mosq_gauge_message_store_bytes */\n\t{ 1, 0, \"$SYS/broker/subscriptions/count\", NULL, false }, /* mosq_gauge_subscription_count */\n\t{ 1, 0, \"$SYS/broker/shared_subscriptions/count\", NULL, false }, /* mosq_gauge_shared_subscription_count */\n\t{ 1, 0, \"$SYS/broker/retained messages/count\", NULL, false }, /* mosq_gauge_retained_message_count */\n#ifdef WITH_MEMORY_TRACKING\n\t{ 1, 0, \"$SYS/broker/heap/current\", NULL, false }, /* mosq_gauge_heap_current */\n\t{ 1, 0, \"$SYS/broker/heap/maximum\", NULL, true }, /* mosq_gauge_heap_maximum */\n#else\n\t{ 1, 0, NULL, NULL, 0 }, /* mosq_gauge_heap_current */\n\t{ 1, 0, NULL, NULL, 0 }, /* mosq_gauge_heap_maximum */\n#endif\n\t{ 1, 0, \"$SYS/broker/messages/received\", NULL, false }, /* mosq_counter_messages_received */\n\t{ 1, 0, \"$SYS/broker/messages/sent\", NULL, false }, /* mosq_counter_messages_sent */\n\t{ 1, 0, \"$SYS/broker/bytes/received\", NULL, false }, /* mosq_counter_bytes_received */\n\t{ 1, 0, \"$SYS/broker/bytes/sent\", NULL, false }, /* mosq_counter_bytes_sent */\n\t{ 1, 0, \"$SYS/broker/publish/bytes/received\", NULL, false }, /* mosq_counter_pub_bytes_received */\n\t{ 1, 0, \"$SYS/broker/publish/bytes/sent\", NULL, false }, /* mosq_counter_pub_bytes_sent */\n\t{ 1, 0, \"$SYS/broker/packet/out/count\", NULL, false }, /* mosq_gauge_out_packet_count */\n\t{ 1, 0, \"$SYS/broker/packet/out/bytes\", NULL, false }, /* mosq_gauge_out_packet_bytes */\n\t{ 1, 0, \"$SYS/broker/connections/socket/count\", NULL, false }, /* mosq_counter_socket_connections */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_connect_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_connect_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_connack_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_connack_sent */\n\t{ 1, 0, \"$SYS/broker/publish/messages/dropped\", NULL, false }, /* mosq_counter_mqtt_publish_dropped */\n\t{ 1, 0, \"$SYS/broker/publish/messages/received\", NULL, false }, /* mosq_counter_mqtt_publish_received */\n\t{ 1, 0, \"$SYS/broker/publish/messages/sent\", NULL, false }, /* mosq_counter_mqtt_publish_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_puback_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_puback_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_pubrec_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_pubrec_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_pubrel_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_pubrel_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_pubcomp_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_pubcomp_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_subscribe_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_subscribe_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_suback_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_suback_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_unsubscribe_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_unsubscribe_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_unsuback_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_unsuback_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_pingreq_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_pingreq_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_pingresp_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_pingresp_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_disconnect_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_disconnect_sent */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_auth_received */\n\t{ 1, 0, NULL, NULL, false }, /* mosq_counter_mqtt_auth_sent */\n};\n\nstruct metric_load metric_loads[mosq_metric_load_max] = {\n\t{ 0.0, \"$SYS/broker/load/messages/received/1min\", mosq_counter_messages_received, METRIC_LOAD_1MIN }, /* metric_load_messages_received_1min */\n\t{ 0.0, \"$SYS/broker/load/messages/received/5min\", mosq_counter_messages_received, METRIC_LOAD_5MIN }, /* metric_load_messages_received_5min */\n\t{ 0.0, \"$SYS/broker/load/messages/received/15min\", mosq_counter_messages_received, METRIC_LOAD_15MIN }, /* metric_load_messages_received_15min */\n\t{ 0.0, \"$SYS/broker/load/messages/sent/1min\", mosq_counter_messages_sent, METRIC_LOAD_1MIN }, /* metric_load_messages_sent_1min */\n\t{ 0.0, \"$SYS/broker/load/messages/sent/5min\", mosq_counter_messages_sent, METRIC_LOAD_5MIN }, /* metric_load_messages_sent_5min */\n\t{ 0.0, \"$SYS/broker/load/messages/sent/15min\", mosq_counter_messages_sent, METRIC_LOAD_15MIN }, /* metric_load_messages_sent_15min */\n\t{ 0.0, \"$SYS/broker/load/publish/dropped/1min\", mosq_counter_mqtt_publish_dropped, METRIC_LOAD_1MIN }, /* metric_load_pub_messages_dropped_1min */\n\t{ 0.0, \"$SYS/broker/load/publish/dropped/5min\", mosq_counter_mqtt_publish_dropped, METRIC_LOAD_5MIN }, /* metric_load_pub_messages_dropped_5min */\n\t{ 0.0, \"$SYS/broker/load/publish/dropped/15min\", mosq_counter_mqtt_publish_dropped, METRIC_LOAD_15MIN }, /* metric_load_pub_messages_dropped_15min */\n\t{ 0.0, \"$SYS/broker/load/publish/received/1min\", mosq_counter_mqtt_publish_received, METRIC_LOAD_1MIN }, /* metric_load_pub_messages_received_1min */\n\t{ 0.0, \"$SYS/broker/load/publish/received/5min\", mosq_counter_mqtt_publish_received, METRIC_LOAD_5MIN }, /* metric_load_pub_messages_received_5min */\n\t{ 0.0, \"$SYS/broker/load/publish/received/15min\", mosq_counter_mqtt_publish_received, METRIC_LOAD_15MIN }, /* metric_load_pub_messages_received_15min */\n\t{ 0.0, \"$SYS/broker/load/publish/sent/1min\", mosq_counter_mqtt_publish_sent, METRIC_LOAD_1MIN }, /* metric_load_pub_messages_sent_1min */\n\t{ 0.0, \"$SYS/broker/load/publish/sent/5min\", mosq_counter_mqtt_publish_sent, METRIC_LOAD_5MIN }, /* metric_load_pub_messages_sent_5min */\n\t{ 0.0, \"$SYS/broker/load/publish/sent/15min\", mosq_counter_mqtt_publish_sent, METRIC_LOAD_15MIN }, /* metric_load_pub_messages_sent_15min */\n\t{ 0.0, \"$SYS/broker/load/bytes/received/1min\", mosq_counter_bytes_received, METRIC_LOAD_1MIN }, /* metric_load_bytes_received_1min */\n\t{ 0.0, \"$SYS/broker/load/bytes/received/5min\", mosq_counter_bytes_received, METRIC_LOAD_5MIN }, /* metric_load_bytes_received_5min */\n\t{ 0.0, \"$SYS/broker/load/bytes/received/15min\", mosq_counter_bytes_received, METRIC_LOAD_15MIN }, /* metric_load_bytes_received_15min */\n\t{ 0.0, \"$SYS/broker/load/bytes/sent/1min\", mosq_counter_bytes_sent, METRIC_LOAD_1MIN }, /* metric_load_bytes_sent_1min */\n\t{ 0.0, \"$SYS/broker/load/bytes/sent/5min\", mosq_counter_bytes_sent, METRIC_LOAD_5MIN }, /* metric_load_bytes_sent_5min */\n\t{ 0.0, \"$SYS/broker/load/bytes/sent/15min\", mosq_counter_bytes_sent, METRIC_LOAD_15MIN }, /* metric_load_bytes_sent_15min */\n\t{ 0.0, \"$SYS/broker/load/sockets/1min\", mosq_counter_socket_connections, METRIC_LOAD_1MIN }, /* metric_load_socket_connections_1min */\n\t{ 0.0, \"$SYS/broker/load/sockets/5min\", mosq_counter_socket_connections, METRIC_LOAD_5MIN }, /* metric_load_socket_connections_5min */\n\t{ 0.0, \"$SYS/broker/load/sockets/15min\", mosq_counter_socket_connections, METRIC_LOAD_15MIN }, /* metric_load_socket_connections_15min */\n\t{ 0.0, \"$SYS/broker/load/connections/1min\", mosq_counter_mqtt_connect_received, METRIC_LOAD_1MIN }, /* metric_load_connections_1min */\n\t{ 0.0, \"$SYS/broker/load/connections/5min\", mosq_counter_mqtt_connect_received, METRIC_LOAD_5MIN }, /* metric_load_connections_5min */\n\t{ 0.0, \"$SYS/broker/load/connections/15min\", mosq_counter_mqtt_connect_received, METRIC_LOAD_15MIN }, /* metric_load_connections_15min */\n};\n\nstatic time_t start_time = 0;\nstatic time_t last_update = 0;\n\n\ntime_t broker_uptime(void)\n{\n\treturn db.now_s - start_time;\n}\n\n\nstatic void calc_load(char *buf, double exponent, double i_mult, struct metric_load *m, bool force)\n{\n\tdouble new_value;\n\tuint32_t len;\n\tdouble interval;\n\n\tinterval = (double)(metrics[m->load_ref].next - metrics[m->load_ref].current)*i_mult;\n\tnew_value = interval + exponent*(m->current - interval);\n\tif(fabs(new_value - (m->current)) >= 0.01 || force){\n\t\tlen = (uint32_t)snprintf(buf, BUFLEN, \"%.2f\", new_value);\n\t\tdb__messages_easy_queue(NULL, m->topic, SYS_TREE_QOS, len, buf, 1, MSG_EXPIRY_INFINITE, NULL);\n\t}\n\tm->current = new_value;\n}\n\n\nvoid sys_tree__init(void)\n{\n\tchar buf[BUFLEN];\n\tuint32_t len;\n\n\tif(db.config->sys_interval == 0){\n\t\treturn;\n\t}\n\n\t/* Set static $SYS messages */\n\tlen = (uint32_t)snprintf(buf, 64, \"mosquitto version %s\", VERSION);\n\tdb__messages_easy_queue(NULL, \"$SYS/broker/version\", SYS_TREE_QOS, len, buf, 1, MSG_EXPIRY_INFINITE, NULL);\n\n\tstart_time = mosquitto_time();\n\tlast_update = start_time;\n\n\tsys_tree__update(true);\n\n\t/* Force published load values to 0 */\n\tfor(int i=0; i<mosq_metric_load_max; i++){\n\t\tif(metric_loads[i].interval == METRIC_LOAD_1MIN){\n\t\t\tcalc_load(buf, 0.0, 0.0, &metric_loads[i], true);\n\t\t}else if(metric_loads[i].interval == METRIC_LOAD_5MIN){\n\t\t\tcalc_load(buf, 0.0, 0.0, &metric_loads[i], true);\n\t\t}else{\n\t\t\tcalc_load(buf, 0.0, 0.0, &metric_loads[i], true);\n\t\t}\n\t}\n}\n\n\nvoid metrics__int_inc(enum mosq_metric_type m, int64_t value)\n{\n\tif(m < mosq_metric_max){\n\t\tmetrics[m].next += value;\n\t}\n}\n\n\nvoid metrics__int_dec(enum mosq_metric_type m, int64_t value)\n{\n\tif(m < mosq_metric_max){\n\t\tmetrics[m].next -= value;\n\t}\n}\n\n\n/* Send messages for the $SYS hierarchy if the last update is longer than\n * 'interval' seconds ago.\n * 'interval' is the amount of seconds between updates. If 0, then no periodic\n * messages are sent for the $SYS hierarchy.\n * 'start_time' is the result of time() that the broker was started at.\n */\nvoid sys_tree__update(bool force)\n{\n\ttime_t uptime;\n\tchar buf[BUFLEN];\n\tuint32_t len;\n\ttime_t next_event;\n\tstatic time_t last_update_real = 0;\n\n\tif(db.config->sys_interval){\n\t\tnext_event = db.config->sys_interval - db.now_real_s % db.config->sys_interval - 1;\n\t\tif(next_event <= 0){\n\t\t\tnext_event = db.config->sys_interval;\n\t\t}\n\t\tloop__update_next_event(next_event*1000);\n\t}\n\n\tif(db.config->sys_interval\n\t\t\t&& ((db.now_real_s % db.config->sys_interval == 0 && last_update_real != db.now_real_s) || force)){\n\n\t\tuptime = db.now_s - start_time;\n\t\tlen = (uint32_t)snprintf(buf, BUFLEN, \"%\" PRIu64 \" seconds\", (uint64_t)uptime);\n\t\tdb__messages_easy_queue(NULL, \"$SYS/broker/uptime\", SYS_TREE_QOS, len, buf, 1, MSG_EXPIRY_INFINITE, NULL);\n\n\t\t/*  Update metrics values where not otherwise updated */\n\t\tmetrics[mosq_gauge_message_store_count].next = db.msg_store_count;\n\t\tmetrics[mosq_gauge_message_store_bytes].next = (int64_t)db.msg_store_bytes;\n\t\tmetrics[mosq_gauge_subscriptions].next = db.subscription_count;\n\t\tmetrics[mosq_gauge_shared_subscriptions].next = db.shared_subscription_count;\n\t\tmetrics[mosq_gauge_retained_messages].next = db.retained_count;\n#ifdef WITH_MEMORY_TRACKING\n\t\tmetrics[mosq_gauge_heap_current].next = (int64_t)mosquitto_memory_used();\n\t\tmetrics[mosq_counter_heap_maximum].next = (int64_t)mosquitto_max_memory_used();\n#endif\n\t\tmetrics[mosq_gauge_clients_total].next = HASH_CNT(hh_id, db.contexts_by_id);\n\t\tmetrics[mosq_counter_clients_maximum].next = HASH_CNT(hh_id, db.contexts_by_id);\n\t\tmetrics[mosq_gauge_clients_connected].next = HASH_CNT(hh_sock, db.contexts_by_sock);\n\t\tmetrics[mosq_gauge_clients_disconnected].next = HASH_CNT(hh_id, db.contexts_by_id) - HASH_CNT(hh_sock, db.contexts_by_sock);\n\n\t\t/* Handle loads first, because they reference other metrics and need next != current */\n\t\tif(db.now_s > last_update){\n\t\t\tdouble i_mult = 60.0/(double)(db.now_s-last_update);\n\n\t\t\tdouble exponent_1min = exp(-1.0*(double)(db.now_s-last_update)/60.0);\n\t\t\tdouble exponent_5min = exp(-1.0*(double)(db.now_s-last_update)/300.0);\n\t\t\tdouble exponent_15min = exp(-1.0*(double)(db.now_s-last_update)/900.0);\n\n\t\t\tfor(int i=0; i<mosq_metric_load_max; i++){\n\t\t\t\tif(metric_loads[i].interval == METRIC_LOAD_1MIN){\n\t\t\t\t\tcalc_load(buf, exponent_1min, i_mult, &metric_loads[i], false);\n\t\t\t\t}else if(metric_loads[i].interval == METRIC_LOAD_5MIN){\n\t\t\t\t\tcalc_load(buf, exponent_5min, i_mult, &metric_loads[i], false);\n\t\t\t\t}else{\n\t\t\t\t\tcalc_load(buf, exponent_15min, i_mult, &metric_loads[i], false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor(int i=0; i<mosq_metric_max; i++){\n\t\t\tif((metrics[i].is_max && metrics[i].next > metrics[i].current) ||\n\t\t\t\t\t(!metrics[i].is_max && metrics[i].next != metrics[i].current)){\n\n\t\t\t\tmetrics[i].current = metrics[i].next;\n\t\t\t\tlen = (uint32_t)snprintf(buf, BUFLEN, \"%lu\", metrics[i].current);\n\t\t\t\tif(metrics[i].topic){\n\t\t\t\t\tdb__messages_easy_queue(NULL, metrics[i].topic, SYS_TREE_QOS, len, buf, 1, MSG_EXPIRY_INFINITE, NULL);\n\t\t\t\t}\n\t\t\t\tif(metrics[i].topic_alias){\n\t\t\t\t\tdb__messages_easy_queue(NULL, metrics[i].topic_alias, SYS_TREE_QOS, len, buf, 1, MSG_EXPIRY_INFINITE, NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlast_update = db.now_s;\n\t\tlast_update_real = db.now_real_s;\n\t}\n}\n\n#endif\n"
  },
  {
    "path": "src/sys_tree.h",
    "content": "/*\nCopyright (c) 2015-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef SYS_TREE_H\n#define SYS_TREE_H\n\n#if defined(WITH_SYS_TREE) && defined(WITH_BROKER)\n\n/* This ordering *must* match the metrics array in sys_tree.c. */\nenum mosq_metric_type {\n\tmosq_gauge_clients_total = 0,\n\tmosq_counter_clients_maximum = 1,\n\tmosq_gauge_clients_disconnected = 2,\n\tmosq_gauge_clients_connected = 3,\n\tmosq_counter_clients_expired = 4,\n\tmosq_gauge_message_store_count = 5,\n\tmosq_gauge_message_store_bytes = 6,\n\tmosq_gauge_subscriptions = 7,\n\tmosq_gauge_shared_subscriptions = 8,\n\tmosq_gauge_retained_messages = 9,\n\tmosq_gauge_heap_current = 10,\n\tmosq_counter_heap_maximum = 11,\n\tmosq_counter_messages_received = 12,\n\tmosq_counter_messages_sent = 13,\n\tmosq_counter_bytes_received = 14,\n\tmosq_counter_bytes_sent = 15,\n\tmosq_counter_pub_bytes_received = 16,\n\tmosq_counter_pub_bytes_sent = 17,\n\tmosq_gauge_out_packets = 18,\n\tmosq_gauge_out_packet_bytes = 19,\n\tmosq_counter_socket_connections = 20,\n\tmosq_counter_mqtt_connect_received = 21,\n\tmosq_counter_mqtt_connect_sent = 22,\n\tmosq_counter_mqtt_connack_received = 23,\n\tmosq_counter_mqtt_connack_sent = 24,\n\tmosq_counter_mqtt_publish_dropped = 25,\n\tmosq_counter_mqtt_publish_received = 26,\n\tmosq_counter_mqtt_publish_sent = 27,\n\tmosq_counter_mqtt_puback_received = 28,\n\tmosq_counter_mqtt_puback_sent = 29,\n\tmosq_counter_mqtt_pubrec_received = 30,\n\tmosq_counter_mqtt_pubrec_sent = 31,\n\tmosq_counter_mqtt_pubrel_received = 32,\n\tmosq_counter_mqtt_pubrel_sent = 33,\n\tmosq_counter_mqtt_pubcomp_received = 34,\n\tmosq_counter_mqtt_pubcomp_sent = 35,\n\tmosq_counter_mqtt_subscribe_received = 36,\n\tmosq_counter_mqtt_subscribe_sent = 37,\n\tmosq_counter_mqtt_suback_received = 38,\n\tmosq_counter_mqtt_suback_sent = 39,\n\tmosq_counter_mqtt_unsubscribe_received = 40,\n\tmosq_counter_mqtt_unsubscribe_sent = 41,\n\tmosq_counter_mqtt_unsuback_received = 42,\n\tmosq_counter_mqtt_unsuback_sent = 43,\n\tmosq_counter_mqtt_pingreq_received = 44,\n\tmosq_counter_mqtt_pingreq_sent = 45,\n\tmosq_counter_mqtt_pingresp_received = 46,\n\tmosq_counter_mqtt_pingresp_sent = 47,\n\tmosq_counter_mqtt_disconnect_received = 48,\n\tmosq_counter_mqtt_disconnect_sent = 49,\n\tmosq_counter_mqtt_auth_received = 50,\n\tmosq_counter_mqtt_auth_sent = 51,\n\n\tmosq_metric_max,\n};\n\n/* This ordering *must* match the metrics_load array in sys_tree.c. */\nenum mosq_metric_load_type {\n\tmosq_load_messages_received_1min = 0,\n\tmosq_load_messages_received_5min = 1,\n\tmosq_load_messages_received_15min = 2,\n\tmosq_load_messages_sent_1min = 3,\n\tmosq_load_messages_sent_5min = 4,\n\tmosq_load_messages_sent_15min = 5,\n\tmosq_load_pub_messages_dropped_1min = 6,\n\tmosq_load_pub_messages_dropped_5min = 7,\n\tmosq_load_pub_messages_dropped_15min = 8,\n\tmosq_load_pub_messages_received_1min = 9,\n\tmosq_load_pub_messages_received_5min = 10,\n\tmosq_load_pub_messages_received_15min = 11,\n\tmosq_load_pub_messages_sent_1min = 12,\n\tmosq_load_pub_messages_sent_5min = 13,\n\tmosq_load_pub_messages_sent_15min = 14,\n\tmosq_load_bytes_received_1min = 15,\n\tmosq_load_bytes_received_5min = 16,\n\tmosq_load_bytes_received_15min = 17,\n\tmosq_load_bytes_sent_1min = 18,\n\tmosq_load_bytes_sent_5min = 19,\n\tmosq_load_bytes_sent_15min = 20,\n\tmosq_load_sockets_1min = 21,\n\tmosq_load_sockets_5min = 22,\n\tmosq_load_sockets_15min = 23,\n\tmosq_load_connections_1min = 24,\n\tmosq_load_connections_5min = 25,\n\tmosq_load_connections_15min = 26,\n\n\tmosq_metric_load_max,\n};\n\nvoid metrics__int_inc(enum mosq_metric_type m, int64_t value);\nvoid metrics__int_dec(enum mosq_metric_type m, int64_t value);\n\n#else\n#  define metrics__int_inc(A, B)\n#  define metrics__int_dec(A, B)\n\n#endif\n\n#endif\n"
  },
  {
    "path": "src/topic_tok.c",
    "content": "/*\nCopyright (c) 2010-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"util_mosq.h\"\n\n#include \"utlist.h\"\n\n\nstatic char *strtok_hier(char *str, char **saveptr)\n{\n\tchar *c;\n\n\tif(str != NULL){\n\t\t*saveptr = str;\n\t}\n\n\tif(*saveptr == NULL){\n\t\treturn NULL;\n\t}\n\n\tc = strchr(*saveptr, '/');\n\tif(c){\n\t\tstr = *saveptr;\n\t\t*saveptr = c+1;\n\t\tc[0] = '\\0';\n\t}else if(*saveptr){\n\t\t/* No match, but surplus string */\n\t\tstr = *saveptr;\n\t\t*saveptr = NULL;\n\t}\n\treturn str;\n}\n\n\nint sub__topic_tokenise(const char *subtopic, char **local_sub, char ***topics, const char **sharename)\n{\n\tchar *saveptr = NULL;\n\tchar *token;\n\tint count;\n\tint topic_index = 0;\n\tsize_t len;\n\n\tif(!subtopic){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\tlen = strlen(subtopic);\n\tif(len == 0){\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\t*local_sub = mosquitto_strdup(subtopic);\n\tif((*local_sub) == NULL){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tcount = 0;\n\tsaveptr = *local_sub;\n\twhile(saveptr){\n\t\tsaveptr = strchr(&saveptr[1], '/');\n\t\tcount++;\n\t}\n\t*topics = mosquitto_calloc((size_t)(count+3) /* 3=$shared,sharename,NULL */, sizeof(char *));\n\tif((*topics) == NULL){\n\t\tmosquitto_FREE(*local_sub);\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\tif((*local_sub)[0] != '$'){\n\t\t(*topics)[topic_index] = \"\";\n\t\ttopic_index++;\n\t}\n\n\ttoken = strtok_hier((*local_sub), &saveptr);\n\twhile(token){\n\t\t(*topics)[topic_index] = token;\n\t\ttopic_index++;\n\t\ttoken = strtok_hier(NULL, &saveptr);\n\t}\n\n\tif(!strcmp((*topics)[0], \"$share\")){\n\t\tif(count < 3 || (count == 3 && strlen((*topics)[2]) == 0)){\n\t\t\tmosquitto_FREE(*local_sub);\n\t\t\tmosquitto_FREE(*topics);\n\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t}\n\n\t\tif(sharename){\n\t\t\tif(strpbrk((*topics)[1], \"+#\")){\n\t\t\t\tmosquitto_FREE(*local_sub);\n\t\t\t\tmosquitto_FREE(*topics);\n\t\t\t\treturn MOSQ_ERR_PROTOCOL;\n\t\t\t}\n\t\t\t(*sharename) = (*topics)[1];\n\t\t}\n\n\t\tfor(int i=1; i<count-1; i++){\n\t\t\t(*topics)[i] = (*topics)[i+1];\n\t\t}\n\t\t(*topics)[0] = \"\";\n\t\t(*topics)[count-1] = NULL;\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "src/watchdog.c",
    "content": "/*\nCopyright (c) 2009-2020 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n   Tatsuzo Osawa - Add epoll.\n*/\n\n#include \"config.h\"\n\n#include <stdlib.h>\n#include <time.h>\n#include \"mosquitto.h\"\n\n#ifdef WITH_SYSTEMD\n#  include <systemd/sd-daemon.h>\n#endif\n\n#ifdef WITH_SYSTEMD\nstatic time_t next_ping = 0;\nstatic time_t ping_sec = 0;\n#endif\n\n\nvoid watchdog__init(void)\n{\n#ifdef WITH_SYSTEMD\n\tchar *watchdog_usec = getenv(\"WATCHDOG_USEC\");\n\tnext_ping = mosquitto_time();\n\tping_sec = 0;\n\n\tif(watchdog_usec){\n\t\tchar *endptr = NULL;\n\t\tlong usec = strtol(watchdog_usec, &endptr, 10);\n\t\tif(watchdog_usec[0] != '\\0' && endptr[0] == '\\0' && usec > 0){\n\t\t\tping_sec = (usec / 1000000) / 2;\n\t\t}\n\t\tnext_ping = mosquitto_time();\n\t}\n#endif\n}\n\n\nvoid watchdog__check(void)\n{\n#ifdef WITH_SYSTEMD\n\tif(ping_sec){\n\t\ttime_t now = mosquitto_time();\n\t\tif(now > next_ping){\n\t\t\tsd_notify(0, \"WATCHDOG=1\");\n\t\t\tnext_ping = now + ping_sec;\n\t\t}\n\t}\n#endif\n}\n"
  },
  {
    "path": "src/websockets.c",
    "content": "/*\nCopyright (c) 2014-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifdef WITH_WEBSOCKETS\n\n#include \"config.h\"\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n#  include <libwebsockets.h>\n#endif\n#include \"mosquitto_internal.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"packet_mosq.h\"\n#include \"sys_tree.h\"\n#include \"util_mosq.h\"\n\n#include <stdlib.h>\n#include <errno.h>\n#include <sys/stat.h>\n\n#ifndef WIN32\n#  include <sys/socket.h>\n#endif\n\n#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS\n/* Be careful if changing these, if TX is not bigger than SERV then there can\n * be very large write performance penalties.\n */\n#define WS_SERV_BUF_SIZE 4096\n#define WS_TX_BUF_SIZE (WS_SERV_BUF_SIZE*2)\n\nstatic int callback_mqtt(\n\t\tstruct lws *wsi,\n\t\tenum lws_callback_reasons reason,\n\t\tvoid *user,\n\t\tvoid *in,\n\t\tsize_t len);\n\nstatic int callback_http(\n\t\tstruct lws *wsi,\n\t\tenum lws_callback_reasons reason,\n\t\tvoid *user,\n\t\tvoid *in,\n\t\tsize_t len);\n\nenum mosq_ws_protocols {\n\tPROTOCOL_HTTP = 0,\n\tPROTOCOL_MQTT,\n\tDEMO_PROTOCOL_COUNT,\n};\n\nstruct libws_http_data {\n\tFILE *fptr;\n};\n\nstatic struct lws_protocols protocols[] = {\n\t/* first protocol must always be HTTP handler */\n\t{\n\t\t\"http-only\",                        /* name */\n\t\tcallback_http,                      /* lws_callback_function */\n\t\tsizeof (struct libws_http_data),    /* per_session_data_size */\n\t\t0,                                  /* rx_buffer_size */\n\t\t0,                                  /* id */\n\t\tNULL,                               /* user v1.4 on */\n\t\tWS_TX_BUF_SIZE                      /* tx_packet_size v2.3.0 */\n\t},\n\t{\n\t\t\"mqtt\",\n\t\tcallback_mqtt,\n\t\tsizeof(struct libws_mqtt_data),\n\t\t0,                                  /* rx_buffer_size */\n\t\t1,                                  /* id */\n\t\tNULL,                               /* user v1.4 on */\n\t\tWS_TX_BUF_SIZE                      /* tx_packet_size v2.3.0 */\n\t},\n\t{\n\t\t\"mqttv3.1\",\n\t\tcallback_mqtt,\n\t\tsizeof(struct libws_mqtt_data),\n\t\t0,                                  /* rx_buffer_size */\n\t\t2,                                  /* id */\n\t\tNULL,                               /* user v1.4 on */\n\t\tWS_TX_BUF_SIZE                      /* tx_packet_size v2.3.0 */\n\t},\n\t{\n\t\tNULL,\n\t\tNULL,\n\t\t0,\n\t\t0,                                  /* rx_buffer_size */\n\t\t0,                                  /* id */\n\t\tNULL,                               /* user v1.4 on */\n\t\t0                                   /* tx_packet_size v2.3.0 */\n\t}\n};\n\n\nstatic void easy_address(int sock, struct mosquitto *mosq)\n{\n\tchar address[1024];\n\n\tif(!net__socket_get_address(sock, address, 1024, &mosq->remote_port)){\n\t\tmosq->address = mosquitto_strdup(address);\n\t}\n}\n\n\nstatic int callback_mqtt(\n\t\tstruct lws *wsi,\n\t\tenum lws_callback_reasons reason,\n\t\tvoid *user,\n\t\tvoid *in,\n\t\tsize_t len)\n{\n\tstruct mosquitto *mosq = NULL;\n\tstruct mosquitto__packet *packet;\n\tint count;\n\tunsigned int ucount;\n\tconst struct lws_protocols *p;\n\tstruct libws_mqtt_data *u = (struct libws_mqtt_data *)user;\n\tsize_t pos;\n\tuint8_t *buf;\n\tint rc;\n\tuint8_t byte;\n\tchar ip_addr_buff[1024];\n\n\tswitch(reason){\n\t\tcase LWS_CALLBACK_ESTABLISHED:\n\t\t\tmosq = context__init();\n\t\t\tif(mosq){\n\t\t\t\tp = lws_get_protocol(wsi);\n\t\t\t\tmosq->listener = p->user;\n\t\t\t\tif(!mosq->listener){\n\t\t\t\t\tmosquitto_FREE(mosq);\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tmosq->wsi = wsi;\n#ifdef WITH_TLS\n\t\t\t\tif(in){\n\t\t\t\t\tmosq->ssl = (SSL *)in;\n\t\t\t\t\tif(!mosq->listener->ssl_ctx){\n\t\t\t\t\t\tmosq->listener->ssl_ctx = SSL_get_SSL_CTX(mosq->ssl);\n\t\t\t\t\t}\n\t\t\t\t}\n#endif\n\t\t\t\tu->mosq = mosq;\n\t\t\t}else{\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\teasy_address(lws_get_socket_fd(wsi), mosq);\n\t\t\tif(!mosq->address){\n\t\t\t\t/* getpeername and inet_ntop failed and not a bridge */\n\t\t\t\tmosquitto_FREE(mosq);\n\t\t\t\tu->mosq = NULL;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tmosq->listener->client_count++;\n\t\t\tif((mosq->listener->max_connections > 0 && mosq->listener->client_count > mosq->listener->max_connections)\n\t\t\t\t\t|| (db.config->global_max_connections > 0 && HASH_CNT(hh_sock, db.contexts_by_sock) > (unsigned int)db.config->global_max_connections)){\n\n\t\t\t\tif(db.config->connection_messages == true){\n\t\t\t\t\tlog__printf(NULL, MOSQ_LOG_NOTICE, \"Client connection from %s denied: max_connections exceeded.\", mosq->address);\n\t\t\t\t}\n\t\t\t\tmosq->listener->client_count--;\n\t\t\t\tmosquitto_FREE(mosq->address);\n\t\t\t\tmosquitto_FREE(mosq);\n\t\t\t\tu->mosq = NULL;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tmosq->sock = lws_get_socket_fd(wsi);\n\t\t\tHASH_ADD(hh_sock, db.contexts_by_sock, sock, sizeof(mosq->sock), mosq);\n\t\t\tmux__new(mosq);\n\t\t\tbreak;\n\n\t\tcase LWS_CALLBACK_CLOSED:\n\t\t\tif(!u){\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tmosq = u->mosq;\n\t\t\tif(mosq){\n\t\t\t\tif(mosq->sock != INVALID_SOCKET){\n\t\t\t\t\tHASH_DELETE(hh_sock, db.contexts_by_sock, mosq);\n\t\t\t\t\tmosq->sock = INVALID_SOCKET;\n\t\t\t\t\tmux__delete(mosq);\n\t\t\t\t}\n\t\t\t\tmosq->wsi = NULL;\n#ifdef WITH_TLS\n\t\t\t\tmosq->ssl = NULL;\n#endif\n\t\t\t\tdo_disconnect(mosq, MOSQ_ERR_CONN_LOST);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase LWS_CALLBACK_SERVER_WRITEABLE:\n\t\t\tif(!u){\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tmosq = u->mosq;\n\t\t\tif(!mosq){\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\trc = db__message_write_inflight_out_latest(mosq);\n\t\t\tif(rc){\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\trc = db__message_write_queued_out(mosq);\n\t\t\tif(rc){\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\twhile(mosq->out_packet && !lws_send_pipe_choked(mosq->wsi)){\n\t\t\t\tpacket = mosq->out_packet;\n\t\t\t\tcount = lws_write(wsi, &packet->payload[packet->pos], packet->to_process, LWS_WRITE_BINARY);\n\t\t\t\tif(count < 0){\n\t\t\t\t\tif(mosq->state == mosq_cs_disconnect_ws\n\t\t\t\t\t\t\t|| mosq->state == mosq_cs_disconnecting\n\t\t\t\t\t\t\t|| mosq->state == mosq_cs_disused){\n\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tucount = (unsigned int)count;\n#ifdef WITH_SYS_TREE\n\t\t\t\tmetrics__int_inc(mosq_counter_bytes_sent, ucount);\n#endif\n\t\t\t\tpacket->to_process -= ucount;\n\t\t\t\tpacket->pos += ucount;\n\t\t\t\tif(packet->to_process > 0){\n\t\t\t\t\tif(mosq->state == mosq_cs_disconnect_ws\n\t\t\t\t\t\t\t|| mosq->state == mosq_cs_disconnecting\n\t\t\t\t\t\t\t|| mosq->state == mosq_cs_disused){\n\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n#ifdef WITH_SYS_TREE\n\t\t\t\tmetrics__int_inc(mosq_counter_messages_sent, 1);\n\t\t\t\tif(((packet->command)&0xF0) == CMD_PUBLISH){\n\t\t\t\t\tmetrics__int_inc(mosq_counter_mqtt_publish_sent, 1);\n\t\t\t\t}\n#endif\n\n\t\t\t\tpacket__get_next_out(mosq);\n\t\t\t\tmosquitto_FREE(packet);\n\n\t\t\t\tmosq->next_msg_out = db.now_s + mosq->keepalive;\n\t\t\t}\n\t\t\tif(mosq->state == mosq_cs_disconnect_ws\n\t\t\t\t\t|| mosq->state == mosq_cs_disconnecting\n\t\t\t\t\t|| mosq->state == mosq_cs_disused){\n\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif(mosq->out_packet){\n\t\t\t\tlws_callback_on_writable(mosq->wsi);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase LWS_CALLBACK_RECEIVE:\n\t\t\tif(!u || !u->mosq){\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tmosq = u->mosq;\n\t\t\tpos = 0;\n\t\t\tbuf = (uint8_t *)in;\n\t\t\tmetrics__int_inc(mosq_counter_bytes_received, (int64_t)len);\n\t\t\twhile(pos < len){\n\t\t\t\tif(!mosq->in_packet.command){\n\t\t\t\t\tmosq->in_packet.command = buf[pos];\n\t\t\t\t\tpos++;\n\t\t\t\t\t/* Clients must send CONNECT as their first command. */\n\t\t\t\t\tif(mosq->state == mosq_cs_new && (mosq->in_packet.command&0xF0) != CMD_CONNECT){\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(mosq->in_packet.remaining_count <= 0){\n\t\t\t\t\tdo{\n\t\t\t\t\t\tif(pos == len){\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbyte = buf[pos];\n\t\t\t\t\t\tpos++;\n\n\t\t\t\t\t\tmosq->in_packet.remaining_count--;\n\t\t\t\t\t\t/* Max 4 bytes length for remaining length as defined by protocol.\n\t\t\t\t\t\t* Anything more likely means a broken/malicious client.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tif(mosq->in_packet.remaining_count < -4){\n\t\t\t\t\t\t\treturn -1;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmosq->in_packet.remaining_length += (byte & 127) * mosq->in_packet.remaining_mult;\n\t\t\t\t\t\tmosq->in_packet.remaining_mult *= 128;\n\t\t\t\t\t}while((byte & 128) != 0);\n\t\t\t\t\tmosq->in_packet.remaining_count = (int8_t)(mosq->in_packet.remaining_count * -1);\n\n\t\t\t\t\tif(mosq->in_packet.remaining_length > 0){\n\t\t\t\t\t\tmosq->in_packet.payload = mosquitto_malloc(mosq->in_packet.remaining_length*sizeof(uint8_t));\n\t\t\t\t\t\tif(!mosq->in_packet.payload){\n\t\t\t\t\t\t\treturn -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmosq->in_packet.to_process = mosq->in_packet.remaining_length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(mosq->in_packet.to_process>0){\n\t\t\t\t\tif((uint32_t)len - pos >= mosq->in_packet.to_process){\n\t\t\t\t\t\tmemcpy(&mosq->in_packet.payload[mosq->in_packet.pos], &buf[pos], mosq->in_packet.to_process);\n\t\t\t\t\t\tmosq->in_packet.pos += mosq->in_packet.to_process;\n\t\t\t\t\t\tpos += mosq->in_packet.to_process;\n\t\t\t\t\t\tmosq->in_packet.to_process = 0;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tmemcpy(&mosq->in_packet.payload[mosq->in_packet.pos], &buf[pos], len-pos);\n\t\t\t\t\t\tmosq->in_packet.pos += (uint32_t)(len-pos);\n\t\t\t\t\t\tmosq->in_packet.to_process -= (uint32_t)(len-pos);\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t/* All data for this packet is read. */\n\t\t\t\tmosq->in_packet.pos = 0;\n\n#ifdef WITH_SYS_TREE\n\t\t\t\tmetrics__int_inc(mosq_counter_messages_received, 1);\n#endif\n\t\t\t\trc = handle__packet(mosq);\n\n\t\t\t\t/* Free data and reset values */\n\t\t\t\tpacket__cleanup(&mosq->in_packet);\n\n\t\t\t\tkeepalive__update(mosq);\n\n\t\t\t\tif(rc && mosq->out_packet){\n\t\t\t\t\tif(mosq->state != mosq_cs_disconnecting){\n\t\t\t\t\t\tmosquitto__set_state(mosq, mosq_cs_disconnect_ws);\n\t\t\t\t\t}\n\t\t\t\t\tlws_callback_on_writable(mosq->wsi);\n\t\t\t\t}else if(rc){\n\t\t\t\t\tdo_disconnect(mosq, MOSQ_ERR_CONN_LOST);\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\n\nstatic char *http__canonical_filename(\n\t\tstruct lws *wsi,\n\t\tconst char *in,\n\t\tconst char *http_dir)\n{\n\tsize_t inlen, slen;\n\tchar *filename, *filename_canonical;\n\n\tinlen = strlen(in);\n\tif(in[inlen-1] == '/'){\n\t\tslen = strlen(http_dir) + inlen + strlen(\"/index.html\") + 2;\n\t}else{\n\t\tslen = strlen(http_dir) + inlen + 2;\n\t}\n\tfilename = mosquitto_malloc(slen);\n\tif(!filename){\n\t\tlws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);\n\t\treturn NULL;\n\t}\n\tif(((char *)in)[inlen-1] == '/'){\n\t\tsnprintf(filename, slen, \"%s%sindex.html\", http_dir, (char *)in);\n\t}else{\n\t\tsnprintf(filename, slen, \"%s%s\", http_dir, (char *)in);\n\t}\n\n\n\t/* Get canonical path and check it is within our http_dir */\n#ifdef WIN32\n\tfilename_canonical = _fullpath(NULL, filename, 0);\n\tmosquitto_FREE(filename);\n\tif(!filename_canonical){\n\t\tlws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);\n\t\treturn NULL;\n\t}\n#else\n\tfilename_canonical = realpath(filename, NULL);\n\tmosquitto_FREE(filename);\n\tif(!filename_canonical){\n\t\tif(errno == EACCES){\n\t\t\tlws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);\n\t\t}else if(errno == EINVAL || errno == EIO || errno == ELOOP){\n\t\t\tlws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);\n\t\t}else if(errno == ENAMETOOLONG){\n\t\t\tlws_return_http_status(wsi, HTTP_STATUS_REQ_URI_TOO_LONG, NULL);\n\t\t}else if(errno == ENOENT || errno == ENOTDIR){\n\t\t\tlws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);\n\t\t}\n\t\treturn NULL;\n\t}\n#endif\n\tif(strncmp(http_dir, filename_canonical, strlen(http_dir))){\n\t\t/* Requested file isn't within http_dir, deny access. */\n\t\tSAFE_FREE(filename_canonical);\n\t\tlws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);\n\t\treturn NULL;\n\t}\n\n\treturn filename_canonical;\n}\n\n\nstatic int callback_http(\n\t\tstruct lws *wsi,\n\t\tenum lws_callback_reasons reason,\n\t\tvoid *user,\n\t\tvoid *in,\n\t\tsize_t len)\n{\n\tstruct libws_http_data *u = (struct libws_http_data *)user;\n\tstruct libws_mqtt_hack *hack;\n\tchar *http_dir;\n\tsize_t buflen;\n\tsize_t wlen;\n\tint rc;\n\tchar *filename_canonical;\n\tunsigned char buf[4096];\n\tstruct stat filestat;\n\tstruct mosquitto *mosq;\n\tstruct lws_pollargs *pollargs = (struct lws_pollargs *)in;\n\tint hlen;\n\n\t/* FIXME - ssl cert verification is done here. */\n\n\tswitch(reason){\n\t\tcase LWS_CALLBACK_HTTP:\n\t\t\tif(!u){\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\thack = (struct libws_mqtt_hack *)lws_context_user(lws_get_context(wsi));\n\t\t\tif(!hack){\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\thttp_dir = hack->http_dir;\n\n\t\t\tif(!http_dir){\n\t\t\t\t/* http disabled */\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\t/* Forbid POST */\n\t\t\tif(lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)){\n\t\t\t\tlws_return_http_status(wsi, HTTP_STATUS_METHOD_NOT_ALLOWED, NULL);\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tfilename_canonical = http__canonical_filename(wsi, (char *)in, http_dir);\n\t\t\tif(!filename_canonical){\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tu->fptr = fopen(filename_canonical, \"rb\");\n\t\t\tif(!u->fptr){\n\t\t\t\tSAFE_FREE(filename_canonical);\n\t\t\t\tlws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif(fstat(fileno(u->fptr), &filestat) < 0){\n\t\t\t\tSAFE_FREE(filename_canonical);\n\t\t\t\tlws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);\n\t\t\t\tfclose(u->fptr);\n\t\t\t\tu->fptr = NULL;\n\t\t\t\treturn -1;\n\t\t\t}\n\n\n\t\t\tif((filestat.st_mode & S_IFDIR) == S_IFDIR){\n\t\t\t\tfclose(u->fptr);\n\t\t\t\tu->fptr = NULL;\n\t\t\t\tSAFE_FREE(filename_canonical);\n\n\t\t\t\t/* FIXME - use header functions from lws 2.x */\n\t\t\t\tbuflen = (size_t)snprintf((char *)buf, 4096, \"HTTP/1.0 302 OK\\r\\n\"\n\t\t\t\t\t\t\"Location: %s/\\r\\n\\r\\n\",\n\t\t\t\t\t\t(char *)in);\n\t\t\t\treturn lws_write(wsi, buf, buflen, LWS_WRITE_HTTP);\n\t\t\t}\n\n\t\t\tif((filestat.st_mode & S_IFREG) != S_IFREG){\n\t\t\t\tlws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);\n\t\t\t\tfclose(u->fptr);\n\t\t\t\tu->fptr = NULL;\n\t\t\t\tSAFE_FREE(filename_canonical);\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tlog__printf(NULL, MOSQ_LOG_DEBUG, \"http serving file \\\"%s\\\".\", filename_canonical);\n\t\t\tSAFE_FREE(filename_canonical);\n\t\t\t/* FIXME - use header functions from lws 2.x */\n\t\t\tbuflen = (size_t)snprintf((char *)buf, 4096, \"HTTP/1.0 200 OK\\r\\n\"\n\t\t\t\t\t\"Server: mosquitto\\r\\n\"\n\t\t\t\t\t\"Content-Length: %u\\r\\n\\r\\n\",\n\t\t\t\t\t(unsigned int)filestat.st_size);\n\t\t\tif(lws_write(wsi, buf, buflen, LWS_WRITE_HTTP) < 0){\n\t\t\t\tfclose(u->fptr);\n\t\t\t\tu->fptr = NULL;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tlws_callback_on_writable(wsi);\n\t\t\tbreak;\n\n\t\tcase LWS_CALLBACK_HTTP_BODY:\n\t\t\t/* For extra POST data? */\n\t\t\treturn -1;\n\n\t\tcase LWS_CALLBACK_HTTP_BODY_COMPLETION:\n\t\t\t/* For end of extra POST data? */\n\t\t\treturn -1;\n\n\t\tcase LWS_CALLBACK_FILTER_HTTP_CONNECTION:\n\t\t\t/* Access control here */\n\t\t\treturn 0;\n\n#if LWS_LIBRARY_VERSION_NUMBER >= 3001000\n\t\tcase LWS_CALLBACK_HTTP_CONFIRM_UPGRADE:\n\t\t\thack = lws_context_user(lws_get_context(wsi));\n\t\t\tif(hack->listener->ws_origin_count){\n\t\t\t\thlen = lws_hdr_total_length(wsi, WSI_TOKEN_ORIGIN);\n\t\t\t\tif(hlen <= 0 || hlen >= (int)sizeof(buf)){\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tfor(int i=0; i<hack->listener->ws_origin_count; i++){\n\t\t\t\t\tlws_hdr_copy(wsi, (char *)buf, sizeof(buf), WSI_TOKEN_ORIGIN);\n\t\t\t\t\tif((!strcmp(hack->listener->ws_origins[i], (char *)buf))){\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn -1;\n\t\t\t}else{\n\t\t\t\treturn 0;\n\t\t\t}\n#endif\n\n\t\tcase LWS_CALLBACK_HTTP_WRITEABLE:\n\t\t\t/* Send our data here */\n\t\t\tif(u && u->fptr){\n\t\t\t\tdo{\n\t\t\t\t\tbuflen = fread(buf, 1, sizeof(buf), u->fptr);\n\t\t\t\t\tif(buflen < 1){\n\t\t\t\t\t\tfclose(u->fptr);\n\t\t\t\t\t\tu->fptr = NULL;\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\trc = lws_write(wsi, buf, buflen, LWS_WRITE_HTTP);\n\t\t\t\t\tif(rc < 0){\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\twlen = (size_t)rc;\n\t\t\t\t\t/* while still active, extend timeout */\n\t\t\t\t\tif(wlen){\n\t\t\t\t\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 10);\n\t\t\t\t\t}\n\t\t\t\t\tif(wlen < buflen){\n\t\t\t\t\t\tif(fseek(u->fptr, (long)(buflen-wlen), SEEK_CUR) < 0){\n\t\t\t\t\t\t\tfclose(u->fptr);\n\t\t\t\t\t\t\tu->fptr = NULL;\n\t\t\t\t\t\t\treturn -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\tif(buflen < sizeof(buf)){\n\t\t\t\t\t\t\tfclose(u->fptr);\n\t\t\t\t\t\t\tu->fptr = NULL;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}while(u->fptr && !lws_send_pipe_choked(wsi));\n\t\t\t\tlws_callback_on_writable(wsi);\n\t\t\t}else{\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase LWS_CALLBACK_CLOSED:\n\t\tcase LWS_CALLBACK_CLOSED_HTTP:\n\t\tcase LWS_CALLBACK_HTTP_FILE_COMPLETION:\n\t\t\tif(u && u->fptr){\n\t\t\t\tfclose(u->fptr);\n\t\t\t\tu->fptr = NULL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase LWS_CALLBACK_ADD_POLL_FD:\n\t\t\tHASH_FIND(hh_sock, db.contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq);\n\t\t\tif(mosq){\n\t\t\t\tif(pollargs->events & LWS_POLLOUT){\n\t\t\t\t\tmux__add_out(mosq);\n\t\t\t\t}else{\n\t\t\t\t\tmux__remove_out(mosq);\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tif(pollargs->events & POLLIN){\n\t\t\t\t\t/* Assume this is a new listener */\n\t\t\t\t\tlisteners__add_websockets(lws_get_context(wsi), pollargs->fd);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase LWS_CALLBACK_DEL_POLL_FD:\n\t\t\tHASH_FIND(hh_sock, db.contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq);\n\t\t\tif(mosq){\n\t\t\t\tmux__delete(mosq);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase LWS_CALLBACK_CHANGE_MODE_POLL_FD:\n\t\t\tHASH_FIND(hh_sock, db.contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq);\n\t\t\tif(mosq){\n\t\t\t\tif(pollargs->events & LWS_POLLHUP){\n\t\t\t\t\treturn 1;\n\t\t\t\t}else if(pollargs->events & LWS_POLLOUT){\n\t\t\t\t\tmux__add_out(mosq);\n\t\t\t\t}else{\n\t\t\t\t\tmux__remove_out(mosq);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n#ifdef WITH_TLS\n\t\tcase LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:\n\t\t\tif(!len || (SSL_get_verify_result((SSL *)in) != X509_V_OK)){\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tbreak;\n#endif\n\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n\n\treturn 0;\n}\n\n\nstatic void log_wrap(int level, const char *line)\n{\n\tchar *l = (char *)line;\n\tUNUSED(level);\n\tl[strlen(line)-1] = '\\0'; /* Remove \\n */\n\tlog__printf(NULL, MOSQ_LOG_WEBSOCKETS, \"%s\", l);\n}\n\n\nvoid mosq_websockets_init(struct mosquitto__listener *listener, const struct mosquitto__config *conf)\n{\n\tstruct lws_context_creation_info info;\n\tstruct lws_protocols *p;\n\tsize_t protocol_count;\n\tint i;\n\tstruct libws_mqtt_hack *user;\n\n\t/* Count valid protocols */\n\tfor(protocol_count=0; protocols[protocol_count].name; protocol_count++){\n\t\t;\n\t}\n\n\tp = mosquitto_calloc(protocol_count+1, sizeof(struct lws_protocols));\n\tif(!p){\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Out of memory.\");\n\t\treturn;\n\t}\n\tfor(i=0; protocols[i].name; i++){\n\t\tp[i].name = protocols[i].name;\n\t\tp[i].callback = protocols[i].callback;\n\t\tp[i].per_session_data_size = protocols[i].per_session_data_size;\n\t\tp[i].rx_buffer_size = protocols[i].rx_buffer_size;\n\t\tp[i].user = listener;\n\t}\n\n\tmemset(&info, 0, sizeof(info));\n\tinfo.iface = listener->host;\n\tinfo.port = listener->port;\n\tinfo.protocols = p;\n\tinfo.gid = -1;\n\tinfo.uid = -1;\n#ifdef WITH_TLS\n\tif(listener->cafile){\n\t\tinfo.ssl_ca_filepath = listener->cafile;\n\t}else if(listener->capath){\n\t\tlog__printf(NULL, MOSQ_LOG_WARNING, \"Warning: CA path option is not supported for websockets\");\n\t}\n\tinfo.ssl_cert_filepath = listener->certfile;\n\tinfo.ssl_private_key_filepath = listener->keyfile;\n\tinfo.ssl_cipher_list = listener->ciphers;\n\t/* HTTP 1 only, due to HTTP 2 issues in Firefox:\n\t   https://github.com/eclipse-mosquitto/mosquitto/issues/1211\n\t*/\n\tinfo.alpn = \"h1\";\n#if defined(WITH_WEBSOCKETS) && LWS_LIBRARY_VERSION_NUMBER>=3001000\n\tinfo.tls1_3_plus_cipher_list = listener->ciphers_tls13;\n#endif\n\tif(listener->require_certificate){\n\t\tinfo.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;\n\t}\n#endif\n\n\tinfo.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;\n\tif(listener->socket_domain == AF_INET){\n\t\tinfo.options |= LWS_SERVER_OPTION_DISABLE_IPV6;\n\t}\n\tinfo.max_http_header_data = conf->packet_buffer_size;\n\n\tuser = mosquitto_calloc(1, sizeof(struct libws_mqtt_hack));\n\tif(!user){\n\t\tmosquitto_FREE(p);\n\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Out of memory.\");\n\t\treturn;\n\t}\n\n\tif(listener->http_dir){\n#ifdef WIN32\n\t\tuser->http_dir = _fullpath(NULL, listener->http_dir, 0);\n#else\n\t\tuser->http_dir = realpath(listener->http_dir, NULL);\n#endif\n\t\tif(!user->http_dir){\n\t\t\tmosquitto_FREE(user);\n\t\t\tmosquitto_FREE(p);\n\t\t\tlog__printf(NULL, MOSQ_LOG_ERR, \"Error: Unable to open http dir \\\"%s\\\".\", listener->http_dir);\n\t\t\treturn;\n\t\t}\n\t}\n\tuser->listener = listener;\n\n\tinfo.user = user;\n\tinfo.pt_serv_buf_size = WS_SERV_BUF_SIZE;\n\tlistener->ws_protocol = p;\n\n\tlws_set_log_level(conf->websockets_log_level, log_wrap);\n\n\tlog__printf(NULL, MOSQ_LOG_INFO, \"Opening websockets listen socket on port %d.\", listener->port);\n\tlistener->ws_in_init = true;\n\tlistener->ws_context = lws_create_context(&info);\n\tlistener->ws_in_init = false;\n}\n\n#endif\n#endif\n"
  },
  {
    "path": "src/will_delay.c",
    "content": "/*\nCopyright (c) 2019-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#include \"config.h\"\n\n#include <math.h>\n#include <stdio.h>\n#include <utlist.h>\n\n#include \"mosquitto_broker_internal.h\"\n\nstatic struct will_delay_list *delay_list = NULL;\nstatic time_t last_check = 0;\n\n\nstatic int will_delay__cmp(struct will_delay_list *i1, struct will_delay_list *i2)\n{\n\treturn (int)(i1->context->will_delay_interval - i2->context->will_delay_interval);\n}\n\n\nint will_delay__add(struct mosquitto *context)\n{\n\tstruct will_delay_list *item;\n\n\tif(context->will_delay_entry){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\titem = mosquitto_calloc(1, sizeof(struct will_delay_list));\n\tif(!item){\n\t\treturn MOSQ_ERR_NOMEM;\n\t}\n\n\titem->context = context;\n\tcontext->will_delay_entry = item;\n\titem->context->will_delay_time = db.now_real_s + item->context->will_delay_interval;\n\n\tDL_INSERT_INORDER(delay_list, item, will_delay__cmp);\n\n\tloop__update_next_event(item->context->will_delay_interval*1000);\n\tplugin_persist__handle_client_update(context);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\n/* Call on broker shutdown only */\nvoid will_delay__send_all(void)\n{\n\tstruct will_delay_list *item, *tmp;\n\n\tDL_FOREACH_SAFE(delay_list, item, tmp){\n\t\tDL_DELETE(delay_list, item);\n\t\titem->context->will_delay_interval = 0;\n\t\titem->context->will_delay_entry = NULL;\n\t\tcontext__send_will(item->context);\n\t\tmosquitto_FREE(item);\n\t}\n}\n\n\nvoid will_delay__check(void)\n{\n\tstruct will_delay_list *item, *tmp;\n\n\tif(db.now_real_s <= last_check){\n\t\tif(delay_list){\n\t\t\tloop__update_next_event(1000);\n\t\t}\n\t\treturn;\n\t}\n\n\tlast_check = db.now_real_s;\n\n\tDL_FOREACH_SAFE(delay_list, item, tmp){\n\t\tif(item->context->will_delay_time <= db.now_real_s){\n\t\t\tDL_DELETE(delay_list, item);\n\t\t\titem->context->will_delay_interval = 0;\n\t\t\titem->context->will_delay_entry = NULL;\n\t\t\tcontext__send_will(item->context);\n\t\t\tif(item->context->session_expiry_interval == MQTT_SESSION_EXPIRY_IMMEDIATE){\n\t\t\t\tcontext__add_to_disused(item->context);\n\t\t\t}\n\t\t\tmosquitto_FREE(item);\n\t\t}else{\n\t\t\tloop__update_next_event((item->context->will_delay_time - db.now_real_s)*1000);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n\nvoid will_delay__remove(struct mosquitto *mosq)\n{\n\tif(mosq->will_delay_entry != NULL){\n\t\tDL_DELETE(delay_list, mosq->will_delay_entry);\n\t\tmosquitto_FREE(mosq->will_delay_entry);\n\t}\n}\n\n"
  },
  {
    "path": "src/xtreport.c",
    "content": "/*\nCopyright (c) 2020-2021 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nSPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifdef WITH_XTREPORT\n\n/* This file allows reporting of internal parameters to a kcachegrind\n * compatible output file. It is for debugging purposes only and is most likely\n * of no interest to end users.\n */\n#include \"config.h\"\n\n#include <inttypes.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <uthash.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"mosquitto_internal.h\"\n#include \"net_mosq.h\"\n\n\nstatic void client_cost(FILE *fptr, struct mosquitto *context, int fn_index)\n{\n\tlong pkt_count, pkt_bytes;\n\tlong cmsg_count;\n\tlong cmsg_bytes;\n\tlong tBytes;\n\n\tpkt_count = 1;\n\tpkt_bytes = context->in_packet.packet_length + context->out_packet_bytes;\n\n\tcmsg_count = context->msgs_in.inflight_count + context->msgs_in.queued_count;\n\tcmsg_bytes = context->msgs_in.inflight_bytes + context->msgs_in.queued_bytes;\n\tcmsg_count += context->msgs_out.inflight_count + context->msgs_out.queued_count;\n\tcmsg_bytes += context->msgs_out.inflight_bytes + context->msgs_out.queued_bytes;\n\n\ttBytes = pkt_bytes + cmsg_bytes;\n\tif(context->id){\n\t\ttBytes += (long)strlen(context->id);\n\t}\n\tfprintf(fptr, \"%d %ld %lu %lu %lu %lu %d 0 0 %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \"\\n\", fn_index,\n\t\t\ttBytes,\n\t\t\tpkt_count, cmsg_count,\n\t\t\tpkt_bytes, cmsg_bytes,\n\t\t\tcontext->sock == INVALID_SOCKET?0:context->sock,\n\t\t\tcontext->stats.messages_sent,\n\t\t\tcontext->stats.messages_received,\n\t\t\tcontext->stats.messages_dropped\n\t\t\t);\n}\n\n\nstatic void report_subscriptions(FILE *fptr, struct mosquitto *context, int *fn_index_max)\n{\n\tfor(int i=0; i<context->subs_count; i++){\n\t\tif(context->subs && context->subs[i]){\n\t\t\tstruct mosquitto__subhier *subhier = context->subs[i]->hier;\n\t\t\tint topic_count = 0;\n\t\t\tchar *topics[100];\n\n\t\t\tdo{\n\t\t\t\ttopics[topic_count] = subhier->topic;\n\t\t\t\tsubhier = subhier->parent;\n\t\t\t\ttopic_count++;\n\t\t\t\tif(topic_count == 100){\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}while(subhier);\n\t\t\tif(topic_count == 100){\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfprintf(fptr, \"cfn=(%d) \", *fn_index_max);\n\t\t\tif(topics[topic_count-2] && topics[topic_count-2][0] == '\\0'){\n\t\t\t\ttopic_count--;\n\t\t\t}\n\t\t\tfor(int j=topic_count-2; j>0; j--){\n\t\t\t\tif(topics[j]){\n\t\t\t\t\tfprintf(fptr, \"%s/\", topics[j]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(topics[0]){\n\t\t\t\tfprintf(fptr, \"%s\\n\", topics[0]);\n\t\t\t}\n\n\t\t\tfprintf(fptr, \"calls=1 %d\\n\", *fn_index_max);\n\t\t\tfprintf(fptr, \"1 0 0 0 0 0 0 1 0 0 0 0\\n\");\n\t\t\t(*fn_index_max)++;\n\t\t}\n\t}\n}\n\n\nvoid xtreport(void)\n{\n\tpid_t pid;\n\tchar filename[40];\n\tFILE *fptr;\n\tstruct mosquitto *context, *ctxt_tmp;\n\tint fn_index = 2, fn_index_max;\n\tstatic int iter = 1;\n\n\tpid = getpid();\n\tsnprintf(filename, 40, \"/tmp/xtmosquitto.kcg.%d.%d\", pid, iter);\n\titer++;\n\tfptr = fopen(filename, \"wt\");\n\tif(fptr == NULL){\n\t\treturn;\n\t}\n\n\tfprintf(fptr, \"# callgrind format\\n\");\n\tfprintf(fptr, \"version: 1\\n\");\n\tfprintf(fptr, \"creator: mosquitto\\n\");\n\tfprintf(fptr, \"pid: %d\\n\", pid);\n\tfprintf(fptr, \"cmd: mosquitto\\n\\n\");\n\n\tfprintf(fptr, \"positions: line\\n\");\n\tfprintf(fptr, \"event: tB : total bytes\\n\");\n\tfprintf(fptr, \"event: pkt : currently queued packets\\n\");\n\tfprintf(fptr, \"event: cmsg : currently pending client messages\\n\");\n\tfprintf(fptr, \"event: pktB : currently queued packet bytes\\n\");\n\tfprintf(fptr, \"event: cmsgB : currently pending client message bytes\\n\");\n\tfprintf(fptr, \"event: sock : client socket number\\n\");\n\tfprintf(fptr, \"event: sub : client subscriptions\\n\");\n\tfprintf(fptr, \"event: refc : message store ref counts\\n\");\n\tfprintf(fptr, \"event: msgS : client message sent count\\n\");\n\tfprintf(fptr, \"event: msgR : client message received count\\n\");\n\tfprintf(fptr, \"event: msgD : client message dropped count\\n\");\n\tfprintf(fptr, \"events: tB pkt cmsg pktB cmsgB sock sub refc msgS msgR msgD\\n\");\n\n\tfprintf(fptr, \"\\nfn=(1) clients\\n\");\n\tfprintf(fptr, \"1 0 0 0 0 0 0 0 0 0 0 0\\n\");\n\n\tfn_index = 2;\n\tHASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){\n\t\tif(context->id){\n\t\t\tfprintf(fptr, \"cfn=(%d) %s\\n\", fn_index, context->id);\n\t\t}else{\n\t\t\tfprintf(fptr, \"cfn=(%d) unknown\\n\", fn_index);\n\t\t}\n\t\tfprintf(fptr, \"calls=1 %d\\n\", fn_index);\n\t\tclient_cost(fptr, context, fn_index);\n\t\tfn_index++;\n\t}\n\tfn_index_max = fn_index;\n\n\tfn_index = 2;\n\tHASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){\n\t\tfprintf(fptr, \"\\nfn=(%d)\\n\", fn_index);\n\t\tclient_cost(fptr, context, fn_index);\n\t\tfn_index++;\n\n\t\treport_subscriptions(fptr, context, &fn_index_max);\n\t}\n\n\tfprintf(fptr, \"\\nfn=(%d) messages\\n\", fn_index_max);\n\tfprintf(fptr, \"1 0 0 0 0 0 0 0 0 0 0 0\\n\");\n\n\tstruct mosquitto__base_msg *base_msg, *base_msg_tmp;\n\n\tHASH_ITER(hh, db.msg_store, base_msg, base_msg_tmp){\n\t\tif(base_msg->ref_count > 1){\n\t\t\tfprintf(fptr, \"cfn=(%d) %\" PRIu64 \"\\n\", fn_index_max, base_msg->data.store_id);\n\t\t\tfprintf(fptr, \"calls=%d %d\\n\", base_msg->ref_count, fn_index_max);\n\t\t\tfprintf(fptr, \"%d 0 0 0 0 0 0 0 %d 0 0 0\\n\", fn_index_max, base_msg->ref_count);\n\t\t\tfn_index_max++;\n\t\t}\n\t}\n\n\tfclose(fptr);\n}\n#endif\n"
  },
  {
    "path": "test/CMakeLists.txt",
    "content": "add_subdirectory(mock)\n\nadd_subdirectory(apps)\nadd_subdirectory(broker)\nadd_subdirectory(client)\nadd_subdirectory(lib)\nadd_subdirectory(unit)\n\nadd_custom_target(coverage\n    COMMAND lcov --quiet --capture --directory . --output-file ${PROJECT_BINARY_DIR}/coverage.info --no-external\n    COMMAND genhtml --quiet ${PROJECT_BINARY_DIR}/coverage.info --output-directory ${PROJECT_BINARY_DIR}/coverage\n    COMMENT \"Generating coverage.info and coverage/index.html in ${PROJECT_BINARY_DIR}\"\n    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}\n)\n"
  },
  {
    "path": "test/Makefile",
    "content": "R=..\ninclude ${R}/config.mk\n\n.PHONY: all check test test-compile ptest clean ssl\n\nall :\n\ntest-compile:\nifeq ($(WITH_GMOCK),yes)\n\t$(MAKE) -C mock test-compile\nendif\n\t$(MAKE) -C broker test-compile\n\t$(MAKE) -C client test-compile\n\t$(MAKE) -C lib test-compile\n\t$(MAKE) -C unit test-compile\n\t$(MAKE) -C apps test-compile\n\ncheck : test\n\nssl: ssl/all-ca.crt\n\nssl/all-ca.crt: ssl/gen.sh\n\tcd \"${<D}\" && \"${CURDIR}/${<}\"\n\ttest -e \"$@\"\n\ntest : utest ssl\n\t$(MAKE) -C broker test\n\t$(MAKE) -C lib test\n\t$(MAKE) -C client test\n\t$(MAKE) -C apps test\n\nptest : utest ssl\n\t$(MAKE) -C broker ptest\n\t$(MAKE) -C lib ptest\n\t$(MAKE) -C client ptest\n\t$(MAKE) -C apps ptest\n\nutest :\n\t$(MAKE) -C unit test\n\nreallyclean : clean\nclean :\n\t$(MAKE) -C lib clean\n\t$(MAKE) -C broker clean\n\t$(MAKE) -C client clean\n\t$(MAKE) -C unit clean\n\t$(MAKE) -C apps clean\n"
  },
  {
    "path": "test/__init__.py",
    "content": ""
  },
  {
    "path": "test/apps/CMakeLists.txt",
    "content": "add_subdirectory(ctrl)\nadd_subdirectory(db_dump)\nadd_subdirectory(passwd)\nadd_subdirectory(signal)\n"
  },
  {
    "path": "test/apps/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\n.PHONY: all check test ptest clean\n.NOTPARALLEL:\n\nall :\n\ncheck : test\n\ntest-compile:\n\t$(MAKE) -C ctrl test-compile\n\ntest :\n\t$(MAKE) -C db_dump test\n\t$(MAKE) -C ctrl test\n\t$(MAKE) -C passwd test\n\t$(MAKE) -C signal test\n\nptest :\n\t$(MAKE) -C db_dump ptest\n\t$(MAKE) -C ctrl ptest\n\t$(MAKE) -C passwd ptest\n\t$(MAKE) -C signal ptest\n\nreallyclean : clean\nclean :\n\t$(MAKE) -C db_dump clean\n\t$(MAKE) -C ctrl clean\n\t$(MAKE) -C passwd clean\n\t$(MAKE) -C signal clean\n"
  },
  {
    "path": "test/apps/TODO.md",
    "content": "# mosquitto_ctrl\n\nThe tests in 02-ctrl-dynsec.py test the operation of the mosquitto_ctrl dynsec\nmodule by using the output of mosquitto_ctrl. The tests should be extended by\nconnecting clients to ensure that auth and ACLs are being set correctly.\n"
  },
  {
    "path": "test/apps/ctrl/CMakeLists.txt",
    "content": "file(GLOB PY_TEST_FILES ctrl-*.py)\n\nset(EXCLUDE_LIST\n    # none\n)\n\nforeach(PY_TEST_FILE ${PY_TEST_FILES})\n    get_filename_component(PY_TEST_NAME ${PY_TEST_FILE} NAME_WE)\n    if(${PY_TEST_NAME} IN_LIST EXCLUDE_LIST)\n        continue()\n    endif()\n    add_test(NAME apps-${PY_TEST_NAME}\n        COMMAND ${PY_TEST_FILE}\n    )\n    set_tests_properties(apps-${PY_TEST_NAME}\n        PROPERTIES\n            ENVIRONMENT \"BUILD_ROOT=${CMAKE_BINARY_DIR}\"\n    )\nendforeach()\n\n\nfunction(add_ctrl_shell_test TEST_NAME)\n\tif(NOT WITH_CTRL_SHELL OR NOT EDITLINE_FOUND)\n\t\treturn()\n\tendif()\n\tadd_executable(${TEST_NAME}\n\t\t${TEST_NAME}.cpp\n\t\t${mosquitto_SOURCE_DIR}/common/json_help.c\n\t\t$<TARGET_OBJECTS:ctrl_shell>\n\t\t$<TARGET_OBJECTS:ctrl_shell_mock>\n\t\t$<TARGET_OBJECTS:libmosquitto_mock>\n\t)\n\ttarget_compile_definitions(${TEST_NAME} PRIVATE\n\t\tWITH_CTRL_SHELL\n\t\tWITH_EDITLINE\n\t)\n\ttarget_include_directories(${TEST_NAME} PRIVATE\n\t\t${mosquitto_SOURCE_DIR}\n\t\t${mosquitto_SOURCE_DIR}/apps/mosquitto_ctrl\n\t\t${mosquitto_SOURCE_DIR}/common\n\t\t${mosquitto_SOURCE_DIR}/include\n\t\t${mosquitto_SOURCE_DIR}/lib\n\t\t${mosquitto_SOURCE_DIR}/test/mock\n\t\t${mosquitto_SOURCE_DIR}/test/mock/apps/mosquitto_ctrl\n\t\t${mosquitto_SOURCE_DIR}/test/mock/lib\n\t)\n\ttarget_link_libraries(${TEST_NAME} PRIVATE\n\t\tGTest::gmock_main\n\t\teditline_mock\n\t\tpthread_mock\n\t\tcjson\n\t\tlibmosquitto_common\n\t)\n\tgtest_discover_tests(${TEST_NAME})\nendfunction()\n\nfunction(add_ctrl_shell_test_real_editline TEST_NAME)\n\tif(NOT WITH_CTRL_SHELL OR NOT EDITLINE_FOUND)\n\t\treturn()\n\tendif()\n\tadd_executable(${TEST_NAME}\n\t\t${TEST_NAME}.cpp\n\t\t${mosquitto_SOURCE_DIR}/common/json_help.c\n\t\t$<TARGET_OBJECTS:ctrl_shell>\n\t\t$<TARGET_OBJECTS:ctrl_shell_mock>\n\t\t$<TARGET_OBJECTS:libmosquitto_mock>\n\t)\n\ttarget_compile_definitions(${TEST_NAME} PRIVATE\n\t\tWITH_CTRL_SHELL\n\t)\n\ttarget_include_directories(${TEST_NAME} PRIVATE\n\t\t${mosquitto_SOURCE_DIR}\n\t\t${mosquitto_SOURCE_DIR}/apps/mosquitto_ctrl\n\t\t${mosquitto_SOURCE_DIR}/common\n\t\t${mosquitto_SOURCE_DIR}/include\n\t\t${mosquitto_SOURCE_DIR}/lib\n\t\t${mosquitto_SOURCE_DIR}/test/mock\n\t\t${mosquitto_SOURCE_DIR}/test/mock/apps/mosquitto_ctrl\n\t\t${mosquitto_SOURCE_DIR}/test/mock/lib\n\t)\n\ttarget_link_libraries(${TEST_NAME} PRIVATE\n\t\tLineEditing::LineEditing\n\t\tGTest::gmock_main\n\t\tpthread_mock\n\t\tcjson\n\t\tlibmosquitto_common\n\t)\n\tgtest_discover_tests(${TEST_NAME})\nendfunction()\n\nadd_ctrl_shell_test(ctrl_shell_test)\nadd_ctrl_shell_test(ctrl_shell_broker_test)\nadd_ctrl_shell_test_real_editline(ctrl_shell_completion_test)\nadd_ctrl_shell_test(ctrl_shell_dynsec_test)\nadd_ctrl_shell_test(ctrl_shell_help_test)\nadd_ctrl_shell_test(ctrl_shell_options_test)\nadd_ctrl_shell_test(ctrl_shell_pre_connect_test)\n"
  },
  {
    "path": "test/apps/ctrl/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all check test-compile test test-mock test-lib clean\n\nLOCAL_CPPFLAGS+= \\\n\t-DWITH_THREADING \\\n\t-DWITH_TLS \\\n\t-I../ \\\n\t-I${R}/include \\\n\t-I${R} \\\n\t-I${R}/apps/mosquitto_ctrl \\\n\t-I${R}/lib \\\n\t-I${R}/test/mock \\\n\t-I${R}/test/mock/apps/mosquitto_ctrl \\\n\t-I${R}/test/mock/lib\n\nLOCAL_CXXFLAGS+=-std=c++20 -Wall -ggdb -D TEST_SOURCE_DIR='\"$(realpath .)\"'\nLOCAL_LDFLAGS+=\nLOCAL_LIBADD+=-lcjson -lgmock -lgtest_main -lgtest ${R}/libcommon/libmosquitto_common.a\n\nCTRL_OBJS = \\\n\t${R}/apps/mosquitto_ctrl/ctrl_shell_broker.o \\\n\t${R}/apps/mosquitto_ctrl/ctrl_shell.o \\\n\t${R}/apps/mosquitto_ctrl/ctrl_shell_client.o \\\n\t${R}/apps/mosquitto_ctrl/ctrl_shell_completion_tree.o \\\n\t${R}/apps/mosquitto_ctrl/ctrl_shell_dynsec.o \\\n\t${R}/apps/mosquitto_ctrl/ctrl_shell_post_connect.o \\\n\t${R}/apps/mosquitto_ctrl/ctrl_shell_pre_connect.o \\\n\t${R}/apps/mosquitto_ctrl/ctrl_shell_printf.o\n\nLIBMOSQ_MOCKS = \\\n\t${R}/test/mock/lib/libmosquitto_mock.o \\\n\t${R}/test/mock/lib/actions_publish_mock.o \\\n\t${R}/test/mock/lib/actions_subscribe_mock.o \\\n\t${R}/test/mock/lib/callbacks_mock.o \\\n\t${R}/test/mock/lib/connect_mock.o \\\n\t${R}/test/mock/lib/loop_mock.o \\\n\t${R}/test/mock/lib/options_mock.o \\\n\t${R}/test/mock/lib/thread_mosq_mock.o\n\nLIB_OBJS = \\\n\t${CTRL_OBJS} \\\n\t${R}/common/json_help.o \\\n\t${R}/test/mock/apps/mosquitto_ctrl/ctrl_shell_mock.o \\\n\t${R}/test/mock/editline_mock.o \\\n\t${R}/test/mock/pthread_mock.o \\\n\t${LIBMOSQ_MOCKS}\n\nCOMPLETION_OBJS = \\\n\t${CTRL_OBJS} \\\n\t${R}/common/json_help.o \\\n\t${R}/test/mock/apps/mosquitto_ctrl/ctrl_shell_mock.o \\\n\t${R}/test/mock/pthread_mock.o \\\n\t${LIBMOSQ_MOCKS}\n\nTEST_OBJS = \\\n\tctrl_shell_test.o \\\n\tctrl_shell_broker_test.o \\\n\tctrl_shell_completion_test.o \\\n\tctrl_shell_dynsec_test.o \\\n\tctrl_shell_help_test.o \\\n\tctrl_shell_options_test.o \\\n\tctrl_shell_pre_connect_test.o\n\nifeq ($(WITH_GMOCK),yes)\nifeq ($(WITH_EDITLINE),yes)\nLOCAL_CPPFLAGS+=-DWITH_CTRL_SHELL -DWITH_EDITLINE\nMOCK_TESTS = \\\n\tctrl_shell_test \\\n\tctrl_shell_broker_test \\\n\tctrl_shell_completion_test \\\n\tctrl_shell_dynsec_test \\\n\tctrl_shell_help_test \\\n\tctrl_shell_options_test \\\n\tctrl_shell_pre_connect_test\nelse\nMOCK_TESTS =\nendif\nelse\nMOCK_TESTS =\nendif\n\nall : test-compile\n\ncheck : test\n\n# DEPS\n\n${LIBMOSQ_MOCKS}:\n\t$(MAKE) -C ${R}/lib\n\n${CTRL_OBJS} :\n\t$(MAKE) -C ${R}/apps/mosquitto_ctrl\n\n${R}/common/json_help.o : ${R}/common/json_help.c ${R}/common/json_help.h\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n# MOCKS\n\n${R}/test/mock/apps/mosquitto_ctrl/ctrl_shell_mock.o : ${R}/test/mock/apps/mosquitto_ctrl/ctrl_shell_mock.cpp ${R}/test/mock/apps/mosquitto_ctrl/ctrl_shell_mock.hpp\n\t$(MAKE) -C ${R}/test/mock/apps/mosquitto_ctrl test-compile\n\n${R}/test/mock/editline_mock.o : ${R}/test/mock/editline_mock.cpp ${R}/test/mock/editline_mock.hpp\n\t$(MAKE) -C ${R}/test/mock test-compile\n\n${R}/test/mock/pthread_mock.o : ${R}/test/mock/pthread_mock.cpp ${R}/test/mock/pthread_mock.hpp\n\t$(MAKE) -C ${R}/test/mock test-compile\n\n# TESTS\n\n${TEST_OBJS} : %.o: %.cpp\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_CXXFLAGS) -c $< -o $@\n\nctrl_shell_test : ctrl_shell_test.o ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\nctrl_shell_broker_test : ctrl_shell_broker_test.o ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\nctrl_shell_completion_test : ctrl_shell_completion_test.o ${COMPLETION_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD) -ledit\n\nctrl_shell_dynsec_test : ctrl_shell_dynsec_test.o ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\nctrl_shell_help_test : ctrl_shell_help_test.o ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\nctrl_shell_options_test : ctrl_shell_options_test.o ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\nctrl_shell_pre_connect_test : ctrl_shell_pre_connect_test.o ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LIBADD)\n\n\ntest-compile : $(MOCK_TESTS)\n\ntest-mock : $(MOCK_TESTS)\nifeq ($(WITH_GMOCK),yes)\nifeq ($(WITH_EDITLINE),yes)\n\t./ctrl_shell_broker_test\n\t./ctrl_shell_completion_test\n\t./ctrl_shell_dynsec_test\n\t./ctrl_shell_help_test\n\t./ctrl_shell_options_test\n\t./ctrl_shell_pre_connect_test\n\t./ctrl_shell_test\nendif\nendif\n\ntest : test-mock\n\t./ctrl-args.py\n\t./ctrl-broker.py\n\t./ctrl-dynsec.py\n\nptest: test-mock\n\t./test.py\n\nclean :\n\t-rm -rf $(MOCK_TESTS)\n\t-rm -rf *.o *.gcda *.gcno coverage.info out/\n\ncoverage :\n\tlcov --capture --directory . --output-file coverage.info\n\tgenhtml coverage.info --output-directory out\n\ninstall:\n\nuninstall:\n"
  },
  {
    "path": "test/apps/ctrl/ctrl-args.py",
    "content": "#!/usr/bin/env python3\n\n# Test parsing of command line args and errors. Does not test arg functionality.\n\nfrom mosq_test_helper import *\n\ndef do_test(args, rc_expected, response=None):\n    proc = subprocess.run([mosq_test.get_build_root()+\"/apps/mosquitto_ctrl/mosquitto_ctrl\"]\n                    + args,\n                    env=env, capture_output=True, encoding='utf-8', timeout=2)\n\n    if response is not None:\n        if proc.stderr != response:\n            print(len(proc.stderr))\n            print(len(response))\n            raise ValueError(proc.stderr)\n\n    if proc.returncode != rc_expected:\n        raise ValueError(f\"return code {proc.returncode} != expected {rc_expected} while testing args: {args}\")\n\nenv = mosq_test.env_add_ld_library_path()\n\ndo_test([\"-A\"], 1, response=\"Error: -A argument given but no address specified.\\n\\n\")\ndo_test([\"--cafile\"], 1, response=\"Error: --cafile argument given but no file specified.\\n\\n\")\ndo_test([\"--cafile\", \"missing\", \"broker\", \"listListeners\"], 1, \"Error: Problem setting TLS options: File not found.\\n\")\ndo_test([\"--capath\"], 1, response=\"Error: --capath argument given but no directory specified.\\n\\n\")\ndo_test([\"--cert\"], 1, response=\"Error: --cert argument given but no file specified.\\n\\n\")\ndo_test([\"--cert\", ssl_dir / \"client.crt\"], 1, response=\"Error: Both certfile and keyfile must be provided if one of them is set.\\n\")\ndo_test([\"--help\"], 1) # Gives generic help\ndo_test([\"--key\"], 1, response=\"Error: --key argument given but no file specified.\\n\\n\")\ndo_test([\"--key\", ssl_dir / \"client.key\"], 1, response=\"Error: Both certfile and keyfile must be provided if one of them is set.\\n\")\ndo_test([\"--ciphers\"], 1, response=\"Error: --ciphers argument given but no ciphers specified.\\n\\n\")\ndo_test([\"-f\"], 1, response=\"Error: -f argument given but no data file specified.\\n\\n\")\ndo_test([\"--host\"], 1, response=\"Error: -h argument given but no host specified.\\n\\n\")\ndo_test([\"-i\"], 1, response=\"Error: -i argument given but no id specified.\\n\\n\")\ndo_test([\"--keyform\"], 1, response=\"Error: --keyform argument given but no keyform specified.\\n\\n\")\ndo_test([\"--keyform\", \"key\"], 1, response=\"Error: If keyform is set, keyfile must be also specified.\\n\")\ndo_test([\"--keyform\", \"key\", \"--cafile\", \"file\", \"--cert\", \"file\", \"--key\", \"file\", \"broker\", \"listListeners\"], 1,\n        response=\"Error: Problem setting key form, it must be one of 'pem' or 'engine'.\\n\")\ndo_test(['-L'], 1, response=\"Error: -L argument given but no URL specified.\\n\\n\")\ndo_test(['-L', 'invalid://'], 1, response=\"Error: Unsupported URL scheme.\\n\\n\")\ndo_test(['-L', 'mqtt://localhost'], 1, response=\"Error: Invalid URL for -L argument specified - topic missing.\\n\")\ndo_test(['-L', 'mqtts://localhost'], 1, response=\"Error: Invalid URL for -L argument specified - topic missing.\\n\")\ndo_test(['-L', 'mqtts://:@localhost/topic'], 1, response=\"Error: Empty username in URL.\\n\")\ndo_test(['-L', 'mqtts://localhost:/topic'], 1, response=\"Error: Empty port in URL.\\n\")\ndo_test([\"-o\"], 1, response=\"Error: -o argument given but no options file specified.\\n\\n\")\ndo_test([\"-p\"], 1, response=\"Error: -p argument given but no port specified.\\n\\n\")\ndo_test([\"-p\", \"-1\"], 1, response=\"Error: Invalid port given: -1\\n\")\ndo_test([\"-p\", \"65536\"], 1, response=\"Error: Invalid port given: 65536\\n\")\ndo_test([\"-P\"], 1, response=\"Error: -P argument given but no password specified.\\n\\n\")\ndo_test([\"--proxy\"], 1, response=\"Error: --proxy argument given but no proxy url specified.\\n\\n\")\ndo_test([\"--proxy\", \"mqtt://localhost\"], 1, response=\"Error: Unsupported proxy protocol: mqtt://localhost\\n\")\ndo_test([\"--proxy\", \"socks5h://\"], 1, response=\"Error: Invalid proxy.\\n\")\ndo_test([\"--proxy\", \"socks5h://localhost:0\"], 1, response=\"Error: Invalid proxy port 0\\n\")\ndo_test([\"--proxy\", \"socks5h://localhost:65536\"], 1, response=\"Error: Invalid proxy port 65536\\n\")\ndo_test([\"--proxy\", \"socks5h://username%@localhost\"], 1, response=\"Error: Invalid URL encoding in username.\\n\")\ndo_test([\"--proxy\", \"socks5h://username%41@localhost\"], 1, response=\"Error: Invalid URL encoding in username.\\n\")\ndo_test([\"--proxy\", \"socks5h://username:password%@localhost\"], 1, response=\"Error: Invalid URL encoding in password.\\n\")\ndo_test([\"--proxy\", \"socks5h://username:password%41@localhost\"], 1, response=\"Error: Invalid URL encoding in password.\\n\")\ndo_test([\"--psk\"], 1, response=\"Error: --psk argument given but no key specified.\\n\\n\")\ndo_test([\"--psk\", \"missing.psk\"], 1, response=\"Error: --psk-identity required if --psk used.\\n\")\ndo_test([\"--psk-identity\"], 1, response=\"Error: --psk-identity argument given but no identity specified.\\n\\n\")\ndo_test([\"--cafile\", ssl_dir / \"all-ca.crt\", \"--psk\", \"missing.psk\", \"--psk-identity\", \"identity\"], 1, response=\"Error: Only one of --psk or --cafile/--capath may be used at once.\\n\")\ndo_test([\"-q\"], 1, response=\"Error: -q argument given but no QoS specified.\\n\\n\")\ndo_test([\"-q\", \"-1\"], 1, response=\"Error: Invalid QoS given: -1\\n\")\ndo_test([\"-q\", \"3\"], 1, response=\"Error: Invalid QoS given: 3\\n\")\ndo_test([\"--tls-alpn\"], 1, response=\"Error: --tls-alpn argument given but no protocol specified.\\n\\n\")\ndo_test([\"--tls-engine\"], 1, response=\"Error: --tls-engine argument given but no engine_id specified.\\n\\n\")\ndo_test([\"--tls-engine-kpass-sha1\"], 1, response=\"Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\\n\\n\")\ndo_test([\"--tls-version\"], 1, response=\"Error: --tls-version argument given but no version specified.\\n\\n\")\ndo_test([\"--username\"], 1, response=\"Error: -u argument given but no username specified.\\n\\n\")\ndo_test([\"--unix\"], 1, response=\"Error: --unix argument given but no socket path specified.\\n\\n\")\ndo_test([\"-V\"], 1, response=\"Error: --protocol-version argument given but no version specified.\\n\\n\")\ndo_test([\"-V\", \"2\"], 1, response=\"Error: Invalid protocol version argument given.\\n\\n\")\ndo_test([\"-V\", \"6\"], 1, response=\"Error: Invalid protocol version argument given.\\n\\n\")\ndo_test([\"--unknown\"], 1, response=\"Error: Unknown option '--unknown'.\\n\")\ndo_test([\"--version\"], 1) # Gives generic help\n\n# Behaviour with incomplete args is now to run the shell, so these tests don't work\n#do_test([], 1)\n#do_test([\"broker\"], 1)\n#do_test([\"-A\", \"127.0.0.1\"], 1) # Gives generic help\n#do_test([\"--cafile\", ssl_dir / \"all-ca.crt\"], 1) # Gives generic help\n#do_test([\"--capath\", ssl_dir], 1) # Gives generic help\n#do_test([\"--ciphers\", \"DEFAULT\"], 1) # Gives generic help\n#do_test([\"--debug\"], 1) # Gives generic help\n#do_test([\"-f\", ssl_dir / \"test\"], 1) # Gives generic help\n#do_test([\"--host\", \"127.0.0.1\"], 1) # Gives generic help\n#do_test([\"-i\", \"clientid\"], 1) # Gives generic help\n#do_test([\"--insecure\"], 1) # Gives generic help\n#do_test(['-L', 'mqtts://localhost/'], 1)\n#do_test(['-L', 'mqtts://username:password@localhost:1887/topic'], 1)\n#do_test([\"-o\", \"file\"], 1) # Gives generic help\n#do_test([\"-p\", \"1887\"], 1) # Gives generic help\n#do_test([\"-P\", \"password\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://username@localhost@localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://username@localhost:localhost:1080\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://localhost:1080\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://username@localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://username:password@localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://username:password@localhost:1080\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://:\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://@\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://username%25@localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://%25username@localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://user%3aname@localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://user%40name@localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://username:password%25@localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://username:%25password@localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://username:password%3a@localhost\"], 1) # Gives generic help\n#do_test([\"--proxy\", \"socks5h://username:password%40@localhost\"], 1) # Gives generic help\n#do_test([\"-q\", \"1\"], 1) # Gives generic help\n#do_test([\"--quiet\"], 1) # Gives generic help\n#do_test([\"--tls-alpn\", \"protocol\"], 1) # Gives generic help\n#do_test([\"--tls-engine\", \"engine\"], 1) # Gives generic help\n#do_test([\"--tls-engine-kpass-sha1\", \"sha1\"], 1) # Gives generic help\n#do_test([\"--tls-version\", \"tlsv1.3\"], 1) # Gives generic help\n#do_test([\"--unix\", \"sock\"], 1) # Gives generic help\n#do_test([\"--username\", \"username\"], 1) # Gives generic help\n#do_test([\"-V\", \"31\"], 1) # Gives generic help\n#do_test([\"-V\", \"311\"], 1) # Gives generic help\n#do_test([\"-V\", \"5\"], 1) # Gives generic help\n#do_test([\"--verbose\"], 1) # Gives generic help\n\n# Broker\ndo_test([\"broker\", \"unknown\"], 13, response=\"Command 'unknown' not recognised.\\n\")\n\n# Dynsec\ndo_test([\"dynsec\", \"unknown\"], 13, response=\"Command 'unknown' not recognised.\\n\")\ndo_test([\"-f\", \"file\", \"dynsec\", \"setClientPassword\", \"admin\", \"admin\", \"-i\"], 3, response=\"Error: -i argument given, but no iterations provided.\\nError: Invalid input.\\n\")\ndo_test([\"-f\", \"file\", \"dynsec\", \"setClientPassword\", \"admin\", \"admin\", \"-c\"], 3, response=\"Error: Unknown argument: -c\\nError: Invalid input.\\n\")\ndo_test([\"dynsec\", \"createClient\", \"client\", \"-i\"], 3, response=\"Error: -i argument given, but no clientid provided.\\nError: Invalid input.\\n\")\ndo_test([\"dynsec\", \"createClient\", \"client\", \"-p\"], 3, response=\"Error: -p argument given, but no password provided.\\nError: Invalid input.\\n\")\n\n# Env modification\n\n# Missing file\nenv[\"HOME\"] = \"/tmp\"\ndo_test([\"--cert\", ssl_dir / \"client.crt\"], 1, response=\"Error: Both certfile and keyfile must be provided if one of them is set.\\n\")\n\n# Invalid file\nenv[\"XDG_CONFIG_HOME\"] = \".\"\nwith open(\"mosquitto_ctrl\", \"w\") as f:\n    f.write(f\"--cert {ssl_dir}/client.crt\\n\")\n    f.write(f\"--key\\n\")\ndo_test([\"broker\"], 1, response=\"Error: --key argument given but no file specified.\\n\\n\")\n\n# Empty file\nenv[\"XDG_CONFIG_HOME\"] = \".\"\nwith open(\"mosquitto_ctrl\", \"w\") as f:\n    pass\ndo_test([\"--cert\", ssl_dir / \"client.crt\"], 1, response=\"Error: Both certfile and keyfile must be provided if one of them is set.\\n\")\nos.remove(\"mosquitto_ctrl\")\n\nexit(0)\n"
  },
  {
    "path": "test/apps/ctrl/ctrl-broker.py",
    "content": "#!/usr/bin/env python3\n\n# mosquitto_ctrl broker\n\nfrom mosq_test_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, ports):\n    with open(filename, 'w') as f:\n        f.write(\"enable_control_api true\\n\")\n        f.write(f\"global_plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(f\"plugin_opt_config_file {ports[0]}/dynamic-security.json\\n\")\n        f.write(\"allow_anonymous false\\n\")\n        f.write(f\"listener {ports[0]}\\n\")\n        f.write(f\"listener {ports[1]}\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n\ndef ctrl_cmd(cmd, args, ports, response=None):\n    opts = [\"-u\", \"admin\",\n            \"-P\", \"admin\",\n            \"-V\", \"5\"]\n\n    if response is None:\n        opts += [\n                \"-p\", str(ports[0]),\n                \"-q\", \"1\"\n                 ]\n        capture_output = False\n    else:\n        opts += [\"-p\", str(ports[1])]\n        opts += [\"--cafile\", f\"{ssl_dir}/all-ca.crt\"]\n        capture_output = True\n\n    proc = subprocess.run([mosq_test.get_build_root() + \"/apps/mosquitto_ctrl/mosquitto_ctrl\"]\n                    + opts + [cmd] + args,\n                    env=env, capture_output=True, encoding='utf-8')\n\n    if response is not None:\n        if proc.stdout != response:\n            raise ValueError(proc.stdout)\n\n    if proc.returncode != 0:\n        raise ValueError(args)\n\n\nrc = 0\nports = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, ports)\n\nenv = mosq_test.env_add_ld_library_path()\n\nif not os.path.exists(str(ports[0])):\n    os.mkdir(str(ports[0]))\n\n# Generate initial dynsec file\nctrl_cmd(\"dynsec\", [\"init\", f\"{ports[0]}/dynamic-security.json\", \"admin\", \"admin\"], ports)\nctrl_cmd(\"broker\", [\"help\"], ports)\n\n# Then start broker\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=ports[0])\n\ntry:\n    ctrl_cmd(\"dynsec\", [\"addRoleACL\", \"admin\", \"publishClientSend\", \"$CONTROL/#\", \"allow\"], ports)\n    ctrl_cmd(\"dynsec\", [\"addRoleACL\", \"admin\", \"publishClientReceive\", \"$CONTROL/#\", \"allow\"], ports)\n    ctrl_cmd(\"dynsec\", [\"addRoleACL\", \"admin\", \"subscribePattern\", \"$CONTROL/#\", \"allow\"], ports)\n\n    ctrl_cmd(\"broker\", [\"listListeners\"], ports, response=f\"Listener 1:\\n  Port:              {ports[0]}\\n  Protocol:          mqtt\\n  TLS:               false\\n\\nListener 2:\\n  Port:              {ports[1]}\\n  Protocol:          mqtt\\n  TLS:               true\\n\\n\")\n\n    ctrl_cmd(\"broker\", [\"listPlugins\"], ports, response=\"Plugin:            dynamic-security\\nControl endpoints: $CONTROL/dynamic-security/v1\\n\")\n\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nexcept Exception as err:\n    print(err)\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{ports[0]}/dynamic-security.json\")\n        pass\n    except FileNotFoundError:\n        pass\n    shutil.rmtree(f\"{ports[0]}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (_, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/apps/ctrl/ctrl-dynsec.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport json\nimport os\nimport shutil\n\ndef write_config(filename, ports):\n    with open(filename, 'w') as f:\n        f.write(f\"global_plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(f\"plugin_opt_config_file {ports[0]}/dynamic-security.json\\n\")\n        f.write(\"allow_anonymous false\\n\")\n        f.write(f\"listener {ports[0]}\\n\")\n        f.write(f\"listener {ports[1]}\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n\ndef ctrl_dynsec_cmd(args, ports, response=None, input=None):\n    opts = [\"-u\", \"admin\",\n            \"-P\", \"newadmin\",]\n\n    if response is None:\n        opts += [\n                \"-p\", str(ports[0]),\n                \"-q\", \"1\"\n                 ]\n    else:\n        opts += [\"-p\", str(ports[1])]\n        opts += [\"--cafile\", f\"{ssl_dir}/all-ca.crt\"]\n\n    proc = subprocess.run([mosq_test.get_build_root()+\"/apps/mosquitto_ctrl/mosquitto_ctrl\"]\n                    + opts + [\"dynsec\"] + args,\n                    env=env, capture_output=True, encoding='utf-8', timeout=2, input=input)\n\n    if response is not None:\n        if proc.stdout != response:\n            print(len(proc.stdout))\n            print(len(response))\n            raise ValueError(proc.stdout)\n\n    if proc.returncode != 0:\n        raise ValueError(args)\n\ndef ctrl_dynsec_file_cmd(args, ports, response=None):\n    opts = [\"-f\", f\"{ports[0]}/dynamic-security.json\"]\n\n    proc = subprocess.run([mosq_test.get_build_root()+\"/apps/mosquitto_ctrl/mosquitto_ctrl\"]\n                    + opts + [\"dynsec\"] + args,\n                    env=env, capture_output=True, encoding='utf-8')\n\n    if response is not None:\n        if proc.stdout != response:\n            raise ValueError(proc.stdout)\n\n    if proc.returncode != 0:\n        raise ValueError(args)\n\n\n\nports = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, ports)\n\nenv = mosq_test.env_add_ld_library_path()\n\nif not os.path.exists(str(ports[0])):\n    os.mkdir(str(ports[0]))\n\n# Generate initial dynsec file\nctrl_dynsec_cmd([\"init\", f\"{ports[0]}/dynamic-security.json\", \"admin\", \"admin\"], ports)\ntry:\n    # If we're root, set file ownership to \"nobody\", because that is the user\n    # the broker will change to.\n    os.chown(f\"{ports[0]}/dynamic-security.json\", 65534, 65534)\nexcept PermissionError:\n    pass\n\nctrl_dynsec_file_cmd([\"help\"], ports) # get the help, don't check the response though\nctrl_dynsec_file_cmd([\"setClientPassword\", \"admin\", \"newadmin\", \"-i\", \"10000\"], ports)\nctrl_dynsec_file_cmd([\"setClientPassword\", \"admin\", \"newadmin\"], ports)\n\n# Then start broker\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=ports[0], nolog=True)\n\ntry:\n    rc = 1\n\n    # Set default access to opposite of normal\n    ctrl_dynsec_cmd([\"setDefaultACLAccess\", \"publishClientSend\", \"allow\"], ports)\n    ctrl_dynsec_cmd([\"setDefaultACLAccess\", \"publishClientReceive\", \"deny\"], ports)\n    ctrl_dynsec_cmd([\"setDefaultACLAccess\", \"subscribe\", \"allow\"], ports)\n    ctrl_dynsec_cmd([\"setDefaultACLAccess\", \"unsubscribe\", \"deny\"], ports)\n\n    # Verify\n    ctrl_dynsec_cmd([\"getDefaultACLAccess\"], ports, response=\"publishClientSend    : allow\\npublishClientReceive : deny\\nsubscribe            : allow\\nunsubscribe          : deny\\n\")\n\n    # Create clients\n    ctrl_dynsec_cmd([\"createClient\", \"username1\", \"-p\", \"password1\"], ports) # password, no client id\n    ctrl_dynsec_cmd([\"createClient\", \"username2\", \"-p\", \"password2\", \"-c\", \"clientid2\"], ports) # password and client id\n    ctrl_dynsec_cmd([\"createClient\", \"username3\"], ports, input=\"pw\\npw\\n\") # password, no client id\n    ctrl_dynsec_cmd([\"createClient\", \"username4\"], ports, input=\"\\n\\n\") # password, no client id\n    ctrl_dynsec_cmd([\"createClient\", \"username5\"], ports, input=\"not\\nmatching\\n\")\n\n    # List clients\n    ctrl_dynsec_cmd([\"listClients\"], ports, response=\"admin\\nusername1\\nusername2\\nusername3\\nusername4\\n\")\n    ctrl_dynsec_cmd([\"listClients\", \"1\"], ports, response=\"admin\\n\") # with count\n    ctrl_dynsec_cmd([\"listClients\", \"1\", \"1\"], ports, response=\"username1\\n\") # with count, offset\n\n    # Create groups\n    ctrl_dynsec_cmd([\"createGroup\", \"group1\"], ports)\n    ctrl_dynsec_cmd([\"createGroup\", \"group2\"], ports)\n    ctrl_dynsec_cmd([\"createGroup\", \"group3\"], ports)\n\n    #List groups\n    ctrl_dynsec_cmd([\"listGroups\"], ports, response=\"group1\\ngroup2\\ngroup3\\n\")\n    ctrl_dynsec_cmd([\"listGroups\", \"1\"], ports, response=\"group1\\n\")\n    ctrl_dynsec_cmd([\"listGroups\", \"1\", \"1\"], ports, response=\"group2\\n\")\n\n    # Add client to group\n    ctrl_dynsec_cmd([\"addGroupClient\", \"group1\", \"username1\", \"10\"], ports)\n\n    # Get anonymous group\n    ctrl_dynsec_cmd([\"getAnonymousGroup\"], ports, response=\"\\n\")\n\n    # Set anon as anonymous group\n    ctrl_dynsec_cmd([\"setAnonymousGroup\", \"group2\"], ports)\n\n    # Verify\n    ctrl_dynsec_cmd([\"getAnonymousGroup\"], ports, response=\"group2\\n\")\n\n    # Create roles\n    ctrl_dynsec_cmd([\"createRole\", \"role1\"], ports)\n    ctrl_dynsec_cmd([\"createRole\", \"role2\"], ports)\n    ctrl_dynsec_cmd([\"createRole\", \"role3\"], ports)\n    #Delete a role:               deleteRole        <rolename>\n\n    ctrl_dynsec_cmd([\"deleteRole\", \"role3\"], ports) # repeat with count, offset\n\n    # Add a role to a client\n    ctrl_dynsec_cmd([\"addClientRole\", \"username1\", \"role1\", \"20\"], ports)\n\n    # Add a role to a group\n    ctrl_dynsec_cmd([\"addGroupRole\", \"group1\", \"role2\", \"15\"], ports)\n\n    ctrl_dynsec_cmd([\"getGroup\", \"group1\"], ports) # repeat with count, offset\n\n    # Add ACLs\n    ctrl_dynsec_cmd([\"addRoleACL\", \"role1\", \"publishClientSend\", \"#\", \"allow\", \"1\"], ports)\n    ctrl_dynsec_cmd([\"addRoleACL\", \"role1\", \"publishClientReceive\", \"#\", \"allow\", \"2\"], ports)\n    ctrl_dynsec_cmd([\"addRoleACL\", \"role1\", \"subscribeLiteral\", \"#\", \"allow\", \"1\"], ports)\n    ctrl_dynsec_cmd([\"addRoleACL\", \"role1\", \"subscribePattern\", \"#\", \"allow\", \"2\"], ports)\n    ctrl_dynsec_cmd([\"addRoleACL\", \"role1\", \"unsubscribeLiteral\", \"#\", \"deny\", \"1\"], ports)\n    ctrl_dynsec_cmd([\"addRoleACL\", \"role1\", \"unsubscribePattern\", \"#\", \"deny\", \"2\"], ports)\n    ctrl_dynsec_cmd([\"addRoleACL\", \"role2\", \"publishClientSend\", \"#\", \"allow\", \"3\"], ports)\n    ctrl_dynsec_cmd([\"addRoleACL\", \"role2\", \"publishClientReceive\", \"#\", \"allow\"], ports)\n\n    # List roles\n    ctrl_dynsec_cmd([\"listRoles\"], ports, response=\"admin\\nrole1\\nrole2\\n\")\n    ctrl_dynsec_cmd([\"listRoles\", \"1\"], ports, response=\"admin\\n\")\n    ctrl_dynsec_cmd([\"listRoles\", \"1\", \"1\"], ports, response=\"role1\\n\")\n\n    # Get role\n    ctrl_dynsec_cmd([\"getRole\", \"role1\"], ports, response=\"Rolename: role1\\nACLs:     publishClientSend    : allow : # (priority: 1)\\n          publishClientReceive : allow : # (priority: 2)\\n          subscribeLiteral     : allow : # (priority: 1)\\n          subscribePattern     : allow : # (priority: 2)\\n          unsubscribeLiteral   : deny  : # (priority: 1)\\n          unsubscribePattern   : deny  : # (priority: 2)\\n\")\n\n    # Get client\n    ctrl_dynsec_cmd([\"getClient\", \"username1\"], ports, response=\"Username:    username1\\nClientid:\\nRoles:       role1 (priority: 20)\\nGroups:      group1 (priority: 10)\\n\")\n\n    # Disable client\n    ctrl_dynsec_cmd([\"disableClient\", \"username1\"], ports)\n\n    # Verify client\n    ctrl_dynsec_cmd([\"getClient\", \"username1\"], ports, response=\"Username:    username1\\nClientid:\\nDisabled:    true\\nRoles:       role1 (priority: 20)\\nGroups:      group1 (priority: 10)\\n\")\n\n    # Set clientid\n    ctrl_dynsec_cmd([\"setClientID\", \"username1\", \"fixed-id\"], ports)\n\n    # Verify client\n    ctrl_dynsec_cmd([\"getClient\", \"username1\"], ports, response=\"Username:    username1\\nClientid:    fixed-id\\nDisabled:    true\\nRoles:       role1 (priority: 20)\\nGroups:      group1 (priority: 10)\\n\")\n\n    # Clear clientid\n    ctrl_dynsec_cmd([\"setClientID\", \"username1\"], ports)\n\n    # Enable client\n    ctrl_dynsec_cmd([\"enableClient\", \"username1\"], ports)\n\n    # Verify client\n    ctrl_dynsec_cmd([\"getClient\", \"username1\"], ports, response=\"Username:    username1\\nClientid:\\nRoles:       role1 (priority: 20)\\nGroups:      group1 (priority: 10)\\n\")\n\n    # Set client password\n    ctrl_dynsec_cmd([\"setClientPassword\", \"username1\", \"new-password\"], ports)\n    ctrl_dynsec_cmd([\"setClientPassword\", \"username1\"], ports, input=\"not\\nmatch\\n\")\n\n    # Remove an ACL\n    ctrl_dynsec_cmd([\"removeRoleACL\", \"role1\", \"publishClientReceive\", \"#\"], ports)\n    ctrl_dynsec_cmd([\"getRole\", \"role1\"], ports, response=\"Rolename: role1\\nACLs:     publishClientSend    : allow : # (priority: 1)\\n          subscribeLiteral     : allow : # (priority: 1)\\n          subscribePattern     : allow : # (priority: 2)\\n          unsubscribeLiteral   : deny  : # (priority: 1)\\n          unsubscribePattern   : deny  : # (priority: 2)\\n\")\n\n    ctrl_dynsec_cmd([\"removeRoleACL\", \"role1\", \"publishClientSend\", \"#\"], ports)\n    ctrl_dynsec_cmd([\"getRole\", \"role1\"], ports, response=\"Rolename: role1\\nACLs:     subscribeLiteral     : allow : # (priority: 1)\\n          subscribePattern     : allow : # (priority: 2)\\n          unsubscribeLiteral   : deny  : # (priority: 1)\\n          unsubscribePattern   : deny  : # (priority: 2)\\n\")\n\n    ctrl_dynsec_cmd([\"removeRoleACL\", \"role1\", \"subscribeLiteral\", \"#\"], ports)\n    ctrl_dynsec_cmd([\"removeRoleACL\", \"role1\", \"subscribePattern\", \"#\"], ports)\n    ctrl_dynsec_cmd([\"removeRoleACL\", \"role1\", \"unsubscribeLiteral\", \"#\"], ports)\n    ctrl_dynsec_cmd([\"removeRoleACL\", \"role1\", \"unsubscribePattern\", \"#\"], ports)\n    ctrl_dynsec_cmd([\"getRole\", \"role1\"], ports, response=\"Rolename: role1\\n\")\n\n    # Remove a Role\n    ctrl_dynsec_cmd([\"deleteRole\", \"role2\"], ports)\n\n    # Remove client from a group\n    ctrl_dynsec_cmd([\"removeGroupClient\", \"group1\", \"username1\"], ports)\n\n    # Remove role from a group\n    ctrl_dynsec_cmd([\"removeGroupRole\", \"group1\", \"role2\"], ports)\n\n    # Delete group\n    ctrl_dynsec_cmd([\"deleteGroup\", \"group1\"], ports)\n\n    ctrl_dynsec_cmd([\"removeClientRole\", \"username1\", \"role1\"], ports)\n\n    # Delete client\n    ctrl_dynsec_cmd([\"deleteClient\", \"username1\"], ports)\n\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{ports[0]}/dynamic-security.json\")\n        pass\n    except FileNotFoundError:\n        pass\n    shutil.rmtree(f\"{ports[0]}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n\nexit(rc)\n"
  },
  {
    "path": "test/apps/ctrl/ctrl_shell_broker_test.cpp",
    "content": "/*\nCopyright (c) 2022 Cedalo GmbH\n*/\n\n// clang-format off\n#include \"mosquitto_internal.h\" // keep this at the top for `#define uthash_free`\n#include \"ctrl_shell.h\"\n#include \"ctrl_shell_internal.h\"\n#include \"json_help.h\"\n#include \"utlist.h\"\n// clang-format on\n#include <gmock/gmock-actions.h>\n#include <gtest/gtest.h>\n\n#include <cstring>\n\n#include \"ctrl_shell_mock.hpp\"\n#include \"editline_mock.hpp\"\n#include \"libmosquitto_mock.hpp\"\n#include \"pthread_mock.hpp\"\n\nnamespace t = testing;\n\nstruct pending_payload {\n\tstruct pending_payload *next, *prev;\n\tchar payload[1024];\n};\n\nclass CtrlShellBrokerTest : public ::t::Test\n{\npublic:\n\t::t::StrictMock<CtrlShellMock> ctrl_shell_mock_{};\n\t::t::StrictMock<EditLineMock> editline_mock_{};\n\t::t::StrictMock<LibMosquittoMock> libmosquitto_mock_{};\n\t::t::StrictMock<PThreadMock> pthread_mock_{};\n\tLIBMOSQ_CB_connect on_connect{};\n\tLIBMOSQ_CB_message on_message{};\n\tLIBMOSQ_CB_subscribe on_subscribe{};\n\tLIBMOSQ_CB_publish_v5 on_publish{};\n\tstruct pending_payload *pending_payloads = nullptr;\n\n\n\tvoid expect_setup(struct mosq_config *config)\n\t{\n\t\teditline_mock_.reset();\n\t\tEXPECT_CALL(editline_mock_, rl_bind_key(t::Eq('\\t'), t::_));\n\t\tEXPECT_CALL(editline_mock_, add_history(t::_)).WillRepeatedly(t::Return(0));\n\t\tEXPECT_CALL(editline_mock_, clear_history()).Times(t::AnyNumber());\n\t\tconfig->no_colour = true;\n\n\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StartsWith(\"mosquitto_ctrl shell v\")));\n\t}\n\n\n\tvoid expect_connect(struct mosquitto *mosq, const char *host, int port)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_new(t::Eq(nullptr), t::Eq(true), t::Eq(nullptr)))\n\t\t\t.WillOnce(t::Return(mosq));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_int_option(t::Eq(mosq), MOSQ_OPT_PROTOCOL_VERSION, 5));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_subscribe>()))\n\t\t\t.WillRepeatedly(t::SaveArg<1>(&this->on_subscribe));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish_v5_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_publish_v5>()))\n\t\t\t.WillRepeatedly(t::SaveArg<1>(&this->on_publish));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect(t::Eq(mosq), t::StrEq(host), port, 60));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_start(t::Eq(mosq)));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_connect>()))\n\t\t\t.WillRepeatedly(t::SaveArg<1>(&this->on_connect));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_message_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_message>()))\n\t\t\t.WillOnce(t::SaveArg<1>(&this->on_message));\n\t}\n\n\n\tvoid expect_disconnect(struct mosquitto *mosq)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_disconnect(t::Eq(mosq)));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_stop(t::Eq(mosq), false));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_destroy(t::Eq(mosq)));\n\t}\n\n\n\tvoid expect_outputs(const char **outputs, size_t count)\n\t{\n\t\tfor(size_t i=0; i<count; i++){\n\t\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StrEq(outputs[i]))).Times(t::AtLeast(1));\n\t\t}\n\t}\n\n\n\tvoid expect_request_response(struct mosquitto *mosq, const char *request, const char *respons)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload), \"%s\", respons);\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(mosq), nullptr, t::StrEq(\"$CONTROL/broker/v1\"), t::_,\n\t\t\t\tt::StrEq(request), 1, false))\n\t\t\t.WillOnce(t::Invoke([this, pp](){\n\t\t\tDL_APPEND(this->pending_payloads, pp);\n\t\t\treturn 0;\n\t\t}));\n\t}\n\n\n\tvoid expect_request_response_success(struct mosquitto *mosq, const char *request, const char *command)\n\t{\n\t\tchar response[100];\n\t\tsnprintf(response, sizeof(response), \"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\"}]}\", command);\n\t\texpect_request_response(mosq, request, response);\n\t}\n\n\n\tvoid expect_request_response_empty(struct mosquitto *mosq, const char *command)\n\t{\n\t\tchar request[100];\n\t\tchar response[100];\n\n\t\tsnprintf(request, sizeof(request), \"{\\\"commands\\\":[{\\\"command\\\":\\\"%s\\\"}]}\", command);\n\t\tsnprintf(response, sizeof(response), \"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\",\\\"data\\\":{}}]}\", command);\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(mosq), nullptr, t::StrEq(\"$CONTROL/broker/v1\"), t::_,\n\t\t\t\tt::StrEq(request), 1, false))\n\t\t\t.WillOnce(t::Invoke([this, &command](){\n\t\t\tappend_empty_response(command);\n\t\t\treturn 0;\n\t\t}));\n\t}\n\n\n\tvoid append_response(const char *response)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload), \"%s\", response);\n\t\tDL_APPEND(this->pending_payloads, pp);\n\t}\n\n\n\tvoid append_empty_response(const char *command)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload),\n\t\t\t\t\"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\",\\\"data\\\":{}}]}\", command);\n\t\tDL_APPEND(this->pending_payloads, pp);\n\t}\n\n\n\tvoid expect_broker(const char *host, int port)\n\t{\n\t\tchar buf[200];\n\t\tsnprintf(buf, sizeof(buf), \"connect mqtt://%s:%d\", host, port);\n\t\tchar *s_conn = strdup(buf);\n\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t\t.WillOnce(t::Return(s_conn));\n\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t\t.WillOnce(t::Return(strdup(\"broker\")));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe(t::_, nullptr, t::StrEq(\"$CONTROL/broker/v1/response\"), 1))\n\t\t\t.WillOnce(t::Return(0));\n\t}\n\n\n\tvoid expect_connect_and_messages(struct mosquitto *mosq)\n\t{\n\t\t/* This is a hacky way of working around the async mqtt send/receive which we don't directly control.\n\t\t\t    * Each send starts a wait which times out after two seconds. We use that call to produce the effect we want.\n\t\t\t    */\n\t\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t\t.WillOnce(t::Invoke([this, mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\t\tthis->on_connect(mosq, nullptr, 0);\n\t\t\tdata.response_received = true;\n\t\t\treturn 0;\n\t\t}))\n\t\t\t.WillRepeatedly(t::Invoke([this, mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\t\tmosquitto_message msg{};\n\t\t\tstruct pending_payload *pp = pending_payloads;\n\t\t\tif(pp){\n\t\t\t\tDL_DELETE(pending_payloads, pp);\n\t\t\t\tmsg.payload = pp->payload;\n\t\t\t\tmsg.payloadlen = (int)strlen((char *)msg.payload);\n\t\t\t\tthis->on_message(mosq, nullptr, &msg);\n\t\t\t\tfree(pp);\n\t\t\t}\n\t\t\tdata.response_received = true;\n\t\t\treturn 0;\n\t\t}));\n\t}\n};\n\n\nTEST_F(CtrlShellBrokerTest, LineEmpty)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_broker(host, port);\n\texpect_connect_and_messages(&mosq);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|broker> \")))\n\t\t.WillOnce(t::Return(strdup(\"\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tconst char *outputs[] = {\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellBrokerTest, SubscribeDenied)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\n\tchar buf[200];\n\tsnprintf(buf, sizeof(buf), \"connect mqtt://%s:%d\", host, port);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t.WillOnce(t::Return(strdup(\"broker\")));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|broker> \")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe(t::_, nullptr, t::StrEq(\"$CONTROL/broker/v1/response\"), 1));\n\n\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tthis->on_connect(&mosq, nullptr, 0);\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](){\n\t\tint granted_qos[1] = {128};\n\t\tthis->on_subscribe(&mosq, nullptr, 1, 1, granted_qos);\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}))\n\t\t.WillRepeatedly(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tmosquitto_message msg{};\n\t\tstruct pending_payload *pp = pending_payloads;\n\t\tif(pp){\n\t\t\tDL_DELETE(pending_payloads, pp);\n\t\t\tmsg.payload = pp->payload;\n\t\t\tmsg.payloadlen = (int)strlen((char *)msg.payload);\n\t\t\tthis->on_message(&mosq, nullptr, &msg);\n\t\t\tfree(pp);\n\t\t}\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}));\n\n\tconst char *outputs[] = {\n\t\t\"Subscribe failed, check you have permission to access this module.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellBrokerTest, PublishDenied)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\n\tchar buf[200];\n\tsnprintf(buf, sizeof(buf), \"connect mqtt://%s:%d\", host, port);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t.WillOnce(t::Return(strdup(\"broker\")));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|broker> \")))\n\t\t.WillOnce(t::Return(strdup(\"listListeners\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe(t::_, nullptr, t::StrEq(\"$CONTROL/broker/v1/response\"), 1));\n\n\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tthis->on_connect(&mosq, nullptr, 0);\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](){\n\t\tint granted_qos[1] = {1};\n\t\tthis->on_subscribe(&mosq, nullptr, 1, 1, granted_qos);\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](){\n\t\tthis->on_publish(&mosq, nullptr, 1, 128, nullptr);\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/broker/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listListeners\\\"}]}\"), 1, false));\n\n\tconst char *outputs[] = {\n\t\t\"Publish failed, check you have permission to access this module.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellBrokerTest, ListListeners)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_broker(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|broker> \")))\n\t\t.WillOnce(t::Return(strdup(\"listListeners\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"listListeners\\\"}]}\";\n\tconst char response[] = \"{\\\"responses\\\":[{\\\"command\\\":\\\"listListeners\\\",\\\"data\\\":{\"\n\t\t\t\"\\\"listeners\\\":[\"\n\t\t\t\"{\\\"port\\\":1883,\\\"protocol\\\":\\\"mqtt\\\",\\\"tls\\\":false}\"\n\t\t\t\"]}}]}\";\n\texpect_request_response(&mosq, request, response);\n\n\tconst char *outputs[] = {\n\t\t\"Listener:\",\n\t\t\"  Port:\",\n\t\t\"    1883\\n\",\n\t\t\"  Protocol:\",\n\t\t\"    mqtt\\n\",\n\t\t\"  TLS:\",\n\t\t\"    false\\n\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellBrokerTest, ListListenersInvalidResponse)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_broker(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|broker> \")))\n\t\t.WillOnce(t::Return(strdup(\"listListeners\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"listListeners\\\"}]}\";\n\tconst char response[] = \"{\\\"responses\\\":[{\\\"command\\\":\\\"listListeners\\\",\\\"data\\\":{\"\n\t\t\t\"\\\"listeners\\\":[\"\n\t\t\t\"{\\\"protocol\\\":\\\"mqtt\\\",\\\"tls\\\":false}\"\n\t\t\t\"]}}]}\";\n\texpect_request_response(&mosq, request, response);\n\n\tconst char *outputs[] = {\n\t\t\"Invalid response from broker.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellBrokerTest, ListPlugins)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_broker(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|broker> \")))\n\t\t.WillOnce(t::Return(strdup(\"listPlugins\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"listPlugins\\\"}]}\";\n\tconst char response[] = \"{\\\"responses\\\":[{\\\"command\\\":\\\"listPlugins\\\",\\\"data\\\":{\"\n\t\t\t\"\\\"plugins\\\":[\"\n\t\t\t\"{\\\"name\\\":\\\"plugin1\\\",\\\"control-endpoints\\\":[\\\"$CONTROL/plugin1\\\"]}\"\n\t\t\t\"]}}]}\";\n\texpect_request_response(&mosq, request, response);\n\n\tconst char *outputs[] = {\n\t\t\"Plugin:\",\n\t\t\"  plugin1\\n\",\n\t\t\"Control endpoints:\",\n\t\t\"  $CONTROL/plugin1\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellBrokerTest, ListPluginsInvalidResponse)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_broker(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|broker> \")))\n\t\t.WillOnce(t::Return(strdup(\"listPlugins\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"listPlugins\\\"}]}\";\n\tconst char response[] = \"{\\\"responses\\\":[{\\\"command\\\":\\\"listPlugins\\\",\\\"data\\\":{\"\n\t\t\t\"\\\"plugins\\\":[\"\n\t\t\t\"{\\\"control-endpoints\\\":[\\\"$CONTROL/plugin1\\\"]}\"\n\t\t\t\"]}}]}\";\n\texpect_request_response(&mosq, request, response);\n\n\tconst char *outputs[] = {\n\t\t\"Invalid response from broker.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n"
  },
  {
    "path": "test/apps/ctrl/ctrl_shell_completion_test.cpp",
    "content": "/*\nCopyright (c) 2022 Cedalo GmbH\n*/\n\n// clang-format off\n#include \"mosquitto_internal.h\" // keep this at the top for `#define uthash_free`\n#include \"ctrl_shell.h\"\n#include \"ctrl_shell_internal.h\"\n#include \"json_help.h\"\n#include \"utlist.h\"\n// clang-format on\n#include <gmock/gmock-actions.h>\n#include <gtest/gtest.h>\n\n#include <cstring>\n\n#include \"ctrl_shell_mock.hpp\"\n#include \"libmosquitto_mock.hpp\"\n#include \"pthread_mock.hpp\"\n\nextern \"C\" {\nchar **completion_matcher(const char *text, int start, int end);\nchar *completion_generator(const char *text, int state);\nvoid ctrl_shell__cleanup(void);\n}\n\nnamespace t = testing;\n\nclass CtrlShellCompletionTest : public ::t::Test\n{\npublic:\n\t::t::StrictMock<CtrlShellMock> ctrl_shell_mock_{};\n\t::t::StrictMock<LibMosquittoMock> libmosquitto_mock_{};\n\t::t::StrictMock<PThreadMock> pthread_mock_{};\n\n\n\tvoid expect_setup()\n\t{\n\t\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t\t.WillRepeatedly(t::Invoke([](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\t\tdata.response_received = true;\n\t\t\treturn 0;\n\t\t}));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe(t::_, t::_, t::_, 1))\n\t\t\t.WillRepeatedly(t::Return(0));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::_, nullptr, t::_, t::_, t::_, 1, false))\n\t\t\t.WillRepeatedly(t::Return(0));\n\n\t\trl_readline_name = \"mosquitto_ctrl\";\n\t\trl_completion_entry_function = completion_generator;\n\t\trl_attempted_completion_function = completion_matcher;\n\n\t\tctrl_shell__load_module(ctrl_shell__dynsec_init);\n\t}\n\n\n\tvoid expect_outputs(const char **outputs, size_t count)\n\t{\n\t\tfor(size_t i=0; i<count; i++){\n\t\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StrEq(outputs[i]))).Times(t::AtLeast(1));\n\t\t}\n\t}\n};\n\nTEST_F(CtrlShellCompletionTest, NoMatch)\n{\n\texpect_setup();\n\n\trl_line_buffer = strdup(\"q\");\n\tchar **matches = completion_matcher(\"q\", 0, 0);\n\tfree(rl_line_buffer);\n\tASSERT_EQ(matches, nullptr);\n}\n\nTEST_F(CtrlShellCompletionTest, MatchArg1)\n{\n\tint match_count;\n\n\texpect_setup();\n\n\trl_line_buffer = strdup(\"a\");\n\tchar **matches = completion_matcher(\"a\", 0, 0);\n\tfree(rl_line_buffer);\n\tASSERT_NE(matches, nullptr);\n\n\tEXPECT_STREQ(matches[0], \"add\");\n\tfor(match_count = 1; matches[match_count]; match_count++){\n\t\t;\n\t}\n\tASSERT_EQ(match_count, 5);\n\n\tchar *match_array[4] = {matches[1], matches[2], matches[3], matches[4]};\n\n\tEXPECT_THAT(match_array, t::UnorderedElementsAreArray({\n\t\tt::StrEq(\"addGroupRole\"),\n\t\tt::StrEq(\"addRoleACL\"),\n\t\tt::StrEq(\"addGroupClient\"),\n\t\tt::StrEq(\"addClientRole\")\n\t}));\n\tfor(int i=0; i<match_count; i++){\n\t\tfree(matches[i]);\n\t}\n\tfree(matches);\n\tctrl_shell__cleanup();\n}\n"
  },
  {
    "path": "test/apps/ctrl/ctrl_shell_dynsec_test.cpp",
    "content": "/*\nCopyright (c) 2022 Cedalo GmbH\n*/\n\n// clang-format off\n#include \"mosquitto_internal.h\" // keep this at the top for `#define uthash_free`\n#include \"ctrl_shell.h\"\n#include \"ctrl_shell_internal.h\"\n#include \"json_help.h\"\n#include \"utlist.h\"\n// clang-format on\n#include <gmock/gmock-actions.h>\n#include <gtest/gtest.h>\n\n#include <cstring>\n\n#include \"ctrl_shell_mock.hpp\"\n#include \"editline_mock.hpp\"\n#include \"libmosquitto_mock.hpp\"\n#include \"pthread_mock.hpp\"\n\nnamespace t = testing;\n\nstruct pending_payload {\n\tstruct pending_payload *next, *prev;\n\tchar payload[1024];\n};\n\nclass CtrlShellDynsecTest : public ::t::Test\n{\npublic:\n\t::t::StrictMock<CtrlShellMock> ctrl_shell_mock_{};\n\t::t::StrictMock<EditLineMock> editline_mock_{};\n\t::t::StrictMock<LibMosquittoMock> libmosquitto_mock_{};\n\t::t::StrictMock<PThreadMock> pthread_mock_{};\n\tLIBMOSQ_CB_connect on_connect{};\n\tLIBMOSQ_CB_message on_message{};\n\tstruct pending_payload *pending_payloads = nullptr;\n\n\n\tvoid expect_setup(struct mosq_config *config)\n\t{\n\t\teditline_mock_.reset();\n\t\tEXPECT_CALL(editline_mock_, rl_bind_key(t::Eq('\\t'), t::_));\n\t\tEXPECT_CALL(editline_mock_, add_history(t::_)).WillRepeatedly(t::Return(0));\n\t\tEXPECT_CALL(editline_mock_, clear_history()).Times(t::AnyNumber());\n\t\tconfig->no_colour = true;\n\n\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StartsWith(\"mosquitto_ctrl shell v\")));\n\t}\n\n\n\tvoid expect_connect(struct mosquitto *mosq, const char *host, int port)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_new(t::Eq(nullptr), t::Eq(true), t::Eq(nullptr)))\n\t\t\t.WillOnce(t::Return(mosq));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_int_option(t::Eq(mosq), MOSQ_OPT_PROTOCOL_VERSION, 5));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe_callback_set(t::Eq(mosq), t::_));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish_v5_callback_set(t::Eq(mosq), t::_));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect(t::Eq(mosq), t::StrEq(host), port, 60));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_start(t::Eq(mosq)));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_connect>()))\n\t\t\t.WillRepeatedly(t::SaveArg<1>(&this->on_connect));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_message_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_message>()))\n\t\t\t.WillOnce(t::SaveArg<1>(&this->on_message));\n\t}\n\n\n\tvoid expect_disconnect(struct mosquitto *mosq)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_disconnect(t::Eq(mosq)));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_stop(t::Eq(mosq), false));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_destroy(t::Eq(mosq)));\n\t}\n\n\n\tvoid expect_outputs(const char **outputs, size_t count)\n\t{\n\t\tfor(size_t i=0; i<count; i++){\n\t\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StrEq(outputs[i]))).Times(t::AtLeast(1));\n\t\t}\n\t}\n\n\n\tvoid expect_request_response(struct mosquitto *mosq, const char *request, const char *respons)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload), \"%s\", respons);\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\t\tt::StrEq(request), 1, false))\n\t\t\t.WillOnce(t::Invoke([this, pp](){\n\t\t\tDL_APPEND(this->pending_payloads, pp);\n\t\t\treturn 0;\n\t\t}));\n\t}\n\n\n\tvoid expect_request_response_success(struct mosquitto *mosq, const char *request, const char *command)\n\t{\n\t\tchar response[100];\n\t\tsnprintf(response, sizeof(response), \"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\"}]}\", command);\n\t\texpect_request_response(mosq, request, response);\n\t}\n\n\n\tvoid expect_request_response_empty(struct mosquitto *mosq, const char *command)\n\t{\n\t\tchar request[100];\n\t\tchar response[100];\n\n\t\tsnprintf(request, sizeof(request), \"{\\\"commands\\\":[{\\\"command\\\":\\\"%s\\\"}]}\", command);\n\t\tsnprintf(response, sizeof(response), \"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\",\\\"data\\\":{}}]}\", command);\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\t\tt::StrEq(request), 1, false))\n\t\t\t.WillOnce(t::Invoke([this, command](){\n\t\t\tappend_empty_response(command);\n\t\t\treturn 0;\n\t\t}));\n\t}\n\n\n\tvoid append_response(const char *response)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload), \"%s\", response);\n\t\tDL_APPEND(this->pending_payloads, pp);\n\t}\n\n\n\tvoid append_empty_response(const char *command)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload),\n\t\t\t\t\"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\",\\\"data\\\":{}}]}\", command);\n\t\tDL_APPEND(this->pending_payloads, pp);\n\t}\n\n\n\tvoid expect_single_lists(struct mosquitto *mosq)\n\t{\n\t\texpect_request_response_empty(mosq, \"listClients\");\n\t\texpect_request_response_empty(mosq, \"listGroups\");\n\t\texpect_request_response_empty(mosq, \"listRoles\");\n\t}\n\n\n\tvoid expect_dynsec(const char *host, int port)\n\t{\n\t\tchar buf[200];\n\t\tsnprintf(buf, sizeof(buf), \"connect mqtt://%s:%d\", host, port);\n\t\tchar *s_conn = strdup(buf);\n\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t\t.WillOnce(t::Return(s_conn));\n\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t\t.WillOnce(t::Return(strdup(\"dynsec\")));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe(t::_, nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1/response\"), 1))\n\t\t\t.WillOnce(t::Return(0));\n\t}\n\n\n\tvoid expect_connect_and_messages(struct mosquitto *mosq)\n\t{\n\t\t/* This is a hacky way of working around the async mqtt send/receive which we don't directly control.\n\t\t\t    * Each send starts a wait which times out after two seconds. We use that call to produce the effect we want.\n\t\t\t    */\n\t\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t\t.WillOnce(t::Invoke([this, mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\t\tthis->on_connect(mosq, nullptr, 0);\n\t\t\tdata.response_received = true;\n\t\t\treturn 0;\n\t\t}))\n\t\t\t.WillRepeatedly(t::Invoke([this, mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\t\tmosquitto_message msg{};\n\t\t\tstruct pending_payload *pp = this->pending_payloads;\n\t\t\tif(pp){\n\t\t\t\tDL_DELETE(pending_payloads, pp);\n\t\t\t\tmsg.payload = pp->payload;\n\t\t\t\tmsg.payloadlen = (int)strlen((char *)msg.payload);\n\t\t\t\tthis->on_message(mosq, nullptr, &msg);\n\t\t\t\tfree(pp);\n\t\t\t}\n\t\t\tdata.response_received = true;\n\t\t\treturn 0;\n\t\t}));\n\t}\n\n\n\tvoid expect_generic_arg1(const char *command, const char *itemlabel, const char *itemvalue)\n\t{\n\t\tmosq_config config{};\n\t\tmosquitto mosq{};\n\t\tconst char host[] = \"localhost\";\n\t\tint port = 1883;\n\t\tchar line[200];\n\t\tchar payload[500];\n\n\t\texpect_setup(&config);\n\t\texpect_connect(&mosq, host, port);\n\t\texpect_dynsec(host, port);\n\n\t\tsnprintf(line, sizeof(line), \"%s %s\", command, itemvalue);\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t\t.WillOnce(t::Return(strdup(line)))\n\t\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\t\texpect_connect_and_messages(&mosq);\n\t\texpect_single_lists(&mosq);\n\n\t\tsnprintf(payload, sizeof(payload),\n\t\t\t\t\"{\\\"commands\\\":[{\\\"command\\\":\\\"%s\\\",\\\"%s\\\":\\\"%s\\\"}]}\",\n\t\t\t\tcommand, itemlabel, itemvalue);\n\t\texpect_request_response_success(&mosq, payload, command);\n\n\t\tconst char *outputs[] = {\n\t\t\t\"OK\\n\",\n\t\t\t\"\\n\",\n\t\t};\n\t\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\t\tctrl_shell__main(&config);\n\t}\n\n\n\tvoid expect_generic_arg1_with_list_update(const char *command, const char *itemlabel, const char *itemvalue, const char *listcmd)\n\t{\n\t\tmosq_config config{};\n\t\tmosquitto mosq{};\n\t\tconst char host[] = \"localhost\";\n\t\tint port = 1883;\n\t\tchar line[200];\n\t\tchar request[500];\n\n\t\texpect_setup(&config);\n\t\texpect_connect(&mosq, host, port);\n\t\texpect_dynsec(host, port);\n\n\t\tsnprintf(line, sizeof(line), \"%s %s\", command, itemvalue);\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t\t.WillOnce(t::Return(strdup(line)))\n\t\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\t\texpect_connect_and_messages(&mosq);\n\t\texpect_single_lists(&mosq);\n\n\t\tsnprintf(request, sizeof(request),\n\t\t\t\t\"{\\\"commands\\\":[{\\\"command\\\":\\\"%s\\\",\\\"%s\\\":\\\"%s\\\"}]}\",\n\t\t\t\tcommand, itemlabel, itemvalue);\n\t\texpect_request_response_success(&mosq, request, command);\n\n\t\texpect_request_response_empty(&mosq, listcmd);\n\n\t\tconst char *outputs[] = {\n\t\t\t\"OK\\n\",\n\t\t\t\"\\n\",\n\t\t};\n\t\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\t\tctrl_shell__main(&config);\n\t}\n\n\n\tvoid expect_generic_arg1_with_error(const char *command, const char *itemlabel)\n\t{\n\t\tmosq_config config{};\n\t\tmosquitto mosq{};\n\t\tconst char host[] = \"localhost\";\n\t\tint port = 1883;\n\t\tchar line[200];\n\t\tchar error[200];\n\n\t\texpect_setup(&config);\n\t\texpect_connect(&mosq, host, port);\n\t\texpect_dynsec(host, port);\n\n\t\tsnprintf(line, sizeof(line), \"%s\", command);\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t\t.WillOnce(t::Return(strdup(line)))\n\t\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\t\texpect_connect_and_messages(&mosq);\n\t\texpect_single_lists(&mosq);\n\n\t\tsnprintf(error, sizeof(error), \"%s %s\\n\", command, itemlabel);\n\t\tconst char *outputs[] = {\n\t\t\terror,\n\t\t\t\"\\n\",\n\t\t};\n\t\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\t\tctrl_shell__main(&config);\n\t}\n\n\n\tvoid expect_generic_arg2(const char *command, const char *itemlabel1, const char *itemvalue1, const char *itemlabel2, const char *itemvalue2)\n\t{\n\t\tmosq_config config{};\n\t\tmosquitto mosq{};\n\t\tconst char host[] = \"localhost\";\n\t\tint port = 1883;\n\t\tchar line[200];\n\t\tchar payload[500];\n\n\t\texpect_setup(&config);\n\t\texpect_connect(&mosq, host, port);\n\t\texpect_dynsec(host, port);\n\n\t\tsnprintf(line, sizeof(line), \"%s %s %s\", command, itemvalue1, itemvalue2);\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t\t.WillOnce(t::Return(strdup(line)))\n\t\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\t\texpect_connect_and_messages(&mosq);\n\t\texpect_single_lists(&mosq);\n\n\t\tsnprintf(payload, sizeof(payload),\n\t\t\t\t\"{\\\"commands\\\":[{\\\"command\\\":\\\"%s\\\",\"\n\t\t\t\t\"\\\"%s\\\":\\\"%s\\\",\"\n\t\t\t\t\"\\\"%s\\\":\\\"%s\\\"\"\n\t\t\t\t\"}]}\",\n\t\t\t\tcommand, itemlabel1, itemvalue1, itemlabel2, itemvalue2);\n\t\texpect_request_response_success(&mosq, payload, command);\n\n\t\tconst char *outputs[] = {\n\t\t\t\"OK\\n\",\n\t\t\t\"\\n\",\n\t\t};\n\t\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\t\tctrl_shell__main(&config);\n\t}\n\n\n\tvoid expect_send_set_acl_default_access(const char *acltype)\n\t{\n\t\tmosq_config config{};\n\t\tmosquitto mosq{};\n\t\tconst char host[] = \"localhost\";\n\t\tint port = 1883;\n\t\tchar buf[500];\n\n\t\texpect_setup(&config);\n\t\texpect_connect(&mosq, host, port);\n\t\texpect_dynsec(host, port);\n\n\t\tsnprintf(buf, sizeof(buf), \"setDefaultACLAccess %s allow\", acltype);\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t\t.WillOnce(t::Return(strdup(buf)))\n\t\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\t\texpect_connect_and_messages(&mosq);\n\t\texpect_single_lists(&mosq);\n\n\t\tsnprintf(buf, sizeof(buf),\n\t\t\t\t\"{\\\"commands\\\":[{\\\"command\\\":\\\"setDefaultACLAccess\\\",\"\n\t\t\t\t\"\\\"acls\\\":[\"\n\t\t\t\t\"{\"\n\t\t\t\t\"\\\"acltype\\\":\\\"%s\\\",\"\n\t\t\t\t\"\\\"allow\\\":true}]}]}\",\n\t\t\t\tacltype);\n\t\texpect_request_response_success(&mosq, buf, \"setDefaultACLAccess\");\n\n\t\tconst char *outputs[] = {\n\t\t\t\"OK\\n\",\n\t\t\t\"\\n\",\n\t\t};\n\t\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\t\tctrl_shell__main(&config);\n\t}\n};\n\n\nTEST_F(CtrlShellDynsecTest, NoDynsec)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tthis->on_connect(&mosq, nullptr, 0);\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\t// Subscribe\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Return(ETIMEDOUT)); // First message to dynsec fails\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listClients\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Return(0));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(nullptr));\n\n\tconst char *outputs[] = {\n\t\t\"Timed out with no response.\\n\",\n\t\t\"Check the dynsec module is configured on the broker.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, CreateClient)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"createClient\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"createClient username [password [clientid]]\\n\",\n\t\t\"createClient username password [clientid]\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, CreateClientWithPassword)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"createClient username password\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listClients\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listClients\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listClients\");\n\t\treturn 0;\n\t}));\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\texpect_request_response_success(&mosq,\n\t\t\t\"{\\\"commands\\\":[{\\\"command\\\":\\\"createClient\\\",\\\"username\\\":\\\"username\\\",\\\"password\\\":\\\"password\\\"}]}\",\n\t\t\t\"createClient\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, CreateClientWithPasswordAndClientid)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"createClient username1 password1 clientid1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listClients\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listClients\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listClients\");\n\t\treturn 0;\n\t}));\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\texpect_request_response_success(&mosq,\n\t\t\t\"{\\\"commands\\\":[{\\\"command\\\":\\\"createClient\\\",\\\"username\\\":\\\"username1\\\",\\\"password\\\":\\\"password1\\\",\\\"clientid\\\":\\\"clientid1\\\"}]}\",\n\t\t\t\"createClient\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, CreateClientPasswordCliMatching)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"createClient username1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listClients\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listClients\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listClients\");\n\t\treturn 0;\n\t}));\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell_fgets(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"password1\");\n\t\treturn s;\n\t}))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"password1\");\n\t\treturn s;\n\t}));\n\n\texpect_request_response_success(&mosq,\n\t\t\t\"{\\\"commands\\\":[{\\\"command\\\":\\\"createClient\\\",\\\"username\\\":\\\"username1\\\",\\\"password\\\":\\\"password1\\\"}]}\",\n\t\t\t\"createClient\");\n\n\tconst char *outputs[] = {\n\t\t\"password:\",\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, CreateClientPasswordCliNotMatching)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"createClient username\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell_fgets(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"mypassword\");\n\t\treturn s;\n\t}))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"nomatch\");\n\t\treturn s;\n\t}));\n\n\tconst char *outputs[] = {\n\t\t\"password:\",\n\t\t\"Passwords do not match.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, CreateClientPasswordCliOneOnly)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"createClient username\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell_fgets(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"mypassword\");\n\t\treturn s;\n\t}))\n\t\t.WillOnce(t::Return(nullptr));\n\n\tconst char *outputs[] = {\n\t\t\"password:\",\n\t\t\"No password.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, EnableClient)\n{\n\texpect_generic_arg1(\"enableClient\", \"username\", \"username1\");\n}\n\nTEST_F(CtrlShellDynsecTest, EnableClientMissing)\n{\n\texpect_generic_arg1_with_error(\"enableClient\", \"username\");\n}\n\n/* FIXME - Not found cases */\n\nTEST_F(CtrlShellDynsecTest, DisableClient)\n{\n\texpect_generic_arg1(\"disableClient\", \"username\", \"username1\");\n}\n\nTEST_F(CtrlShellDynsecTest, DisableClientMissing)\n{\n\texpect_generic_arg1_with_error(\"disableClient\", \"username\");\n}\n\nTEST_F(CtrlShellDynsecTest, SetAnonGroup)\n{\n\texpect_generic_arg1(\"setAnonymousGroup\", \"groupname\", \"groupname1\");\n}\n\nTEST_F(CtrlShellDynsecTest, SetAnonGroupMissing)\n{\n\texpect_generic_arg1_with_error(\"setAnonymousGroup\", \"groupname\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddClientRole)\n{\n\texpect_generic_arg2(\"addClientRole\", \"username\", \"username1\", \"rolename\", \"rolename1\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddGroupClient)\n{\n\texpect_generic_arg2(\"addGroupClient\", \"groupname\", \"groupname1\", \"username\", \"username1\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddGroupRole)\n{\n\texpect_generic_arg2(\"addGroupRole\", \"groupname\", \"groupname1\", \"rolename\", \"rolename1\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveClientRole)\n{\n\texpect_generic_arg2(\"removeClientRole\", \"username\", \"username1\", \"rolename\", \"rolename1\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveGroupClient)\n{\n\texpect_generic_arg2(\"removeGroupClient\", \"groupname\", \"groupname1\", \"username\", \"username1\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveGroupRole)\n{\n\texpect_generic_arg2(\"removeGroupRole\", \"groupname\", \"groupname1\", \"rolename\", \"rolename1\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, SetClientId)\n{\n\texpect_generic_arg2(\"setClientId\", \"username\", \"username1\", \"clientid\", \"clientid1\");\n}\n\nTEST_F(CtrlShellDynsecTest, SetDefaultACLAccessPublishClientReceive)\n{\n\texpect_send_set_acl_default_access(\"publishClientReceive\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, SetDefaultACLAccessPublishClientSend)\n{\n\texpect_send_set_acl_default_access(\"publishClientSend\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, SetDefaultACLAccessSubscribe)\n{\n\texpect_send_set_acl_default_access(\"subscribe\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, SetDefaultACLAccessUnsubscribe)\n{\n\texpect_send_set_acl_default_access(\"unsubscribe\");\n}\n\n\nTEST_F(CtrlShellDynsecTest, SetDefaultACLAccessBadType)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"setDefaultACLAccess badtype allow\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"setDefaultACLAccess acltype allow|deny\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, SetDefaultACLAccessBadAllow)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"setDefaultACLAccess subscribe bad\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"setDefaultACLAccess acltype allow|deny\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, SetDefaultACLAccessNoAllow)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"setDefaultACLAccess publishClientSend\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"setDefaultACLAccess acltype allow|deny\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\nTEST_F(CtrlShellDynsecTest, GetDefaultACLAccess)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"getDefaultACLAccess\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"getDefaultACLAccess\\\"}]}\";\n\tconst char response[] =\n\t\t\t\"{\\\"responses\\\":[{\\\"command\\\":\\\"getDefaultACLAccess\\\",\\\"data\\\":{\"\n\t\t\t\"\\\"acls\\\":[\"\n\t\t\t\"{\\\"acltype\\\":\\\"publishClientSend\\\",\\\"allow\\\":false},\"\n\t\t\t\"{\\\"acltype\\\":\\\"publishClientReceive\\\",\\\"allow\\\":true},\"\n\t\t\t\"{\\\"acltype\\\":\\\"subscribe\\\",\\\"allow\\\":false},\"\n\t\t\t\"{\\\"acltype\\\":\\\"unsubscribe\\\",\\\"allow\\\":true}\"\n\t\t\t\"]}}]}\";\n\n\texpect_request_response(&mosq, request, response);\n\n\tconst char *outputs[] = {\n\t\t\"publishClientSend    deny\\n\",\n\t\t\"publishClientReceive allow\\n\",\n\t\t\"subscribe            deny\\n\",\n\t\t\"unsubscribe          allow\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, GetAnonymousGroup)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"getAnonymousGroup\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"getAnonymousGroup\\\"}]}\";\n\tconst char response[] =\n\t\t\t\"{\\\"responses\\\":[{\\\"command\\\":\\\"getAnonymousGroup\\\",\\\"data\\\":{\"\n\t\t\t\"\\\"group\\\":{\\\"groupname\\\":\\\"group1\\\"}\"\n\t\t\t\"}}]}\";\n\texpect_request_response(&mosq, request, response);\n\n\tconst char *outputs[] = {\n\t\t\"group1\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, GetClient)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"getClient username1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"getClient\\\",\\\"username\\\":\\\"username1\\\"}]}\";\n\tconst char response[] =\n\t\t\t\"{\\\"responses\\\":[{\\\"command\\\":\\\"getClient\\\",\\\"data\\\":{\"\n\t\t\t\"\\\"client\\\":{\"\n\t\t\t\"\\\"username\\\":\\\"username1\\\",\"\n\t\t\t\"\\\"clientid\\\":\\\"clientid1\\\",\"\n\t\t\t\"\\\"disabled\\\":true,\"\n\t\t\t\"\\\"textname\\\":\\\"textname1\\\",\"\n\t\t\t\"\\\"textdescription\\\":\\\"textdescription1\\\",\"\n\t\t\t\"\\\"roles\\\":[\"\n\t\t\t\"{\\\"rolename\\\":\\\"role1\\\",\\\"priority\\\":1}\"\n\t\t\t\"],\"\n\t\t\t\"\\\"groups\\\":[\"\n\t\t\t\"{\\\"groupname\\\":\\\"group1\\\",\\\"priority\\\":2}\"\n\t\t\t\"]}}}]}\";\n\texpect_request_response(&mosq, request, response);\n\n\tconst char *outputs[] = {\n\t\t\"Username:\",\n\t\t\"  username1\\n\",\n\t\t\"Clientid:\",\n\t\t\"  clientid1\\n\",\n\t\t\"Text name:\",\n\t\t\"  textname1\\n\",\n\t\t\"Text description:\",\n\t\t\"  textdescription1\\n\",\n\t\t\"Disabled:\",\n\t\t\"  true\\n\",\n\t\t\"Roles:\",\n\t\t\"  role1\",\n\t\t\"Groups:\",\n\t\t\"  group1\",\n\t\t\" (priority: \",\n\t\t\"1\",\n\t\t\"2\",\n\t\t\")\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, GetGroup)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"getGroup groupname1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"getGroup\\\",\\\"groupname\\\":\\\"groupname1\\\"}]}\";\n\tconst char response[] =\n\t\t\t\"{\\\"responses\\\":[{\\\"command\\\":\\\"getGroup\\\",\\\"data\\\":{\"\n\t\t\t\"\\\"group\\\":{\"\n\t\t\t\"\\\"groupname\\\":\\\"groupname1\\\",\"\n\t\t\t\"\\\"textname\\\":\\\"textname1\\\",\"\n\t\t\t\"\\\"textdescription\\\":\\\"textdescription1\\\",\"\n\t\t\t\"\\\"roles\\\":[\"\n\t\t\t\"{\\\"rolename\\\":\\\"role1\\\",\\\"priority\\\":1}\"\n\t\t\t\"],\"\n\t\t\t\"\\\"clients\\\":[\"\n\t\t\t\"{\\\"username\\\":\\\"username1\\\",\\\"priority\\\":2}\"\n\t\t\t\"]}}}]}\";\n\texpect_request_response(&mosq, request, response);\n\n\tconst char *outputs[] = {\n\t\t\"Group name:\",\n\t\t\"  groupname1\\n\",\n\t\t\"Text name:\",\n\t\t\"  textname1\\n\",\n\t\t\"Text description:\",\n\t\t\"  textdescription1\\n\",\n\t\t\"Roles:\",\n\t\t\"  role1\",\n\t\t\"Clients:\",\n\t\t\"  username1\",\n\t\t\" (priority: \",\n\t\t\"1\",\n\t\t\")\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellDynsecTest, GetRole)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"getRole rolename1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"getRole\\\",\\\"rolename\\\":\\\"rolename1\\\"}]}\";\n\tconst char response[] =\n\t\t\t\"{\\\"responses\\\":[{\\\"command\\\":\\\"getRole\\\",\\\"data\\\":{\"\n\t\t\t\"\\\"role\\\":{\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"textname\\\":\\\"textname1\\\",\"\n\t\t\t\"\\\"textdescription\\\":\\\"textdescription1\\\",\"\n\t\t\t\"\\\"allowwildcardsubs\\\":true,\"\n\t\t\t\"\\\"acls\\\":[\"\n\t\t\t\"{\\\"acltype\\\":\\\"subscribeLiteral\\\",\\\"topic\\\":\\\"topic1\\\",\\\"allow\\\":true,\\\"priority\\\":1}\"\n\t\t\t\"]}}}]}\";\n\texpect_request_response(&mosq, request, response);\n\n\tconst char *outputs[] = {\n\t\t\"Role name:\",\n\t\t\"  rolename1\\n\",\n\t\t\"Text name:\",\n\t\t\"  textname1\\n\",\n\t\t\"Text description:\",\n\t\t\"  textdescription1\\n\",\n\t\t\"Allow wildcard subscriptions:\",\n\t\t\"  true\\n\",\n\t\t\"ACLs:\",\n\t\t\"  subscribeLiteral     allow topic1 (priority 1)\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, SetClientPasswordCliMatching)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"setClientPassword username1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell_fgets(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"password1\");\n\t\treturn s;\n\t}))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"password1\");\n\t\treturn s;\n\t}));\n\n\texpect_request_response_success(&mosq,\n\t\t\t\"{\\\"commands\\\":[{\\\"command\\\":\\\"setClientPassword\\\",\\\"username\\\":\\\"username1\\\",\\\"password\\\":\\\"password1\\\"}]}\",\n\t\t\t\"setClientPassword\");\n\n\tconst char *outputs[] = {\n\t\t\"password:\",\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, SetClientPasswordCliNotMatching)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"setClientPassword username1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell_fgets(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"password1\");\n\t\treturn s;\n\t}))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"password2\");\n\t\treturn s;\n\t}));\n\n\tconst char *outputs[] = {\n\t\t\"password:\",\n\t\t\"Passwords do not match.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\nTEST_F(CtrlShellDynsecTest, SetClientPasswordCliNoPassword)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"setClientPassword username1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell_fgets(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([](char *, int, FILE *){\n\t\treturn nullptr;\n\t}));\n\n\tconst char *outputs[] = {\n\t\t\"password:\",\n\t\t\"No password.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\nTEST_F(CtrlShellDynsecTest, SetClientPasswordCliNoUsername)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"setClientPassword\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"setClientPassword <username> [password]\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddRoleACLPublishClientReceive)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"addRoleACL rolename1 publishClientReceive allow topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"addRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"publishClientReceive\\\",\"\n\t\t\t\"\\\"priority\\\":-1,\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\",\"\n\t\t\t\"\\\"allow\\\":true\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"addRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddRoleACLPublishClientSend)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"addRoleACL rolename1 publishClientSend allow topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"addRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"publishClientSend\\\",\"\n\t\t\t\"\\\"priority\\\":-1,\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\",\"\n\t\t\t\"\\\"allow\\\":true\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"addRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddRoleACLPublishClientSendWithPriority)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"addRoleACL rolename1 publishClientSend allow 42 topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"addRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"publishClientSend\\\",\"\n\t\t\t\"\\\"priority\\\":42,\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\",\"\n\t\t\t\"\\\"allow\\\":true\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"addRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddRoleACLSubscribeLiteral)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"addRoleACL rolename1 subscribeLiteral allow topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"addRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"subscribeLiteral\\\",\"\n\t\t\t\"\\\"priority\\\":-1,\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\",\"\n\t\t\t\"\\\"allow\\\":true\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"addRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddRoleACLSubscribePattern)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"addRoleACL rolename1 subscribePattern allow topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"addRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"subscribePattern\\\",\"\n\t\t\t\"\\\"priority\\\":-1,\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\",\"\n\t\t\t\"\\\"allow\\\":true\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"addRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\n\n\nTEST_F(CtrlShellDynsecTest, AddRoleACLUnsubscribeLiteral)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"addRoleACL rolename1 unsubscribeLiteral allow topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"addRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"unsubscribeLiteral\\\",\"\n\t\t\t\"\\\"priority\\\":-1,\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\",\"\n\t\t\t\"\\\"allow\\\":true\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"addRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddRoleACLUnsubscribePattern)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"addRoleACL rolename1 unsubscribePattern deny topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"addRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"unsubscribePattern\\\",\"\n\t\t\t\"\\\"priority\\\":-1,\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\",\"\n\t\t\t\"\\\"allow\\\":false\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"addRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddRoleACLBadAllow)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"addRoleACL rolename1 unsubscribePattern bad topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"Invalid allow/deny 'bad'\\n\",\n\t\t\"addRoleACL rolename acltype allow|deny [priority] topic\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddRoleACLBadACLType)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"addRoleACL rolename1 bad allow topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"Invalid acltype 'bad'\\n\",\n\t\t\"addRoleACL rolename acltype allow|deny [priority] topic\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, AddRoleACLNoTopic)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"addRoleACL rolename1 subscribeLiteral allow\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"addRoleACL rolename acltype allow|deny [priority] topic\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveRoleACLPublishClientReceive)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"removeRoleACL rolename1 publishClientReceive topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"removeRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"publishClientReceive\\\",\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"removeRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveRoleACLPublishClientSend)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"removeRoleACL rolename1 publishClientSend topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"removeRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"publishClientSend\\\",\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"removeRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveRoleACLUnsubscribeLiteral)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"removeRoleACL rolename1 unsubscribeLiteral topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"removeRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"unsubscribeLiteral\\\",\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"removeRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveRoleACLUnsubscribePattern)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"removeRoleACL rolename1 unsubscribePattern topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"removeRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"unsubscribePattern\\\",\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"removeRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveRoleACLSubscribeLiteral)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"removeRoleACL rolename1 subscribeLiteral topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"removeRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"subscribeLiteral\\\",\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"removeRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveRoleACLSubscribePattern)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"removeRoleACL rolename1 subscribePattern topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"removeRoleACL\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"rolename1\\\",\"\n\t\t\t\"\\\"acltype\\\":\\\"subscribePattern\\\",\"\n\t\t\t\"\\\"topic\\\":\\\"topic1\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"removeRoleACL\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveRoleACLBadACLType)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"removeRoleACL rolename1 bad topic1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"Invalid acltype 'bad'\\n\",\n\t\t\"removeRoleACL rolename acltype topic\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, RemoveRoleACLNoTopic)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"removeRoleACL rolename1 subscribeLiteral \")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"removeRoleACL rolename acltype topic\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\nTEST_F(CtrlShellDynsecTest, CreateGroup)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listGroups\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listGroups\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listGroups\");\n\t\treturn 0;\n\t}));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"createGroup group1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"createGroup\\\",\\\"groupname\\\":\\\"group1\\\"}]}\";\n\texpect_request_response_success(&mosq, request, \"createGroup\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\nTEST_F(CtrlShellDynsecTest, CreateRole)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listRoles\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listRoles\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listRoles\");\n\t\treturn 0;\n\t}));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"createRole role1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"createRole\\\",\\\"rolename\\\":\\\"role1\\\"}]}\";\n\texpect_request_response_success(&mosq, request, \"createRole\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\nTEST_F(CtrlShellDynsecTest, DeleteClient)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listClients\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listClients\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listClients\");\n\t\treturn 0;\n\t}));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"deleteClient user1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"deleteClient\\\",\\\"username\\\":\\\"user1\\\"}]}\";\n\texpect_request_response_success(&mosq, request, \"deleteClient\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, DeleteGroup)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listGroups\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listGroups\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listGroups\");\n\t\treturn 0;\n\t}));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"deleteGroup group1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"deleteGroup\\\",\\\"groupname\\\":\\\"group1\\\"}]}\";\n\texpect_request_response_success(&mosq, request, \"deleteGroup\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\nTEST_F(CtrlShellDynsecTest, DeleteRole)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listRoles\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listRoles\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_empty_response(\"listRoles\");\n\t\treturn 0;\n\t}));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"deleteRole role1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\\\"command\\\":\\\"deleteRole\\\",\\\"rolename\\\":\\\"role1\\\"}]}\";\n\texpect_request_response_success(&mosq, request, \"deleteRole\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ModifyClientTextName)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"modifyClient user1 textName new name\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"modifyClient\\\",\"\n\t\t\t\"\\\"username\\\":\\\"user1\\\",\"\n\t\t\t\"\\\"textName\\\":\\\"new name\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"modifyClient\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ModifyClientTextDescription)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"modifyClient user1 textDescription new description\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"modifyClient\\\",\"\n\t\t\t\"\\\"username\\\":\\\"user1\\\",\"\n\t\t\t\"\\\"textDescription\\\":\\\"new description\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"modifyClient\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ModifyGroupTextName)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"modifyGroup group1 textName new name\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"modifyGroup\\\",\"\n\t\t\t\"\\\"groupname\\\":\\\"group1\\\",\"\n\t\t\t\"\\\"textName\\\":\\\"new name\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"modifyGroup\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ModifyGroupTextDescription)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"modifyGroup group1 textDescription new description\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"modifyGroup\\\",\"\n\t\t\t\"\\\"groupname\\\":\\\"group1\\\",\"\n\t\t\t\"\\\"textDescription\\\":\\\"new description\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"modifyGroup\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ModifyRoleTextName)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"modifyRole role1 textName new name\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"modifyRole\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"role1\\\",\"\n\t\t\t\"\\\"textName\\\":\\\"new name\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"modifyRole\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ModifyRoleTextDescription)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"modifyRole role1 textDescription new description\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"modifyRole\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"role1\\\",\"\n\t\t\t\"\\\"textDescription\\\":\\\"new description\\\"\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"modifyRole\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ModifyRoleWildcardSubs)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"modifyRole role1 allowWildcardSubs true\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char request[] = \"{\\\"commands\\\":[{\"\n\t\t\t\"\\\"command\\\":\\\"modifyRole\\\",\"\n\t\t\t\"\\\"rolename\\\":\\\"role1\\\",\"\n\t\t\t\"\\\"allowWildcardSubs\\\":true\"\n\t\t\t\"}]}\";\n\texpect_request_response_success(&mosq, request, \"modifyRole\");\n\n\tconst char *outputs[] = {\n\t\t\"OK\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ModifyRoleAllowWildcardSubsBadValue)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"modifyRole role1 allowWildcardSubs bad\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"modifyRole rolename <property> <value>\\n\",\n\t\t\"Invalid value 'bad'\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ModifyRoleBadProp)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"modifyRole role1 badprop new description\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"modifyRole rolename <property> <value>\\n\",\n\t\t\"Unknown property 'badprop'\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ModifyRoleNoProp)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"modifyRole role1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_single_lists(&mosq);\n\n\tconst char *outputs[] = {\n\t\t\"modifyRole rolename <property> <value>\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ListClients)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"listClients\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listClients\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listClients\\\",\\\"data\\\":{\"\n\t\t\"\\\"clients\\\":[\\\"client1\\\",\\\"client2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listClients\\\",\\\"data\\\":{\"\n\t\t\"\\\"clients\\\":[\\\"client1\\\",\\\"client2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}));\n\n\n\tconst char *outputs[] = {\n\t\t\"client1\\n\",\n\t\t\"client2\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ListClientsWithCount)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"listClients 2\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listClients\\\",\\\"count\\\":2}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listClients\\\",\\\"data\\\":{\"\n\t\t\"\\\"clients\\\":[\\\"client1\\\",\\\"client2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}));\n\n\n\tconst char *outputs[] = {\n\t\t\"client1\\n\",\n\t\t\"client2\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ListClientsWithCountAndOffset)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"listClients 2 3\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listClients\\\",\\\"count\\\":2,\\\"offset\\\":3}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listClients\\\",\\\"data\\\":{\"\n\t\t\"\\\"clients\\\":[\\\"client1\\\",\\\"client2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}));\n\n\n\tconst char *outputs[] = {\n\t\t\"client1\\n\",\n\t\t\"client2\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ListGroups)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"listGroups\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listGroups\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listGroups\\\",\\\"data\\\":{\"\n\t\t\"\\\"groups\\\":[\\\"group1\\\",\\\"group2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listGroups\\\",\\\"data\\\":{\"\n\t\t\"\\\"groups\\\":[\\\"group1\\\",\\\"group2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}));\n\n\n\tconst char *outputs[] = {\n\t\t\"group1\\n\",\n\t\t\"group2\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ListGroupsWithCount)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"listGroups 2\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listGroups\\\",\\\"count\\\":2}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listGroups\\\",\\\"data\\\":{\"\n\t\t\"\\\"groups\\\":[\\\"group1\\\",\\\"group2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}));\n\n\n\tconst char *outputs[] = {\n\t\t\"group1\\n\",\n\t\t\"group2\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ListGroupsWithCountAndOffset)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"listGroups 2 3\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listGroups\\\",\\\"count\\\":2,\\\"offset\\\":3}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listGroups\\\",\\\"data\\\":{\"\n\t\t\"\\\"groups\\\":[\\\"group1\\\",\\\"group2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}));\n\n\n\tconst char *outputs[] = {\n\t\t\"group1\\n\",\n\t\t\"group2\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ListRoles)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"listRoles\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listRoles\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listRoles\\\",\\\"data\\\":{\"\n\t\t\"\\\"roles\\\":[\\\"role1\\\",\\\"role2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listRoles\\\",\\\"data\\\":{\"\n\t\t\"\\\"roles\\\":[\\\"role1\\\",\\\"role2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}));\n\n\n\tconst char *outputs[] = {\n\t\t\"role1\\n\",\n\t\t\"role2\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ListRolesWithCount)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"listRoles 2\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listRoles\\\",\\\"count\\\":2}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listRoles\\\",\\\"data\\\":{\"\n\t\t\"\\\"roles\\\":[\\\"role1\\\",\\\"role2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}));\n\n\n\tconst char *outputs[] = {\n\t\t\"role1\\n\",\n\t\t\"role2\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, ListRolesWithCountAndOffset)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"listRoles 2 3\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listRoles\\\",\\\"count\\\":2,\\\"offset\\\":3}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"listRoles\\\",\\\"data\\\":{\"\n\t\t\"\\\"roles\\\":[\\\"role1\\\",\\\"role2\\\"]\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}));\n\n\n\tconst char *outputs[] = {\n\t\t\"role1\\n\",\n\t\t\"role2\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n\n\nTEST_F(CtrlShellDynsecTest, GetDetails)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_dynsec(host, port);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"getDetails\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\texpect_connect_and_messages(&mosq);\n\texpect_request_response_empty(&mosq, \"listClients\");\n\texpect_request_response_empty(&mosq, \"listGroups\");\n\texpect_request_response_empty(&mosq, \"listRoles\");\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"getDetails\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Invoke([this](){\n\t\tappend_response(\"{\\\"responses\\\":[{\\\"command\\\":\\\"getDetails\\\",\\\"data\\\":{\"\n\t\t\"\\\"clientCount\\\":1,\"\n\t\t\"\\\"groupCount\\\":2,\"\n\t\t\"\\\"roleCount\\\":3,\"\n\t\t\"\\\"changeIndex\\\":4\"\n\t\t\"}}]}\");\n\t\treturn 0;\n\t}));\n\n\n\tconst char *outputs[] = {\n\t\t\"Client count:\",\n\t\t\"Group count:\",\n\t\t\"Role count:\",\n\t\t\"Change index:\",\n\t\t\"   1\\n\",\n\t\t\"    2\\n\",\n\t\t\"     3\\n\",\n\t\t\"   4\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tEXPECT_EQ(pending_payloads, nullptr);\n}\n"
  },
  {
    "path": "test/apps/ctrl/ctrl_shell_help_test.cpp",
    "content": "/*\nCopyright (c) 2022 Cedalo GmbH\n*/\n\n// clang-format off\n#include \"mosquitto_internal.h\" // keep this at the top for `#define uthash_free`\n#include \"ctrl_shell.h\"\n#include \"ctrl_shell_internal.h\"\n#include \"json_help.h\"\n// clang-format on\n#include <gmock/gmock-actions.h>\n#include <gtest/gtest.h>\n\n#include <cstring>\n\n#include \"ctrl_shell_mock.hpp\"\n#include \"editline_mock.hpp\"\n#include \"libmosquitto_mock.hpp\"\n#include \"pthread_mock.hpp\"\n\nnamespace t = testing;\n\nclass CtrlShellHelpTest : public ::t::Test\n{\npublic:\n\t::t::StrictMock<CtrlShellMock> ctrl_shell_mock_{};\n\t::t::StrictMock<EditLineMock> editline_mock_{};\n\t::t::StrictMock<LibMosquittoMock> libmosquitto_mock_{};\n\t::t::StrictMock<PThreadMock> pthread_mock_{};\n\tLIBMOSQ_CB_connect on_connect{};\n\tLIBMOSQ_CB_message on_message{};\n\n\n\tvoid expect_setup(struct mosq_config *config)\n\t{\n\t\teditline_mock_.reset();\n\t\tEXPECT_CALL(editline_mock_, rl_bind_key(t::Eq('\\t'), t::_));\n\t\tEXPECT_CALL(editline_mock_, add_history(t::_)).WillRepeatedly(t::Return(0));\n\t\tEXPECT_CALL(editline_mock_, clear_history()).Times(t::AnyNumber());\n\t\tconfig->no_colour = true;\n\n\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StartsWith(\"mosquitto_ctrl shell v\")));\n\t}\n\n\n\tvoid expect_connect(struct mosquitto *mosq, const char *host, int port)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_new(t::Eq(nullptr), t::Eq(true), t::Eq(nullptr)))\n\t\t\t.WillOnce(t::Return(mosq));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_int_option(t::Eq(mosq), MOSQ_OPT_PROTOCOL_VERSION, 5));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe_callback_set(t::Eq(mosq), t::_));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish_v5_callback_set(t::Eq(mosq), t::_));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect(t::Eq(mosq), t::StrEq(host), port, 60));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_start(t::Eq(mosq)));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_connect>()))\n\t\t\t.WillRepeatedly(t::SaveArg<1>(&this->on_connect));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_message_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_message>()))\n\t\t\t.WillOnce(t::SaveArg<1>(&this->on_message));\n\t}\n\n\n\tvoid expect_disconnect(struct mosquitto *mosq)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_disconnect(t::Eq(mosq)));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_stop(t::Eq(mosq), false));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_destroy(t::Eq(mosq)));\n\t}\n\n\n\tvoid expect_outputs(const char **outputs, size_t count)\n\t{\n\t\tfor(size_t i=0; i<count; i++){\n\t\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StrEq(outputs[i]))).Times(t::AtLeast(1));\n\t\t}\n\t}\n};\n\nTEST_F(CtrlShellHelpTest, PreConnectHelp)\n{\n\tmosq_config config{};\n\n\texpect_setup(&config);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(strdup(\"help\")))\n\t\t.WillOnce(t::Return(strdup(\"help auth\")))\n\t\t.WillOnce(t::Return(strdup(\"help connect\")))\n\t\t.WillOnce(t::Return(strdup(\"help exit\")))\n\t\t.WillOnce(t::Return(strdup(\"help help\")))\n\t\t.WillOnce(t::Return(strdup(\"help unknown\")))\n\t\t.WillOnce(t::Return(strdup(\"unknown\")))\n\t\t.WillOnce(t::Return(nullptr));\n\n\tconst char *outputs[] = {\n\t\t\"This is the mosquitto_ctrl interactive shell, for controlling aspects of a mosquitto broker.\\n\",\n\t\t\"Find help on a command using 'help <command>'\\n\",\n\t\t\"Press tab multiple times to find currently available commands.\\n\\n\",\n\t\t\"\\n\",\n\t\t\"Example workflow:\\n\\n\",\n\t\t\"> auth\\n\",\n\t\t\"username: admin\\n\",\n\t\t\"password:\\n\",\n\t\t\"> connect mqtt://localhost\\n\",\n\t\t\"mqtt://localhost:1883> dynsec\\n\",\n\t\t\"mqtt://localhost:1883|dynsec> createGroup newgroup\\n\",\n\t\t\"OK\\n\\n\",\n\n\t\t\"auth [username]\\n\",\n\t\t\"\\nSet a username and password prior to connecting to a broker.\\n\",     /* help auth */\n\t\t\"connect\\n\",\n\t\t\"connect mqtt://hostname[:port]\\n\",\n\t\t\"connect mqtts://hostname[:port]\\n\",\n\t\t\"connect ws://hostname[:port]\\n\",\n\t\t\"connect wss://hostname[:port]\\n\",\n\t\t\"\\nConnect to a broker using the provided transport and port.\\n\",\n\t\t\"If no URL is provided, connects to mqtt://localhost:1883\\n\",     /* help connect */\n\t\t\"exit\\n\",\n\t\t\"\\nQuit the program\\n\",     /* help exit */\n\t\t\"help <command>\\n\",\n\t\t\"\\nFind help on a command using 'help <command>'\\n\",\n\t\t\"Press tab multiple times to find currently available commands.\\n\",     /* help help */\n\t\t\"Unknown command 'unknown'\\n\", /* help unknown */\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\nTEST_F(CtrlShellHelpTest, Exit)\n{\n\tmosq_config config{};\n\n\texpect_setup(&config);\n\n\tchar *s = strdup(\"exit\");\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s));\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StrEq(\"\\n\")));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellHelpTest, Connect)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\tchar buf[200];\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\n\tsnprintf(buf, sizeof(buf), \"connect mqtt://%s:%d\", host, port);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t.WillOnce(t::Return(nullptr));\n\n\t/* This is a hacky way of working around the async mqtt CONNECT/CONNACK which we don't directly control. */\n\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tthis->on_connect(&mosq, nullptr, 0);\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}));\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StrEq(\"\\n\")));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellHelpTest, PostConnectHelp)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\tchar buf[200];\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\n\tsnprintf(buf, sizeof(buf), \"connect mqtt://%s:%d\", host, port);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t.WillOnce(t::Return(strdup(\"help\")))\n\t\t.WillOnce(t::Return(strdup(\"help dynsec\")))\n\t\t.WillOnce(t::Return(strdup(\"help broker\")))\n\t\t.WillOnce(t::Return(strdup(\"help disconnect\")))\n\t\t.WillOnce(t::Return(strdup(\"help exit\")))\n\t\t.WillOnce(t::Return(strdup(\"help help\")))\n\t\t.WillOnce(t::Return(strdup(\"help unknown\")))\n\t\t.WillOnce(t::Return(strdup(\"unknown\")))\n\t\t.WillOnce(t::Return(strdup(\"disconnect\")));\n\n\t/* This is a hacky way of working around the async mqtt send/receive which we don't directly control.\n\t * Each send starts a wait which times out after two seconds. We use that call to produce the effect we want.\n\t */\n\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tthis->on_connect(&mosq, nullptr, 0);\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}));\n\n\tconst char *outputs[] = {\n\t\t\"This is the mosquitto_ctrl interactive shell, for controlling aspects of a mosquitto broker.\\n\",\n\t\t\"Find help on a command using 'help <command>'\\n\",\n\t\t\"Press tab multiple times to find currently available commands.\\n\\n\",\n\t\t\"Example workflow:\\n\\n\",\n\t\t\"> auth\\n\",\n\t\t\"username: admin\\n\",\n\t\t\"password:\\n\",\n\t\t\"> connect mqtt://localhost\\n\",\n\t\t\"mqtt://localhost:1883> dynsec\\n\",\n\t\t\"mqtt://localhost:1883|dynsec> createGroup newgroup\\n\",\n\t\t\"OK\\n\\n\",\n\t\t\"\\n\",\n\n\t\t\"dynsec\\n\",\n\t\t\"\\nStart the dynamic-security control mode.\\n\",     /* help dynsec */\n\t\t\"broker\\n\",\n\t\t\"\\nStart the broker control mode.\\n\",     /* help broker */\n\t\t\"disconnect\\n\",\n\t\t\"\\nDisconnect from the broker\\n\",     /* help disconnect */\n\t\t\"exit\\n\",\n\t\t\"\\nQuit the program\\n\",     /* help exit */\n\t\t\"help <command>\\n\",\n\t\t\"\\nFind help on a command using 'help <command>'\\n\",\n\t\t\"Press tab multiple times to find currently available commands.\\n\",     /* help help */\n\t\t\"Unknown command 'unknown'\\n\", /* help unknown */\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\texpect_disconnect(&mosq);\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellHelpTest, BrokerHelp)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\tchar buf[200];\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\n\tsnprintf(buf, sizeof(buf), \"connect mqtt://%s:%d\", host, port);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t.WillOnce(t::Return(strdup(\"broker\")))\n\t\t.WillOnce(t::Return(strdup(\"disconnect\")));\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|broker> \")))\n\t\t.WillOnce(t::Return(strdup(\"help \"))) // Extra space on end to invoke trim\n\t\t.WillOnce(t::Return(strdup(\"help listPlugins\")))\n\t\t.WillOnce(t::Return(strdup(\"help listListeners\")))\n\t\t.WillOnce(t::Return(strdup(\"help disconnect\")))\n\t\t.WillOnce(t::Return(strdup(\"help help\")))\n\t\t.WillOnce(t::Return(strdup(\"help return\")))\n\t\t.WillOnce(t::Return(strdup(\"help exit\")))\n\t\t.WillOnce(t::Return(strdup(\"help unknown\")))\n\t\t.WillOnce(t::Return(strdup(\"unknown\")))\n\t\t.WillOnce(t::Return(strdup(\"return\")));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe(t::_, nullptr, t::StrEq(\"$CONTROL/broker/v1/response\"), 1))\n\t\t.WillOnce(t::Return(0));\n\n\t/* This is a hacky way of working around the async mqtt send/receive which we don't directly control.\n\t * Each send starts a wait which times out after two seconds. We use that call to produce the effect we want.\n\t */\n\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tthis->on_connect(&mosq, nullptr, 0);\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tmosquitto_message msg{};\n\t\tthis->on_message(&mosq, nullptr, &msg);\n\t\tdata.response_received = true;\n\t\treturn 0;\n\t}));\n\n\tconst char *outputs[] = {\n\t\t\"This is the mosquitto_ctrl interactive shell, for controlling aspects of a mosquitto broker.\\n\",\n\t\t\"You are in broker mode, for controlling some core broker functionality.\\n\",\n\t\t\"Use 'return' to leave this mode.\\n\",\n\t\t\"Find help on a command using 'help <command>'\\n\",\n\t\t\"Press tab multiple times to find currently available commands.\\n\\n\",\n\t\t\"\\n\",\n\t\t\"Unknown command 'unknown'\\n\",\n\n\t\t\"listPlugins\\n\",\n\t\t\"\\nLists currently loaded plugins.\\n\",\n\t\t\"listListeners\\n\",\n\t\t\"\\nLists current listeners.\\n\",\n\t\t\"disconnect\\n\",\n\t\t\"\\nDisconnect from the broker\\n\",     /* help disconnect */\n\t\t\"return\\n\",\n\t\t\"\\nLeave broker mode.\\n\",\n\t\t\"exit\\n\",\n\t\t\"\\nQuit the program\\n\",     /* help exit */\n\t\t\"help <command>\\n\",\n\t\t\"\\nFind help on a command using 'help <command>'\\n\",\n\t\t\"Press tab multiple times to find currently available commands.\\n\",     /* help help */\n\t\t\"Invalid response from broker.\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\texpect_disconnect(&mosq);\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellHelpTest, DynsecHelp)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\tchar buf[200];\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\n\tsnprintf(buf, sizeof(buf), \"connect mqtt://%s:%d\", host, port);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t.WillOnce(t::Return(strdup(\"dynsec\")))\n\t\t.WillOnce(t::Return(nullptr));\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883|dynsec> \")))\n\t\t.WillOnce(t::Return(strdup(\"help\")))\n\t\t.WillOnce(t::Return(strdup(\"help addClientRole\")))\n\t\t.WillOnce(t::Return(strdup(\"help addGroupClient\")))\n\t\t.WillOnce(t::Return(strdup(\"help addGroupRole\")))\n\t\t.WillOnce(t::Return(strdup(\"help addRoleACL\")))\n\t\t.WillOnce(t::Return(strdup(\"help createClient\")))\n\t\t.WillOnce(t::Return(strdup(\"help createGroup\")))\n\t\t.WillOnce(t::Return(strdup(\"help createRole\")))\n\t\t.WillOnce(t::Return(strdup(\"help deleteClient\")))\n\t\t.WillOnce(t::Return(strdup(\"help deleteGroup\")))\n\t\t.WillOnce(t::Return(strdup(\"help deleteRole\")))\n\t\t.WillOnce(t::Return(strdup(\"help disableClient\")))\n\t\t.WillOnce(t::Return(strdup(\"help enableClient\")))\n\t\t.WillOnce(t::Return(strdup(\"help getAnonymousGroup\")))\n\t\t.WillOnce(t::Return(strdup(\"help getClient\")))\n\t\t.WillOnce(t::Return(strdup(\"help getDetails\")))\n\t\t.WillOnce(t::Return(strdup(\"help getDefaultACLAccess\")))\n\t\t.WillOnce(t::Return(strdup(\"help getGroup\")))\n\t\t.WillOnce(t::Return(strdup(\"help getRole\")))\n\t\t.WillOnce(t::Return(strdup(\"help listClients\")))\n\t\t.WillOnce(t::Return(strdup(\"help listGroups\")))\n\t\t.WillOnce(t::Return(strdup(\"help listRoles\")))\n\t\t.WillOnce(t::Return(strdup(\"help removeClientRole\")))\n\t\t.WillOnce(t::Return(strdup(\"help removeGroupClient\")))\n\t\t.WillOnce(t::Return(strdup(\"help removeGroupRole\")))\n\t\t.WillOnce(t::Return(strdup(\"help removeRoleACL\")))\n\t\t.WillOnce(t::Return(strdup(\"help setAnonymousGroup\")))\n\t\t.WillOnce(t::Return(strdup(\"help setClientId\")))\n\t\t.WillOnce(t::Return(strdup(\"help setClientPassword\")))\n\t\t.WillOnce(t::Return(strdup(\"help setDefaultACLAccess\")))\n\t\t.WillOnce(t::Return(strdup(\"help modifyClient\")))\n\t\t.WillOnce(t::Return(strdup(\"help modifyGroup\")))\n\t\t.WillOnce(t::Return(strdup(\"help modifyRole\")))\n\t\t.WillOnce(t::Return(strdup(\"help disconnect\")))\n\t\t.WillOnce(t::Return(strdup(\"help help\")))\n\t\t.WillOnce(t::Return(strdup(\"help return\")))\n\t\t.WillOnce(t::Return(strdup(\"help exit\")))\n\t\t.WillOnce(t::Return(strdup(\"help unknown\")))\n\t\t.WillOnce(t::Return(strdup(\"unknown\")))\n\t\t.WillOnce(t::Return(strdup(\"return\")));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe(t::_, nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1/response\"), 1))\n\t\t.WillOnce(t::Return(0));\n\n\t/* This is a hacky way of working around the async mqtt send/receive which we don't directly control.\n\t * Each send starts a wait which times out after two seconds. We use that call to produce the effect we want.\n\t */\n\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tthis->on_connect(&mosq, nullptr, 0); data.response_received = true; return 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tmosquitto_message msg{};\n\t\tthis->on_message(&mosq, nullptr, &msg); data.response_received = true; return 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tmosquitto_message msg{};\n\t\tthis->on_message(&mosq, nullptr, &msg); data.response_received = true; return 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tmosquitto_message msg{};\n\t\tthis->on_message(&mosq, nullptr, &msg); data.response_received = true; return 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tmosquitto_message msg{};\n\t\tthis->on_message(&mosq, nullptr, &msg); data.response_received = true; return 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tmosquitto_message msg{};\n\t\tthis->on_message(&mosq, nullptr, &msg); data.response_received = true; return 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tmosquitto_message msg{};\n\t\tthis->on_message(&mosq, nullptr, &msg); data.response_received = true; return 0;\n\t}))\n\t\t.WillOnce(t::Invoke([this, &mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\tmosquitto_message msg{};\n\t\tthis->on_message(&mosq, nullptr, &msg); data.response_received = true; return 0;\n\t}));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listClients\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Return(0));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listGroups\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Return(0));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(&mosq), nullptr, t::StrEq(\"$CONTROL/dynamic-security/v1\"), t::_,\n\t\t\tt::StrEq(\"{\\\"commands\\\":[{\\\"command\\\":\\\"listRoles\\\"}]}\"), 1, false))\n\t\t.WillOnce(t::Return(0));\n\n\tconst char *outputs[] = {\n\t\t\"This is the mosquitto_ctrl interactive shell, for controlling aspects of a mosquitto broker.\\n\",\n\t\t\"You are in dynsec mode, for controlling the dynamic-security clients, groups, and roles used in authentication and authorisation.\\n\",\n\t\t\"Use 'return' to leave dynsec mode.\\n\",\n\t\t\"Find help on a command using 'help <command>'\\n\",\n\t\t\"Press tab multiple times to find currently available commands.\\n\\n\",\n\t\t\"\\n\",\n\t\t\"Unknown command 'unknown'\\n\",\n\n\t\t\"addClientRole <username> <rolename>\\n\",\n\t\t\"\\nAdds a role directly to a client.\\n\",\n\t\t\"addGroupClient <groupname> <username>\\n\",\n\t\t\"\\nAdds a client to a group.\\n\",\n\t\t\"addGroupRole <groupname> <rolename>\\n\",\n\t\t\"\\nAdds a role to a group.\\n\",\n\t\t\"addRoleACL <rolename> publishClientReceive allow|deny [priority] <topic>\\n\",\n\t\t\"addRoleACL <rolename> publishClientSend allow|deny [priority] <topic>\\n\",\n\t\t\"addRoleACL <rolename> subscribeLiteral allow|deny [priority] <topic>\\n\",\n\t\t\"addRoleACL <rolename> subscribePattern allow|deny [priority] <topic>\\n\",\n\t\t\"addRoleACL <rolename> unsubscribeLiteral allow|deny [priority] <topic>\\n\",\n\t\t\"addRoleACL <rolename> unsubscribePattern allow|deny [priority] <topic>\\n\",\n\t\t\"\\nAdds an ACL to a role, with an optional priority.\\n\",\n\t\t\"\\nACLs of a specific type within a role are processed in order from highest to lowest priority with the first matching ACL applying.\\n\",\n\t\t\"createClient <username> [password [clientid]]\\n\",\n\t\t\"\\nCreate a client with password and optional client id.\\n\",\n\t\t\"createGroup <groupname>\\n\",\n\t\t\"\\nCreate a new group.\\n\",\n\t\t\"createRole <rolename>\\n\",\n\t\t\"\\nCreate a new role.\\n\",\n\t\t\"deleteClient <username>\\n\",\n\t\t\"\\nDelete a client\\n\",\n\t\t\"deleteGroup <groupname>\\n\",\n\t\t\"\\nDelete a group\\n\",\n\t\t\"deleteRole <rolename>\\n\",\n\t\t\"\\nDelete a role\\n\",\n\t\t\"disableClient <username>\\n\",\n\t\t\"\\nDisable a client. This client will not be able to log in, and will be kicked if it has an existing session.\\n\",\n\t\t\"enableClient <username>\\n\",\n\t\t\"\\nEnable a client. Disabled clients are unable to log in.\\n\",\n\t\t\"getAnonymousGroup\\n\",\n\t\t\"\\nPrint the group configured as the anonymous group.\\n\",\n\t\t\"getClient <username>\\n\",\n\t\t\"\\nPrint details of a client and its groups and direct roles.\\n\",\n\t\t\"getDefaultACLAccess\\n\",\n\t\t\"\\nPrint the default allow/deny values for the different classes of ACL.\\n\",\n\t\t\"getDetails\\n\",\n\t\t\"\\nPrint details including the client, group, and role count, and the current change index.\\n\",\n\t\t\"getGroup <groupname>\\n\",\n\t\t\"\\nPrint details of a group and its roles.\\n\",\n\t\t\"getRole <rolename>\\n\",\n\t\t\"\\nPrint details of a role and its ACLs.\\n\",\n\t\t\"listClients [count [offset]]\\n\",\n\t\t\"\\nPrint a list of clients configured in the dynsec plugin, with an optional total count and list offset.\\n\",\n\t\t\"listGroups [count [offset]]\\n\",\n\t\t\"\\nPrint a list of groups configured in the dynsec plugin, with an optional total count and list offset.\\n\",\n\t\t\"listRoles [count [offset]]\\n\",\n\t\t\"\\nPrint a list of roles configured in the dynsec plugin, with an optional total count and list offset.\\n\",\n\t\t\"removeClientRole <username> <rolename>\\n\",\n\t\t\"\\nRemoves a role from a client, where the role was directly attached to the client.\\n\",\n\t\t\"removeGroupClient <groupname> <username>\\n\",\n\t\t\"\\nRemoves a client from a group.\\n\",\n\t\t\"removeGroupRole <groupname> <rolename>\\n\",\n\t\t\"\\nRemoves a role from a group.\\n\",\n\t\t\"removeRoleACL <rolename> publishClientReceive <topic>\\n\",\n\t\t\"removeRoleACL <rolename> publishClientSend <topic>\\n\",\n\t\t\"removeRoleACL <rolename> subscribeLiteral <topic>\\n\",\n\t\t\"removeRoleACL <rolename> subscribePattern <topic>\\n\",\n\t\t\"removeRoleACL <rolename> unsubscribeLiteral <topic>\\n\",\n\t\t\"removeRoleACL <rolename> unsubscribePattern <topic>\\n\",\n\t\t\"\\nRemoves an ACL from a role.\\n\",\n\t\t\"setAnonymousGroup <groupname>\\n\",\n\t\t\"\\nSets the anonymous group to a new group.\\n\",\n\t\t\"setClientId <username>\\n\",\n\t\t\"setClientId <username> <clientid>\\n\",\n\t\t\"\\nSets or clears the clientid associated with a client. If a client has a clientid, all three of username, password, and clientid must match for a client to be able to authenticate.\\n\",\n\t\t\"setClientPassword <username> [password]\\n\",\n\t\t\"\\nSets a new password for a client.\\n\",\n\t\t\"setDefaultACLAccess publishClientReceive allow|deny\\n\",\n\t\t\"setDefaultACLAccess publishClientSend allow|deny\\n\",\n\t\t\"setDefaultACLAccess subscribe allow|deny\\n\",\n\t\t\"setDefaultACLAccess unsubscribe allow|deny\\n\",\n\t\t\"\\nSets the default ACL access to use for an ACL type. The default access will be applied if no other ACL rules match.\\n\",\n\t\t\"Setting a rule to 'allow' means that if no ACLs match, it will be accepted.\\n\",\n\t\t\"Setting a rule to 'deny' means that if no ACLs match, it will be denied.\\n\",\n\t\t\"modifyClient <username> textName <textname>\\n\",\n\t\t\"modifyClient <username> textDescription <textdescription>\\n\",\n\t\t\"\\nModify the text name or text description for a client.\\n\",\n\t\t\"These are free-text fields for your own use.\\n\",\n\t\t\"modifyGroup <groupname> textName <textname>\\n\",\n\t\t\"modifyGroup <groupname> textDescription <textdescription>\\n\",\n\t\t\"\\nModify the text name or text description for a group.\\n\",\n\t\t\"modifyRole <rolename> textName <textname>\\n\",\n\t\t\"modifyRole <rolename> textDescription <textdescription>\\n\",\n\t\t\"modifyRole <rolename> allowWildcardSubs true|false\\n\",\n\t\t\"\\nModify the text name or text description for a role.\\n\",\n\n\t\t\"disconnect\\n\",\n\t\t\"\\nDisconnect from the broker\\n\",     /* help disconnect */\n\t\t\"return\\n\",\n\t\t\"\\nLeave dynsec mode.\\n\",\n\t\t\"exit\\n\",\n\t\t\"\\nQuit the program\\n\",     /* help exit */\n\t\t\"help <command>\\n\",\n\t\t\"\\nFind help on a command using 'help <command>'\\n\", \"Press tab multiple times to find currently available commands.\\n\",     /* help help */\n\t\t\"Invalid response from broker.\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n"
  },
  {
    "path": "test/apps/ctrl/ctrl_shell_options_test.cpp",
    "content": "/*\nCopyright (c) 2022 Cedalo GmbH\n*/\n\n// clang-format off\n#include \"mosquitto_internal.h\" // keep this at the top for `#define uthash_free`\n#include \"ctrl_shell.h\"\n#include \"ctrl_shell_internal.h\"\n#include \"json_help.h\"\n#include \"utlist.h\"\n// clang-format on\n#include <gmock/gmock-actions.h>\n#include <gtest/gtest.h>\n\n#include <cstring>\n\n#include \"ctrl_shell_mock.hpp\"\n#include \"editline_mock.hpp\"\n#include \"libmosquitto_mock.hpp\"\n#include \"pthread_mock.hpp\"\n\nnamespace t = testing;\n\nstruct pending_payload {\n\tstruct pending_payload *next, *prev;\n\tchar payload[1024];\n};\n\nclass CtrlShellOptionsTest : public ::t::Test\n{\npublic:\n\t::t::StrictMock<CtrlShellMock> ctrl_shell_mock_{};\n\t::t::StrictMock<EditLineMock> editline_mock_{};\n\t::t::StrictMock<LibMosquittoMock> libmosquitto_mock_{};\n\t::t::StrictMock<PThreadMock> pthread_mock_{};\n\tLIBMOSQ_CB_connect on_connect{};\n\tLIBMOSQ_CB_message on_message{};\n\tstruct pending_payload *pending_payloads = nullptr;\n\n\n\tvoid expect_setup(struct mosq_config *config)\n\t{\n\t\teditline_mock_.reset();\n\t\tEXPECT_CALL(editline_mock_, rl_bind_key(t::Eq('\\t'), t::_));\n\t\tEXPECT_CALL(editline_mock_, add_history(t::_)).WillRepeatedly(t::Return(0));\n\t\tEXPECT_CALL(editline_mock_, clear_history()).Times(t::AnyNumber());\n\t\tconfig->no_colour = true;\n\t\tconfig->port = PORT_UNDEFINED;\n\n\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StartsWith(\"mosquitto_ctrl shell v\")));\n\t}\n\n\n\tvoid expect_connect(struct mosquitto *mosq, const char *host, int port)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_new(t::Eq(nullptr), t::Eq(true), t::Eq(nullptr)))\n\t\t\t.WillOnce(t::Return(mosq));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_int_option(t::Eq(mosq), MOSQ_OPT_PROTOCOL_VERSION, 5));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe_callback_set(t::Eq(mosq), t::_));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish_v5_callback_set(t::Eq(mosq), t::_));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect(t::Eq(mosq), t::StrEq(host), port, 60));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_start(t::Eq(mosq)));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_connect>()))\n\t\t\t.WillRepeatedly(t::SaveArg<1>(&this->on_connect));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_message_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_message>()))\n\t\t\t.WillOnce(t::SaveArg<1>(&this->on_message));\n\t}\n\n\n\tvoid expect_disconnect(struct mosquitto *mosq)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_disconnect(t::Eq(mosq)));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_stop(t::Eq(mosq), false));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_destroy(t::Eq(mosq)));\n\t}\n\n\n\tvoid expect_outputs(const char **outputs, size_t count)\n\t{\n\t\tfor(size_t i=0; i<count; i++){\n\t\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StrEq(outputs[i]))).Times(t::AtLeast(1));\n\t\t}\n\t}\n\n\n\tvoid expect_request_response(struct mosquitto *mosq, const char *request, const char *respons)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload), \"%s\", respons);\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(mosq), nullptr, t::StrEq(\"$CONTROL/broker/v1\"), t::_,\n\t\t\t\tt::StrEq(request), 1, false))\n\t\t\t.WillOnce(t::Invoke([this, pp](){\n\t\t\tDL_APPEND(this->pending_payloads, pp);\n\t\t\treturn 0;\n\t\t}));\n\t}\n\n\n\tvoid expect_request_response_success(struct mosquitto *mosq, const char *request, const char *command)\n\t{\n\t\tchar response[100];\n\t\tsnprintf(response, sizeof(response), \"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\"}]}\", command);\n\t\texpect_request_response(mosq, request, response);\n\t}\n\n\n\tvoid expect_request_response_empty(struct mosquitto *mosq, const char *command)\n\t{\n\t\tchar request[100];\n\t\tchar response[100];\n\n\t\tsnprintf(request, sizeof(request), \"{\\\"commands\\\":[{\\\"command\\\":\\\"%s\\\"}]}\", command);\n\t\tsnprintf(response, sizeof(response), \"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\",\\\"data\\\":{}}]}\", command);\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(mosq), nullptr, t::StrEq(\"$CONTROL/broker/v1\"), t::_,\n\t\t\t\tt::StrEq(request), 1, false))\n\t\t\t.WillOnce(t::Invoke([this, &command](){\n\t\t\tappend_empty_response(command);\n\t\t\treturn 0;\n\t\t}));\n\t}\n\n\n\tvoid append_response(const char *response)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload), \"%s\", response);\n\t\tDL_APPEND(this->pending_payloads, pp);\n\t}\n\n\n\tvoid append_empty_response(const char *command)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload),\n\t\t\t\t\"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\",\\\"data\\\":{}}]}\", command);\n\t\tDL_APPEND(this->pending_payloads, pp);\n\t}\n\n\n\tvoid expect_broker(const char *host, int port)\n\t{\n\t\tchar buf[200];\n\t\tsnprintf(buf, sizeof(buf), \"connect mqtt://%s:%d\", host, port);\n\t\tchar *s_conn = strdup(buf);\n\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t\t.WillOnce(t::Return(s_conn));\n\n\t\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t\t.WillOnce(t::Return(strdup(\"broker\")));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe(t::_, nullptr, t::StrEq(\"$CONTROL/broker/v1/response\"), 1))\n\t\t\t.WillOnce(t::Return(0));\n\t}\n\n\n\tvoid expect_connect_and_messages(struct mosquitto *mosq)\n\t{\n\t\t/* This is a hacky way of working around the async mqtt send/receive which we don't directly control.\n\t\t\t    * Each send starts a wait which times out after two seconds. We use that call to produce the effect we want.\n\t\t\t    */\n\t\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t\t.WillOnce(t::Invoke([this, mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\t\tthis->on_connect(mosq, nullptr, 0);\n\t\t\tdata.response_received = true;\n\t\t\treturn 0;\n\t\t}))\n\t\t\t.WillRepeatedly(t::Invoke([this, mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\t\tmosquitto_message msg{};\n\t\t\tstruct pending_payload *pp = pending_payloads;\n\t\t\tif(pp){\n\t\t\t\tDL_DELETE(pending_payloads, pp);\n\t\t\t\tmsg.payload = pp->payload;\n\t\t\t\tmsg.payloadlen = (int)strlen((char *)msg.payload);\n\t\t\t\tthis->on_message(mosq, nullptr, &msg);\n\t\t\t\tfree(pp);\n\t\t\t}\n\t\t\tdata.response_received = true;\n\t\t\treturn 0;\n\t\t}));\n\t}\n};\n\n\nTEST_F(CtrlShellOptionsTest, Empty)\n{\n\tmosq_config config{};\n\n\texpect_setup(&config);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(strdup(\"\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tconst char *outputs[] = {\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellOptionsTest, ConnectUrlMissingHost)\n{\n\tmosq_config config{};\n\n\texpect_setup(&config);\n\n\tchar buf[200];\n\tsnprintf(buf, sizeof(buf), \"connect mqtt://\");\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tconst char *outputs[] = {\n\t\t\"connect mqtt[s]://<hostname>:port\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellOptionsTest, ConnectTLS)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 8883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_connect_and_messages(&mosq);\n\n\n\tchar buf[200];\n\tsnprintf(buf, sizeof(buf), \"connect mqtts://%s:%d\", host, port);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_int_option(t::_, MOSQ_OPT_TLS_USE_OS_CERTS, 1));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtts://localhost:8883> \")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tconst char *outputs[] = {\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellOptionsTest, ConnectWebsockets)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 8080;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_connect_and_messages(&mosq);\n\n\n\tchar buf[200];\n\tsnprintf(buf, sizeof(buf), \"connect ws://%s:%d\", host, port);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_int_option(t::_, MOSQ_OPT_TRANSPORT, MOSQ_T_WEBSOCKETS));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"ws://localhost:8080> \")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tconst char *outputs[] = {\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellOptionsTest, ConnectWebsocketsTLS)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 8081;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_connect_and_messages(&mosq);\n\n\n\tchar buf[200];\n\tsnprintf(buf, sizeof(buf), \"connect wss://%s:%d\", host, port);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_int_option(t::_, MOSQ_OPT_TLS_USE_OS_CERTS, 1));\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_int_option(t::_, MOSQ_OPT_TRANSPORT, MOSQ_T_WEBSOCKETS));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"wss://localhost:8081> \")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tconst char *outputs[] = {\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellOptionsTest, ConnectImplicitHostname)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_connect_and_messages(&mosq);\n\n\n\tchar buf[200];\n\tsnprintf(buf, sizeof(buf), \"connect\");\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tconst char *outputs[] = {\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellOptionsTest, ConnectImplicitPort)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_connect_and_messages(&mosq);\n\n\n\tchar buf[200];\n\tsnprintf(buf, sizeof(buf), \"connect %s\", host);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tconst char *outputs[] = {\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellOptionsTest, ConnectCertNotFound)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_connect_and_messages(&mosq);\n\n\tconfig.cafile = strdup(\"missing cafile\");\n\tconfig.capath = strdup(\"missing capath\");\n\tconfig.certfile = strdup(\"missing certfile\");\n\tconfig.keyfile = strdup(\"missing keyfile\");\n\n\tchar buf[200];\n\tsnprintf(buf, sizeof(buf), \"connect %s\", host);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_tls_set(\n\t\t\tt::_,\n\t\t\tt::StrEq(\"missing cafile\"),\n\t\t\tt::StrEq(\"missing capath\"),\n\t\t\tt::StrEq(\"missing certfile\"),\n\t\t\tt::StrEq(\"missing keyfile\"),\n\t\t\tnullptr))\n\t\t.WillOnce(t::Return(MOSQ_ERR_INVAL));\n\n\tconst char *outputs[] = {\n\t\t\"Error setting TLS options: File not found.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tfree(config.cafile);\n\tfree(config.capath);\n\tfree(config.certfile);\n\tfree(config.keyfile);\n}\n\n\nTEST_F(CtrlShellOptionsTest, ConnectCertError)\n{\n\tmosq_config config{};\n\tmosquitto mosq{};\n\tconst char host[] = \"localhost\";\n\tint port = 1883;\n\n\texpect_setup(&config);\n\texpect_connect(&mosq, host, port);\n\texpect_connect_and_messages(&mosq);\n\n\tconfig.cafile = strdup(\"cafile\");\n\tconfig.capath = strdup(\"capath\");\n\tconfig.certfile = strdup(\"certfile\");\n\tconfig.keyfile = strdup(\"keyfile\");\n\n\tchar buf[200];\n\tsnprintf(buf, sizeof(buf), \"connect %s\", host);\n\tchar *s_conn = strdup(buf);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(s_conn));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"mqtt://localhost:1883> \")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tEXPECT_CALL(libmosquitto_mock_, mosquitto_tls_set(\n\t\t\tt::_,\n\t\t\tt::StrEq(\"cafile\"),\n\t\t\tt::StrEq(\"capath\"),\n\t\t\tt::StrEq(\"certfile\"),\n\t\t\tt::StrEq(\"keyfile\"),\n\t\t\tnullptr))\n\t\t.WillOnce(t::Return(MOSQ_ERR_TLS));\n\n\tconst char *outputs[] = {\n\t\t\"Error setting TLS options: A TLS error occurred.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n\tfree(config.cafile);\n\tfree(config.capath);\n\tfree(config.certfile);\n\tfree(config.keyfile);\n}\n"
  },
  {
    "path": "test/apps/ctrl/ctrl_shell_pre_connect_test.cpp",
    "content": "/*\nCopyright (c) 2022 Cedalo GmbH\n*/\n\n// clang-format off\n#include \"mosquitto_internal.h\" // keep this at the top for `#define uthash_free`\n#include \"ctrl_shell.h\"\n#include \"ctrl_shell_internal.h\"\n#include \"json_help.h\"\n// clang-format on\n#include <gmock/gmock-actions.h>\n#include <gtest/gtest.h>\n\n#include <cstring>\n\n#include \"ctrl_shell_mock.hpp\"\n#include \"editline_mock.hpp\"\n#include \"libmosquitto_mock.hpp\"\n#include \"pthread_mock.hpp\"\n\nnamespace t = testing;\n\nclass CtrlShellPreConnectTest : public ::t::Test\n{\npublic:\n\t::t::StrictMock<CtrlShellMock> ctrl_shell_mock_{};\n\t::t::StrictMock<EditLineMock> editline_mock_{};\n\t::t::StrictMock<LibMosquittoMock> libmosquitto_mock_{};\n\t::t::StrictMock<PThreadMock> pthread_mock_{};\n\tLIBMOSQ_CB_connect on_connect{};\n\tLIBMOSQ_CB_message on_message{};\n\n\n\tvoid expect_setup(struct mosq_config *config)\n\t{\n\t\teditline_mock_.reset();\n\t\tEXPECT_CALL(editline_mock_, rl_bind_key(t::Eq('\\t'), t::_));\n\t\tEXPECT_CALL(editline_mock_, add_history(t::_)).WillRepeatedly(t::Return(0));\n\t\tEXPECT_CALL(editline_mock_, clear_history()).Times(t::AnyNumber());\n\t\tconfig->no_colour = true;\n\n\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StartsWith(\"mosquitto_ctrl shell v\")));\n\t}\n\n\n\tvoid expect_connect(struct mosquitto *mosq, const char *host, int port)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_new(t::Eq(nullptr), t::Eq(true), t::Eq(nullptr)))\n\t\t\t.WillOnce(t::Return(mosq));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_int_option(t::Eq(mosq), MOSQ_OPT_PROTOCOL_VERSION, 5));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe_callback_set(t::Eq(mosq), t::_));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish_v5_callback_set(t::Eq(mosq), t::_));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect(t::Eq(mosq), t::StrEq(host), port, 60));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_start(t::Eq(mosq)));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_connect>()))\n\t\t\t.WillRepeatedly(t::SaveArg<1>(&this->on_connect));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_message_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_message>()))\n\t\t\t.WillOnce(t::SaveArg<1>(&this->on_message));\n\t}\n\n\n\tvoid expect_disconnect(struct mosquitto *mosq)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_disconnect(t::Eq(mosq)));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_stop(t::Eq(mosq), false));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_destroy(t::Eq(mosq)));\n\t}\n\n\n\tvoid expect_outputs(const char **outputs, size_t count)\n\t{\n\t\tfor(size_t i=0; i<count; i++){\n\t\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StrEq(outputs[i]))).Times(t::AtLeast(1));\n\t\t}\n\t}\n\n\n\tvoid expect_empty_connect_and_messages(struct mosquitto *mosq)\n\t{\n\t\t/* This is a hacky way of working around the async mqtt send/receive which we don't directly control.\n\t\t\t    * Each send starts a wait which times out after two seconds. We use that call to produce the effect we want.\n\t\t\t    */\n\t\tEXPECT_CALL(pthread_mock_, pthread_cond_timedwait(t::_, t::_, t::_))\n\t\t\t.WillOnce(t::Invoke([this, mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\t\tthis->on_connect(mosq, nullptr, 0);\n\t\t\tdata.response_received = true;\n\t\t\treturn 0;\n\t\t}))\n\t\t\t.WillRepeatedly(t::Invoke([this, mosq](pthread_cond_t *, pthread_mutex_t *, const struct timespec *){\n\t\t\tmosquitto_message msg{};\n\t\t\tthis->on_message(mosq, nullptr, &msg);\n\t\t\tdata.response_received = true;\n\t\t\treturn 0;\n\t\t}));\n\t}\n};\n\n\nTEST_F(CtrlShellPreConnectTest, AuthNoUsername)\n{\n\tmosq_config config{};\n\n\texpect_setup(&config);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(strdup(\"auth\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"username: \")))\n\t\t.WillOnce(t::Return(strdup(\"username1\")));\n\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell_fgets(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"password1\");\n\t\treturn s;\n\t}));\n\n\tconst char *outputs[] = {\n\t\t\"password:\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellPreConnectTest, AuthWithUsername)\n{\n\tmosq_config config{};\n\n\texpect_setup(&config);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(strdup(\"auth username1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell_fgets(t::_, t::_, t::_))\n\t\t.WillOnce(t::Invoke([](char *s, int size, FILE *){\n\t\tsnprintf(s, (size_t)size, \"password1\");\n\t\treturn s;\n\t}));\n\n\tconst char *outputs[] = {\n\t\t\"password:\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n\n\nTEST_F(CtrlShellPreConnectTest, AuthNoPassword)\n{\n\tmosq_config config{};\n\n\texpect_setup(&config);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"> \")))\n\t\t.WillOnce(t::Return(strdup(\"auth username1\")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell_fgets(t::_, t::_, t::_))\n\t\t.WillOnce(t::Return(nullptr));\n\n\tconst char *outputs[] = {\n\t\t\"password:\",\n\t\t\"No password.\\n\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(&config);\n}\n"
  },
  {
    "path": "test/apps/ctrl/ctrl_shell_test.cpp",
    "content": "/*\nCopyright (c) 2022 Cedalo GmbH\n*/\n\n// clang-format off\n#include \"mosquitto_internal.h\" // keep this at the top for `#define uthash_free`\n#include \"ctrl_shell.h\"\n#include \"ctrl_shell_internal.h\"\n#include \"json_help.h\"\n#include \"utlist.h\"\n// clang-format on\n#include <gmock/gmock-actions.h>\n#include <gtest/gtest.h>\n\n#include <cstring>\n\n#include \"ctrl_shell_mock.hpp\"\n#include \"editline_mock.hpp\"\n#include \"libmosquitto_mock.hpp\"\n#include \"pthread_mock.hpp\"\n\nnamespace t = testing;\n\nstruct pending_payload {\n\tstruct pending_payload *next, *prev;\n\tchar payload[1024];\n};\n\nclass CtrlShellTest : public ::t::Test\n{\npublic:\n\t::t::StrictMock<CtrlShellMock> ctrl_shell_mock_{};\n\t::t::StrictMock<EditLineMock> editline_mock_{};\n\t::t::StrictMock<LibMosquittoMock> libmosquitto_mock_{};\n\t::t::StrictMock<PThreadMock> pthread_mock_{};\n\tLIBMOSQ_CB_connect on_connect{};\n\tLIBMOSQ_CB_message on_message{};\n\tstruct pending_payload *pending_payloads = nullptr;\n\n\n\tvoid expect_setup(struct mosq_config *config)\n\t{\n\t\teditline_mock_.reset();\n\t\tEXPECT_CALL(editline_mock_, rl_bind_key(t::Eq('\\t'), t::_));\n\t\tEXPECT_CALL(editline_mock_, add_history(t::_)).WillRepeatedly(t::Return(0));\n\t\tEXPECT_CALL(editline_mock_, clear_history()).Times(t::AnyNumber());\n\t\tconfig->no_colour = true;\n\n\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StartsWith(\"mosquitto_ctrl shell v\")));\n\t}\n\n\n\tvoid expect_connect(struct mosquitto *mosq, const char *host, int port)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_new(t::Eq(nullptr), t::Eq(true), t::Eq(nullptr)))\n\t\t\t.WillOnce(t::Return(mosq));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_int_option(t::Eq(mosq), MOSQ_OPT_PROTOCOL_VERSION, 5));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_subscribe_callback_set(t::Eq(mosq), t::_));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish_v5_callback_set(t::Eq(mosq), t::_));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect(t::Eq(mosq), t::StrEq(host), port, 60));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_start(t::Eq(mosq)));\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_connect_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_connect>()))\n\t\t\t.WillRepeatedly(t::SaveArg<1>(&this->on_connect));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_message_callback_set(t::Eq(mosq), t::A<LIBMOSQ_CB_message>()))\n\t\t\t.WillOnce(t::SaveArg<1>(&this->on_message));\n\t}\n\n\n\tvoid expect_disconnect(struct mosquitto *mosq)\n\t{\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_disconnect(t::Eq(mosq)));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_loop_stop(t::Eq(mosq), false));\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_destroy(t::Eq(mosq)));\n\t}\n\n\n\tvoid expect_outputs(const char **outputs, size_t count)\n\t{\n\t\tfor(size_t i=0; i<count; i++){\n\t\t\tEXPECT_CALL(ctrl_shell_mock_, ctrl_shell__output(t::StrEq(outputs[i]))).Times(t::AtLeast(1));\n\t\t}\n\t}\n\n\n\tvoid expect_request_response(struct mosquitto *mosq, const char *request, const char *respons)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload), \"%s\", respons);\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(mosq), nullptr, t::StrEq(\"$CONTROL/broker/v1\"), t::_,\n\t\t\t\tt::StrEq(request), 1, false))\n\t\t\t.WillOnce(t::Invoke([this, pp](){\n\t\t\tDL_APPEND(this->pending_payloads, pp);\n\t\t\treturn 0;\n\t\t}));\n\t}\n\n\n\tvoid expect_request_response_success(struct mosquitto *mosq, const char *request, const char *command)\n\t{\n\t\tchar response[100];\n\t\tsnprintf(response, sizeof(response), \"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\"}]}\", command);\n\t\texpect_request_response(mosq, request, response);\n\t}\n\n\n\tvoid expect_request_response_empty(struct mosquitto *mosq, const char *command)\n\t{\n\t\tchar request[100];\n\t\tchar response[100];\n\n\t\tsnprintf(request, sizeof(request), \"{\\\"commands\\\":[{\\\"command\\\":\\\"%s\\\"}]}\", command);\n\t\tsnprintf(response, sizeof(response), \"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\",\\\"data\\\":{}}]}\", command);\n\n\t\tEXPECT_CALL(libmosquitto_mock_, mosquitto_publish(t::Eq(mosq), nullptr, t::StrEq(\"$CONTROL/broker/v1\"), t::_,\n\t\t\t\tt::StrEq(request), 1, false))\n\t\t\t.WillOnce(t::Invoke([this, &command](){\n\t\t\tappend_empty_response(command);\n\t\t\treturn 0;\n\t\t}));\n\t}\n\n\n\tvoid append_response(const char *response)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload), \"%s\", response);\n\t\tDL_APPEND(this->pending_payloads, pp);\n\t}\n\n\n\tvoid append_empty_response(const char *command)\n\t{\n\t\tstruct pending_payload *pp = (struct pending_payload *)calloc(1, sizeof(struct pending_payload));\n\t\tsnprintf(pp->payload, sizeof(pp->payload),\n\t\t\t\t\"{\\\"responses\\\":[{\\\"command\\\":\\\"%s\\\",\\\"data\\\":{}}]}\", command);\n\t\tDL_APPEND(this->pending_payloads, pp);\n\t}\n};\n\n\n#if 0\n/* Hangs on CI, presumably due to blocking read() */\nTEST_F(CtrlShellTest, NoConfig)\n{\n\t/* No config means we have colour mode enabled */\n\tmosq_config config{};\n\n\texpect_setup(&config);\n\n\tEXPECT_CALL(editline_mock_, readline(t::StrEq(\"\\x1\\x1B[38;5;80m\\x2>\\x1\\x1B[0m\\x2 \")))\n\t\t.WillOnce(t::Return(strdup(\"exit\")));\n\n\tconst char *outputs[] = {\n\t\t\"\\x1B]10;?\\a\\x1B]11;?\\a\",\n\t\t\"\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tctrl_shell__main(nullptr);\n}\n#endif\n\nextern \"C\" { void set_no_colour(void); }\n\nTEST_F(CtrlShellTest, PrintLabelValue)\n{\n\tconst char *outputs[] = {\n\t\t\"My Label:\",\n\t\t\"   10\\n\",\n\t};\n\texpect_outputs(outputs, sizeof(outputs)/sizeof(char *));\n\n\tset_no_colour();\n\tctrl_shell_print_label_value(0, \"My Label:\", 10, \"%u\\n\", 10);\n}\n"
  },
  {
    "path": "test/apps/ctrl/mosq_test_helper.py",
    "content": "import logging\nimport sys\nfrom pathlib import Path\n\nlogging.basicConfig(\n    level=logging.INFO,\n    format=\"%(levelname)s %(asctime)s.%(msecs)03d %(module)s: %(message)s\",\n    datefmt=\"%H:%M:%S\",\n)\n\ncurrent_source_dir = Path(__file__).resolve().parent\ntest_dir = current_source_dir.parents[1]\nif test_dir not in sys.path:\n    sys.path.insert(0, str(test_dir))\n\nssl_dir = test_dir / \"ssl\"\n\nimport mosq_test\nimport subprocess\nimport os\n"
  },
  {
    "path": "test/apps/ctrl/test.py",
    "content": "#!/usr/bin/env python3\n\nimport sys\nsys.path.insert(0, \"../..\")\nimport ptest\n\ntests = [\n    #(ports, 'path'),\n    (1, './ctrl-args.py'),\n    (2, './ctrl-broker.py'),\n    (2, './ctrl-dynsec.py'),\n]\n\nif __name__ == \"__main__\":\n    test = ptest.PTest()\n    test.run_tests(tests)\n"
  },
  {
    "path": "test/apps/db_dump/CMakeLists.txt",
    "content": "file(GLOB PY_TEST_FILES db-dump-*.py)\n\nset(EXCLUDE_LIST\n    # none\n)\n\nforeach(PY_TEST_FILE ${PY_TEST_FILES})\n    get_filename_component(PY_TEST_NAME ${PY_TEST_FILE} NAME_WE)\n    if(${PY_TEST_NAME} IN_LIST EXCLUDE_LIST)\n        continue()\n    endif()\n    add_test(NAME apps-${PY_TEST_NAME}\n        COMMAND ${PY_TEST_FILE}\n    )\n    set_tests_properties(apps-${PY_TEST_NAME}\n        PROPERTIES\n            ENVIRONMENT \"BUILD_ROOT=${CMAKE_BINARY_DIR}\"\n    )\nendforeach()\n"
  },
  {
    "path": "test/apps/db_dump/Makefile",
    "content": ".PHONY: all check test test-compile ptest clean\n\nall :\n\ncheck : test\n\ntest-compile:\n\ntest:\n\t./db-dump-client-stats.py\n\t./db-dump-corrupt.py\n\t./db-dump-json-v6-mqtt-v5-props.py\n\t./db-dump-print-empty.py\n\t./db-dump-print-v6-all.py\n\t./db-dump-print-v6-mqtt-v5-props.py\n\t./db-dump-stats.py\n\t./db-dump-stats-current.py\n\nptest:\n\t./test.py\n\nclean :\n"
  },
  {
    "path": "test/apps/db_dump/data/bad-magic.test-db",
    "content": "This is a text file, not a persistence file.\n"
  },
  {
    "path": "test/apps/db_dump/db-dump-client-stats.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(file, counts):\n    stdout = f\"SC: {counts[0]} \" + \\\n        f\"SS: {counts[1]} \" + \\\n        f\"MC: {counts[2]} \" + \\\n        f\"MS: {counts[3]} \" + \\\n        f\"  {counts[4]}\\n\"\n\n    cmd = [\n        mosq_test.get_build_root()+'/apps/db_dump/mosquitto_db_dump',\n        '--client-stats',\n        f'{test_dir}/apps/db_dump/data/{file}'\n    ]\n\n    res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1, encoding='utf-8')\n    if res.stdout != stdout:\n        print(res.stdout)\n        print(stdout)\n        raise mosq_test.TestError\n\ndo_test('v6-single-all.test-db', [1,27,1,111,'single-all'])\n"
  },
  {
    "path": "test/apps/db_dump/db-dump-corrupt.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(file, stderr, rc_expected):\n\n    cmd = [\n        mosq_test.get_build_root()+'/apps/db_dump/mosquitto_db_dump',\n        f'{test_dir}/apps/db_dump/data/{file}'\n    ]\n\n    res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1, encoding='utf-8')\n    if res.stderr != stderr:\n        print(res.stderr)\n        raise mosq_test.TestError\n    if res.returncode != rc_expected:\n        print(file)\n        print(res.returncode)\n        raise mosq_test.TestError\n\ndo_test('missing.test-db', f\"Error: Unable to open {test_dir}/apps/db_dump/data/missing.test-db\\n\", 0)\ndo_test('bad-magic.test-db', \"Error: Unrecognised file format.\\n\", 1)\ndo_test('short.test-db', \"\", 1)\ndo_test('bad-dbid-size.test-db', \"Error: Incompatible database configuration (dbid size is 5 bytes, expected 8)\", 1)\ndo_test('bad-chunk.test-db', 'Warning: Unsupported chunk \"2816\" of length 65696 in persistent database file at position 29. Ignoring.\\n', 0)\ndo_test('v3-corrupt.test-db', \"Error: Corrupt persistent database.\\n\", 1)\ndo_test('v4-corrupt.test-db', \"Error: Corrupt persistent database.\\n\", 1)\ndo_test('v5-corrupt.test-db', \"Error: Corrupt persistent database.\\n\", 1)\ndo_test('v6-corrupt.test-db', \"Error: Corrupt persistent database.\\n\", 1)\ndo_test('v6-corrupt-client.test-db', \"Error: Corrupt persistent database.\\n\", 1)\ndo_test('v6-corrupt-cmsg.test-db', \"Error: Corrupt persistent database.\\n\", 1)\ndo_test('v6-corrupt-retain.test-db', \"Error: Corrupt persistent database.\\n\", 1)\ndo_test('v6-corrupt-sub.test-db', \"Error: Corrupt persistent database.\\n\", 1)\n"
  },
  {
    "path": "test/apps/db_dump/db-dump-json-v6-mqtt-v5-props.py",
    "content": "#!/usr/bin/env python3\n\nimport json\nfrom mosq_test_helper import *\n\ndef do_test(file, json_expected):\n\n    cmd = [\n        mosq_test.get_build_root() + '/apps/db_dump/mosquitto_db_dump',\n        '--json',\n        f'{test_dir}/apps/db_dump/data/{file}'\n    ]\n\n    res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=3, encoding='utf-8')\n    json_result = json.loads(res.stdout)\n    if json.dumps(json_result) != json.dumps(json_expected):\n        print(json.dumps(json_result))\n        print(json.dumps(json_expected))\n        raise mosq_test.TestError\n\njson_expected = {\n    \"base-messages\": [{\n        \"storeid\": 273732462748327,\n        \"expiry-time\": 1669799825,\n        \"source-mid\": 1,\n        \"source-port\": 1883,\n        \"qos\": 1,\n        \"retain\": 0,\n        \"topic\": \"test-topic\",\n        \"clientid\": \"auto-1F56F09A-97D3-2395-8B77-185E54E0A83C\",\n        \"payload\": \"bWVzc2FnZQ==\", # \"message\"\n        \"properties\": [{\n            \"identifier\": \"content-type\",\n            \"value\": \"text/plain\"\n        }, {\n            \"identifier\": \"correlation-data\",\n            \"value\": \"35636638653064652D356666612D346131302D393036622D346535623266393038363162\"\n        }, {\n            \"identifier\": \"payload-format-indicator\",\n            \"value\": 1\n        }, {\n            \"identifier\": \"response-topic\",\n            \"value\": \"pub-response-topic\"\n        }, {\n            \"identifier\": \"user-property\",\n            \"name\": \"pub-key\",\n            \"value\": \"pub-value\"\n        }]\n    },{\n        \"storeid\": 273732472648936,\n        \"expiry-time\": 1669799786,\n        \"source-mid\": 0,\n        \"source-port\": 0,\n        \"qos\": 2,\n        \"retain\": 1,\n        \"topic\": \"will-topic\",\n        \"clientid\": \"clientid\",\n        \"payload\": \"d2lsbC1wYXlsb2Fk\",\n        \"properties\": [{\n            \"identifier\": \"content-type\",\n            \"value\": \"text/plain\"\n        }, {\n            \"identifier\": \"correlation-data\",\n            \"value\": \"636F7272656C6174696F6E2D64617461\"\n        }, {\n            \"identifier\": \"payload-format-indicator\",\n            \"value\": 1\n        }, {\n            \"identifier\": \"response-topic\",\n            \"value\": \"will-response-topic\"\n        }, {\n            \"identifier\": \"user-property\",\n            \"name\": \"key\",\n            \"value\": \"value\"\n        }]\n    }],\n\n    \"clients\": [{\n        \"clientid\": \"clientid\",\n        \"session-expiry-time\": 1669799784,\n        \"session-expiry-interval\": 60,\n        \"last-mid\": 1,\n        \"listener-port\": 0\n    }],\n\n    \"client-messages\": [{\n        \"clientid\": \"clientid\",\n        \"storeid\": 42,\n        \"mid\": 1,\n        \"qos\": 1,\n        \"state\": 11,\n        \"retain-dup\": 0,\n        \"direction\": 1,\n        \"subscription-identifier\": 42\n    }],\n\n    \"retained-messages\": [{\n        \"storeid\": 273732472648936\n        }],\n    \"subscriptions\": [{\n        \"clientid\": \"clientid\",\n        \"topic\": \"test-topic\",\n        \"qos\": 1,\n        \"options\": 0,\n        \"identifier\": 42\n    }]\n}\n\ndo_test('v6-mqtt-v5-props.test-db', json_expected)\n"
  },
  {
    "path": "test/apps/db_dump/db-dump-print-empty.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(file, stdout):\n\n    cmd = [\n        mosq_test.get_build_root()+'/apps/db_dump/mosquitto_db_dump',\n        f'{test_dir}/apps/db_dump/data/{file}'\n    ]\n\n    res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1, encoding='utf-8')\n    if res.stdout != stdout:\n        raise mosq_test.TestError\n\nv3_empty = \"\"\"Mosquitto DB dump\nCRC: 0\nDB version: 3\nDB_CHUNK_CFG:\n\tLength: 10\n\tShutdown: 1\n\tDB ID size: 8\n\tLast DB ID: 51\n\"\"\"\ndo_test('v3-empty.test-db', v3_empty)\n\nv4_empty = \"\"\"Mosquitto DB dump\nCRC: 0\nDB version: 4\nDB_CHUNK_CFG:\n\tLength: 10\n\tShutdown: 1\n\tDB ID size: 8\n\tLast DB ID: 102\n\"\"\"\ndo_test('v4-empty.test-db', v4_empty)\n\nv5_empty = \"\"\"Mosquitto DB dump\nCRC: 0\nDB version: 5\nDB_CHUNK_CFG:\n\tLength: 16\n\tShutdown: 1\n\tDB ID size: 8\n\tLast DB ID: 52\n\"\"\"\ndo_test('v5-empty.test-db', v5_empty)\n\nv6_empty = \"\"\"Mosquitto DB dump\nCRC: 0\nDB version: 6\nDB_CHUNK_CFG:\n\tLength: 16\n\tShutdown: 1\n\tDB ID size: 8\n\tLast DB ID: 208485212291791\n\"\"\"\ndo_test('v6-empty.test-db', v6_empty)\n"
  },
  {
    "path": "test/apps/db_dump/db-dump-print-v6-all.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(file, stdout):\n\n    cmd = [\n        mosq_test.get_build_root()+'/apps/db_dump/mosquitto_db_dump',\n            f'{test_dir}/apps/db_dump/data/{file}'\n            ]\n\n    res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1, encoding='utf-8')\n\n    if res.stdout != stdout:\n        read_lines = res.stdout.splitlines()\n        expected_lines = stdout.splitlines()\n        for (read,expected) in zip(read_lines,expected_lines):\n            if read != expected:\n                print(f\"- {expected}\")\n                print(f\"+ {read}\")\n            else:\n                print(f\"  {read}\")\n        raise mosq_test.TestError\n\nstdout = \"\"\"Mosquitto DB dump\nCRC: 0\nDB version: 6\nDB_CHUNK_CFG:\n\tLength: 16\n\tShutdown: 1\n\tDB ID size: 8\n\tLast DB ID: 208508774941868\nDB_CHUNK_BASE_MSG:\n\tLength: 85\n\tStore ID: 208508774941868\n\tSource Port: 1883\n\tSource MID: 1\n\tTopic: topic\n\tQoS: 1\n\tRetain: 1\n\tPayload Length: 7\n\tExpiry Time: 0\n\tPayload: message\nDB_CHUNK_CLIENT:\n\tLength: 34\n\tClient ID: single-all\n\tLast MID: 1\n\tSession expiry time: 0\n\tSession expiry interval: 4294967295\nDB_CHUNK_CLIENT_MSG:\n\tLength: 26\n\tClient ID: single-all\n\tStore ID: 208508774941868\n\tMID: 1\n\tQoS: 1\n\tRetain: 0\n\tDirection: 1\n\tState: 11\n\tDup: 0\nDB_CHUNK_SUB:\n\tLength: 27\n\tClient ID: single-all\n\tTopic: topic\n\tQoS: 1\n\tSubscription ID: 0\n\tOptions: 0x00\nDB_CHUNK_RETAIN:\n\tLength: 8\n\tStore ID: 208508774941868\n\"\"\"\ndo_test('v6-single-all.test-db', stdout)\n"
  },
  {
    "path": "test/apps/db_dump/db-dump-print-v6-mqtt-v5-props.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(file, stdout):\n\n    cmd = [\n        mosq_test.get_build_root() + '/apps/db_dump/mosquitto_db_dump',\n        f'{test_dir}/apps/db_dump/data/{file}'\n    ]\n\n    res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=3, encoding='utf-8')\n    if res.stdout != stdout:\n        print(res.stdout)\n        raise mosq_test.TestError\n\nstdout = \"\"\"Mosquitto DB dump\nCRC: 0\nDB version: 6\nDB_CHUNK_CFG:\n\tLength: 16\n\tShutdown: 1\n\tDB ID size: 8\n\tLast DB ID: 273732472648936\nDB_CHUNK_BASE_MSG:\n\tLength: 187\n\tStore ID: 273732462748327\n\tSource Port: 1883\n\tSource MID: 1\n\tTopic: test-topic\n\tQoS: 1\n\tRetain: 0\n\tPayload Length: 7\n\tExpiry Time: 1669799825\n\tPayload: message\n\tProperties:\n\t\tContent type: text/plain\n\t\tCorrelation data: 35636638653064652D356666612D346131302D393036622D346535623266393038363162\n\t\tPayload format indicator: 1\n\t\tResponse topic: pub-response-topic\n\t\tUser property: pub-key , pub-value\nDB_CHUNK_BASE_MSG:\n\tLength: 132\n\tStore ID: 273732472648936\n\tSource Port: 0\n\tSource MID: 0\n\tTopic: will-topic\n\tQoS: 2\n\tRetain: 1\n\tPayload Length: 12\n\tExpiry Time: 1669799786\n\tPayload: will-payload\n\tProperties:\n\t\tContent type: text/plain\n\t\tCorrelation data: 636F7272656C6174696F6E2D64617461\n\t\tPayload format indicator: 1\n\t\tResponse topic: will-response-topic\n\t\tUser property: key , value\nDB_CHUNK_CLIENT:\n\tLength: 32\n\tClient ID: clientid\n\tLast MID: 1\n\tSession expiry time: 1669799784\n\tSession expiry interval: 60\nDB_CHUNK_CLIENT_MSG:\n\tLength: 27\n\tClient ID: clientid\n\tStore ID: 273732462748327\n\tMID: 1\n\tQoS: 1\n\tRetain: 0\n\tDirection: 1\n\tState: 11\n\tDup: 0\n\tSubscription identifier: 42\nDB_CHUNK_SUB:\n\tLength: 30\n\tClient ID: clientid\n\tTopic: test-topic\n\tQoS: 1\n\tSubscription ID: 42\n\tOptions: 0x00\nDB_CHUNK_RETAIN:\n\tLength: 8\n\tStore ID: 273732472648936\n\"\"\"\ndo_test('v6-mqtt-v5-props.test-db', stdout)\n"
  },
  {
    "path": "test/apps/db_dump/db-dump-stats-current.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether output from the current broker can be read by the current db_dump.\n\nfrom mosq_test_helper import *\nimport shutil\n\ndef write_config(conf_file, port):\n    with open(conf_file, 'w') as f:\n        f.write(f\"listener {port}\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(f\"persistence_location {port}\\n\")\n\ndef check_db(port, counts):\n    stdout = f\"DB_CHUNK_CFG:        {counts[0]}\\n\" + \\\n        f\"DB_CHUNK_BASE_MSG:   {counts[1]}\\n\" + \\\n        f\"DB_CHUNK_CLIENT_MSG: {counts[2]}\\n\" + \\\n        f\"DB_CHUNK_RETAIN:     {counts[3]}\\n\" + \\\n        f\"DB_CHUNK_SUB:        {counts[4]}\\n\" + \\\n        f\"DB_CHUNK_CLIENT:     {counts[5]}\\n\"\n\n    cmd = [\n        mosq_test.get_build_root()+'/apps/db_dump/mosquitto_db_dump',\n        '--stats',\n        f'{port}/mosquitto.db'\n    ]\n    res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1, encoding='utf-8')\n    if res.stdout != stdout:\n        print(res.stdout)\n        raise mosq_test.TestError\n\n\ndef do_test(counts):\n    rc = 1\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    try:\n        if not os.path.exists(str(port)):\n            os.mkdir(str(port))\n    except FileExistsError:\n        pass\n    write_config(conf_file, port)\n\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n        env = {\n            'XDG_CONFIG_HOME':'/tmp/missing'\n        }\n        env = mosq_test.env_add_ld_library_path(env)\n\n        # Set up persistent client session, including a subscription\n        cmd = [\n            mosq_test.get_build_root()+'/client/mosquitto_sub',\n            '-c',\n            '-i', 'client-id',\n            '-p', str(port),\n            '-q', '1',\n            '-t', 'sub-topic',\n            '-E'\n        ]\n        subprocess.run(cmd, timeout=1, env=env)\n\n        # Publish a retained message which is also queued for the subscriber\n        cmd = [\n            mosq_test.get_build_root()+'/client/mosquitto_pub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', 'sub-topic',\n            '-m', 'message',\n            '-r'\n        ]\n        subprocess.run(cmd, timeout=1, env=env)\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            raise mosq_test.TestError\n        check_db(port, counts)\n        rc = 0\n\n    except Exception as e:\n        print(e)\n    finally:\n        os.remove(conf_file)\n        os.remove(f\"{port}/mosquitto.db\")\n        shutil.rmtree(str(port))\n        if broker is not None:\n            broker.terminate()\n\n    exit(rc)\n\ndo_test([1,1,1,1,1,1])\n"
  },
  {
    "path": "test/apps/db_dump/db-dump-stats.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(file, counts):\n    stdout = f\"DB_CHUNK_CFG:        {counts[0]}\\n\" + \\\n        f\"DB_CHUNK_BASE_MSG:   {counts[1]}\\n\" + \\\n        f\"DB_CHUNK_CLIENT_MSG: {counts[2]}\\n\" + \\\n        f\"DB_CHUNK_RETAIN:     {counts[3]}\\n\" + \\\n        f\"DB_CHUNK_SUB:        {counts[4]}\\n\" + \\\n        f\"DB_CHUNK_CLIENT:     {counts[5]}\\n\"\n\n    cmd = [\n        mosq_test.get_build_root()+'/apps/db_dump/mosquitto_db_dump',\n            '--stats',\n            f'{test_dir}/apps/db_dump/data/{file}'\n    ]\n\n    res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1, encoding='utf-8')\n    if res.stdout != stdout:\n        print(res.stdout)\n        raise mosq_test.TestError\n\ndo_test('v3-empty.test-db', [1,0,0,0,0,0])\ndo_test('v4-empty.test-db', [1,0,0,0,0,0])\ndo_test('v5-empty.test-db', [1,0,0,0,0,0])\ndo_test('v6-empty.test-db', [1,0,0,0,0,0])\n\ndo_test('v4-single-client.test-db', [1,0,0,0,0,1])\ndo_test('v6-single-client.test-db', [1,0,0,0,0,1])\n\ndo_test('v4-single-retain.test-db', [1,1,0,1,0,0])\ndo_test('v6-single-retain.test-db', [1,1,0,1,0,0])\n\ndo_test('v4-single-sub.test-db', [1,0,0,0,1,1])\ndo_test('v6-single-sub.test-db', [1,0,0,0,1,1])\n\ndo_test('v4-single-cmsg.test-db', [1,1,1,0,1,1])\ndo_test('v6-single-cmsg.test-db', [1,1,1,0,1,1])\n\ndo_test('v6-single-all.test-db', [1,1,1,1,1,1])\n"
  },
  {
    "path": "test/apps/db_dump/mosq_test_helper.py",
    "content": "import logging\nimport sys\nfrom pathlib import Path\n\nlogging.basicConfig(\n    level=logging.INFO,\n    format=\"%(levelname)s %(asctime)s.%(msecs)03d %(module)s: %(message)s\",\n    datefmt=\"%H:%M:%S\",\n)\n\ncurrent_source_dir = Path(__file__).resolve().parent\ntest_dir = current_source_dir.parents[1]\nif test_dir not in sys.path:\n    sys.path.insert(0, str(test_dir))\n\nssl_dir = test_dir / \"ssl\"\n\nimport mosq_test\nimport subprocess\nimport os\n"
  },
  {
    "path": "test/apps/db_dump/test.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport pathlib\nimport sys\nsys.path.insert(0, \"../..\")\nimport ptest\n\ntests = []\n\nfor test_file in pathlib.Path(os.path.abspath(os.path.dirname(__file__))).glob('db-dump-*.py'):\n    tests.append((1, test_file.resolve()))\n\nif __name__ == \"__main__\":\n    test = ptest.PTest()\n    test.run_tests(tests)\n"
  },
  {
    "path": "test/apps/mosq_test_helper.py",
    "content": "import inspect, os, sys\n\n# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder\ncmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],\"..\")))\nif cmd_subfolder not in sys.path:\n    sys.path.insert(0, cmd_subfolder)\n\nimport mosq_test\nimport mqtt5_opts\nimport mqtt5_props\nimport mqtt5_rc\n\nimport socket\nimport ssl\nimport struct\nimport subprocess\nimport time\nimport errno\n"
  },
  {
    "path": "test/apps/passwd/CMakeLists.txt",
    "content": "file(GLOB PY_TEST_FILES passwd-*.py)\n\nset(EXCLUDE_LIST\n    # none\n)\n\nforeach(PY_TEST_FILE ${PY_TEST_FILES})\n    get_filename_component(PY_TEST_NAME ${PY_TEST_FILE} NAME_WE)\n    if(${PY_TEST_NAME} IN_LIST EXCLUDE_LIST)\n        continue()\n    endif()\n    add_test(NAME apps-${PY_TEST_NAME}\n        COMMAND ${PY_TEST_FILE}\n    )\n    set_tests_properties(apps-${PY_TEST_NAME}\n        PROPERTIES\n            ENVIRONMENT \"BUILD_ROOT=${CMAKE_BINARY_DIR}\"\n    )\nendforeach()\n"
  },
  {
    "path": "test/apps/passwd/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all check test ptest clean\n.NOTPARALLEL:\n\nall :\n\ncheck : test\n\ntest :\n\t./passwd-args.py\n\t./passwd-changes.py\n\t./passwd-stdout.py\n\nptest :\n\t./test.py\n\nclean:\n"
  },
  {
    "path": "test/apps/passwd/mosq_test_helper.py",
    "content": "import logging\nimport sys\nfrom pathlib import Path\n\nlogging.basicConfig(\n    level=logging.INFO,\n    format=\"%(levelname)s %(asctime)s.%(msecs)03d %(module)s: %(message)s\",\n    datefmt=\"%H:%M:%S\",\n)\n\ncurrent_source_dir = Path(__file__).resolve().parent\ntest_dir = current_source_dir.parents[1]\nif test_dir not in sys.path:\n    sys.path.insert(0, str(test_dir))\n\nssl_dir = test_dir / \"ssl\"\n\nimport mosq_test\nimport subprocess\nimport os\n"
  },
  {
    "path": "test/apps/passwd/passwd-args.py",
    "content": "#!/usr/bin/env python3\n\n# Test parsing of command line args and errors. Does not test arg functionality.\n\nfrom mosq_test_helper import *\n\ndef do_test(args, rc_expected, response=None, input=None):\n    proc = subprocess.run([mosq_test.get_build_root()+\"/apps/mosquitto_passwd/mosquitto_passwd\"]\n                    + args,\n                    capture_output=True, encoding='utf-8', timeout=2, input=input)\n\n    if response is not None:\n        if proc.stderr != response:\n            print(len(proc.stderr))\n            print(len(response))\n            raise ValueError(proc.stderr)\n\n    if proc.returncode != rc_expected:\n        print(proc.returncode)\n        raise ValueError(args)\n\ndo_test([], 1) # For the usage message\ndo_test([\"-H\"], 1, response=\"Error: -H argument given but not enough other arguments.\\n\")\ndo_test([\"-H\", \"nohash\"], 1, response=\"Error: Unknown hash type 'nohash'\\n\")\ndo_test([\"-I\"], 1, response=\"Error: -I argument given but not enough other arguments.\\n\")\ndo_test([\"-I\", \"0\"], 1, response=\"Error: Number of iterations must be > 0.\\n\")\ndo_test([\"-c\", \"-D\"], 1, response=\"Error: -c and -D cannot be used together.\\n\")\ndo_test([\"-c\", \"-U\"], 1, response=\"Error: -c and -U cannot be used together.\\n\")\ndo_test([\"-U\", \"-D\"], 1, response=\"Error: -D and -U cannot be used together.\\n\")\ndo_test([\"-b\", \"-D\"], 1, response=\"Error: -b and -D cannot be used together.\\n\")\ndo_test([\"-c\", \"-b\"], 1, response=\"Error: -c argument given but password file, username, or password missing.\\n\")\ndo_test([\"-c\"], 1, response=\"Error: -c argument given but password file or username missing.\\n\")\ndo_test([\"-D\"], 1, response=\"Error: -D argument given but password file or username missing.\\n\")\ndo_test([\"-U\"], 1, response=\"Error: -U argument given but password file missing.\\n\")\ndo_test([\"-D\", \"pwfile\", \"bad-username:\"], 1, response=\"Error: Username must not contain the ':' character.\\n\")\ndo_test([\"-D\", \"pwfile\", \"bad-username\\n\"], 1, response=\"Error: Username must not contain control characters.\\n\")\ndo_test([\"-D\", \"pwfile\", \"a\"*65536], 1, response=\"Error: Username must be less than 65536 characters long.\\n\")\n\ndo_test([\"-c\", \"file\", \"username\"], 2, response=\"Error: Passwords do not match.\\n\", input=\"not\\nmatching\\n\")\n\nexit(0)\n"
  },
  {
    "path": "test/apps/passwd/passwd-changes.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport json\nimport shutil\nimport signal\n\ndef write_config(filename, pw_file, port):\n    with open(filename, 'w') as f:\n        f.write(f\"listener {port}\\n\")\n        f.write(\"allow_anonymous false\\n\")\n        f.write(f\"password_file {pw_file}\\n\")\n\ndef client_check(port, username, password, rc):\n    connect_packet = mosq_test.gen_connect(\"pwd-test\", username=username, password=password)\n    connack_packet = mosq_test.gen_connack(rc=rc)\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    sock.close()\n\n\ndef passwd_cmd(args, response=None, input=None, expected_rc=0):\n    proc = subprocess.run([mosq_test.get_build_root()+\"/apps/mosquitto_passwd/mosquitto_passwd\"]\n                    + args,\n                    capture_output=True, encoding='utf-8', timeout=2, input=input)\n\n    if response is not None:\n        if proc.stdout != response and proc.stderr != response:\n            print(f\"stdout: {proc.stdout}\")\n            print(f\"stderr: {proc.stderr}\")\n            print(f\"expected: {response}\")\n            raise ValueError(proc.stdout)\n\n    if proc.returncode != expected_rc:\n        print(proc.returncode)\n        print(expected_rc)\n        raise ValueError(args)\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npw_file = os.path.basename(__file__).replace('.py', '.pwfile')\nwrite_config(conf_file, pw_file, port)\n\n# Generate initial password file\npasswd_cmd([\"-H\", \"sha512\", \"-c\", \"-b\", pw_file, \"user1\", \"pass1\"])\npasswd_cmd([\"-H\", \"sha512-pbkdf2\", pw_file, \"user2\"], input=\"cmd\\ncmd\\n\")\npasswd_cmd([\"-H\", \"sha512-pbkdf2\", pw_file, \"user3\"], input=\"pass3\\npass3\\n\")\n#passwd_cmd([\"-H\", \"argon2id\", pw_file, \"user3\"], input=\"pass3\\npass3\\n\")\ntry:\n    # If we're root, set file ownership to \"nobody\", because that is the user\n    # the broker will change to.\n    os.chown(pw_file, 65534, 65534)\nexcept PermissionError:\n    pass\n\n# Then start broker\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port, nolog=True)\n\ntry:\n    rc = 1\n    client_check(port, \"user1\", \"badpass\", 5)\n    client_check(port, \"user1\", \"pass1\", 0)\n    client_check(port, \"user2\", \"badpass\", 5)\n    client_check(port, \"user2\", \"cmd\", 0)\n    client_check(port, \"user3\", \"badpass\", 5)\n    client_check(port, \"user3\", \"pass3\", 0)\n    client_check(port, \"baduser\", \"badpass\", 5)\n    client_check(port, \"baduser\", \"goodpass\", 5)\n\n    # Update password\n    passwd_cmd([\"-H\", \"sha512-pbkdf2\", \"-b\", pw_file, \"user1\", \"newpass\"])\n    broker.send_signal(signal.SIGHUP)\n\n    client_check(port, \"user1\", \"badpass\", 5)\n    client_check(port, \"user1\", \"newpass\", 0)\n    client_check(port, \"user2\", \"badpass\", 5)\n    client_check(port, \"user2\", \"cmd\", 0)\n    client_check(port, \"user3\", \"badpass\", 5)\n    client_check(port, \"user3\", \"pass3\", 0)\n    client_check(port, \"baduser\", \"badpass\", 5)\n    client_check(port, \"baduser\", \"goodpass\", 5)\n\n    # New user\n    passwd_cmd([\"-b\", pw_file, \"newuser\", \"goodpass\"])\n    broker.send_signal(signal.SIGHUP)\n\n    client_check(port, \"user1\", \"badpass\", 5)\n    client_check(port, \"user1\", \"newpass\", 0)\n    client_check(port, \"user2\", \"badpass\", 5)\n    client_check(port, \"user2\", \"cmd\", 0)\n    client_check(port, \"user3\", \"badpass\", 5)\n    client_check(port, \"user3\", \"pass3\", 0)\n    client_check(port, \"newuser\", \"badpass\", 5)\n    client_check(port, \"newuser\", \"goodpass\", 0)\n\n    # Delete user\n    passwd_cmd([\"-D\", pw_file, \"user2\"])\n    passwd_cmd([\"-D\", pw_file, \"user2\"], response=\"Warning: User user2 not found in password file.\\n\", expected_rc=1)\n    broker.send_signal(signal.SIGHUP)\n\n    client_check(port, \"user1\", \"badpass\", 5)\n    client_check(port, \"user1\", \"newpass\", 0)\n    client_check(port, \"user2\", \"badpass\", 5)\n    client_check(port, \"user2\", \"cmd\", 5)\n    client_check(port, \"user3\", \"badpass\", 5)\n    client_check(port, \"user3\", \"pass3\", 0)\n    client_check(port, \"newuser\", \"badpass\", 5)\n    client_check(port, \"newuser\", \"goodpass\", 0)\n\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nexcept Exception as err:\n    print(err)\nfinally:\n    os.remove(conf_file)\n    os.remove(pw_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n\nexit(rc)\n"
  },
  {
    "path": "test/apps/passwd/passwd-stdout.py",
    "content": "#!/usr/bin/env python3\n\n# Test parsing of command line args and errors. Does not test arg functionality.\n\nfrom mosq_test_helper import *\n\ndef do_test(args, rc_expected, response=None, input=None):\n    proc = subprocess.run([mosq_test.get_build_root()+\"/apps/mosquitto_passwd/mosquitto_passwd\"]\n                    + args,\n                    capture_output=True, encoding='utf-8', timeout=2, input=input)\n\n    if response is not None:\n        if proc.stdout[0:len(response)] != response:\n            print(len(proc.stdout))\n            print(len(response))\n            print(proc.stdout[0:len(response)])\n            print(response)\n            raise ValueError(proc.stdout)\n\n    if proc.returncode != rc_expected:\n        print(proc.returncode)\n        raise ValueError(args)\n\nresp = \"Password: \\nReenter password: \\nstdout:$7$1000$\"\ndo_test([\"-c\", \"-\", \"stdout\"], 0, response=resp, input=\"pw\\npw\\n\")\n\nresp = \"stdout:$7$1000$\"\ndo_test([\"-b\", \"-c\", \"-\", \"stdout\", \"pw\"], 0, response=resp)\n\nexit(0)\n"
  },
  {
    "path": "test/apps/passwd/test.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport pathlib\nimport sys\nsys.path.insert(0, \"../..\")\nimport ptest\n\ntests = []\n\nfor test_file in pathlib.Path(os.path.abspath(os.path.dirname(__file__))).glob('passwd-*.py'):\n    tests.append((1, test_file.resolve()))\n\nif __name__ == \"__main__\":\n    test = ptest.PTest()\n    test.run_tests(tests)\n"
  },
  {
    "path": "test/apps/signal/CMakeLists.txt",
    "content": "file(GLOB PY_TEST_FILES signal-*.py)\n\nset(EXCLUDE_LIST\n    # none\n)\n\nforeach(PY_TEST_FILE ${PY_TEST_FILES})\n    get_filename_component(PY_TEST_NAME ${PY_TEST_FILE} NAME_WE)\n    if(${PY_TEST_NAME} IN_LIST EXCLUDE_LIST)\n        continue()\n    endif()\n    add_test(NAME apps-${PY_TEST_NAME}\n        COMMAND ${PY_TEST_FILE}\n    )\n    set_tests_properties(apps-${PY_TEST_NAME}\n        PROPERTIES\n            ENVIRONMENT \"BUILD_ROOT=${CMAKE_BINARY_DIR}\"\n    )\nendforeach()\n"
  },
  {
    "path": "test/apps/signal/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all check test ptest clean\n.NOTPARALLEL:\n\nall :\n\ncheck : test\n\ntest :\n\t./signal-args.py\n\nptest :\n\t./test.py\n\nclean:\n"
  },
  {
    "path": "test/apps/signal/mosq_test_helper.py",
    "content": "import logging\nimport sys\nfrom pathlib import Path\n\nlogging.basicConfig(\n    level=logging.INFO,\n    format=\"%(levelname)s %(asctime)s.%(msecs)03d %(module)s: %(message)s\",\n    datefmt=\"%H:%M:%S\",\n)\n\ncurrent_source_dir = Path(__file__).resolve().parent\ntest_dir = current_source_dir.parents[1]\nif test_dir not in sys.path:\n    sys.path.insert(0, str(test_dir))\n\nssl_dir = test_dir / \"ssl\"\n\nimport mosq_test\nimport subprocess\nimport os\n"
  },
  {
    "path": "test/apps/signal/signal-args.py",
    "content": "#!/usr/bin/env python3\n\n# Test parsing of command line args and errors. Does not test arg functionality.\n\nfrom mosq_test_helper import *\n\ndef do_test(args, rc_expected, response=None, input=None):\n    proc = subprocess.run([mosq_test.get_build_root()+\"/apps/mosquitto_signal/mosquitto_signal\"]\n                    + args,\n                    capture_output=True, encoding='utf-8', timeout=2, input=input)\n\n    if response is not None:\n        if proc.stderr != response:\n            print(len(proc.stderr))\n            print(len(response))\n            raise ValueError(proc.stderr)\n\n    if proc.returncode != rc_expected:\n        print(proc.returncode)\n        raise ValueError(args)\n\ndo_test([], 1) # For the usage message\ndo_test([\"--help\"], 1)\ndo_test([\"--invalid\"], 1, response=\"Error: One of -a or -p must be used.\\n\")\ndo_test([\"-p\"], 1, response=\"Error: -p argument given but process ID missing.\\n\")\ndo_test([\"-p\", \"0\"], 1, response=\"Error: Process ID must be >0.\\n\")\ndo_test([\"-p\", \"1\"], 1, response=\"Error: No signal given.\\n\")\ndo_test([\"-a\"], 1, response=\"Error: No signal given.\\n\")\ndo_test([\"-p\", \"1\", \"invalid\"], 1, response=\"Error: Unknown signal 'invalid'.\\n\")\ndo_test([\"-p\", \"1\", \"config-reload\"], 0)\ndo_test([\"-p\", \"1\", \"log-rotate\"], 0)\ndo_test([\"-p\", \"1\", \"shutdown\"], 0)\ndo_test([\"-p\", \"1\", \"tree-print\"], 0)\ndo_test([\"-p\", \"1\", \"xtreport\"], 0)\ndo_test([\"-a\", \"config-reload\"], 0)\nexit(0)\n"
  },
  {
    "path": "test/apps/signal/test.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport pathlib\nimport sys\nsys.path.insert(0, \"../..\")\nimport ptest\n\ntests = []\n\nfor test_file in pathlib.Path(os.path.abspath(os.path.dirname(__file__))).glob('signal-*.py'):\n    tests.append((1, test_file.resolve()))\n\nif __name__ == \"__main__\":\n    test = ptest.PTest()\n    test.run_tests(tests)\n"
  },
  {
    "path": "test/broker/01-bad-initial-packets.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether non-CONNECT packets as an initial packet can cause excess memory use\n\nfrom mosq_test_helper import *\ntry:\n    import psutil\nexcept ModuleNotFoundError:\n    print(\"WARNING: Test not running due to missing psutil module\")\n    exit(0)\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(f\"listener {port}\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"sys_interval 1\\n\")\n\ndef do_send(port, socks, payload):\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    socks.append(sock)\n    sock.connect((\"127.0.0.1\", port))\n    try:\n        sock.send(payload)\n    except (ConnectionResetError, BrokenPipeError):\n        pass\n\ndef do_test(port):\n    rc = 1\n\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\n    # Get the base memory useage before any connection attempt has happen\n    base_mem = psutil.Process(broker.pid).memory_info().vms\n    try:\n        socks = []\n\n        do_send(port, socks, b\"\\x20\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # CONNACK\n        do_send(port, socks, b\"\\x30\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # PUBLISH\n        do_send(port, socks, b\"\\x40\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # PUBACK\n        do_send(port, socks, b\"\\x50\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # PUBREC\n        do_send(port, socks, b\"\\x60\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # PUBREL\n        do_send(port, socks, b\"\\x70\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # PUBCOMP\n        do_send(port, socks, b\"\\x80\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # SUBSCRIBE\n        do_send(port, socks, b\"\\x90\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # SUBACK\n        do_send(port, socks, b\"\\xA0\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # UNSUBSCRIBE\n        do_send(port, socks, b\"\\xB0\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # UNSUBACK\n        do_send(port, socks, b\"\\xC0\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # PINGREQ\n        do_send(port, socks, b\"\\xD0\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # PINGRESP\n        do_send(port, socks, b\"\\xE0\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # DISCONNECT\n        do_send(port, socks, b\"\\xF0\\x80\\x80\\x80t\" + b\"\\01\"*100000000) # AUTH\n\n        mem = psutil.Process(broker.pid).memory_info().vms - base_mem\n\n        for s in socks:\n            s.close()\n\n        limit = 20000000\n        if mem > limit:\n            raise mosq_test.TestError(f\"Process memory {mem} greater than limit of {limit}\")\n\n        rc = 0\n    except MemoryError:\n        print(\"Memory error!\")\n    except Exception as e:\n        print(e)\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        broker.wait()\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\nport = mosq_test.get_port()\n\ndo_test(port)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-575314.py",
    "content": "#!/usr/bin/env python3\n\n# Check for performance of processing user-property on CONNECT\n\nfrom mosq_test_helper import *\n\ndef do_test():\n    rc = 1\n    props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\n    for i in range(0, 5000):\n        props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\n    connect_packet_slow = mosq_test.gen_connect(\"connect-user-property\", proto_ver=5, properties=props)\n    connect_packet_fast = mosq_test.gen_connect(\"a\"*65000, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        t_start = time.monotonic()\n        sock = mosq_test.do_client_connect(connect_packet_slow, connack_packet, port=port)\n        t_stop = time.monotonic()\n        sock.close()\n\n        t_diff_slow = t_stop - t_start\n\n        t_start = time.monotonic()\n        sock = mosq_test.do_client_connect(connect_packet_fast, connack_packet, port=port)\n        t_stop = time.monotonic()\n        sock.close()\n\n        t_diff_fast = t_stop - t_start\n        # 20 is chosen as a factor that works in plain mode and running under\n        # valgrind. The slow performance manifests as a factor of >100. Fast is <10.\n        if t_diff_slow / t_diff_fast < 20:\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test()\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-accept-protocol.py",
    "content": "#!/usr/bin/env python3\n\n# Test accept_protocol_version option\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, accept):\n    with open(filename, 'w') as f:\n        f.write(\"listener %s\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"accept_protocol_versions %s\\n\" % (accept))\n\ndef do_test(accept, expect_success):\n    port = mosq_test.get_port()\n\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, accept)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        for proto_ver in [3, 4, 5]:\n            rc = 1\n            connect_packet = mosq_test.gen_connect(\"accept-protocol-test-%d\" % (proto_ver), proto_ver=proto_ver)\n\n            if proto_ver == 5:\n                if proto_ver in expect_success:\n                    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n                else:\n                    connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.UNSUPPORTED_PROTOCOL_VERSION, proto_ver=proto_ver, properties=None)\n            else:\n                if proto_ver in expect_success:\n                    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n                else:\n                    connack_packet = mosq_test.gen_connack(rc=1, proto_ver=proto_ver)\n\n\n            sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n            sock.close()\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        if write_config is not None:\n            os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(accept=\"3,4,5\", expect_success=[3, 4, 5])\ndo_test(accept=\"5,4,3\", expect_success=[3, 4, 5])\ndo_test(accept=\"3 ,4, 5\", expect_success=[3, 4, 5])\ndo_test(accept=\"    ,   3   ,    4  ,   5    \", expect_success=[3, 4, 5])\ndo_test(accept=\"3\", expect_success=[3])\ndo_test(accept=\"4\", expect_success=[4])\ndo_test(accept=\"5\", expect_success=[5])\ndo_test(accept=\"3,4\", expect_success=[3, 4])\ndo_test(accept=\"3,5\", expect_success=[3, 5])\ndo_test(accept=\"4,3\", expect_success=[3, 4])\ndo_test(accept=\"4,5\", expect_success=[4, 5])\ndo_test(accept=\"5,3\", expect_success=[3, 5])\n"
  },
  {
    "path": "test/broker/01-connect-allow-anonymous.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether an anonymous connection is correctly denied.\n\nfrom mosq_test_helper import *\n\ndef write_config1(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"max_connections 10\\n\") # So the file isn't completely empty\n\ndef write_config2(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"port %d\\n\" % (port))\n\ndef write_config3(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n\ndef write_config4(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"port %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n\ndef write_config5(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n\ndef write_config6(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous false\\n\")\n\ndef write_config7(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n\ndef write_config8(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"listener_allow_anonymous true\\n\")\n\ndef write_config9(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"listener_allow_anonymous false\\n\")\n\n\ndef do_test(use_conf, write_config, expect_success):\n    port = mosq_test.get_port()\n    if write_config is not None:\n        conf_file = os.path.basename(__file__).replace('.py', '.conf')\n        write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=use_conf, port=port)\n\n    try:\n        for proto_ver in [4, 5]:\n            rc = 1\n            connect_packet = mosq_test.gen_connect(\"connect-anon-test-%d\" % (proto_ver), proto_ver=proto_ver)\n\n            if proto_ver == 5:\n                if expect_success == True:\n                    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n                else:\n                    connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=proto_ver, properties=None)\n            else:\n                if expect_success == True:\n                    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n                else:\n                    connack_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver)\n\n\n            sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n            sock.close()\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        if write_config is not None:\n            os.remove(conf_file)\n            pass\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\n# No config file - allow_anonymous should be true\ndo_test(use_conf=False, write_config=None, expect_success=True)\n\n# Config file but no listener - allow_anonymous should be true\ndo_test(use_conf=True, write_config=write_config1, expect_success=True)\n\n# Config file with \"port\" - allow_anonymous should be false\ndo_test(use_conf=True, write_config=write_config2, expect_success=False)\n\n# Config file with \"listener\" - allow_anonymous should be false\ndo_test(use_conf=True, write_config=write_config3, expect_success=False)\n\n# Config file with \"port\" - allow_anonymous explicitly true\ndo_test(use_conf=True, write_config=write_config4, expect_success=True)\n\n# Config file with \"listener\" - allow_anonymous explicitly true\ndo_test(use_conf=True, write_config=write_config5, expect_success=True)\n\n# Config file without \"listener\" - allow_anonymous explicitly false\ndo_test(use_conf=True, write_config=write_config6, expect_success=False)\n\n# Config file without \"listener\" - allow_anonymous explicitly true\ndo_test(use_conf=True, write_config=write_config7, expect_success=True)\n\n# Config file with \"listener\" - allow_anonymous explicitly false and listener_allow_anonymous explicitly true\ndo_test(use_conf=True, write_config=write_config8, expect_success=True)\n\n# Config file with \"listener\" - allow_anonymous explicitly true and listener_allow_anonymous explicitly false\ndo_test(use_conf=True, write_config=write_config9, expect_success=False)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-auto-id.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config1(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(f\"listener {port2}\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port1}\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\ndef write_config2(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"auto_id_prefix new-\\n\")\n        f.write(f\"listener {port2}\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port1}\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\ndef write_config3(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(f\"listener {port2}\\n\")\n        f.write(\"listener_auto_id_prefix port2-\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port1}\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\ndef write_config4(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(f\"listener {port2}\\n\")\n        f.write(\"listener_auto_id_prefix port2-\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port1}\\n\")\n        f.write(\"listener_auto_id_prefix port1-\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\ndef write_config5(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"auto_id_prefix global-\\n\")\n        f.write(f\"listener {port2}\\n\")\n        f.write(\"listener_auto_id_prefix port2-\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port1}\\n\")\n        f.write(\"listener_auto_id_prefix port1-\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\ndef write_config6(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"auto_id_prefix global-\\n\")\n        f.write(f\"listener {port2}\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port1}\\n\")\n        f.write(\"listener_auto_id_prefix port1-\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef do_test(config_func, client_port, auto_id):\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    config_func(conf_file, port1, port2)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"\", proto_ver=5)\n    props = mqtt5_props.gen_string_prop(mqtt5_props.ASSIGNED_CLIENT_IDENTIFIER, f\"{auto_id}xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\")\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n    # Remove the \"xxxx\" part - this means the front part of the packet\n    # is correct (so remaining length etc. is correct), but we don't\n    # need to match against the random id.\n    connack_packet = connack_packet[:-44]\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port1, use_conf=True)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=client_port)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        os.remove(conf_file)\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\n(port1, port2) = mosq_test.get_port(2)\n\ndo_test(config_func=write_config1, client_port=port1, auto_id=\"auto-\")\ndo_test(config_func=write_config1, client_port=port2, auto_id=\"auto-\")\ndo_test(config_func=write_config2, client_port=port1, auto_id=\"new-\")\ndo_test(config_func=write_config2, client_port=port2, auto_id=\"new-\")\ndo_test(config_func=write_config3, client_port=port1, auto_id=\"auto-\")\ndo_test(config_func=write_config3, client_port=port2, auto_id=\"port2-\")\ndo_test(config_func=write_config4, client_port=port1, auto_id=\"port1-\")\ndo_test(config_func=write_config4, client_port=port2, auto_id=\"port2-\")\ndo_test(config_func=write_config5, client_port=port1, auto_id=\"port1-\")\ndo_test(config_func=write_config5, client_port=port2, auto_id=\"port2-\")\ndo_test(config_func=write_config6, client_port=port1, auto_id=\"port1-\")\ndo_test(config_func=write_config6, client_port=port2, auto_id=\"global-\")\n\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-disconnect-v5.py",
    "content": "#!/usr/bin/env python3\n\n# loop through the different v5 DISCONNECT reason_code/properties options.\n\nfrom mosq_test_helper import *\n\nrc = 0\nport = mosq_test.get_port()\n\ndef disco_test(test, disconnect_packet):\n    global rc\n\n    connect1_packet = mosq_test.gen_connect(\"sub\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"failure\", 0, proto_ver=5)\n    suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    connect2_packet = mosq_test.gen_connect(\"connect-disconnect-test\", proto_ver=5, will_topic=\"failure\", will_payload=b\"failure\")\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, port=port)\n    mosq_test.do_send_receive(sock1, subscribe1_packet, suback1_packet, \"suback1\")\n\n\n    sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port)\n    sock2.send(disconnect_packet)\n    sock2.close()\n\n    # If this fails then we probably received the will\n    mosq_test.do_ping(sock1)\n\n    rc -= 1\n\ndef do_test(start_broker):\n    global rc\n\n    rc = 4\n\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n\n    try:\n        # No reason code, no properties, len=0\n        disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n        disco_test(\"disco len=0\", disconnect_packet)\n\n        # Reason code, no properties, len=1\n        disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=0)\n        disco_test(\"disco len=1\", disconnect_packet)\n\n        # Reason code, empty properties, len=2\n        disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=0, properties=\"\")\n        disco_test(\"disco len=2\", disconnect_packet)\n\n        # Reason code, one property, len>2\n        props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\n        disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=0, properties=props)\n        disco_test(\"disco len>2\", disconnect_packet)\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n            exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/01-connect-global-max-clients.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether global_max_clients works\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"global_max_clients 10\\n\")\n\ndef do_test():\n    rc = 1\n\n    connect_packets_ok = []\n    connack_packets_ok = []\n    connect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\n    for i in range(0, 10):\n        connect_packets_ok.append(mosq_test.gen_connect(\"max-conn-%d\"%i, proto_ver=5, properties=connect_props))\n        connack_packets_ok.append(mosq_test.gen_connack(rc=0, proto_ver=5))\n\n    connect_packet_bad = mosq_test.gen_connect(\"max-conn-bad\", proto_ver=5)\n    connack_packet_bad = mosq_test.gen_connack(rc=mqtt5_rc.SERVER_BUSY, proto_ver=5, property_helper=False)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    socks = []\n    try:\n        # Open all allowed connections, a limit of 10\n        for i in range(0, 10):\n            socks.append(mosq_test.do_client_connect(connect_packets_ok[i], connack_packets_ok[i], port=port))\n\n        # Try to open an 11th connection\n        try:\n            sock_bad = mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port)\n        except (ConnectionResetError, BrokenPipeError):\n            # Expected behaviour\n            pass\n\n        # Close all allowed connections\n        for i in range(0, 10):\n            socks[i].close()\n\n        ## Session expiry means those clients sessions are still active\n\n        # Try to open an 11th connection\n        try:\n            sock_bad = mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port)\n        except (ConnectionResetError, BrokenPipeError):\n            # Expected behaviour\n            pass\n\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    except Exception as err:\n        print(err)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n    return rc\n\nsys.exit(do_test())\n"
  },
  {
    "path": "test/broker/01-connect-global-max-connections.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether global_max_connections works\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"global_max_connections 10\\n\")\n\ndef do_test():\n    rc = 1\n\n    connect_packets_ok = []\n    connack_packets_ok = []\n    for i in range(0, 10):\n        connect_packets_ok.append(mosq_test.gen_connect(\"max-conn-%d\"%i, proto_ver=5))\n        connack_packets_ok.append(mosq_test.gen_connack(rc=0, proto_ver=5))\n\n    connect_packet_bad = mosq_test.gen_connect(\"max-conn-bad\", proto_ver=5)\n    connack_packet_bad = b\"\"\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    socks = []\n    try:\n        # Open all allowed connections, a limit of 10\n        for i in range(0, 10):\n            socks.append(mosq_test.do_client_connect(connect_packets_ok[i], connack_packets_ok[i], port=port))\n\n        # Try to open an 11th connection\n        try:\n            mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port)\n            print(\"did not throw when trying to open 11th connection (first time)\")\n            return rc\n        except (ConnectionResetError, BrokenPipeError, OSError):\n            # Expected behaviour\n            pass\n        finally:\n            # Close all allowed connections\n            for sock in socks:\n                sock.close()\n            socks.clear()\n\n        ## Now repeat - check it works as before\n\n        if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n            time.sleep(0.5)\n\n        # Open all allowed connections, a limit of 10\n        for i in range(0, 10):\n            socks.append(mosq_test.do_client_connect(connect_packets_ok[i], connack_packets_ok[i], port=port))\n\n        # Try to open an 11th connection\n        try:\n            mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port)\n            print(\"did not throw when trying to open 11th connection (second time)\")\n            return rc\n        except (ConnectionResetError, BrokenPipeError, OSError):\n            # Expected behaviour\n            pass\n        finally:\n            # Close all allowed connections\n            for sock in socks:\n                sock.close()\n            socks.clear()\n\n        rc = 0\n    except Exception as err:\n        raise err\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n    return rc\n\nsys.exit(do_test())\n"
  },
  {
    "path": "test/broker/01-connect-listener-allow-anonymous.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether an anonymous connection is correctly denied.\n\nfrom mosq_test_helper import *\n\ndef write_config1(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"listener_allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"listener_allow_anonymous false\\n\")\n\ndef write_config2(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"listener_allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"listener_allow_anonymous false\\n\")\n\ndef write_config3(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"listener_allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"listener_allow_anonymous true\\n\")\n\ndef write_config4(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"listener_allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"listener_allow_anonymous false\\n\")\n\ndef write_config5(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"listener_allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"listener_allow_anonymous false\\n\")\n\ndef write_config6(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"listener_allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"listener_allow_anonymous true\\n\")\n\ndef write_config7(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"listener_allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"listener_allow_anonymous false\\n\")\n\ndef write_config8(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"listener_allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"listener_allow_anonymous false\\n\")\n\ndef write_config9(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"listener_allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"listener_allow_anonymous true\\n\")\n\n\ndef do_test(write_config, expect_success1, expect_success2):\n    port1, port2 = mosq_test.get_port(2)\n    if write_config is not None:\n        conf_file = os.path.basename(__file__).replace('.py', '.conf')\n        write_config(conf_file, port1, port2)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1)\n\n    try:\n        for proto_ver in [4, 5]:\n            rc = 1\n            connect_packet = mosq_test.gen_connect(f\"connect-anon-test-{proto_ver}-{expect_success1}-{expect_success2}\", proto_ver=proto_ver)\n\n            if proto_ver == 5:\n                connack_packet_success = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n                connack_packet_rejected = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=proto_ver, properties=None)\n            else:\n                connack_packet_success = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n                connack_packet_rejected = mosq_test.gen_connack(rc=5, proto_ver=proto_ver)\n\n\n            if expect_success1:\n                sock = mosq_test.do_client_connect(connect_packet, connack_packet_success, port=port1)\n            else:\n                sock = mosq_test.do_client_connect(connect_packet, connack_packet_rejected, port=port1)\n            sock.close()\n\n            if expect_success2:\n                sock = mosq_test.do_client_connect(connect_packet, connack_packet_success, port=port2)\n            else:\n                sock = mosq_test.do_client_connect(connect_packet, connack_packet_rejected, port=port2)\n            sock.close()\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        if write_config is not None:\n            os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(write_config=write_config1, expect_success1=False, expect_success2=False)\ndo_test(write_config=write_config2, expect_success1=True, expect_success2=False)\ndo_test(write_config=write_config3, expect_success1=False, expect_success2=True)\ndo_test(write_config=write_config4, expect_success1=False, expect_success2=False)\ndo_test(write_config=write_config5, expect_success1=True, expect_success2=False)\ndo_test(write_config=write_config6, expect_success1=False, expect_success2=True)\ndo_test(write_config=write_config7, expect_success1=False, expect_success2=False)\ndo_test(write_config=write_config8, expect_success1=True, expect_success2=False)\ndo_test(write_config=write_config9, expect_success1=False, expect_success2=True)\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-max-connections.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether max_connections works with repeated connections\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"max_connections 10\\n\")\n\ndef test_iteration(port, connect_packets_ok, connack_packets_ok, connect_packet_bad, connack_packet_bad):\n    socks = []\n\n    # Open all allowed connections, a limit of 10\n    for i in range(0, 10):\n        socks.append(mosq_test.do_client_connect(connect_packets_ok[i], connack_packets_ok[i], port=port))\n\n    # Try to open an 11th connection\n    try:\n        sock_bad = mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port)\n    except (ConnectionResetError, BrokenPipeError):\n        # Expected behaviour\n        pass\n    except OSError as e:\n        if e.errno == errno.ENOTCONN:\n            pass\n        else:\n            raise e\n\n    # Close all allowed connections\n    for i in range(0, 10):\n        socks[i].close()\n\n\ndef do_test():\n    rc = 1\n\n    connect_packets_ok = []\n    connack_packets_ok = []\n    for i in range(0, 10):\n        connect_packets_ok.append(mosq_test.gen_connect(\"max-conn-%d\"%i, proto_ver=5))\n        connack_packets_ok.append(mosq_test.gen_connack(rc=0, proto_ver=5))\n\n    connect_packet_bad = mosq_test.gen_connect(\"max-conn-bad\", proto_ver=5)\n    connack_packet_bad = b\"\"\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        test_iteration(port, connect_packets_ok, connack_packets_ok, connect_packet_bad, connack_packet_bad)\n\n        ## Now repeat - check it works as before\n\n        if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n            time.sleep(0.5)\n\n        test_iteration(port, connect_packets_ok, connack_packets_ok, connect_packet_bad, connack_packet_bad)\n\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n    return rc\n\nsys.exit(do_test())\n"
  },
  {
    "path": "test/broker/01-connect-max-keepalive.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether max_keepalive violations are rejected for MQTT < 5.0.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"max_keepalive 100\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n\n    connect_packet = mosq_test.gen_connect(\"max-keepalive\", keepalive=101, proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=2, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    socks = []\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    except Exception as err:\n        print(err)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(3)\ndo_test(4)\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-take-over.py",
    "content": "#!/usr/bin/env python3\n\n# MQTT v5 session takeover test\n\nfrom mosq_test_helper import *\n\nport = mosq_test.get_port()\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\ntry:\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"take-over\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n    disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.SESSION_TAKEN_OVER, proto_ver=5)\n\n    sock1 = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    sock2 = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    mosq_test.expect_packet(sock1, \"disconnect\", disconnect_packet)\n    mosq_test.do_ping(sock2)\n\n    sock2.close()\n    sock1.close()\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nexcept Exception as e:\n    print(e)\nfinally:\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n        exit(rc)\n"
  },
  {
    "path": "test/broker/01-connect-uname-no-password-denied.pwfile",
    "content": "user:$6$Ut1cUS9PG8+gC3vn$tOjCfSJJDe1Alu9HktxxyyzwN4+6mAMSWGRAF9gmMN8pzcGTPVEYYMAZpCEp96Oz2ZRRz5YKM6lPMf1tUbb6zA==\n"
  },
  {
    "path": "test/broker/01-connect-uname-no-password-denied.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is denied if it provides just a username when it\n# needs a username and password.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"password_file %s/%s\\n\" % (Path(__file__).resolve().parent, filename.replace('.conf', '.pwfile')))\n        f.write(\"allow_anonymous false\\n\")\n\n\ndef do_test(proto_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-uname-test\", username=\"user\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=proto_ver, properties=None)\n    else:\n        connack_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-uname-or-anon.pwfile",
    "content": "user:$6$Ut1cUS9PG8+gC3vn$tOjCfSJJDe1Alu9HktxxyyzwN4+6mAMSWGRAF9gmMN8pzcGTPVEYYMAZpCEp96Oz2ZRRz5YKM6lPMf1tUbb6zA==\n"
  },
  {
    "path": "test/broker/01-connect-uname-or-anon.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether an anonymous connection is correctly denied.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, allow_anonymous, password_file):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        if allow_anonymous:\n            f.write(\"allow_anonymous true\\n\")\n        else:\n            f.write(\"allow_anonymous false\\n\")\n        if password_file:\n            f.write(\"password_file %s/%s\\n\" % (Path(__file__).resolve().parent, filename.replace('.conf', '.pwfile')))\n\ndef do_test(allow_anonymous, password_file, username, expect_success):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, allow_anonymous, password_file)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        for proto_ver in [3, 4, 5]:\n            rc = 1\n            if username:\n                connect_packet = mosq_test.gen_connect(\"connect-test-%d\" % (proto_ver), proto_ver=proto_ver, username=\"user\", password=\"password\")\n            else:\n                connect_packet = mosq_test.gen_connect(\"connect-test-%d\" % (proto_ver), proto_ver=proto_ver)\n\n            if proto_ver == 5:\n                if expect_success == True:\n                    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n                else:\n                    connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=proto_ver, properties=None)\n            else:\n                if expect_success == True:\n                    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n                else:\n                    connack_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver)\n\n\n            sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n            sock.close()\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d, allow_anonymous=%d, password_file=%d, username=%d\" % (proto_ver, allow_anonymous, password_file, username))\n            exit(rc)\n\n\ndo_test(allow_anonymous=True,  password_file=True,  username=True,  expect_success=True)\ndo_test(allow_anonymous=True,  password_file=True,  username=False, expect_success=True)\ndo_test(allow_anonymous=True,  password_file=False, username=True,  expect_success=True)\ndo_test(allow_anonymous=True,  password_file=False, username=False, expect_success=True)\ndo_test(allow_anonymous=False, password_file=True,  username=True,  expect_success=True)\ndo_test(allow_anonymous=False, password_file=True,  username=False, expect_success=False)\ndo_test(allow_anonymous=False, password_file=False, username=True,  expect_success=False)\ndo_test(allow_anonymous=False, password_file=False, username=False, expect_success=False)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-uname-password-denied-no-will.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is denied if it provides a correct username but\n# incorrect password. The client has a will, but it should not be sent. Check that.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, pw_file):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"password_file %s\\n\" % (pw_file))\n        f.write(\"allow_anonymous false\\n\")\n\ndef write_pwfile(filename):\n    with open(filename, 'w') as f:\n        # Username user, password password\n        f.write('user:$6$vZY4TS+/HBxHw38S$vvjVFECzb8dyuu/mruD2QKTfdFn0WmKxbc+1TsdB0L8EdHk3v9JRmfjHd56+VaTnUcSZOZ/hzkdvWCtxlX7AUQ==\\n')\n\n\ndef do_test(proto_ver):\n    pw_file = os.path.basename(__file__).replace('.py', '.pwfile')\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, pw_file)\n    write_pwfile(pw_file)\n\n    rc = 1\n    connect1_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"user\", password=\"password\", will_topic=\"will/test\", will_payload=b\"will msg\", proto_ver=proto_ver)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, topic=\"will/test\", qos=0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    connect2_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"user\", password=\"password9\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        connack2_packet = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=proto_ver, properties=None)\n    else:\n        connack2_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, port=port)\n        mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet)\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port)\n        sock2.close()\n\n        # If we receive a will here, this is an error\n        mosq_test.do_ping(sock1)\n        sock1.close()\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        os.remove(pw_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-uname-password-denied.pwfile",
    "content": "user:$6$vZY4TS+/HBxHw38S$vvjVFECzb8dyuu/mruD2QKTfdFn0WmKxbc+1TsdB0L8EdHk3v9JRmfjHd56+VaTnUcSZOZ/hzkdvWCtxlX7AUQ==\n"
  },
  {
    "path": "test/broker/01-connect-uname-password-denied.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is denied if it provides a correct username but\n# incorrect password.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"password_file %s/%s\\n\" % (Path(__file__).resolve().parent, filename.replace('.conf', '.pwfile')))\n        f.write(\"allow_anonymous false\\n\")\n\n\ndef do_test(proto_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"user\", password=\"password9\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=proto_ver, properties=None)\n    else:\n        connack_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver)\n\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-uname-password-success-no-tls.pwfile",
    "content": "user:password\n"
  },
  {
    "path": "test/broker/01-connect-uname-password-success-no-tls.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is denied if it provides a correct username but\n# incorrect password.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"password_file %s/%s\\n\" % (Path(__file__).resolve().parent, filename.replace('.conf', '.pwfile')))\n        f.write(\"allow_anonymous false\\n\")\n\n\ndef do_test(proto_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"user\", password=\"password\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-unix-socket.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether connections to a unix socket work\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener 0 %d.sock\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n\ndef do_test():\n    rc = 1\n\n    connect_packet = mosq_test.gen_connect(\"unix-socket\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=conf_file, check_port=False)\n\n    try:\n        if os.environ.get('MOSQ_USE_VALGRIND') is None:\n            time.sleep(0.1)\n        else:\n            time.sleep(2)\n        sock = mosq_test.do_client_connect_unix(connect_packet, connack_packet, path=f\"{port}.sock\")\n        sock.close()\n\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    except Exception as err:\n        print(err)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        os.remove(conf_file)\n        try:\n            os.remove(f\"{port}.sock\")\n        except FileNotFoundError:\n            pass\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test()\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-windows-line-endings.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether config files with windows line endings are accepted.\n# This just connects anonymously - if the config file causes a failure, the\n# broker won't start so the connection would fail.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\r\\n\" % (port))\n        f.write(\"allow_anonymous true\\r\\n\")\n\ndef do_test():\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        for proto_ver in [4, 5]:\n            rc = 1\n            connect_packet = mosq_test.gen_connect(\"connect-anon-test-%d\" % (proto_ver), proto_ver=proto_ver)\n\n            if proto_ver == 5:\n                connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n            else:\n                connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n            sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n            sock.close()\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test()\nexit(0)\n"
  },
  {
    "path": "test/broker/01-connect-zero-length-id.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a CONNECT with a zero length client id results in the correct behaviour.\n\n# MQTT v3.1.1 - zero length is allowed, unless allow_zero_length_clientid is false, and unless clean_start is False.\n# MQTT v5.0 - zero length is allowed, unless allow_zero_length_clientid is false\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, per_listener, allow_zero):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener))\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        if allow_zero != \"\":\n            f.write(\"allow_zero_length_clientid %s\\n\" % (allow_zero))\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"allow_anonymous true\\n\")\n        if allow_zero != \"\":\n            f.write(\"allow_zero_length_clientid %s\\n\" % (allow_zero))\n\n\ndef do_test(per_listener, proto_ver, clean_start, allow_zero, client_port, expect_fail):\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, per_listener, allow_zero)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"\", proto_ver=proto_ver, clean_session=clean_start)\n    if proto_ver == 4:\n        if expect_fail == True:\n            connack_packet = mosq_test.gen_connack(rc=2, proto_ver=proto_ver)\n        else:\n            connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    else:\n        if expect_fail == True:\n            connack_packet = mosq_test.gen_connack(rc=128, proto_ver=proto_ver, properties=None)\n        else:\n            props = mqtt5_props.gen_string_prop(mqtt5_props.ASSIGNED_CLIENT_IDENTIFIER, \"auto-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\")\n            connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=props)\n            # Remove the \"xxxx\" part - this means the front part of the packet\n            # is correct (so remaining length etc. is correct), but we don't\n            # need to match against the random id.\n            connack_packet = connack_packet[:-44]\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port1, use_conf=True)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=client_port)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        os.remove(conf_file)\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"per_listener:%s proto_ver:%d client_port:%d clean_start:%d allow_zero:%s\" % (per_listener, proto_ver, client_port, clean_start, allow_zero))\n            print(\"port1:%d port2:%d\" % (port1, port2))\n            exit(rc)\n\n\n(port1, port2) = mosq_test.get_port(2)\n\ntest_v4 = True\ntest_v5 = True\n\nif test_v4 == True:\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port1, clean_start=True, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port1, clean_start=True, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port1, clean_start=False, allow_zero=\"true\", expect_fail=True)\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port1, clean_start=False, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port1, clean_start=True, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port1, clean_start=True, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port1, clean_start=False, allow_zero=\"true\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port1, clean_start=False, allow_zero=\"false\", expect_fail=True)\n\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port2, clean_start=True, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port2, clean_start=True, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port2, clean_start=False, allow_zero=\"true\", expect_fail=True)\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port2, clean_start=False, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port2, clean_start=True, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port2, clean_start=True, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port2, clean_start=False, allow_zero=\"true\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port2, clean_start=False, allow_zero=\"false\", expect_fail=True)\n\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port1, clean_start=True, allow_zero=\"\", expect_fail=False)\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port1, clean_start=False, allow_zero=\"\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port1, clean_start=True, allow_zero=\"\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port1, clean_start=False, allow_zero=\"\", expect_fail=True)\n\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port2, clean_start=True, allow_zero=\"\", expect_fail=False)\n    do_test(per_listener=\"false\", proto_ver=4, client_port=port2, clean_start=False, allow_zero=\"\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port2, clean_start=True, allow_zero=\"\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=4, client_port=port2, clean_start=False, allow_zero=\"\", expect_fail=True)\n\nif test_v5 == True:\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port1, clean_start=True, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port1, clean_start=True, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port1, clean_start=False, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port1, clean_start=False, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port1, clean_start=True, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port1, clean_start=True, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port1, clean_start=False, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port1, clean_start=False, allow_zero=\"false\", expect_fail=True)\n\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port2, clean_start=True, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port2, clean_start=True, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port2, clean_start=False, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port2, clean_start=False, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port2, clean_start=True, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port2, clean_start=True, allow_zero=\"false\", expect_fail=True)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port2, clean_start=False, allow_zero=\"true\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port2, clean_start=False, allow_zero=\"false\", expect_fail=True)\n\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port1, clean_start=True, allow_zero=\"\", expect_fail=False)\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port1, clean_start=False, allow_zero=\"\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port1, clean_start=True, allow_zero=\"\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port1, clean_start=False, allow_zero=\"\", expect_fail=False)\n\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port2, clean_start=True, allow_zero=\"\", expect_fail=False)\n    do_test(per_listener=\"false\", proto_ver=5, client_port=port2, clean_start=False, allow_zero=\"\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port2, clean_start=True, allow_zero=\"\", expect_fail=False)\n    do_test(per_listener=\"true\", proto_ver=5, client_port=port2, clean_start=False, allow_zero=\"\", expect_fail=False)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/01-plugin-connect-uname-password-denied.pwfile",
    "content": "user:$6$vZY4TS+/HBxHw38S$vvjVFECzb8dyuu/mruD2QKTfdFn0WmKxbc+1TsdB0L8EdHk3v9JRmfjHd56+VaTnUcSZOZ/hzkdvWCtxlX7AUQ==\n"
  },
  {
    "path": "test/broker/01-plugin-connect-uname-password-denied.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is denied if it provides a correct username but\n# incorrect password.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/password-file/mosquitto_password_file.so\\n\")\n        f.write(\"plugin_opt_password_file %s/%s\\n\" % (Path(__file__).resolve().parent, filename.replace('.conf', '.pwfile')))\n        f.write(\"allow_anonymous false\\n\")\n\n\ndef do_test(proto_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"user\", password=\"password9\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=proto_ver, properties=None)\n    else:\n        connack_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver)\n\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/02-shared-nolocal.py",
    "content": "#!/usr/bin/env python3\n\n# No local option is not allowed on shared subscriptions\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    mid = 1\n\n    connect1_packet = mosq_test.gen_connect(\"02-shared-nolocal-client1\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"$share/sharename/subpub/qos1\", 1 | mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL, proto_ver=5)\n    disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.PROTOCOL_ERROR, proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, disconnect_packet, \"disconnect\")\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n        else:\n            return rc\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-shared-qos0-v5.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether shared subscriptions work\n\n# Client 1 subscribes to 02/#, non shared. Should receive everything.\n# Client 2 subscribes to $share/one/02/share/test\n# Client 3 subscribes to $share/one/02/share/test and $share/two/02/share/test\n# Client 4 subscribes to $share/two/02/share/test\n# Client 5 subscribes to $share/one/02/share/test\n\n# A publish to \"02/share/test\" should always go to client 1.\n# The first publish should also go to client 2 (share one) and client 3 (share two)\n# The second publish should also go to client 3 (share one) and client 4 (share two)\n# The third publish should also go to client 3 (share two) and client 5 (share one)\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    mid = 1\n\n    connect1_packet = mosq_test.gen_connect(\"02-shared-client1\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    connect2_packet = mosq_test.gen_connect(\"02-shared-client2\", proto_ver=5)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    connect3_packet = mosq_test.gen_connect(\"02-shared-client3\", proto_ver=5)\n    connack3_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    connect4_packet = mosq_test.gen_connect(\"02-shared-client4\", proto_ver=5)\n    connack4_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    connect5_packet = mosq_test.gen_connect(\"02-shared-client5\", proto_ver=5)\n    connack5_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"02A/#\", 0, proto_ver=5)\n    suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"$share/one/02A/share/test\", 0, proto_ver=5)\n    suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    subscribe3a_packet = mosq_test.gen_subscribe(mid, \"$share/one/02A/share/test\", 0, proto_ver=5)\n    suback3a_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    subscribe3b_packet = mosq_test.gen_subscribe(mid, \"$share/two/02A/share/test\", 0, proto_ver=5)\n    suback3b_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    subscribe4_packet = mosq_test.gen_subscribe(mid, \"$share/two/02A/share/test\", 0, proto_ver=5)\n    suback4_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    subscribe5_packet = mosq_test.gen_subscribe(mid, \"$share/one/02A/share/test\", 0, proto_ver=5)\n    suback5_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    publish1_packet = mosq_test.gen_publish(\"02A/share/test\", qos=0, payload=\"message1\", proto_ver=5)\n    publish2_packet = mosq_test.gen_publish(\"02A/share/test\", qos=0, payload=\"message2\", proto_ver=5)\n    publish3_packet = mosq_test.gen_publish(\"02A/share/test\", qos=0, payload=\"message3\", proto_ver=5)\n\n    mid = 2\n    unsubscribe1_packet = mosq_test.gen_unsubscribe(mid, \"02A/#\", proto_ver=5)\n    unsuback1_packet = mosq_test.gen_unsuback(mid, proto_ver=5)\n\n    unsubscribe2_packet = mosq_test.gen_unsubscribe(mid, \"$share/one/02A/share/test\", proto_ver=5)\n    unsuback2_packet = mosq_test.gen_unsuback(mid, proto_ver=5)\n\n    unsubscribe3a_packet = mosq_test.gen_unsubscribe(mid, \"$share/one/02A/share/test\", proto_ver=5)\n    unsuback3a_packet = mosq_test.gen_unsuback(mid, proto_ver=5)\n\n    unsubscribe3b_packet = mosq_test.gen_unsubscribe(mid, \"$share/two/02A/share/test\", proto_ver=5)\n    unsuback3b_packet = mosq_test.gen_unsuback(mid, proto_ver=5)\n\n    unsubscribe4_packet = mosq_test.gen_unsubscribe(mid, \"$share/two/02A/share/test\", proto_ver=5)\n    unsuback4_packet = mosq_test.gen_unsuback(mid, proto_ver=5)\n\n    unsubscribe5_packet = mosq_test.gen_unsubscribe(mid, \"$share/one/02A/share/test\", proto_ver=5)\n    unsuback5_packet = mosq_test.gen_unsuback(mid, proto_ver=5)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port)\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port)\n        sock3 = mosq_test.do_client_connect(connect3_packet, connack3_packet, timeout=20, port=port)\n        sock4 = mosq_test.do_client_connect(connect4_packet, connack4_packet, timeout=20, port=port)\n        sock5 = mosq_test.do_client_connect(connect5_packet, connack5_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock1, subscribe1_packet, suback1_packet, \"suback1\")\n        mosq_test.do_send_receive(sock2, subscribe2_packet, suback2_packet, \"suback2\")\n        mosq_test.do_send_receive(sock3, subscribe3a_packet, suback3a_packet, \"suback3a\")\n        mosq_test.do_send_receive(sock3, subscribe3b_packet, suback3b_packet, \"suback3b\")\n        mosq_test.do_send_receive(sock4, subscribe4_packet, suback4_packet, \"suback4\")\n        mosq_test.do_send_receive(sock5, subscribe5_packet, suback5_packet, \"suback5\")\n\n        sock1.send(publish1_packet)\n        mosq_test.expect_packet(sock1, \"publish1 1\", publish1_packet)\n        mosq_test.expect_packet(sock2, \"publish1 2\", publish1_packet)\n        mosq_test.expect_packet(sock3, \"publish1 3\", publish1_packet)\n\n        sock1.send(publish2_packet)\n        mosq_test.expect_packet(sock1, \"publish2 1\", publish2_packet)\n        mosq_test.expect_packet(sock3, \"publish2 3\", publish2_packet)\n        mosq_test.expect_packet(sock4, \"publish2 4\", publish2_packet)\n\n        sock1.send(publish3_packet)\n        mosq_test.expect_packet(sock1, \"publish3 1\", publish3_packet)\n        mosq_test.expect_packet(sock3, \"publish3 3\", publish3_packet)\n        mosq_test.expect_packet(sock5, \"publish3 5\", publish3_packet)\n        mosq_test.do_send_receive(sock1, unsubscribe1_packet, unsuback1_packet, \"unsuback1\")\n        mosq_test.do_send_receive(sock2, unsubscribe2_packet, unsuback2_packet, \"unsuback2\")\n        mosq_test.do_send_receive(sock3, unsubscribe3a_packet, unsuback3a_packet, \"unsuback3a\")\n        mosq_test.do_send_receive(sock3, unsubscribe3b_packet, unsuback3b_packet, \"unsuback3b\")\n        mosq_test.do_send_receive(sock4, unsubscribe4_packet, unsuback4_packet, \"unsuback4\")\n        mosq_test.do_send_receive(sock5, unsubscribe5_packet, unsuback5_packet, \"unsuback5\")\n\n        rc = 0\n\n        sock1.close()\n        sock2.close()\n        sock3.close()\n        sock4.close()\n        sock5.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subhier-crash.py",
    "content": "#!/usr/bin/env python3\n\n# Test related to https://github.com/eclipse/mosquitto/issues/505\n\nfrom mosq_test_helper import *\n\ndef test(port):\n    connect_packet = mosq_test.gen_connect(\"subhier-crash\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 1\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"topic/a\", 0)\n    suback1_packet = mosq_test.gen_suback(mid, 0)\n\n    mid = 2\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"topic/b\", 0)\n    suback2_packet = mosq_test.gen_suback(mid, 0)\n\n    mid = 3\n    unsubscribe1_packet = mosq_test.gen_unsubscribe(mid, \"topic/a\")\n    unsuback1_packet = mosq_test.gen_unsuback(mid)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback 1\")\n    mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback 2\")\n    mosq_test.do_send_receive(sock, unsubscribe1_packet, unsuback1_packet, \"unsuback\")\n\n    sock.send(disconnect_packet)\n    sock.close()\n\n\ndef do_test(start_broker=True):\n    rc = 1\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        test(port)\n        # Repeat test to check broker is still there\n        test(port)\n\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-b2c-topic-alias.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether \"topic alias\" works from the broker\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 65535)\n    connect_packet = mosq_test.gen_connect(\"02-b2c-topic-alias\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(topic=\"02/b2c/topic/alias/#\", qos=0, mid=1, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(qos=0, mid=1, proto_ver=5)\n\n    connect_packet_helper = mosq_test.gen_connect(\"02-b2c-topic-alias-helper\", proto_ver=5)\n    connack_packet_helper = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, nolog=True)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n        helper = mosq_test.do_client_connect(connect_packet_helper, connack_packet_helper, timeout=5, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet)\n\n        # This test allows us to test up to the 65535 aliases, but the default\n        # max_topic_alias_broker setting is 10, so use that.\n        max_alias = 10\n\n        # Send messages so the broker configures topic aliases\n        publish_packet_s = b\"\"\n        publish_packet_r = b\"\"\n        for i in range(1, max_alias):\n            # This doesn't make sense in the max_alias=10 case, but for higher values it speeds up the test\n            if i % 50 == 0:\n                sock.send(publish_packet_s)\n                mosq_test.expect_packet(sock, \"publish %da\"%(i), publish_packet_r)\n                publish_packet_s = b\"\"\n                publish_packet_r = b\"\"\n\n            publish_packet_s += mosq_test.gen_publish(\"02/b2c/topic/alias/%d\"%(i), qos=0, payload=\"message\", proto_ver=5)\n\n            props = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS, i)\n            publish_packet_r += mosq_test.gen_publish(\"02/b2c/topic/alias/%d\"%(i), qos=0, payload=\"message\", proto_ver=5, properties=props)\n\n        if len(publish_packet_s) > 0:\n            sock.send(publish_packet_s)\n            mosq_test.expect_packet(sock, \"publish %da\"%(i), publish_packet_r)\n\n        # Re-send now aliases have been configured by the broker\n        publish_packet_s = b\"\"\n        publish_packet_r = b\"\"\n        for i in range(1, max_alias):\n            if i % 50 == 0:\n                sock.send(publish_packet_s)\n                mosq_test.expect_packet(sock, \"publish %db\"%(i), publish_packet_r)\n                publish_packet_s = b\"\"\n                publish_packet_r = b\"\"\n\n            publish_packet_s += mosq_test.gen_publish(\"02/b2c/topic/alias/%d\"%(i), qos=0, payload=\"message\", proto_ver=5)\n\n            props = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS, i)\n            publish_packet_r += mosq_test.gen_publish(\"\", qos=0, payload=\"message\", proto_ver=5, properties=props)\n\n        if len(publish_packet_s) > 0:\n            sock.send(publish_packet_s)\n            mosq_test.expect_packet(sock, \"publish %db\"%(i), publish_packet_r)\n\n        rc = 0\n\n\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos0-long-topic.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic receives its own message sent to that topic, for long topics.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, topic, succeeds):\n    rc = 1\n    mid = 53\n    connect_packet = mosq_test.gen_connect(\"02-subpub-qos0-long-topic\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, topic, 0)\n    suback_packet = mosq_test.gen_suback(mid, 0)\n\n    publish_packet = mosq_test.gen_publish(topic, qos=0, payload=\"message\")\n\n    port = mosq_test.get_port()\n    broker = None\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        if succeeds:\n            mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n            mosq_test.do_send_receive(sock, publish_packet, publish_packet, \"publish\")\n        else:\n            try:\n                mosq_test.do_send_receive(sock, subscribe_packet, b\"\", \"suback\")\n                return 1\n            except BrokenPipeError:\n                pass\n\n        rc = 0\n\n        sock.close()\n    finally:\n        if broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n    return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, \"/\"*200, True) # 200 max hierarchy limit\n    if rc:\n        return rc\n    rc = do_test(start_broker, \"abc/\"*199+\"d\", True) # 200 max hierarchy limit, longer overall string than 200\n    if rc:\n        return rc\n\n    rc = do_test(start_broker, \"/\"*201, False) # Exceeds 200 max hierarchy limit\n    if rc:\n        return rc\n    rc = do_test(start_broker, \"abc/\"*201+\"d\", False) # Exceeds 200 max hierarchy limit, longer overall string than 200\n    if rc:\n        return rc\n    return 0\n\nif __name__ == '__main__':\n    sys.exit(all_tests(True))\n"
  },
  {
    "path": "test/broker/02-subpub-qos0-oversize-payload.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether message size limits apply.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"message_size_limit 1\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n    mid = 53\n    connect_packet = mosq_test.gen_connect(\"02-subpub-qos0-oversize\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos0/oversize\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    connect2_packet = mosq_test.gen_connect(\"02-subpub-qos0-oversize-helper\", proto_ver=proto_ver)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    publish_packet_ok = mosq_test.gen_publish(\"subpub/qos0/oversize\", qos=0, payload=\"A\", proto_ver=proto_ver)\n    publish_packet_bad = mosq_test.gen_publish(\"subpub/qos0/oversize\", qos=0, payload=\"AB\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port)\n        sock2.send(publish_packet_ok)\n        mosq_test.expect_packet(sock, \"publish 1\", publish_packet_ok)\n\n        # Check all is still well on the publishing client\n        mosq_test.do_ping(sock2)\n\n        sock2.send(publish_packet_bad)\n\n        # Check all is still well on the publishing client\n        mosq_test.do_ping(sock2)\n\n        # The subscribing client shouldn't have received a PUBLISH\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except SyntaxError:\n        raise\n    except TypeError:\n        raise\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/02-subpub-qos0-queued-bytes.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"max_inflight_messages 20\\n\")\n        f.write(\"max_inflight_bytes 1000000\\n\")\n        f.write(\"max_queued_messages 20\\n\")\n        f.write(\"max_queued_bytes 1000000\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"subpub-qos0-bytes\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    connect_packet_helper = mosq_test.gen_connect(\"qos0-bytes-helper\", proto_ver=proto_ver)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos0/queued/bytes\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    publish_packet0 = mosq_test.gen_publish(\"subpub/qos0/queued/bytes\", qos=0, payload=\"message\", proto_ver=proto_ver)\n\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=4, port=port, connack_error=\"connack 1\")\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        helper = mosq_test.do_client_connect(connect_packet_helper, connack_packet, timeout=4, port=port, connack_error=\"connack helper\")\n\n        helper.send(publish_packet0)\n        mosq_test.expect_packet(sock, \"publish0\", publish_packet0)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/02-subpub-qos0-retain-as-publish.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic with retain-as-published set works as expected.\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"02-subpub-qos0-rap\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 530\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"02/subpub/rap/normal\", 0, proto_ver=5)\n    suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    mid = 531\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"02/subpub/rap/rap\", 0 | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED, proto_ver=5)\n    suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    publish1_packet = mosq_test.gen_publish(\"02/subpub/rap/normal\", qos=0, retain=True, payload=\"message\", proto_ver=5)\n    publish2_packet = mosq_test.gen_publish(\"02/subpub/rap/rap\", qos=0, retain=True, payload=\"message\", proto_ver=5)\n\n    publish1r_packet = mosq_test.gen_publish(\"02/subpub/rap/normal\", qos=0, retain=False, payload=\"message\", proto_ver=5)\n    publish2r_packet = mosq_test.gen_publish(\"02/subpub/rap/rap\", qos=0, retain=True, payload=\"message\", proto_ver=5)\n\n    mid = 1\n    publish3_packet = mosq_test.gen_publish(\"02/subpub/rap/receive\", qos=1, mid=mid, payload=\"success\", proto_ver=5)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback1\")\n        mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback2\")\n\n        mosq_test.do_send_receive(sock, publish1_packet, publish1r_packet, \"publish1\")\n        mosq_test.do_send_receive(sock, publish2_packet, publish2r_packet, \"publish2\")\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos0-send-retain.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether \"send retain\" subscribe options work\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"02-subpub-qos0-send-retain\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 530\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"02/subpub/send-retain/always\", 0 | mqtt5_opts.MQTT_SUB_OPT_SEND_RETAIN_ALWAYS, proto_ver=5)\n    suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    mid = 531\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"02/subpub/send-retain/new\", 0 | mqtt5_opts.MQTT_SUB_OPT_SEND_RETAIN_NEW, proto_ver=5)\n    suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    mid = 532\n    subscribe3_packet = mosq_test.gen_subscribe(mid, \"02/subpub/send-retain/never\", 0 | mqtt5_opts.MQTT_SUB_OPT_SEND_RETAIN_NEVER, proto_ver=5)\n    suback3_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n\n    publish1_packet = mosq_test.gen_publish(\"02/subpub/send-retain/always\", qos=0, retain=True, payload=\"message\", proto_ver=5)\n    publish2_packet = mosq_test.gen_publish(\"02/subpub/send-retain/new\", qos=0, retain=True, payload=\"message\", proto_ver=5)\n    publish3_packet = mosq_test.gen_publish(\"02/subpub/send-retain/never\", qos=0, retain=True, payload=\"message\", proto_ver=5)\n\n    publish1r1_packet = mosq_test.gen_publish(\"02/subpub/send-retain/always\", qos=0, retain=True, payload=\"message\", proto_ver=5)\n    publish1r2_packet = mosq_test.gen_publish(\"02/subpub/send-retain/always\", qos=0, retain=True, payload=\"message\", proto_ver=5)\n    publish2r1_packet = mosq_test.gen_publish(\"02/subpub/send-retain/new\", qos=0, retain=True, payload=\"message\", proto_ver=5)\n    publish2r2_packet = mosq_test.gen_publish(\"02/subpub/send-retain/new\", qos=0, retain=False, payload=\"message\", proto_ver=5)\n    publish3r1_packet = mosq_test.gen_publish(\"02/subpub/send-retain/never\", qos=0, retain=False, payload=\"message\", proto_ver=5)\n    publish3r2_packet = mosq_test.gen_publish(\"02/subpub/send-retain/never\", qos=0, retain=False, payload=\"message\", proto_ver=5)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n\n        sock.send(publish1_packet)\n        sock.send(publish2_packet)\n        sock.send(publish3_packet)\n\n        # Don't expect a message after this\n        mosq_test.do_send_receive(sock, subscribe3_packet, suback3_packet, \"suback3\")\n        # Don't expect a message after this\n        mosq_test.do_send_receive(sock, subscribe3_packet, suback3_packet, \"suback3\")\n\n        # Expect a message after this, because it is the first subscribe\n        mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback2\")\n        mosq_test.expect_packet(sock, \"publish2r1\", publish2r1_packet)\n        # Don't expect a message after this, it is the second subscribe\n        mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback2\")\n\n        # Always expect a message after this\n        mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback1\")\n        mosq_test.expect_packet(sock, \"publish1r1\", publish1r1_packet)\n        # Always expect a message after this\n        mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback1\")\n        mosq_test.expect_packet(sock, \"publish1r1\", publish1r2_packet)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos0-subscription-id.py",
    "content": "#!/usr/bin/env python3\n\n# Does setting and updating subscription identifiers work as expected?\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"02-subpub-subid\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    props = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 1)\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"02/subpub/subid/id1\", 0, proto_ver=5, properties=props)\n    suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    mid = 2\n    props = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 14)\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"02/subpub/subid/+/id2\", 0, proto_ver=5, properties=props)\n    suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    mid = 3\n    subscribe3_packet = mosq_test.gen_subscribe(mid, \"02/subpub/subid/noid\", 0, proto_ver=5)\n    suback3_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    # Updated version of subscribe1, now without a subscription identifier\n    mid = 4\n    subscribe1u_packet = mosq_test.gen_subscribe(mid, \"02/subpub/subid/id1\", 0, proto_ver=5)\n    suback1u_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    # Updated version of subscribe2, with a new subscription identifier\n    mid = 5\n    props = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 19)\n    subscribe2u_packet = mosq_test.gen_subscribe(mid, \"02/subpub/subid/+/id2\", 0, proto_ver=5, properties=props)\n    suback2u_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    # Updated version of subscribe3, now with a subscription identifier\n    mid = 6\n    props = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 21)\n    subscribe3u_packet = mosq_test.gen_subscribe(mid, \"02/subpub/subid/noid\", 0, proto_ver=5, properties=props)\n    suback3u_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n\n    publish1_packet = mosq_test.gen_publish(\"02/subpub/subid/id1\", qos=0, payload=\"message1\", proto_ver=5)\n\n    props = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 1)\n    publish1r_packet = mosq_test.gen_publish(\"02/subpub/subid/id1\", qos=0, payload=\"message1\", proto_ver=5, properties=props)\n\n    publish2_packet = mosq_test.gen_publish(\"02/subpub/subid/test/id2\", qos=0, payload=\"message2\", proto_ver=5)\n    props = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 14)\n    publish2r_packet = mosq_test.gen_publish(\"02/subpub/subid/test/id2\", qos=0, payload=\"message2\", proto_ver=5, properties=props)\n\n    publish3_packet = mosq_test.gen_publish(\"02/subpub/subid/noid\", qos=0, payload=\"message3\", proto_ver=5)\n\n\n    # Updated version of publish1r, now with no id\n    publish1ru_packet = mosq_test.gen_publish(\"02/subpub/subid/id1\", qos=0, payload=\"message1\", proto_ver=5)\n\n    # Updated verison of publish2r, with updated id\n    props = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 19)\n    publish2ru_packet = mosq_test.gen_publish(\"02/subpub/subid/test/id2\", qos=0, payload=\"message2\", proto_ver=5, properties=props)\n\n    # Updated version of publish3r, now with an id\n    props = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 21)\n    publish3ru_packet = mosq_test.gen_publish(\"02/subpub/subid/noid\", qos=0, payload=\"message3\", proto_ver=5, properties=props)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback1\")\n        mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback2\")\n        mosq_test.do_send_receive(sock, subscribe3_packet, suback3_packet, \"suback3\")\n\n        mosq_test.do_send_receive(sock, publish3_packet, publish3_packet, \"publish3\")\n        mosq_test.do_send_receive(sock, publish2_packet, publish2r_packet, \"publish2\")\n        mosq_test.do_send_receive(sock, publish1_packet, publish1r_packet, \"publish1\")\n\n        # Now update the subscription identifiers\n        mosq_test.do_send_receive(sock, subscribe1u_packet, suback1u_packet, \"suback1u\")\n        mosq_test.do_send_receive(sock, subscribe2u_packet, suback2u_packet, \"suback2u\")\n        mosq_test.do_send_receive(sock, subscribe3u_packet, suback3u_packet, \"suback3u\")\n\n        mosq_test.do_send_receive(sock, publish2_packet, publish2ru_packet, \"publish2u\")\n        mosq_test.do_send_receive(sock, publish3_packet, publish3ru_packet, \"publish3u\")\n        mosq_test.do_send_receive(sock, publish1_packet, publish1ru_packet, \"publish1u\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker, proto_ver=5)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos0-topic-alias-unknown.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether \"topic alias\" works to the broker\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"02-subpub-alias-unknown\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS, 3)\n    publish1_packet = mosq_test.gen_publish(\"\", qos=0, payload=\"message\", proto_ver=5, properties=props)\n\n    disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.PROTOCOL_ERROR, proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n        sock.send(publish1_packet)\n\n        mosq_test.expect_packet(sock, \"disconnect\", disconnect_packet)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos0-topic-alias.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether \"topic alias\" works to the broker\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    connect1_packet = mosq_test.gen_connect(\"02-subpub-qos0-topic-alias\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    connect2_packet = mosq_test.gen_connect(\"02-subpub-qos0-topic-alias-helper\", proto_ver=5)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"02/subpub/topic-alias/alias\", 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS, 3)\n    publish1_packet = mosq_test.gen_publish(\"02/subpub/topic-alias/alias\", qos=0, payload=\"message\", proto_ver=5, properties=props)\n\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS, 3)\n    publish2s_packet = mosq_test.gen_publish(\"\", qos=0, payload=\"message\", proto_ver=5, properties=props)\n    publish2r_packet = mosq_test.gen_publish(\"02/subpub/topic-alias/alias\", qos=0, payload=\"message\", proto_ver=5)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port)\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=5, port=port)\n\n        sock1.send(publish1_packet)\n\n        mosq_test.do_send_receive(sock2, subscribe_packet, suback_packet, \"suback\")\n\n        sock1.send(publish2s_packet)\n\n        mosq_test.expect_packet(sock2, \"publish2r\", publish2r_packet)\n        rc = 0\n\n        sock1.close()\n        sock2.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos1-message-expiry-retain.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether the broker reduces the message expiry interval when republishing\n# a retained message, and eventually removes it.\n# MQTT v5\n\n# Helper publishes a message, with a medium length expiry with retained set. It\n# publishes a second message with retained set but no expiry.\n# Client connects, subscribes, gets messages, disconnects.\n# We wait until the expiry will have expired.\n# Client connects, subscribes, doesn't get expired message, does get\n# non-expired message.\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n    keepalive = 60\n    connect_packet = mosq_test.gen_connect(\"subpub\", keepalive=keepalive, proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"subpub/expired\", 1, proto_ver=proto_ver)\n    suback1_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    mid = 2\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"subpub/kept\", 1, proto_ver=proto_ver)\n    suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    helper_connect = mosq_test.gen_connect(\"helper\", proto_ver=proto_ver)\n    helper_connack = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid=1\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 2)\n    publish1_packet = mosq_test.gen_publish(\"subpub/expired\", mid=mid, qos=1, retain=True, payload=\"message1\", proto_ver=proto_ver, properties=props)\n    puback1_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n    mid=2\n    publish2s_packet = mosq_test.gen_publish(\"subpub/kept\", mid=mid, qos=1, retain=True, payload=\"message2\", proto_ver=proto_ver)\n    puback2s_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n    mid=1\n    publish2r_packet = mosq_test.gen_publish(\"subpub/kept\", mid=mid, qos=1, retain=True, payload=\"message2\", proto_ver=proto_ver)\n    puback2r_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port)\n        mosq_test.do_send_receive(helper, publish1_packet, puback1_packet, \"puback 1\")\n        mosq_test.do_send_receive(helper, publish2s_packet, puback2s_packet, \"puback 2\")\n        helper.close()\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback 1-1\")\n\n        mosq_test.expect_packet(sock, \"publish 1\", publish1_packet)\n        sock.send(puback1_packet)\n\n        mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback 2-1\")\n        mosq_test.expect_packet(sock, \"publish 2\", publish2s_packet)\n        sock.send(puback2s_packet)\n        sock.close()\n\n        time.sleep(3)\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback 1-2\")\n        # We shouldn't receive a publish here\n        # This will fail if we do receive a publish\n        mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback 2-2\")\n        mosq_test.expect_packet(sock, \"publish 2\", publish2r_packet)\n        sock.send(puback2r_packet)\n        sock.close()\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/02-subpub-qos1-message-expiry-will.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether the broker reduces the message expiry interval when republishing a will.\n# MQTT v5\n\n# Client connects with clean session set false, subscribes with qos=1, then disconnects\n# Helper publishes two messages, one with a short expiry and one with a long expiry\n# We wait until the short expiry will have expired but the long one not.\n# Client reconnects, expects delivery of the long expiry message with a reduced\n# expiry interval property.\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n    mid = 53\n    keepalive = 60\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\n    connect_packet = mosq_test.gen_connect(\"subpub-qos1-test\", keepalive=keepalive, proto_ver=proto_ver, clean_session=False, properties=props)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, flags=1)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos1\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 10)\n    helper_connect = mosq_test.gen_connect(\"helper\", proto_ver=proto_ver, will_topic=\"subpub/qos1\", will_qos=1, will_payload=b\"message\", will_properties=props, keepalive=2)\n    helper_connack = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    #mid=2\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 10)\n    publish2s_packet = mosq_test.gen_publish(\"subpub/qos1\", mid=mid, qos=1, payload=\"message2\", proto_ver=proto_ver, properties=props)\n    puback2s_packet = mosq_test.gen_puback(mid)\n\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        sock.close()\n\n        helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port)\n\n        time.sleep(2)\n\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=20, port=port)\n        packet = sock.recv(len(publish2s_packet))\n        for i in range(10, 5, -1):\n            props = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, i)\n            publish2r_packet = mosq_test.gen_publish(\"subpub/qos1\", mid=1, qos=1, payload=\"message\", proto_ver=proto_ver, properties=props)\n            if packet == publish2r_packet:\n                rc = 0\n                break\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/02-subpub-qos1-message-expiry.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether the broker reduces the message expiry interval when republishing.\n# MQTT v5\n\n# Client connects with clean session set false, subscribes with qos=1, then disconnects\n# Helper publishes two messages, one with a short expiry and one with a long expiry\n# We wait until the short expiry will have expired but the long one not.\n# Client reconnects, expects delivery of the long expiry message with a reduced\n# expiry interval property.\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n    mid = 53\n    keepalive = 60\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\n    connect_packet = mosq_test.gen_connect(\"subpub-qos0-test\", keepalive=keepalive, proto_ver=proto_ver, clean_session=False, properties=props)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, flags=1)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos1\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n\n\n    helper_connect = mosq_test.gen_connect(\"helper\", proto_ver=proto_ver)\n    helper_connack = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid=1\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 1)\n    publish1s_packet = mosq_test.gen_publish(\"subpub/qos1\", mid=mid, qos=1, payload=\"message1\", proto_ver=proto_ver, properties=props)\n    puback1s_packet = mosq_test.gen_puback(mid)\n\n    mid=2\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 10)\n    publish2s_packet = mosq_test.gen_publish(\"subpub/qos1\", mid=mid, qos=1, payload=\"message2\", proto_ver=proto_ver, properties=props)\n    puback2s_packet = mosq_test.gen_puback(mid)\n\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        sock.close()\n\n        helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port)\n        mosq_test.do_send_receive(helper, publish1s_packet, puback1s_packet, \"puback 1\")\n        mosq_test.do_send_receive(helper, publish2s_packet, puback2s_packet, \"puback 2\")\n\n        time.sleep(2)\n\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=20, port=port)\n        packet = sock.recv(len(publish2s_packet))\n        for i in range(9, 5, -1):\n            props = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, i)\n            publish2r_packet = mosq_test.gen_publish(\"subpub/qos1\", mid=2, qos=1, payload=\"message2\", proto_ver=proto_ver, properties=props)\n            if packet == publish2r_packet:\n                rc = 0\n                break\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/02-subpub-qos1-nolocal.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic does not receive its own message\n# sent to that topic if no local is set.\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"02-subpub-qos1-nolocal\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 530\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"02/subpub/qos1/nolocal/qos1\", 1 | mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\n    mid = 531\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"02/subpub/qos1/nolocal/receive\", 1, proto_ver=5)\n    suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\n    mid = 300\n    publish_packet = mosq_test.gen_publish(\"02/subpub/qos1/nolocal/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    mid = 301\n    publish2_packet = mosq_test.gen_publish(\"02/subpub/qos1/nolocal/receive\", qos=1, mid=mid, payload=\"success\", proto_ver=5)\n    puback2_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    mid = 1\n    publish3_packet = mosq_test.gen_publish(\"02/subpub/qos1/nolocal/receive\", qos=1, mid=mid, payload=\"success\", proto_ver=5)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback2\")\n\n        mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n        sock.send(publish2_packet)\n\n        mosq_test.receive_unordered(sock, puback2_packet, publish3_packet, \"puback2/publish3\")\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos1-oversize-payload.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether message size limits apply.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"message_size_limit 1\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n    mid = 53\n    connect_packet = mosq_test.gen_connect(\"subpub-qos1-oversize\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos1/oversize\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    connect2_packet = mosq_test.gen_connect(\"subpub-qos1-oversize-helper\", proto_ver=proto_ver)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet_ok = mosq_test.gen_publish(\"subpub/qos1/oversize\", mid=mid, qos=1, payload=\"A\", proto_ver=proto_ver)\n    puback_packet_ok = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\n\n    mid = 2\n    publish_packet_bad = mosq_test.gen_publish(\"subpub/qos1/oversize\", mid=mid, qos=1, payload=\"AB\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet_bad = mosq_test.gen_puback(reason_code=mqtt5_rc.PACKET_TOO_LARGE, mid=mid, proto_ver=proto_ver)\n    else:\n        puback_packet_bad = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock2, publish_packet_ok, puback_packet_ok, \"puback 1\")\n        mosq_test.expect_packet(sock, \"publish 1\", publish_packet_ok)\n        sock.send(puback_packet_ok)\n\n        # Check all is still well on the publishing client\n        mosq_test.do_ping(sock2)\n\n        mosq_test.do_send_receive(sock2, publish_packet_bad, puback_packet_bad, \"puback 2\")\n\n        # The subscribing client shouldn't have received a PUBLISH\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except SyntaxError:\n        raise\n    except TypeError:\n        raise\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/02-subpub-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic receives its own message sent to that topic.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 530\n    connect_packet = mosq_test.gen_connect(\"subpub-qos1-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos1\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    mid = 300\n    publish_packet = mosq_test.gen_publish(\"subpub/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet2 = mosq_test.gen_publish(\"subpub/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        sock.send(publish_packet)\n        mosq_test.receive_unordered(sock, puback_packet, publish_packet2, \"puback/publish2\")\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos2-1322.py",
    "content": "#!/usr/bin/env python3\n\n# Test for issue 1322:\n\n## restart mosquitto\n#sudo systemctl restart mosquitto.service\n#\n## listen on topic1\n#mosquitto_sub -t \"topic1\"\n#\n## publish to topic1 without clean session\n#mosquitto_pub -t \"topic1\" -q 2 -c --id \"foobar\" -m \"message1\"\n## message1 on topic1 is received as expected\n#\n## publish to topic2 without clean session\n## IMPORTANT: no subscription to this topic is present on broker!\n#mosquitto_pub -t \"topic2\" -q 2 -c --id \"foobar\" -m \"message2\"\n## this goes nowhere, as no subscriber present\n#\n## publish to topic1 without clean session\n#mosquitto_pub -t \"topic1\" -q 2 -c --id \"foobar\" -m \"message3\"\n## message3 on topic1 IS NOT RECEIVED\n#\n## listen on topic2\n#mosquitto_sub -t \"topic2\"\n#\n## publish to topic1 without clean session\n#mosquitto_pub -t \"topic1\" -q 2 -c --id \"foobar\" -m \"message4\"\n## message2 on topic2 is received incorrectly\n#\n## publish to topic1 without clean session\n#mosquitto_pub -t \"topic1\" -q 2 -c --id \"foobar\" -m \"message5\"\n## message5 on topic1 is received as expected (message4 was dropped)\n\n\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    pub_connect_packet = mosq_test.gen_connect(\"02-subpub-qos2-1322-pub\", clean_session=False, proto_ver=proto_ver, session_expiry=60)\n    pub_connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    pub_connack2_packet = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\n    pub_connect_packet_clear = mosq_test.gen_connect(\"02-subpub-qos2-1322-pub\", proto_ver=proto_ver)\n\n    sub1_connect_packet = mosq_test.gen_connect(\"02-subpub-qos2-1322-sub1\", proto_ver=proto_ver)\n    sub1_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    sub2_connect_packet = mosq_test.gen_connect(\"02-subpub-qos2-1322-sub2\", proto_ver=proto_ver)\n    sub2_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"02/subpub/qos2/1322/topic1\", 0, proto_ver=proto_ver)\n    suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    mid = 1\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"02/subpub/qos2/1322/topic2\", 0, proto_ver=proto_ver)\n    suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    # All publishes have the same mid\n    mid = 1\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n    publish1s_packet = mosq_test.gen_publish(\"02/subpub/qos2/1322/topic1\", qos=2, mid=mid, payload=\"message1\", proto_ver=proto_ver)\n    publish2s_packet = mosq_test.gen_publish(\"02/subpub/qos2/1322/topic2\", qos=2, mid=mid, payload=\"message2\", proto_ver=proto_ver)\n    publish3s_packet = mosq_test.gen_publish(\"02/subpub/qos2/1322/topic1\", qos=2, mid=mid, payload=\"message3\", proto_ver=proto_ver)\n    publish4s_packet = mosq_test.gen_publish(\"02/subpub/qos2/1322/topic1\", qos=2, mid=mid, payload=\"message4\", proto_ver=proto_ver)\n    publish5s_packet = mosq_test.gen_publish(\"02/subpub/qos2/1322/topic1\", qos=2, mid=mid, payload=\"message5\", proto_ver=proto_ver)\n\n    publish1r_packet = mosq_test.gen_publish(\"02/subpub/qos2/1322/topic1\", qos=0, payload=\"message1\", proto_ver=proto_ver)\n    publish2r_packet = mosq_test.gen_publish(\"02/subpub/qos2/1322/topic2\", qos=0, payload=\"message2\", proto_ver=proto_ver)\n    publish3r_packet = mosq_test.gen_publish(\"02/subpub/qos2/1322/topic1\", qos=0, payload=\"message3\", proto_ver=proto_ver)\n    publish4r_packet = mosq_test.gen_publish(\"02/subpub/qos2/1322/topic1\", qos=0, payload=\"message4\", proto_ver=proto_ver)\n    publish5r_packet = mosq_test.gen_publish(\"02/subpub/qos2/1322/topic1\", qos=0, payload=\"message5\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sub1 = mosq_test.do_client_connect(sub1_connect_packet, sub1_connack_packet, timeout=10, port=port)\n        mosq_test.do_send_receive(sub1, subscribe1_packet, suback1_packet, \"suback1\")\n\n        pub = mosq_test.do_client_connect(pub_connect_packet, pub_connack1_packet, timeout=10, port=port)\n        mosq_test.do_send_receive(pub, publish1s_packet, pubrec_packet, \"pubrec1\")\n        mosq_test.do_send_receive(pub, pubrel_packet, pubcomp_packet, \"pubcomp1\")\n        pub.close()\n\n        mosq_test.expect_packet(sub1, \"publish1\", publish1r_packet)\n        pub = mosq_test.do_client_connect(pub_connect_packet, pub_connack2_packet, timeout=10, port=port)\n        mosq_test.do_send_receive(pub, publish2s_packet, pubrec_packet, \"pubrec2\")\n        mosq_test.do_send_receive(pub, pubrel_packet, pubcomp_packet, \"pubcomp2\")\n        pub.close()\n\n        # We expect nothing on sub1\n        mosq_test.do_ping(sub1, error_string=\"pingresp1\")\n\n        pub = mosq_test.do_client_connect(pub_connect_packet, pub_connack2_packet, timeout=10, port=port)\n        mosq_test.do_send_receive(pub, publish3s_packet, pubrec_packet, \"pubrec3\")\n        mosq_test.do_send_receive(pub, pubrel_packet, pubcomp_packet, \"pubcomp3\")\n        pub.close()\n\n        mosq_test.expect_packet(sub1, \"publish3\", publish3r_packet)\n        sub2 = mosq_test.do_client_connect(sub2_connect_packet, sub2_connack_packet, timeout=10, port=port)\n        mosq_test.do_send_receive(sub2, subscribe2_packet, suback2_packet, \"suback2\")\n\n        pub = mosq_test.do_client_connect(pub_connect_packet, pub_connack2_packet, timeout=10, port=port)\n        mosq_test.do_send_receive(pub, publish4s_packet, pubrec_packet, \"pubrec4\")\n        mosq_test.do_send_receive(pub, pubrel_packet, pubcomp_packet, \"pubcomp4\")\n        pub.close()\n\n        # We expect nothing on sub2\n        mosq_test.do_ping(sub2, error_string=\"pingresp2\")\n\n        mosq_test.expect_packet(sub1, \"publish4\", publish4r_packet)\n        pub = mosq_test.do_client_connect(pub_connect_packet, pub_connack2_packet, timeout=10, port=port)\n        mosq_test.do_send_receive(pub, publish5s_packet, pubrec_packet, \"pubrec5\")\n        mosq_test.do_send_receive(pub, pubrel_packet, pubcomp_packet, \"pubcomp5\")\n        pub.close()\n\n        # We expect nothing on sub2\n        mosq_test.do_ping(sub2, error_string=\"pingresp2\")\n\n        mosq_test.expect_packet(sub1, \"publish5\", publish5r_packet)\n        rc = 0\n\n        sub2.close()\n        sub1.close()\n\n        # Clear session\n        pub = mosq_test.do_client_connect(pub_connect_packet_clear, pub_connack1_packet, timeout=10, port=port)\n        pub.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos2-max-inflight-bytes.py",
    "content": "#!/usr/bin/env python3\n\n# Does the broker respect max_inflight_bytes?\n# Also check whether the send quota is dealt with properly when both\n# RECEIVE-MAXIMUM and max_inflight_bytes are set.\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef helper(port):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"subpub-qos2-recv-max1-helper\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"message1\", proto_ver=5)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 2\n    publish_packet2 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"message2\", proto_ver=5)\n    pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 3\n    publish_packet3 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"message3\", proto_ver=5)\n    pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n    mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec\")\n    mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"pubcomp\")\n\n    mosq_test.do_send_receive(sock, publish_packet2, pubrec_packet2, \"pubrec2\")\n    mosq_test.do_send_receive(sock, pubrel_packet2, pubcomp_packet2, \"pubcomp2\")\n\n    mosq_test.do_send_receive(sock, publish_packet3, pubrec_packet3, \"pubrec3\")\n    mosq_test.do_send_receive(sock, pubrel_packet3, pubcomp_packet3, \"pubcomp3\")\n    sock.close()\n\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"max_inflight_bytes 16\\n\")\n\n\ndef send_small(port):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"subpub-qos2-test-helper\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n\n    for i in range(0, 10):\n        mid = 1+i\n        publish_packet = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=str(i+1))\n        pubrec_packet = mosq_test.gen_pubrec(mid)\n        pubrel_packet = mosq_test.gen_pubrel(mid)\n        pubcomp_packet = mosq_test.gen_pubcomp(mid)\n\n        mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec\")\n        mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"pubcomp\")\n\n\ndef do_test(proto_ver):\n    if proto_ver == 4:\n        exit(0)\n\n    rc = 1\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 5)\n    connect_packet = mosq_test.gen_connect(\"subpub-qos2-test\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos2\", 2, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        # Repeat many times to stress the send quota\n        mid = 0\n        for i in range(0, 12):\n            helper(port)\n            #pub = subprocess.Popen(['./02-subpub-qos2-receive-maximum-helper.py', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n            #if mosq_test.wait_for_subprocess(pub):\n            #    print(\"pub not terminated\")\n            #    if rc == 0: rc=1\n            #(stdo, stde) = pub.communicate()\n\n            mid += 1\n            publish_packet1 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"message1\", proto_ver=5)\n            pubrec_packet1 = mosq_test.gen_pubrec(mid, proto_ver=5)\n            pubrel_packet1 = mosq_test.gen_pubrel(mid, proto_ver=5)\n            pubcomp_packet1 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n            mid += 1\n            publish_packet2 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"message2\", proto_ver=5)\n            pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5)\n            pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5)\n            pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n            mid += 1\n            publish_packet3 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"message3\", proto_ver=5)\n            pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5)\n            pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5)\n            pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n\n            mosq_test.expect_packet(sock, \"publish1\", publish_packet1)\n            mosq_test.expect_packet(sock, \"publish2\", publish_packet2)\n            mosq_test.do_send_receive(sock, pubrec_packet1, pubrel_packet1, \"pubrel1\")\n            sock.send(pubcomp_packet1)\n\n            mosq_test.expect_packet(sock, \"publish3\", publish_packet3)\n            mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, \"pubrel2\")\n            sock.send(pubcomp_packet2)\n\n            mosq_test.do_send_receive(sock, pubrec_packet3, pubrel_packet3, \"pubrel3\")\n            sock.send(pubcomp_packet3)\n\n        # send messages where count will exceed max_inflight_messages, but the\n        # payload bytes won't exceed max_inflight_bytes\n        send_small(port)\n\n        mid += 1\n        publish_packet1 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"1\", proto_ver=5)\n        pubrec_packet1 = mosq_test.gen_pubrec(mid, proto_ver=5)\n        pubrel_packet1 = mosq_test.gen_pubrel(mid, proto_ver=5)\n        pubcomp_packet1 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n        mid += 1\n        publish_packet2 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"2\", proto_ver=5)\n        pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5)\n        pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5)\n        pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n        mid += 1\n        publish_packet3 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"3\", proto_ver=5)\n        pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5)\n        pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5)\n        pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n        mid += 1\n        publish_packet4 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"4\", proto_ver=5)\n        pubrec_packet4 = mosq_test.gen_pubrec(mid, proto_ver=5)\n        pubrel_packet4 = mosq_test.gen_pubrel(mid, proto_ver=5)\n        pubcomp_packet4 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n        mid += 1\n        publish_packet5 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"5\", proto_ver=5)\n        pubrec_packet5 = mosq_test.gen_pubrec(mid, proto_ver=5)\n        pubrel_packet5 = mosq_test.gen_pubrel(mid, proto_ver=5)\n        pubcomp_packet5 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n        mosq_test.expect_packet(sock, \"publish1s\", publish_packet1)\n        mosq_test.expect_packet(sock, \"publish2s\", publish_packet2)\n        mosq_test.expect_packet(sock, \"publish3s\", publish_packet3)\n        mosq_test.expect_packet(sock, \"publish4s\", publish_packet4)\n        mosq_test.expect_packet(sock, \"publish5s\", publish_packet5)\n        \n        mosq_test.do_send_receive(sock, pubrec_packet1, pubrel_packet1, \"pubrel1s\")\n        mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, \"pubrel2s\")\n        mosq_test.do_send_receive(sock, pubrec_packet3, pubrel_packet3, \"pubrel3s\")\n        mosq_test.do_send_receive(sock, pubrec_packet4, pubrel_packet4, \"pubrel4s\")\n        mosq_test.do_send_receive(sock, pubrec_packet5, pubrel_packet5, \"pubrel5s\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            #print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/02-subpub-qos2-pubrec-error.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBREC with reason code >= 0x80 is handled correctly\n\nfrom mosq_test_helper import *\n\ndef helper(port):\n    connect_packet = mosq_test.gen_connect(\"helper\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 1\n    publish_1_packet = mosq_test.gen_publish(\"qos2/pubrec/rejected\", qos=2, mid=mid, payload=\"rejected-message\")\n    pubrec_1_packet = mosq_test.gen_pubrec(mid)\n    pubrel_1_packet = mosq_test.gen_pubrel(mid)\n    pubcomp_1_packet = mosq_test.gen_pubcomp(mid)\n\n    mid = 2\n    publish_2_packet = mosq_test.gen_publish(\"qos2/pubrec/accepted\", qos=2, mid=mid, payload=\"accepted-message\")\n    pubrec_2_packet = mosq_test.gen_pubrec(mid)\n    pubrel_2_packet = mosq_test.gen_pubrel(mid)\n    pubcomp_2_packet = mosq_test.gen_pubcomp(mid)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error=\"helper connack\", port=port)\n\n    mosq_test.do_send_receive(sock, publish_1_packet, pubrec_1_packet, \"helper pubrec\")\n    mosq_test.do_send_receive(sock, pubrel_1_packet, pubcomp_1_packet, \"helper pubcomp\")\n\n    mosq_test.do_send_receive(sock, publish_2_packet, pubrec_2_packet, \"helper pubrec\")\n    mosq_test.do_send_receive(sock, pubrel_2_packet, pubcomp_2_packet, \"helper pubcomp\")\n    sock.close()\n\n\ndef do_test(proto_ver):\n    rc = 1\n    keepalive = 60\n    connect_packet = mosq_test.gen_connect(\"pub-qo2-timeout-test\", keepalive=keepalive, proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"qos2/pubrec/+\", 2, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 1\n    publish_1_packet = mosq_test.gen_publish(\"qos2/pubrec/rejected\", qos=2, mid=mid, payload=\"rejected-message\", proto_ver=proto_ver)\n    pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver, reason_code=0x80)\n\n    mid = 2\n    publish_2_packet = mosq_test.gen_publish(\"qos2/pubrec/accepted\", qos=2, mid=mid, payload=\"accepted-message\", proto_ver=proto_ver)\n    pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        helper(port)\n\n        # Should have now received a publish command\n        mosq_test.expect_packet(sock, \"publish 1\", publish_1_packet)\n        sock.send(pubrec_1_packet)\n\n        mosq_test.expect_packet(sock, \"publish 2\", publish_2_packet)\n        mosq_test.do_send_receive(sock, pubrec_2_packet, pubrel_2_packet, \"pubrel 2\")\n        sock.send(pubcomp_2_packet)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/02-subpub-qos2-receive-maximum-1.py",
    "content": "#!/usr/bin/env python3\n\n# Does the broker respect receive maximum==1?\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef helper(port):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"subpub-qos2-recv-max1-helper\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"subpub/qos2/receive/maximum1\", qos=2, mid=mid, payload=\"message1\", proto_ver=5)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 2\n    publish_packet2 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum1\", qos=2, mid=mid, payload=\"message2\", proto_ver=5)\n    pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 3\n    publish_packet3 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum1\", qos=2, mid=mid, payload=\"message3\", proto_ver=5)\n    pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n    mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec\")\n    mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"pubcomp\")\n\n    mosq_test.do_send_receive(sock, publish_packet2, pubrec_packet2, \"pubrec2\")\n    mosq_test.do_send_receive(sock, pubrel_packet2, pubcomp_packet2, \"pubcomp2\")\n\n    mosq_test.do_send_receive(sock, publish_packet3, pubrec_packet3, \"pubrec3\")\n    mosq_test.do_send_receive(sock, pubrel_packet3, pubcomp_packet3, \"pubcomp3\")\n    sock.close()\n\n\ndef do_test(start_broker):\n    rc = 1\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 1)\n    connect_packet = mosq_test.gen_connect(\"subpub-qos2-receive-max1\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos2/receive/maximum1\", 2, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5)\n\n    mid = 1\n    publish_packet1 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum1\", qos=2, mid=mid, payload=\"message1\", proto_ver=5)\n    pubrec_packet1 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet1 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet1 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 2\n    publish_packet2 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum1\", qos=2, mid=mid, payload=\"message2\", proto_ver=5)\n    pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 3\n    publish_packet3 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum1\", qos=2, mid=mid, payload=\"message3\", proto_ver=5)\n    pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        helper(port)\n\n        mosq_test.expect_packet(sock, \"publish1\", publish_packet1)\n        mosq_test.do_send_receive(sock, pubrec_packet1, pubrel_packet1, \"pubrel1\")\n        sock.send(pubcomp_packet1)\n\n        mosq_test.expect_packet(sock, \"publish2\", publish_packet2)\n        mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, \"pubrel2\")\n        sock.send(pubcomp_packet2)\n\n        mosq_test.expect_packet(sock, \"publish3\", publish_packet3)\n        mosq_test.do_send_receive(sock, pubrec_packet3, pubrel_packet3, \"pubrel3\")\n        sock.send(pubcomp_packet3)\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos2-receive-maximum-2.py",
    "content": "#!/usr/bin/env python3\n\n# Does the broker respect receive maximum==2?\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef helper(port):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"subpub-qos2-recv-max2-helper\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"subpub/qos2/receive/maximum2\", qos=2, mid=mid, payload=\"message1\", proto_ver=5)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 2\n    publish_packet2 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum2\", qos=2, mid=mid, payload=\"message2\", proto_ver=5)\n    pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 3\n    publish_packet3 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum2\", qos=2, mid=mid, payload=\"message3\", proto_ver=5)\n    pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n    mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec\")\n    mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"pubcomp\")\n\n    mosq_test.do_send_receive(sock, publish_packet2, pubrec_packet2, \"pubrec2\")\n    mosq_test.do_send_receive(sock, pubrel_packet2, pubcomp_packet2, \"pubcomp2\")\n\n    mosq_test.do_send_receive(sock, publish_packet3, pubrec_packet3, \"pubrec3\")\n    mosq_test.do_send_receive(sock, pubrel_packet3, pubcomp_packet3, \"pubcomp3\")\n    sock.close()\n\n\ndef do_test(start_broker, proto_ver):\n    if proto_ver == 4:\n        exit(0)\n\n    rc = 1\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 2)\n    connect_packet = mosq_test.gen_connect(\"subpub-qos2-recv-max2\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos2/receive/maximum2\", 2, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5)\n\n    mid = 1\n    publish_packet1 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum2\", qos=2, mid=mid, payload=\"message1\", proto_ver=5)\n    pubrec_packet1 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet1 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet1 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 2\n    publish_packet2 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum2\", qos=2, mid=mid, payload=\"message2\", proto_ver=5)\n    pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 3\n    publish_packet3 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum2\", qos=2, mid=mid, payload=\"message3\", proto_ver=5)\n    pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        helper(port)\n\n        mosq_test.expect_packet(sock, \"publish1\", publish_packet1)\n        mosq_test.expect_packet(sock, \"publish2\", publish_packet2)\n        mosq_test.do_send_receive(sock, pubrec_packet1, pubrel_packet1, \"pubrel1\")\n        sock.send(pubcomp_packet1)\n\n        mosq_test.expect_packet(sock, \"publish3\", publish_packet3)\n        mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, \"pubrel2\")\n        sock.send(pubcomp_packet2)\n\n        mosq_test.do_send_receive(sock, pubrec_packet3, pubrel_packet3, \"pubrel3\")\n        sock.send(pubcomp_packet3)\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker, proto_ver=5)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic receives its own message sent to that topic.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 530\n    connect_packet = mosq_test.gen_connect(\"subpub-qos2-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos2\", 2, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 301\n    publish_packet = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet2 = mosq_test.gen_publish(\"subpub/qos2\", qos=2, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec\")\n        sock.send(pubrel_packet)\n\n        mosq_test.receive_unordered(sock, pubcomp_packet, publish_packet2, \"pubcomp/publish2\")\n\n        mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, \"pubrel2\")\n        sock.send(pubcomp_packet2)\n        # Broker side of flow complete so can quit here.\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subpub-recover-subscriptions.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether a durable client keeps its subscriptions on reconnecting.\n\nfrom mosq_test_helper import *\n\ndef publish_helper(port):\n    connect_packet = mosq_test.gen_connect(\"subpub-sub-helper\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n    publish1_packet = mosq_test.gen_publish(\"not-shared/sub\", qos=0, payload=\"message1\")\n    publish2_packet = mosq_test.gen_publish(\"shared/sub\", qos=0, payload=\"message2\")\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    sock.send(publish1_packet)\n    sock.send(publish2_packet)\n    sock.close()\n\n\ndef do_test(proto_ver):\n    rc = 1\n    if proto_ver == 5:\n        props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\n        connect_packet = mosq_test.gen_connect(\"subpub-sub-test\", proto_ver=proto_ver, clean_session=False, properties=props)\n    else:\n        connect_packet = mosq_test.gen_connect(\"subpub-sub-test\", proto_ver=proto_ver, clean_session=False)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, flags=1)\n\n    mid = 1\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"not-shared/sub\", 0, proto_ver=proto_ver)\n    suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    mid = 2\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"$share/name/shared/sub\", 0, proto_ver=proto_ver)\n    suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    publish1_packet = mosq_test.gen_publish(\"not-shared/sub\", qos=0, payload=\"message1\", proto_ver=proto_ver)\n    publish2_packet = mosq_test.gen_publish(\"shared/sub\", qos=0, payload=\"message2\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, timeout=2, port=port, connack_error=\"connack 1\")\n\n        mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback1\")\n        mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback2\")\n\n        publish_helper(port)\n        mosq_test.expect_packet(sock, \"publish1\", publish1_packet)\n        if proto_ver == 5:\n            mosq_test.expect_packet(sock, \"publish2\", publish2_packet)\n        sock.close()\n\n        # Reconnect, but don't resubscribe\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=2, port=port, connack_error=\"connack 2\")\n\n        publish_helper(port)\n        mosq_test.expect_packet(sock, \"publish1\", publish1_packet)\n        if proto_ver == 5:\n            mosq_test.expect_packet(sock, \"publish2\", publish2_packet)\n        sock.close()\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as err:\n        print(err)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/02-subscribe-dollar-v5.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a SUBSCRIBE to $SYS or $share succeeds\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"subscribe-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"$SYS/broker/missing\", 0, proto_ver=proto_ver)\n    suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    mid = 2\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"$share/share/#\", 0, proto_ver=proto_ver)\n    suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback1\")\n        mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback2\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/02-subscribe-invalid-utf8.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a SUBSCRIBE to a topic with an invalid UTF-8 topic fails\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 53\n    connect_packet = mosq_test.gen_connect(\"subscribe-invalid-utf8\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"invalid/utf8\", 0, proto_ver=proto_ver)\n    b = list(struct.unpack(\"B\"*len(subscribe_packet), subscribe_packet))\n    b[13] = 0 # Topic should never have a 0x0000\n    subscribe_packet = struct.pack(\"B\"*len(b), *b)\n\n    port = mosq_test.get_port()\n    broker = None\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        if proto_ver == 4:\n            try:\n                mosq_test.do_send_receive(sock, subscribe_packet, b\"\", \"suback\")\n            except BrokenPipeError:\n                rc = 0\n        else:\n            disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code = mqtt5_rc.MALFORMED_PACKET)\n            mosq_test.do_send_receive(sock, subscribe_packet, disconnect_packet, \"suback\")\n            rc = 0\n\n        sock.close()\n    finally:\n        if broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n    return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc\n    return 0\n\nif __name__ == '__main__':\n    sys.exit(all_tests(True))\n"
  },
  {
    "path": "test/broker/02-subscribe-long-topic.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a SUBSCRIBE to a topic with 65535 hierarchy characters fails\n# This needs checking with MOSQ_USE_VALGRIND=1 to detect memory failures\n# https://github.com/eclipse/mosquitto/issues/1412\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 1\n    connect_packet = mosq_test.gen_connect(\"subscribe-long-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"/\"*65535, 0, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    broker = None\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        if proto_ver == 4:\n            try:\n                mosq_test.do_send_receive(sock, subscribe_packet, b\"\", \"suback\")\n            except BrokenPipeError:\n                rc = 0\n        else:\n            disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code = mqtt5_rc.MALFORMED_PACKET)\n            mosq_test.do_send_receive(sock, subscribe_packet, disconnect_packet, \"suback\")\n            rc = 0\n\n        sock.close()\n    finally:\n        if broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n    return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc\n    return 0\n\nif __name__ == '__main__':\n    sys.exit(all_tests(True))\n"
  },
  {
    "path": "test/broker/02-subscribe-persistence-flipflop.py",
    "content": "#!/usr/bin/env python3\n\n# Test switching between persistence and a clean session.\n#\n# Bug #874:\n#\n#\n# mosquitto_sub -i sub -t 'topic' -v -p 29883 -q 1 -d -c\n# ^C\n# mosquitto_sub -i sub -t 'topic' -v -p 29883 -q 1 -d\n# ^C\n#\n# SUBSCRIBE to topic is no longer respected by mosquitto\n#\n# run:\n#\n# mosquitto_sub -i sub -t 'topic' -v -p 29883 -q 1 -d -c\n#\n# and in a separate shell\n#\n# mosquitto_pub -i pub -t topic -m 'hello' -p 29883 -q 1\n#\n# sub does not receive the message\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    connect_packet_sub_persistent = mosq_test.gen_connect(\"flipflop-test\", clean_session=False, proto_ver=proto_ver)\n    connect_packet_sub_clean = mosq_test.gen_connect(\"flipflop-test\", clean_session=True, proto_ver=proto_ver)\n    connack_packet_sub = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    connect_packet_pub = mosq_test.gen_connect(\"flipflop-test-pub\", proto_ver=proto_ver)\n    connack_packet_pub = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid=1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"flipflop/test\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    mid=1\n    publish_packet = mosq_test.gen_publish(\"flipflop/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        # mosquitto_sub -i sub -t 'topic' -q 1 -d -c\n        sub_sock = mosq_test.do_client_connect(connect_packet_sub_persistent, connack_packet_sub, port=port)\n        mosq_test.do_send_receive(sub_sock, subscribe_packet, suback_packet, \"subscribe persistent 1\")\n        # And disconnect\n        sub_sock.close()\n\n        # mosquitto_sub -i sub -t 'topic' -q 1 -d\n        sub_sock = mosq_test.do_client_connect(connect_packet_sub_clean, connack_packet_sub, port=port)\n        mosq_test.do_send_receive(sub_sock, subscribe_packet, suback_packet, \"subscribe clean\")\n        # And disconnect\n        sub_sock.close()\n\n        # mosquitto_sub -i sub -t 'topic' -v -q 1 -d -c\n        sub_sock = mosq_test.do_client_connect(connect_packet_sub_persistent, connack_packet_sub, port=port)\n        mosq_test.do_send_receive(sub_sock, subscribe_packet, suback_packet, \"subscribe persistent 2\")\n\n        # and in a separate shell\n        #\n        # mosquitto_pub -i pub -t topic -m 'hello' -p 29883 -q 1\n        pub_sock = mosq_test.do_client_connect(connect_packet_pub, connack_packet_pub, port=port)\n        mosq_test.do_send_receive(pub_sock, publish_packet, puback_packet, \"publish\")\n\n        mosq_test.expect_packet(sub_sock, \"publish receive\", publish_packet)\n        rc = 0\n\n        sub_sock.close()\n\n        sub_sock = mosq_test.do_client_connect(connect_packet_sub_clean, connack_packet_sub, port=port)\n        sub_sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/03-pattern-matching.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef helper(port, pub_topic):\n    connect_packet = mosq_test.gen_connect(\"test-helper\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    publish_packet = mosq_test.gen_publish(pub_topic, qos=0, retain=True, payload=\"message\")\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error=\"helper connack\", port=port)\n    sock.send(publish_packet)\n    sock.close()\n\n\ndef pattern_test(sub_topic, pub_topic):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"pattern-sub-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    publish_packet = mosq_test.gen_publish(pub_topic, qos=0, payload=\"message\")\n    publish_retained_packet = mosq_test.gen_publish(pub_topic, qos=0, retain=True, payload=\"message\")\n\n    mid = 312\n    subscribe_packet = mosq_test.gen_subscribe(mid, sub_topic, 0)\n    suback_packet = mosq_test.gen_suback(mid, 0)\n\n    mid = 234;\n    unsubscribe_packet = mosq_test.gen_unsubscribe(mid, sub_topic)\n    unsuback_packet = mosq_test.gen_unsuback(mid)\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        helper(port, pub_topic)\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, \"unsuback\")\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        mosq_test.expect_packet(sock, \"publish retained\", publish_retained_packet)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(stdo.decode('utf-8'))\n            sys.exit(rc)\n\n    return rc\n\npattern_test(\"#\", \"test/topic\")\npattern_test(\"#\", \"/test/topic\")\npattern_test(\"foo/#\", \"foo/bar/baz\")\npattern_test(\"foo/+/baz\", \"foo/bar/baz\")\npattern_test(\"foo/+/baz/#\", \"foo/bar/baz\")\npattern_test(\"foo/+/baz/#\", \"foo/bar/baz/bar\")\npattern_test(\"foo/foo/baz/#\", \"foo/foo/baz/bar\")\npattern_test(\"foo/#\", \"foo\")\npattern_test(\"foo/#\", \"foo/\")\npattern_test(\"/#\", \"/foo\")\npattern_test(\"test/topic/\", \"test/topic/\")\npattern_test(\"test/topic/+\", \"test/topic/\")\npattern_test(\"+/+/+/+/+/+/+/+/+/+/test\", \"one/two/three/four/five/six/seven/eight/nine/ten/test\")\n\npattern_test(\"#\", \"test////a//topic\")\npattern_test(\"#\", \"/test////a//topic\")\npattern_test(\"foo/#\", \"foo//bar///baz\")\npattern_test(\"foo/+/baz\", \"foo//baz\")\npattern_test(\"foo/+/baz//\", \"foo//baz//\")\npattern_test(\"foo/+/baz/#\", \"foo//baz\")\npattern_test(\"foo/+/baz/#\", \"foo//baz/bar\")\npattern_test(\"foo//baz/#\", \"foo//baz/bar\")\npattern_test(\"foo/foo/baz/#\", \"foo/foo/baz/bar\")\npattern_test(\"/#\", \"////foo///bar\")\n\nexit(0)\n\n"
  },
  {
    "path": "test/broker/03-publish-b2c-disconnect-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Does an interrupted QoS 1 flow from broker to client get handled correctly?\n\nfrom mosq_test_helper import *\n\ndef helper(port):\n    connect_packet = mosq_test.gen_connect(\"03-b2c-disco-qos1-helper\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 128\n    publish_packet = mosq_test.gen_publish(\"03/b2c/qos1/disconnect/test\", qos=1, mid=mid, payload=\"disconnect-message\")\n    puback_packet = mosq_test.gen_puback(mid)\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error=\"helper connack\", port=port)\n    mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"helper puback\")\n    sock.close()\n\n\ndef do_test(start_broker, proto_ver):\n    port = mosq_test.get_port()\n\n    rc = 1\n    mid = 3265\n    connect_packet = mosq_test.gen_connect(\"03-b2c-disco-qos1\", clean_session=False, proto_ver=proto_ver, session_expiry=60)\n    connack1_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver)\n    connack2_packet = mosq_test.gen_connack(flags=1, rc=0, proto_ver=proto_ver)\n    connect_packet_clear = mosq_test.gen_connect(\"03-b2c-disco-qos1\", proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"03/b2c/qos1/disconnect/test\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/b2c/qos1/disconnect/test\", qos=1, mid=mid, payload=\"disconnect-message\", proto_ver=proto_ver)\n    publish_dup_packet = mosq_test.gen_publish(\"03/b2c/qos1/disconnect/test\", qos=1, mid=mid, payload=\"disconnect-message\", dup=True, proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    mid = 3266\n    publish2_packet = mosq_test.gen_publish(\"03/b2c/qos1/outgoing\", qos=1, mid=mid, payload=\"outgoing-message\", proto_ver=proto_ver)\n    puback2_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        helper(port)\n        # Should have now received a publish command\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        # Send our outgoing message. When we disconnect the broker\n        # should get rid of it and assume we're going to retry.\n        sock.send(publish2_packet)\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port)\n        mosq_test.expect_packet(sock, \"dup publish\", publish_dup_packet)\n        sock.send(puback_packet)\n        rc = 0\n\n        sock.close()\n\n        # clear session\n        sock = mosq_test.do_client_connect(connect_packet_clear, connack1_packet, port=port)\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/03-publish-b2c-disconnect-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Does an interrupted QoS 1 flow from broker to client get handled correctly?\n\nfrom mosq_test_helper import *\n\n\ndef helper(port):\n    connect_packet = mosq_test.gen_connect(\"03-bc2-disco-qos2-helper\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 312\n    publish_packet = mosq_test.gen_publish(\"03/b2c/qos2/disconnect/test\", qos=2, mid=mid, payload=\"disconnect-message\")\n    pubrec_packet = mosq_test.gen_pubrec(mid)\n    pubrel_packet = mosq_test.gen_pubrel(mid)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error=\"helper connack\", port=port)\n\n    mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"helper pubrec\")\n    mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"helper pubcomp\")\n\n    sock.close()\n\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 3265\n    connect_packet = mosq_test.gen_connect(\"03-b2c-disco-qos2-test\", clean_session=False, proto_ver=proto_ver, session_expiry=60)\n    connack1_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver)\n    connack2_packet = mosq_test.gen_connack(flags=1, rc=0, proto_ver=proto_ver)\n    connect_packet_clear = mosq_test.gen_connect(\"03-b2c-disco-qos2-test\", proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"03/b2c/qos2/disconnect/test\", 2, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/b2c/qos2/disconnect/test\", qos=2, mid=mid, payload=\"disconnect-message\", proto_ver=proto_ver)\n    publish_dup_packet = mosq_test.gen_publish(\"03/b2c/qos2/disconnect/test\", qos=2, mid=mid, payload=\"disconnect-message\", dup=True, proto_ver=proto_ver)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n    mid = 3266\n    publish2_packet = mosq_test.gen_publish(\"03/b2c/qos2/outgoing\", qos=1, mid=mid, payload=\"outgoing-message\", proto_ver=proto_ver)\n    puback2_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        helper(port)\n        # Should have now received a publish command\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        # Send our outgoing message. When we disconnect the broker\n        # should get rid of it and assume we're going to retry.\n        sock.send(publish2_packet)\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port)\n        mosq_test.expect_packet(sock, \"dup publish\", publish_dup_packet)\n        mosq_test.do_send_receive(sock, pubrec_packet, pubrel_packet, \"pubrel\")\n\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port)\n        mosq_test.expect_packet(sock, \"dup pubrel\", pubrel_packet)\n        sock.send(pubcomp_packet)\n        rc = 0\n        sock.close()\n\n        # Clear session\n        sock = mosq_test.do_client_connect(connect_packet_clear, connack1_packet, port=port)\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/03-publish-b2c-qos1-len.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether the broker handles a v5 PUBACK with all combinations\n# of with/without reason code and properties.\n\nfrom mosq_test_helper import *\n\ndef helper(port):\n    connect_packet = mosq_test.gen_connect(\"03-b2c-qos1-len-helper\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/b2c/qos1/len/test\", qos=1, mid=mid, payload=\"len-message\")\n    puback_packet = mosq_test.gen_puback(mid)\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error=\"helper connack\", port=port)\n    mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"helper puback\")\n    sock.close()\n\n\ndef do_test(start_broker, test, puback_packet):\n    rc = 1\n    mid = 3265\n    connect_packet = mosq_test.gen_connect(\"03-b2c-qos1-len\", clean_session=False, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"03/b2c/qos1/len/test\", 1, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/b2c/qos1/len/test\", qos=1, mid=mid, payload=\"len-message\", proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        helper(port)\n        # Should have now received a publish command\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        sock.send(puback_packet)\n\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(test)\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    # No reason code, no properties\n    puback_packet = mosq_test.gen_puback(1)\n    rc = do_test(start_broker, \"qos1 len 2\", puback_packet)\n    if rc:\n        return rc\n\n    # Reason code, no properties\n    puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00)\n    rc = do_test(start_broker, \"qos1 len 3\", puback_packet)\n    if rc:\n        return rc\n\n    # Reason code, empty properties\n    puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00, properties=\"\")\n    rc = do_test(start_broker, \"qos1 len 4\", puback_packet)\n    if rc:\n        return rc\n\n    # Reason code, one property\n    props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\n    puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00, properties=props)\n    rc = do_test(start_broker, \"qos1 len >5\", puback_packet)\n    if rc:\n        return rc\n\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n\n\n"
  },
  {
    "path": "test/broker/03-publish-b2c-qos2-len.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether the broker handles a v5 PUBREC, PUBCOMP with all combinations\n# of with/without reason code and properties.\n\nfrom mosq_test_helper import *\n\ndef helper(port):\n    connect_packet = mosq_test.gen_connect(\"03-b2c-qos2-len-helper\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/b2c/qos2/len/test\", qos=2, mid=mid, payload=\"len-message\")\n    pubrec_packet = mosq_test.gen_pubrec(mid)\n    pubrel_packet = mosq_test.gen_pubrel(mid)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error=\"helper connack\", port=port)\n\n    mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"helper pubrec\")\n    mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"helper pubcomp\")\n    sock.close()\n\n\ndef do_test(start_broker, test, pubrec_packet, pubcomp_packet):\n    rc = 1\n    mid = 3265\n    connect_packet = mosq_test.gen_connect(\"03-b2c-qos2-len-test\", clean_session=False, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"03/b2c/qos2/len/test\", 2, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/b2c/qos2/len/test\", qos=2, mid=mid, payload=\"len-message\", proto_ver=5)\n    pubrel_packet = mosq_test.gen_pubrel(mid)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        helper(port)\n        # Should have now received a publish command\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        mosq_test.do_send_receive(sock, pubrec_packet, pubrel_packet, \"pubrel\")\n        sock.send(pubcomp_packet)\n\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(test)\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    # No reason code, no properties\n    pubrec_packet = mosq_test.gen_pubrec(1)\n    pubcomp_packet = mosq_test.gen_pubcomp(1)\n    rc = do_test(start_broker, \"qos2 len 2\", pubrec_packet, pubcomp_packet)\n    if rc:\n        return rc\n\n    # Reason code, no properties\n    pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00)\n    pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00)\n    rc = do_test(start_broker, \"qos2 len 3\", pubrec_packet, pubcomp_packet)\n    if rc:\n        return rc\n\n    # Reason code, empty properties\n    pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00, properties=\"\")\n    pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00, properties=\"\")\n    rc = do_test(start_broker, \"qos2 len 4\", pubrec_packet, pubcomp_packet)\n    if rc:\n        return rc\n\n    # Reason code, one property\n    props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\n    pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00, properties=props)\n    props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\n    pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00, properties=props)\n    rc = do_test(start_broker, \"qos2 len >5\", pubrec_packet, pubcomp_packet)\n    if rc:\n        return rc\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/03-publish-bad-flags.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether the broker handles malformed packets correctly - PUBLISH\n# MQTTv5\n\nfrom mosq_test_helper import *\n\nrc = 1\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"maximum_qos 1\\n\")\n        f.write(\"retain_available false\\n\")\n\ndef do_test(publish_packet, reason_code, error_string):\n    global rc\n\n    rc = 1\n\n    connect_packet = mosq_test.gen_connect(\"13-malformed-publish-v5\", proto_ver=5)\n\n    connack_props = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10)\n    connack_props += mqtt5_props.gen_byte_prop(mqtt5_props.RETAIN_AVAILABLE, 0)\n    connack_props += mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 2000000)\n    connack_props += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n    connack_props += mqtt5_props.gen_byte_prop(mqtt5_props.MAXIMUM_QOS, 1)\n\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=connack_props, property_helper=False)\n\n    mid = 0\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=reason_code)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, error_string=error_string)\n    rc = 0\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    # qos > maximum qos\n    publish_packet = mosq_test.gen_publish(topic=\"test/topic\", qos=2, mid=1, proto_ver=5)\n    do_test(publish_packet, mqtt5_rc.QOS_NOT_SUPPORTED, \"qos > maximum qos\")\n\n    # retain not supported\n    publish_packet = mosq_test.gen_publish(topic=\"test/topic\", qos=0, retain=True, proto_ver=5, payload=\"a\")\n    do_test(publish_packet, mqtt5_rc.RETAIN_NOT_SUPPORTED, \"retain not supported\")\nexcept mosq_test.TestError:\n    pass\nfinally:\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    os.remove(conf_file)\n    if rc:\n        print(stde.decode('utf-8'))\n        exit(rc)\n"
  },
  {
    "path": "test/broker/03-publish-c2b-disconnect-qos2.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 3265\n    connect_packet = mosq_test.gen_connect(\"03-c2b-qos2-disco-test\", clean_session=False, proto_ver=proto_ver, session_expiry=60)\n    connack1_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver)\n    connect_packet_clear = mosq_test.gen_connect(\"03-c2b-qos2-disco-test\", proto_ver=proto_ver)\n\n    if proto_ver == 3:\n        connack2_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver)\n    else:\n        connack2_packet = mosq_test.gen_connack(flags=1, rc=0, proto_ver=proto_ver)\n\n    helper_connect_packet = mosq_test.gen_connect(\"03-c2b-qos2-disco-helper\", clean_session=True, proto_ver=proto_ver)\n    helper_connack_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver)\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"03/c2b/qos2/disconnect/test\", 2, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/c2b/qos2/disconnect/test\", qos=2, mid=mid, payload=\"disconnect-message\", proto_ver=proto_ver)\n    publish_dup_packet = mosq_test.gen_publish(\"03/c2b/qos2/disconnect/test\", qos=2, mid=mid, payload=\"disconnect-message\", dup=True, proto_ver=proto_ver)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    if proto_ver == 3:\n        pubrel_dup_packet = mosq_test.gen_pubrel(mid, dup=True, proto_ver=proto_ver)\n    else:\n        pubrel_dup_packet = mosq_test.gen_pubrel(mid, dup=False, proto_ver=proto_ver)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        # Add a subscriber, so we ensure that the QoS 2 flow must be completed\n        helper = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, port=port)\n        mosq_test.do_send_receive(helper, subscribe_packet, suback_packet)\n\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port)\n\n        mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec\")\n\n        # We're now going to disconnect and pretend we didn't receive the pubrec.\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port, connack_error=\"connack 2\")\n        sock.send(publish_dup_packet)\n\n        mosq_test.expect_packet(sock, \"pubrec\", pubrec_packet)\n        mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"pubcomp\")\n\n        # Again, pretend we didn't receive this pubcomp\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port)\n        mosq_test.do_send_receive(sock, pubrel_dup_packet, pubcomp_packet, \"pubcomp\")\n\n        rc = 0\n\n        sock.close()\n        helper.close()\n\n        # Clear session\n        sock = mosq_test.do_client_connect(connect_packet_clear, connack1_packet, port=port, connack_error=\"connack clear\")\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=3)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/03-publish-c2b-qos2-len.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether the broker handles a v5 PUBREL with all combinations\n# of with/without reason code and properties.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, test, pubrel_packet):\n    rc = 1\n    mid = 3265\n    connect_packet = mosq_test.gen_connect(\"03-c2b-qos2-len\", clean_session=False, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/c2b/qos2/len/test\", qos=2, mid=mid, payload=\"len-message\", proto_ver=5)\n    pubrec_packet = mosq_test.gen_pubrec(mid)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n\n        mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec\")\n        mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"pubcomp\")\n\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(test)\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    # No reason code, no properties\n    pubrel_packet = mosq_test.gen_pubrel(1)\n    rc = do_test(start_broker, \"qos2 len 2\", pubrel_packet)\n    if rc:\n        return rc\n\n    # Reason code, no properties\n    pubrel_packet = mosq_test.gen_pubrel(1, proto_ver=5, reason_code=0x00)\n    rc = do_test(start_broker, \"qos2 len 3\", pubrel_packet)\n    if rc:\n        return rc\n\n    # Reason code, empty properties\n    pubrel_packet = mosq_test.gen_pubrel(1, proto_ver=5, reason_code=0x00, properties=\"\")\n    rc = do_test(start_broker, \"qos2 len 4\", pubrel_packet)\n    if rc:\n        return rc\n\n    # Reason code, one property\n    props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\n    pubrel_packet = mosq_test.gen_pubrel(1, proto_ver=5, reason_code=0x00, properties=props)\n    rc = do_test(start_broker, \"qos2 len >5\", pubrel_packet)\n    if rc:\n        return rc\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/03-publish-dollar-v5.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to $ topics QoS 1 results in the expected PUBACK packet.\n\nfrom mosq_test_helper import *\n\nmid = 1\ndef helper(port, topic, reason_code):\n    global mid\n\n    connect_packet = mosq_test.gen_connect(\"03-publish-dollar-v5\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n    publish_packet = mosq_test.gen_publish(topic, qos=1, mid=mid, payload=\"message\", proto_ver=5)\n\n    if reason_code == 0:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=5)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=reason_code)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback%d\"%(mid))\n    mid += 1\n\n\ndef do_test(start_broker):\n    rc = 1\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        helper(port, \"$SYS/broker/uptime\", mqtt5_rc.NOT_AUTHORIZED)\n        helper(port, \"$SYS/broker/connection/me\", mqtt5_rc.NOT_AUTHORIZED)\n        helper(port, \"$SYS/broker/connection/me/state\", mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n        helper(port, \"$share/share/03/publish/dollar/v5/topic\", mqtt5_rc.NOT_AUTHORIZED)\n\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/03-publish-dollar.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to a topic starting with $ succeeds\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    mid = 19\n    connect_packet = mosq_test.gen_connect(\"pub-dollar-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    publish_packet = mosq_test.gen_publish(\"$test/test\", qos=1, mid=mid, payload=\"message\")\n    puback_packet = mosq_test.gen_puback(mid)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/03-publish-invalid-utf8.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to a topic with an invalid UTF-8 topic fails\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 53\n    connect_packet = mosq_test.gen_connect(\"03-publish-invalid-utf8\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"03/invalid/utf8\", 1, mid=mid, proto_ver=proto_ver)\n    b = list(struct.unpack(\"B\"*len(publish_packet), publish_packet))\n    b[11] = 0 # Topic should never have a 0x0000\n    publish_packet = struct.pack(\"B\"*len(b), *b)\n\n    port = mosq_test.get_port()\n    broker = None\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        if proto_ver == 4:\n            try:\n                mosq_test.do_send_receive(sock, publish_packet, b\"\", \"puback\")\n            except BrokenPipeError:\n                rc = 0\n        else:\n            disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=mqtt5_rc.MALFORMED_PACKET)\n            mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, \"puback\")\n            rc = 0\n\n        sock.close()\n    finally:\n        if broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n    return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc\n    return 0\n\nif __name__ == '__main__':\n    sys.exit(all_tests(True))\n"
  },
  {
    "path": "test/broker/03-publish-long-topic.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to a topic with 65535 hierarchy characters fails\n# This needs checking with MOSQ_USE_VALGRIND=1 to detect memory failures\n# https://github.com/eclipse/mosquitto/issues/1412\n\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 19\n    connect_packet = mosq_test.gen_connect(\"03-pub-long-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"/\"*65535, qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    broker = None\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        if proto_ver == 4:\n            try:\n                mosq_test.do_send_receive(sock, publish_packet, b\"\", \"puback\")\n            except BrokenPipeError:\n                rc = 0\n        else:\n            disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=mqtt5_rc.MALFORMED_PACKET)\n            mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, \"puback\")\n            rc = 0\n\n        sock.close()\n    finally:\n        if broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n    return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc\n    return 0\n\nif __name__ == '__main__':\n    sys.exit(all_tests(True))\n"
  },
  {
    "path": "test/broker/03-publish-qos1-max-inflight-expire.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to a topic with QoS 1 results in the correct packet flow for a subscriber.\n# With max_inflight_messages set to 1\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"max_inflight_messages 1\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n\n    properties = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 1000)\n    sub_connect_packet = mosq_test.gen_connect(\"sub\", properties=properties, proto_ver=proto_ver, clean_session=False)\n\n    properties = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10) \\\n        + mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 2000000) \\\n        + mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 1)\n    sub_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=properties, property_helper=False)\n    sub_connack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver, properties=properties, property_helper=False)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"pub/qos1/test\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    connect_packet = mosq_test.gen_connect(\"pub-qos1-test\", proto_ver=proto_ver)\n    properties = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10) \\\n        + mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 2000000) \\\n        + mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 1)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=properties, property_helper=False)\n\n    mid = 311\n    properties = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 1)\n    publish_packet = mosq_test.gen_publish(\"pub/qos1/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver, properties=properties)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    mid = 1\n    r_publish_packet = mosq_test.gen_publish(\"pub/qos1/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    r_puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sub_sock = mosq_test.do_client_connect(sub_connect_packet, sub_connack_packet, port=port, timeout=10)\n        mosq_test.do_send_receive(sub_sock, subscribe_packet, suback_packet, \"suback\")\n        sub_sock.close()\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, timeout=10)\n        mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n\n        time.sleep(2)\n\n        sub_sock = mosq_test.do_client_connect(sub_connect_packet, sub_connack_packet2, port=port, timeout=10)\n        # This message has expired, so we don't expect it\n        #mosq_test.expect_packet(sub_sock, \"publish 2\", r_publish_packet)\n        #sub_sock.send(r_puback_packet)\n\n        #\n        mid = 1\n        s_publish_packet = mosq_test.gen_publish(\"pub/qos1/test\", qos=1, mid=mid, payload=\"message2\", proto_ver=proto_ver)\n        s_puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n        mosq_test.do_send_receive(sock, s_publish_packet, s_puback_packet, \"puback\")\n\n        mid = 2\n        r_publish_packet = mosq_test.gen_publish(\"pub/qos1/test\", qos=1, mid=mid, payload=\"message2\", proto_ver=proto_ver)\n        r_puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n        mosq_test.expect_packet(sub_sock, \"publish 3\", r_publish_packet)\n        sub_sock.send(r_puback_packet)\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/03-publish-qos1-max-inflight.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to a topic with QoS 1 results in the correct packet flow.\n# With max_inflight_messages set to 1\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"max_inflight_messages 1\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"pub-qos1-test\", proto_ver=proto_ver)\n    properties = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10) \\\n        + mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 2000000) \\\n        + mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 1)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=properties, property_helper=False)\n\n    mid = 311\n    publish_packet = mosq_test.gen_publish(\"pub/qos1/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, timeout=10)\n        mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n\n"
  },
  {
    "path": "test/broker/03-publish-qos1-no-subscribers-v5.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to a topic with QoS 1 results in the correct PUBACK\n# packet when there are no subscribers.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"03-pub-qos1-no-subs\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    publish1_packet = mosq_test.gen_publish(\"03B/no/subs/pub\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback1_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n    mid = 2\n    publish2_packet = mosq_test.gen_publish(\"03B/no/subs/pub/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback2_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n    mid = 3\n    publish3_packet = mosq_test.gen_publish(\"03B/no/subs/pub/qos1/test\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback3_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n    mid = 4\n    publish4_packet = mosq_test.gen_publish(\"03B/no/subs/pub/qos1/test\", qos=1, mid=mid, payload=\"message\", proto_ver=5, retain=True)\n    puback4_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n    mid = 5\n    publish1b_packet = mosq_test.gen_publish(\"03B/no/subs/pub\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback1b_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n    mid = 6\n    publish2b_packet = mosq_test.gen_publish(\"03B/no/subs/pub/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback2b_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n    mid = 7\n    publish3b_packet = mosq_test.gen_publish(\"03B/no/subs/pub/qos1/test\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback3b_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n\n        # None of the pub/qos1/test topic tree exists here\n        mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, \"puback1a\")\n        mosq_test.do_send_receive(sock, publish2_packet, puback2_packet, \"puback2a\")\n        mosq_test.do_send_receive(sock, publish3_packet, puback3_packet, \"puback3a\")\n\n        # This publish sets a retained message, which means the topic tree exists\n        mosq_test.do_send_receive(sock, publish4_packet, puback4_packet, \"puback4\")\n\n        # So now test again\n        mosq_test.do_send_receive(sock, publish1b_packet, puback1b_packet, \"puback1b\")\n        mosq_test.do_send_receive(sock, publish2b_packet, puback2b_packet, \"puback2b\")\n        mosq_test.do_send_receive(sock, publish3b_packet, puback3b_packet, \"puback3b\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/03-publish-qos1-queued-bytes.conf",
    "content": "sys_interval 1\nmax_queued_messages 0\nmax_queued_bytes 400\nport 1888\n"
  },
  {
    "path": "test/broker/03-publish-qos1-queued-bytes.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to a topic with an offline subscriber results in a queued message\nimport Queue\nimport random\nimport string\nimport subprocess\nimport socket\nimport threading\nimport time\n\ntry:\n    import paho.mqtt.client\n    import paho.mqtt.publish\nexcept ImportError:\n    print(\"WARNING: paho.mqtt module not available, skipping byte count test.\")\n    exit(0)\n\n\nfrom mosq_test_helper import *\n\nrc = 1\n\nport = mosq_test.get_port()\n\ndef registerOfflineSubscriber():\n    \"\"\"Just a durable client to trigger queuing\"\"\"\n    client = paho.mqtt.client.Client(\"sub-qos1-offline\", clean_session=False)\n    client.connect(\"localhost\", port=port)\n    client.subscribe(\"test/publish/queueing/#\", 1)\n    client.loop()\n    client.disconnect()\n\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\nclass BrokerMonitor(threading.Thread):\n    def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None):\n        threading.Thread.__init__(self, group=group, target=target, name=name, verbose=verbose)\n        self.rq, self.cq = args\n        self.stored = -1\n        self.stored_bytes = -1\n        self.dropped = -1\n\n    def store_count(self, client, userdata, message):\n        self.stored = int(message.payload)\n\n    def store_bytes(self, client, userdata, message):\n        self.stored_bytes = int(message.payload)\n\n    def publish_dropped(self, client, userdata, message):\n        self.dropped = int(message.payload)\n\n    def run(self):\n        client = paho.mqtt.client.Client(\"broker-monitor\")\n        client.connect(\"localhost\", port=port)\n        client.message_callback_add(\"$SYS/broker/store/messages/count\", self.store_count)\n        client.message_callback_add(\"$SYS/broker/store/messages/bytes\", self.store_bytes)\n        client.message_callback_add(\"$SYS/broker/publish/messages/dropped\", self.publish_dropped)\n        client.subscribe(\"$SYS/broker/store/messages/#\")\n        client.subscribe(\"$SYS/broker/publish/messages/dropped\")\n\n        while True:\n            expect_drops = cq.get()\n            self.cq.task_done()\n            if expect_drops == \"quit\":\n                break\n            first = time.time()\n            while self.stored < 0 or self.stored_bytes < 0 or (expect_drops and self.dropped < 0):\n                client.loop(timeout=0.5)\n                if time.time() - 10 > first:\n                    print(\"ABORT TIMEOUT\")\n                    break\n\n            if expect_drops:\n                self.rq.put((self.stored, self.stored_bytes, self.dropped))\n            else:\n                self.rq.put((self.stored, self.stored_bytes, 0))\n            self.stored = -1\n            self.stored_bytes = -1\n            self.dropped = -1\n\n        client.disconnect()\n\nrq = Queue.Queue()\ncq = Queue.Queue()\nbrokerMonitor = BrokerMonitor(args=(rq,cq))\n\nclass StoreCounts():\n    def __init__(self):\n        self.stored = 0\n        self.bstored = 0\n        self.drops = 0\n        self.diff_stored = 0\n        self.diff_bstored = 0\n        self.diff_drops = 0\n\n    def update(self, tup):\n        self.diff_stored = tup[0] - self.stored\n        self.stored = tup[0]\n        self.diff_bstored = tup[1] - self.bstored\n        self.bstored = tup[1]\n        self.diff_drops = tup[2] - self.drops\n        self.drops = tup[2]\n\n    def __repr__(self):\n        return \"s: %d (%d) b: %d (%d) d: %d (%d)\" % (self.stored, self.diff_stored, self.bstored, self.diff_bstored, self.drops, self.diff_drops)\n\ntry:\n    registerOfflineSubscriber()\n    time.sleep(2.5)  # Wait for first proper dump of stats\n    brokerMonitor.start()\n    counts = StoreCounts()\n    cq.put(True)  # Expect a dropped count (of 0, initial)\n    counts.update(rq.get())  # Initial start\n    print(\"rq.get (INITIAL) gave us: \", counts)\n    rq.task_done()\n\n    # publish 10 short messages, should be no drops\n    print(\"publishing 10 short\")\n    cq.put(False)  # expect no updated drop count\n    msgs_short10 = [(\"test/publish/queueing/%d\" % x,\n             ''.join(random.choice(string.hexdigits) for _ in range(10)),\n             1, False) for x in range(1, 10 + 1)]\n    paho.mqtt.publish.multiple(msgs_short10, port=port)\n    counts.update(rq.get())  # Initial start\n    print(\"rq.get (short) gave us: \", counts)\n    rq.task_done()\n    if counts.diff_stored != 10 or counts.diff_bstored < 100:\n        raise ValueError\n    if counts.diff_drops != 0:\n        raise ValueError\n\n    # publish 10 mediums (40bytes). should fail after 8, when it finally crosses 400\n    print(\"publishing 10 medium\")\n    cq.put(True)  # expect a drop count\n    msgs_medium10 = [(\"test/publish/queueing/%d\" % x,\n             ''.join(random.choice(string.hexdigits) for _ in range(40)),\n             1, False) for x in range(1, 10 + 1)]\n    paho.mqtt.publish.multiple(msgs_medium10, port=port)\n    counts.update(rq.get())  # Initial start\n    print(\"rq.get (medium) gave us: \", counts)\n    rq.task_done()\n    if counts.diff_stored != 8 or counts.diff_bstored < 320:\n        raise ValueError\n    if counts.diff_drops != 2:\n        raise ValueError\n    rc = 0\n\nexcept mosq_test.TestError:\n    pass\nfinally:\n    cq.put(\"quit\")\n    brokerMonitor.join()\n    rq.join()\n    cq.join()\n    broker.terminate()\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/03-publish-qos1-retain-disabled.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH with a retain set when retains are disabled results in\n# the correct DISCONNECT.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"retain_available false\\n\")\n\n\ndef do_test(proto_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    mid = 1\n    connect_packet = mosq_test.gen_connect(\"pub-qos1-test\", proto_ver=5)\n\n    props = mqtt5_props.gen_byte_prop(mqtt5_props.RETAIN_AVAILABLE, 0)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n    publish_packet = mosq_test.gen_publish(\"pub/qos1/test\", qos=1, mid=mid, payload=\"message\", retain=True, proto_ver=5)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(reason_code=154, proto_ver=5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, \"disconnect\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n\n"
  },
  {
    "path": "test/broker/03-publish-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to a topic with QoS 1 results in the correct PUBACK packet.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 19\n    connect_packet = mosq_test.gen_connect(\"03-pub-qos1-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"03/pub/qos1/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n\n        rc = 0\n\n        sock.close()\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/03-publish-qos2-dup.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"03-pub-qos2-dup-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"message\", proto_ver=proto_ver, dup=1)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n\n    disconnect_packet = mosq_test.gen_disconnect(reason_code=130, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec 1\")\n        mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec 2\")\n        if proto_ver == 5:\n            mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, \"disconnect\")\n            rc = 0\n        else:\n            try:\n                mosq_test.do_send_receive(sock, publish_packet, b\"\", \"disconnect1\")\n                rc = 0\n            except BrokenPipeError:\n                rc = 0\n\n        sock.close()\n    except Exception as e:\n        print(e)\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        broker.wait()\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndef all_tests():\n    rc = do_test(proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests()\n"
  },
  {
    "path": "test/broker/03-publish-qos2-max-inflight-exceeded.py",
    "content": "#!/usr/bin/env python3\n\n# What does the broker do if an MQTT v5 client doesn't respect max_inflight_messages?\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    port = mosq_test.get_port()\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"pub-qos2-inflight-exceeded\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, timeout=10)\n\n        for i in range(1, 21):\n            publish_packet = mosq_test.gen_publish(\"pub/qos2/max/inflight/exceeded\", qos=2, mid=i, payload=\"message\", proto_ver=proto_ver)\n            pubrec_packet = mosq_test.gen_pubrec(mid=i, proto_ver=proto_ver)\n            mosq_test.do_send_receive(sock, publish_packet, pubrec_packet)\n\n        i = 21\n        publish_packet = mosq_test.gen_publish(\"pub/qos2/max/inflight/exceeded\", qos=2, mid=i, payload=\"message\", proto_ver=proto_ver)\n        if proto_ver == 5:\n            disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.RECEIVE_MAXIMUM_EXCEEDED, proto_ver=proto_ver)\n        else:\n            disconnect_packet = b\"\"\n        try:\n            mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, \"disconnect\")\n        except BrokenPipeError:\n            pass\n\n        rc = 0\n\n        sock.close()\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n    return rc\n\n\ndef all_test():\n    rc = do_test(proto_ver=4)\n    if rc:\n        return rc\n    rc = do_test(proto_ver=5)\n    return rc\n\nif __name__ == \"__main__\":\n    sys.exit(all_test())\n"
  },
  {
    "path": "test/broker/03-publish-qos2-max-inflight.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow.\n# With max_inflight_messages set to 1\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"max_inflight_messages 1\\n\")\n\n\ndef do_test(proto_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"pub-qos2-test\", proto_ver=proto_ver)\n    properties = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10) \\\n        + mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 2000000) \\\n        + mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 1)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=properties, property_helper=False)\n\n    mid = 312\n    publish_packet = mosq_test.gen_publish(\"pub/qos2/test\", qos=2, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, timeout=10)\n        mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec\")\n        mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"pubcomp\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/03-publish-qos2-reuse-mid.py",
    "content": "#!/usr/bin/env python3\n\n# Test what happens if a client reuses an in-use mid with a different message.\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"pub-qos2-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 312\n    publish_packet1 = mosq_test.gen_publish(\"pub/qos2/test\", qos=2, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n    mid = 312\n    publish_packet2 = mosq_test.gen_publish(\"pub/qos2/reuse\", qos=2, mid=mid, payload=\"message\", proto_ver=proto_ver)\n\n    sub_connect_packet = mosq_test.gen_connect(\"sub-qos2-test\", proto_ver=proto_ver)\n    sub_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"#\", 2, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n    mid = 1\n    publish_packet_expected = mosq_test.gen_publish(\"pub/qos2/reuse\", qos=2, mid=mid, payload=\"message\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        ssock = mosq_test.do_client_connect(sub_connect_packet, sub_connack_packet, port=port)\n        mosq_test.do_send_receive(ssock, subscribe_packet, suback_packet, \"suback\")\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, publish_packet1, pubrec_packet, \"pubrec1\")\n        mosq_test.do_send_receive(sock, publish_packet2, pubrec_packet, \"pubrec2\")\n        mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"pubcomp\")\n\n        mosq_test.expect_packet(ssock, \"publish\", publish_packet_expected)\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/03-publish-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"03-pub-qos2-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 312\n    publish_packet = mosq_test.gen_publish(\"03/pub/qos2/test\", qos=2, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec\")\n        mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"pubcomp\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/04-retain-check-source-persist-diff-port.py",
    "content": "#!/usr/bin/env python3\n\n# Test for CVE-2018-12546, with the broker being stopped to write the persistence file, plus subscriber on different port.\n\nfrom mosq_test_helper import *\nimport os.path\nimport signal\n\ndef write_config(filename, port1, port2, per_listener):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener))\n        f.write(\"check_retain_source true\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"acl_file %s\\n\" % (filename.replace('.conf', '.acl')))\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file %s\\n\" % (filename.replace('.conf', '.db')))\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n\ndef write_acl_1(filename, username):\n    with open(filename, 'w') as f:\n        if username is not None:\n            f.write('user %s\\n' % (username))\n        f.write('topic readwrite test/topic\\n')\n\ndef write_acl_2(filename, username):\n    with open(filename, 'w') as f:\n        if username is not None:\n            f.write('user %s\\n' % (username))\n        f.write('topic read test/topic\\n')\n\n\ndef do_test(proto_ver, per_listener, username):\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, per_listener)\n\n    persistence_file = os.path.basename(__file__).replace('.py', '.db')\n    try:\n        os.remove(persistence_file)\n    except OSError:\n        pass\n\n    acl_file = os.path.basename(__file__).replace('.py', '.acl')\n    write_acl_1(acl_file, username)\n\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"retain-check\", username=username, proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    if per_listener == \"true\":\n        u = None\n    else:\n        # If per listener is false, then the second client will be denied\n        # unless we provide a username\n        u = username\n\n    connect2_packet = mosq_test.gen_connect(\"retain-recv\", username=u, proto_ver=proto_ver)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"test/topic\", qos=0, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"test/topic\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port1)\n        sock.send(publish_packet)\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port2)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback 1\")\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        sock.close()\n\n        # Remove \"write\" ability\n        write_acl_2(acl_file, username)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        if os.path.isfile(persistence_file) == False:\n            raise FileNotFoundError(\"Persistence file not written\")\n\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1)\n\n        sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port2)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback 2\")\n        # If we receive the retained message here, it is a failure.\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        os.remove(conf_file)\n        os.remove(acl_file)\n        try:\n            os.remove(persistence_file)\n        except FileNotFoundError:\n            pass\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\n(port1, port2) = mosq_test.get_port(2)\ndo_test(proto_ver=4, per_listener=\"true\", username=None)\ndo_test(proto_ver=4, per_listener=\"true\", username=\"test\")\ndo_test(proto_ver=4, per_listener=\"false\", username=None)\ndo_test(proto_ver=4, per_listener=\"false\", username=\"test\")\n\ndo_test(proto_ver=5, per_listener=\"true\", username=None)\ndo_test(proto_ver=5, per_listener=\"true\", username=\"test\")\ndo_test(proto_ver=5, per_listener=\"false\", username=None)\ndo_test(proto_ver=5, per_listener=\"false\", username=\"test\")\n"
  },
  {
    "path": "test/broker/04-retain-check-source-persist.py",
    "content": "#!/usr/bin/env python3\n\n# Test for CVE-2018-12546, with the broker being stopped to write the persistence file.\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config(filename, port, per_listener):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener))\n        f.write(\"check_retain_source true\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"acl_file %s\\n\" % (filename.replace('.conf', '.acl')))\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file %s\\n\" % (filename.replace('.conf', '.db')))\n\ndef write_acl_1(filename, username):\n    with open(filename, 'w') as f:\n        if username is not None:\n            f.write('user %s\\n' % (username))\n        f.write('topic readwrite test/topic\\n')\n\ndef write_acl_2(filename, username):\n    with open(filename, 'w') as f:\n        if username is not None:\n            f.write('user %s\\n' % (username))\n        f.write('topic read test/topic\\n')\n\n\ndef do_test(proto_ver, per_listener, username):\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, per_listener)\n\n    persistence_file = os.path.basename(__file__).replace('.py', '.db')\n    try:\n        os.remove(persistence_file)\n    except OSError:\n        pass\n\n    acl_file = os.path.basename(__file__).replace('.py', '.acl')\n    write_acl_1(acl_file, username)\n\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"retain-check\", username=username, proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"test/topic\", qos=0, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"test/topic\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.send(publish_packet)\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback 1\")\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        sock.close()\n\n        # Remove \"write\" ability\n        write_acl_2(acl_file, username)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback 2\")\n        # If we receive the retained message here, it is a failure.\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        os.remove(conf_file)\n        os.remove(acl_file)\n        os.remove(persistence_file)\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\nport = mosq_test.get_port()\ndo_test(proto_ver=4, per_listener=\"true\", username=None)\ndo_test(proto_ver=4, per_listener=\"true\", username=\"test\")\ndo_test(proto_ver=4, per_listener=\"false\", username=None)\ndo_test(proto_ver=4, per_listener=\"false\", username=\"test\")\n\ndo_test(proto_ver=5, per_listener=\"true\", username=None)\ndo_test(proto_ver=5, per_listener=\"true\", username=\"test\")\ndo_test(proto_ver=5, per_listener=\"false\", username=None)\ndo_test(proto_ver=5, per_listener=\"false\", username=\"test\")\n"
  },
  {
    "path": "test/broker/04-retain-check-source.py",
    "content": "#!/usr/bin/env python3\n\n# Test for CVE-2018-12546\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config(filename, port, per_listener):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener))\n        f.write(\"check_retain_source true\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"acl_file %s\\n\" % (filename.replace('.conf', '.acl')))\n\ndef write_acl_1(filename):\n    with open(filename, 'w') as f:\n        f.write('topic readwrite test/topic\\n')\n\ndef write_acl_2(filename):\n    with open(filename, 'w') as f:\n        f.write('topic read test/topic\\n')\n\n\ndef do_test(proto_ver, per_listener):\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, per_listener)\n\n    acl_file = os.path.basename(__file__).replace('.py', '.acl')\n    write_acl_1(acl_file)\n\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"retain-check\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"test/topic\", qos=0, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"test/topic\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.send(publish_packet)\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback 1\")\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        sock.close()\n\n        # Remove \"write\" ability\n        write_acl_2(acl_file)\n        broker.send_signal(signal.SIGHUP)\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback 2\")\n        # If we receive the retained message here, it is a failure.\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        os.remove(acl_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\nport = mosq_test.get_port()\n\ndo_test(proto_ver=4, per_listener=\"true\")\ndo_test(proto_ver=4, per_listener=\"false\")\n\ndo_test(proto_ver=5, per_listener=\"true\")\ndo_test(proto_ver=5, per_listener=\"false\")\n"
  },
  {
    "path": "test/broker/04-retain-clear-multiple.py",
    "content": "#!/usr/bin/env python3\n\n# Exercise multi-level retain clearing\n\nfrom mosq_test_helper import *\n\ndef send_retain(port, topic, payload):\n    connect_packet = mosq_test.gen_connect(\"retain-clear-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    publish_packet = mosq_test.gen_publish(topic, qos=1, mid=1, payload=payload, retain=True)\n    puback_packet = mosq_test.gen_puback(mid=1)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=4, port=port)\n    mosq_test.do_send_receive(sock, publish_packet, puback_packet, f\"set retain {topic}\")\n    sock.close()\n\ndef do_test():\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"retain-clear-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    subscribe_packet = mosq_test.gen_subscribe(1, \"#\", 0)\n    suback_packet = mosq_test.gen_suback(1, 0)\n\n    retain1_packet = mosq_test.gen_publish(\"1/2/3/4/5/6/7\", qos=0, payload=\"retained message\", retain=True)\n    retain2_packet = mosq_test.gen_publish(\"1/2/3/4\", qos=0, payload=\"retained message\", retain=True)\n    retain3_packet = mosq_test.gen_publish(\"1\", qos=0, payload=\"retained message\", retain=True)\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        send_retain(port, \"1/2/3/4/5/6/7\", \"retained message\")\n        send_retain(port, \"1/2/3/4\", \"retained message\")\n        send_retain(port, \"1\", \"retained message\")\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        mosq_test.expect_packet(sock, \"retain3\", retain3_packet)\n        mosq_test.expect_packet(sock, \"retain2\", retain2_packet)\n        mosq_test.expect_packet(sock, \"retain1\", retain1_packet)\n        sock.close()\n\n        send_retain(port, \"1/2/3/4\", None)\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        mosq_test.expect_packet(sock, \"retain3\", retain3_packet)\n        mosq_test.expect_packet(sock, \"retain1\", retain1_packet)\n        sock.close()\n\n        send_retain(port, \"1/2/3/4/5/6/7\", None)\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        mosq_test.expect_packet(sock, \"retain3\", retain3_packet)\n        sock.close()\n\n        send_retain(port, \"1\", None)\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        mosq_test.do_ping(sock)\n        sock.close()\n\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        broker.wait()\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test()\nexit(0)\n\n"
  },
  {
    "path": "test/broker/04-retain-qos0-clear.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a retained PUBLISH is cleared when a zero length retained\n# message is published to a topic.\n\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"retain-qos0-clear-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"retain/qos0/clear/test\", qos=0, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n    retain_clear_packet = mosq_test.gen_publish(\"retain/qos0/clear/test\", qos=0, payload=None, retain=True, proto_ver=proto_ver)\n    mid_sub = 592\n    subscribe_packet = mosq_test.gen_subscribe(mid_sub, \"retain/qos0/clear/test\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid_sub, 0, proto_ver=proto_ver)\n\n    mid_unsub = 593\n    unsubscribe_packet = mosq_test.gen_unsubscribe(mid_unsub, \"retain/qos0/clear/test\", proto_ver=proto_ver)\n    unsuback_packet = mosq_test.gen_unsuback(mid_unsub, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=4, port=port)\n        # Send retained message\n        sock.send(publish_packet)\n        # Subscribe to topic, we should get the retained message back.\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        # Now unsubscribe from the topic before we clear the retained\n        # message.\n        mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, \"unsuback\")\n\n        # Now clear the retained message.\n        sock.send(retain_clear_packet)\n\n        # Subscribe to topic, we shouldn't get anything back apart\n        # from the SUBACK.\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        # If we do get something back, it should be before this ping, so if\n        # this succeeds then we're ok.\n        mosq_test.do_ping(sock)\n        # This is the expected event\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/04-retain-qos0-fresh.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a retained PUBLISH to a topic with QoS 0 is sent with\n# retain=false to an already subscribed client.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 16\n    connect_packet = mosq_test.gen_connect(\"retain-qos0-fresh\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"retain/qos0/fresh\", qos=0, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n    publish_fresh_packet = mosq_test.gen_publish(\"retain/qos0/fresh\", qos=0, payload=\"retained message\", proto_ver=proto_ver)\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"retain/qos0/fresh\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    publish_packet_clear = mosq_test.gen_publish(\"retain/qos0/fresh\", qos=0, payload=None, retain=True, proto_ver=proto_ver)\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        mosq_test.do_send_receive(sock, publish_packet, publish_fresh_packet, \"publish\")\n        sock.send(publish_packet_clear)\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/04-retain-qos0-repeated.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a retained PUBLISH to a topic with QoS 0 is actually retained\n# and delivered when multiple sub/unsub operations are carried out.\n\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 16\n    connect_packet = mosq_test.gen_connect(\"retain-qos0-rep-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"retain/qos0/repeated\", qos=0, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"retain/qos0/repeated\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    unsub_mid = 13\n    unsubscribe_packet = mosq_test.gen_unsubscribe(unsub_mid, \"retain/qos0/repeated\", proto_ver=proto_ver)\n    unsuback_packet = mosq_test.gen_unsuback(unsub_mid, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        sock.send(publish_packet)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, \"unsuback\")\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/04-retain-qos0.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a retained PUBLISH to a topic with QoS 0 is actually retained.\n\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 16\n    connect_packet = mosq_test.gen_connect(\"retain-qos0-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"retain/qos0/test\", qos=0, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"retain/qos0/test\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.send(publish_packet)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/04-retain-qos1-qos0.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a retained PUBLISH to a topic with QoS 1 is retained.\n# Subscription is made with QoS 0 so the retained message should also have QoS\n# 0.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"retain-qos1-qos0-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 6\n    publish_packet = mosq_test.gen_publish(\"retain/qos1/qos0/test\", qos=1, mid=mid, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    mid = 18\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"retain/qos1/qos0/test\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n    publish0_packet = mosq_test.gen_publish(\"retain/qos1/qos0/test\", qos=0, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        mosq_test.expect_packet(sock, \"publish0\", publish0_packet)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/04-retain-upgrade-outgoing-qos.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a retained PUBLISH to a topic with QoS 0 is sent with subscriber QoS\n# when upgrade_outgoing_qos is true\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"upgrade_outgoing_qos true\\n\")\n\n\ndef do_test(proto_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    mid = 16\n    connect_packet = mosq_test.gen_connect(\"retain-qos0-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"retain/qos0/test\", qos=0, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"retain/qos0/test\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    publish_packet2 = mosq_test.gen_publish(\"retain/qos0/test\", mid=1, qos=1, payload=\"retained message\", retain=True, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.send(publish_packet)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet2)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/05-clean-session-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a clean session client has a QoS 1 message queued for it.\n\nfrom mosq_test_helper import *\n\ndef helper(port):\n    connect_packet = mosq_test.gen_connect(\"05-clean-qos1-test-helper\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 128\n    publish_packet = mosq_test.gen_publish(\"qos1/05-clean_session/test\", qos=1, mid=mid, payload=\"clean-session-message\")\n    puback_packet = mosq_test.gen_puback(mid)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n\n    sock.close()\n\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 109\n    connect_packet = mosq_test.gen_connect(\"05-clean-session\", clean_session=False, proto_ver=proto_ver, session_expiry=60)\n    connack1_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver)\n    connack2_packet = mosq_test.gen_connack(flags=1, rc=0, proto_ver=proto_ver)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"qos1/05-clean_session/test\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"qos1/05-clean_session/test\", qos=1, mid=mid, payload=\"clean-session-message\", proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    connect_packet_clear = mosq_test.gen_connect(\"05-clean-session\", clean_session=True, proto_ver=proto_ver, session_expiry=0)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port, connack_error=\"connack 1\")\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        sock.send(disconnect_packet)\n        sock.close()\n\n        helper(port)\n\n        # Now reconnect and expect a publish message.\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=30, port=port, connack_error=\"connack 2\")\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        sock.send(puback_packet)\n        rc = 0\n\n        sock.close()\n\n        # Clear the session\n        sock = mosq_test.do_client_connect(connect_packet_clear, connack1_packet, port=port, connack_error=\"connack clear\")\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/05-session-expiry-kick.py",
    "content": "#!/usr/bin/env python3\n\n# MQTT v5. Test whether session expiry interval works correctly.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/kick_last_client.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"log_type all\\n\")\n\ndef do_test():\n    rc = 1\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Test the case of connect with session-expiry>0, kick, expiry for a crash\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 1)\n    connect_packet = mosq_test.gen_connect(\"05-session-expiry\", clean_session=False, proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5)\n\n    try:\n        sock = mosq_test.client_connect_only(port=port)\n        sock.send(connect_packet)\n        # Immediately disconnect, this should now be queued to expire, but the plugin should kick it first\n        sock.close()\n\n        time.sleep(2)\n\n        # This should succeed if the broker is still online\n        # The \"session present\" flag must *not* be set\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, connack_error=\"connack 1\")\n        sock.close()\n\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        broker.terminate()\n        os.remove(conf_file)\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test()\n"
  },
  {
    "path": "test/broker/05-session-expiry-v5.py",
    "content": "#!/usr/bin/env python3\n\n# MQTT v5. Test whether session expiry interval works correctly.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n\n    # This client exists to test possible fixed size int overflow and sorting of the session intervals\n    # https://github.com/eclipse/mosquitto/issues/1525\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 4294967294)\n    connect0_packet = mosq_test.gen_connect(\"05-session-expiry-overflow\", clean_session=False, proto_ver=5, properties=props)\n    connack0_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5)\n\n\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 1)\n    connect_packet = mosq_test.gen_connect(\"05-session-expiry\", clean_session=False, proto_ver=5, properties=props)\n    connack1_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5)\n\n    connack2_packet = mosq_test.gen_connack(flags=1, rc=0, proto_ver=5)\n\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 3)\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props)\n\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 0)\n    disconnect2_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        # Connect client with wildly different session expiry, this should impact\n        # on the test if all is well\n        sock0 = mosq_test.do_client_connect(connect0_packet, connack0_packet, port=port, connack_error=\"connack 0\")\n        # Immediately disconnect, this should now be queued to expire\n        sock0.close()\n\n        # First connect, clean start is false, we expect a normal connack\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port, connack_error=\"connack 1\")\n        # Forceful disconnect\n        sock.close()\n\n        # Immediate second connect, clean start is false, we expect a connack with\n        # previous state\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port, connack_error=\"connack 2\")\n        sock.close()\n\n        # Session should expire in one second, so sleep longer\n        time.sleep(2)\n\n        # Third connect, clean start is false, session should have expired so we\n        # expect a normal connack\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port, connack_error=\"connack 3\")\n        # Send DISCONNECT with new session expiry, then close\n        sock.send(disconnect_packet)\n        sock.close()\n\n        # Immediate reconnect, clean start is false, we expect a connack with\n        # previous state\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port, connack_error=\"connack 4\")\n        # Send DISCONNECT with new session expiry, then close\n        sock.send(disconnect_packet)\n        sock.close()\n\n        # Session should expire in three seconds if it has been updated, sleep for\n        # 2 to check it is updated from 1 second.\n        time.sleep(2)\n\n        # Immediate reconnect, clean start is false, we expect a connack with\n        # previous state\n        sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port, connack_error=\"connack 5\")\n        # Send DISCONNECT with new session expiry, then close\n        sock.send(disconnect_packet)\n        sock.close()\n\n        # Session should expire in three seconds, so sleep longer\n        time.sleep(4)\n        # Third connect, clean start is false, session should have expired so we\n        # expect a normal connack\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port, connack_error=\"connack 6\")\n        # Send DISCONNECT with 0 session expiry, then close\n        sock.send(disconnect2_packet)\n        sock.close()\n\n        # Immediate reconnect, session should have been removed.\n        sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port, connack_error=\"connack 7\")\n        sock.close()\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/06-bridge-b2br-disconnect-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Does a bridge resend a QoS=1 message correctly after a disconnect?\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic bridge/# both 1\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 5\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n\n\ndef do_test(proto_ver):\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    client_id = socket.gethostname()+\".bridge_sample\"\n    properties = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10)\n    properties += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=proto_ver_connect, properties=properties)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    if proto_ver == 5:\n        opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED\n    else:\n        opts = 0\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 1 | opts, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    mid = 2\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 1 | opts, proto_ver=proto_ver)\n    suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    mid = 3\n    publish_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=1, mid=mid, payload=\"disconnect-message\", proto_ver=proto_ver)\n    publish_dup_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=1, mid=mid, payload=\"disconnect-message\", dup=True, proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    mid = 20\n    publish2_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=1, mid=mid, payload=\"disconnect-message\", proto_ver=proto_ver)\n    puback2_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(40)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"subscribe\", subscribe_packet)\n        bridge.send(suback_packet)\n\n        bridge.send(publish_packet)\n        # Bridge doesn't have time to respond but should expect us to retry\n        # and so remove PUBACK.\n        bridge.close()\n\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"2nd connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"2nd subscribe\", subscribe2_packet)\n        bridge.send(suback2_packet)\n\n        # Send a different publish message to make sure the response isn't to the old one.\n        bridge.send(publish2_packet)\n        mosq_test.expect_packet(bridge, \"puback\", puback2_packet)\n        rc = 0\n\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/06-bridge-b2br-disconnect-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Does a bridge resend a QoS=1 message correctly after a disconnect?\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic bridge/# both 2\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 2\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n\ndef do_test(proto_ver):\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    client_id = socket.gethostname()+\".bridge_sample\"\n    properties = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10)\n    properties += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=proto_ver_connect, properties=properties)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    if proto_ver == 5:\n        opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED\n    else:\n        opts = 0\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 2 | opts, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 2\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 2 | opts, proto_ver=proto_ver)\n    suback2_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 3\n    subscribe3_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 2 | opts, proto_ver=proto_ver)\n    suback3_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 5\n    publish_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=2, mid=mid, payload=\"disconnect-message\", proto_ver=proto_ver)\n    publish_dup_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=2, mid=mid, payload=\"disconnect-message\", dup=True, proto_ver=proto_ver)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(40)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"subscribe\", subscribe_packet)\n        bridge.send(suback_packet)\n\n        bridge.send(publish_packet)\n        bridge.close()\n\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"2nd subscribe\", subscribe2_packet)\n        bridge.send(suback2_packet)\n        bridge.send(publish_dup_packet)\n\n        mosq_test.expect_packet(bridge, \"pubrec\", pubrec_packet)\n        bridge.send(pubrel_packet)\n        bridge.close()\n\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"3rd subscribe\", subscribe3_packet)\n        bridge.send(suback3_packet)\n\n        bridge.send(publish_dup_packet)\n\n        mosq_test.expect_packet(bridge, \"2nd pubrec\", pubrec_packet)\n        bridge.send(pubrel_packet)\n\n        mosq_test.expect_packet(bridge, \"pubcomp\", pubcomp_packet)\n        rc = 0\n\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n\n"
  },
  {
    "path": "test/broker/06-bridge-b2br-late-connection-retain.py",
    "content": "#!/usr/bin/env python3\n\n# Does a bridge queue up retained messages correctly if the remote broker starts up late?\n\nfrom mosq_test_helper import *\n\ndef write_config1(filename, persistence_file, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file %s\\n\" % (persistence_file))\n\ndef write_config2(filename, persistence_file, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic bridge/# out 1\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"bridge_attempt_unsubscribe false\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file %s\\n\" % (persistence_file))\n\ndef do_test(proto_ver):\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    persistence_file = os.path.basename(__file__).replace('.py', '.db')\n\n    rc = 1\n    client_id = socket.gethostname()+\".bridge_sample\"\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    c_connect_packet = mosq_test.gen_connect(\"client\", proto_ver=proto_ver)\n    c_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"bridge/test\", qos=1, mid=mid, payload=\"message\", retain=True, proto_ver=proto_ver)\n\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=16)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(40)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    write_config1(conf_file, persistence_file, port1, port2)\n\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n        client = mosq_test.do_client_connect(c_connect_packet, c_connack_packet, timeout=20, port=port2)\n        mosq_test.do_send_receive(client, publish_packet, puback_packet, \"puback\")\n        client.close()\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n\n        # Restart, with retained message in place\n        write_config2(conf_file, persistence_file, port1, port2, bridge_protocol)\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"publish\", publish_packet)\n        bridge.send(puback_packet)\n        # Guard against multiple retained messages of the same type by\n        # sending a pingreq to give us something to expect back. If we get\n        # a publish, it's a fail.\n        mosq_test.do_ping(bridge)\n        rc = 0\n\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        os.remove(persistence_file)\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/06-bridge-b2br-late-connection.py",
    "content": "#!/usr/bin/env python3\n\n# Does a bridge queue up messages correctly if the remote broker starts up late?\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic bridge/# out 1\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"bridge_attempt_unsubscribe false\\n\")\n        f.write(\"bridge_max_topic_alias 0\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n\ndef do_test(proto_ver):\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    client_id = socket.gethostname()+\".bridge_sample\"\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    c_connect_packet = mosq_test.gen_connect(\"client\", proto_ver=proto_ver)\n    c_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"bridge/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(40)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n    try:\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        client = mosq_test.do_client_connect(c_connect_packet, c_connack_packet, timeout=20, port=port2)\n        mosq_test.do_send_receive(client, publish_packet, puback_packet, \"puback\")\n        client.close()\n        # We've now sent a message to the broker that should be delivered to us via the bridge\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"publish\", publish_packet)\n        rc = 0\n\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n\n"
  },
  {
    "path": "test/broker/06-bridge-b2br-remapping.py",
    "content": "#!/usr/bin/env python3\n\n# Test remapping of topic name for incoming message\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"bridge_attempt_unsubscribe false\\n\")\n        f.write(\"topic # in 0 local/topic/ remote/topic/\\n\")\n        f.write(\"topic prefix/# in 0 local2/topic/ remote2/topic/\\n\")\n        f.write(\"topic +/value in 0 local3/topic/ remote3/topic/\\n\")\n        f.write(\"topic ic/+ in 0 local4/top remote4/tip\\n\")\n        f.write(\"topic clients/total in 0 test/mosquitto/org $SYS/broker/\\n\")\n        f.write('topic rmapped in 0 \"\" remote/mapped/\\n')\n        f.write('topic lmapped in 0 local/mapped/ \"\"\\n')\n        f.write('topic \"\" in 0 local/single remote/single\\n')\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 5\\n\")\n        f.write(\"bridge_max_topic_alias 0\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n\nconnect_packet = None\nconnack_packet = None\n\n\ndef inner_test(bridge, sock, proto_ver):\n    global connect_packet, connack_packet\n\n    if not mosq_test.expect_packet(bridge, \"connect\", connect_packet):\n        return 1\n    bridge.send(connack_packet)\n\n    if proto_ver == 5:\n        opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED\n    else:\n        opts = 0\n\n    mid = 0\n    patterns = [\n        \"remote/topic/#\",\n        \"remote2/topic/prefix/#\",\n        \"remote3/topic/+/value\",\n        \"remote4/tipic/+\",\n        \"$SYS/broker/clients/total\",\n    ]\n    for pattern in (\"remote/topic/#\", \"remote2/topic/prefix/#\", \"remote3/topic/+/value\"):\n        mid += 1\n        subscribe_packet = mosq_test.gen_subscribe(mid, pattern, 0 | opts, proto_ver=proto_ver)\n        suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n        if not mosq_test.expect_packet(bridge, \"subscribe\", subscribe_packet):\n            return 1\n        bridge.send(suback_packet)\n\n    mid += 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"#\", 0 | opts, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n    sock.send(subscribe_packet)\n    if not mosq_test.expect_packet(sock, \"suback\", suback_packet):\n        return 1\n\n    cases = [\n        ('local/topic/something', 'remote/topic/something'),\n        ('local/topic/some/t/h/i/n/g', 'remote/topic/some/t/h/i/n/g'),\n        ('local/topic/value', 'remote/topic/value'),\n        # Don't work, #40 must be fixed before\n        # ('local/topic', 'remote/topic'),\n        ('local2/topic/prefix/something', 'remote2/topic/prefix/something'),\n        ('local3/topic/something/value', 'remote3/topic/something/value'),\n        ('local4/topic/something', 'remote4/tipic/something'),\n        ('test/mosquitto/orgclients/total', '$SYS/broker/clients/total'),\n        ('local/mapped/lmapped', 'lmapped'),\n        ('rmapped', 'remote/mapped/rmapped'),\n        ('local/single', 'remote/single'),\n    ]\n\n    for (local_topic, remote_topic) in cases:\n        mid += 1\n        remote_publish_packet = mosq_test.gen_publish(\n            remote_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver)\n        local_publish_packet = mosq_test.gen_publish(\n            local_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver)\n\n        bridge.send(remote_publish_packet)\n        match = mosq_test.expect_packet(sock, \"publish\", local_publish_packet)\n        if not match:\n            print(\"Fail on cases local_topic=%r, remote_topic=%r\" % (\n                local_topic, remote_topic,\n            ))\n            return 1\n    return 0\n\n\ndef do_test(proto_ver):\n    global connect_packet, connack_packet\n\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n\n    client_id = socket.gethostname()+\".bridge_sample\"\n\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    client_connect_packet = mosq_test.gen_connect(\"pub-test\", proto_ver=proto_ver)\n    client_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(4)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n    try:\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(2)\n\n        sock = mosq_test.do_client_connect(\n            client_connect_packet, client_connack_packet,\n            port=port2,\n        )\n\n        rc = inner_test(bridge, sock, proto_ver)\n\n        sock.close()\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/06-bridge-br2b-disconnect-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Does a bridge resend a QoS=1 message correctly after a disconnect?\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic bridge/# both 1\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 2\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n\n\ndef do_test(proto_ver):\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    client_id = socket.gethostname()+\".bridge_sample\"\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    if proto_ver == 5:\n        opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED\n    else:\n        opts = 0\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 1 | opts, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    mid = 3\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 1 | opts, proto_ver=proto_ver)\n    suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    mid = 2\n    publish_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=1, mid=mid, payload=\"disconnect-message\", proto_ver=proto_ver)\n    publish_dup_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=1, mid=mid, payload=\"disconnect-message\", dup=True, proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(40)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n    try:\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"subscribe\", subscribe_packet)\n        bridge.send(suback_packet)\n\n        # Helper\n        helper_connect_packet = mosq_test.gen_connect(\"test-helper\", proto_ver=proto_ver)\n        helper_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n        mid = 128\n        helper_publish_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=1, mid=mid, payload=\"disconnect-message\", proto_ver=proto_ver)\n        helper_puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n        helper_sock = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, port=port2, connack_error=\"helper connack\")\n        mosq_test.do_send_receive(helper_sock, publish_packet, puback_packet, \"helper puback\")\n        helper_sock.close()\n        # End helper\n\n        mosq_test.expect_packet(bridge, \"publish\", publish_packet)\n        bridge.close()\n\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"2nd connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"2nd subscribe\", subscribe2_packet)\n        bridge.send(suback2_packet)\n\n        mosq_test.expect_packet(bridge, \"2nd publish\", publish_dup_packet)\n        rc = 0\n\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/06-bridge-br2b-disconnect-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Does a bridge resend a QoS=1 message correctly after a disconnect?\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic bridge/# both 2\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 2\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n\n\ndef do_test(proto_ver):\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    client_id = socket.gethostname()+\".bridge_sample\"\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    if proto_ver == 5:\n        opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED\n    else:\n        opts = 0\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 2 | opts, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 3\n    subscribe2_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 2 | opts, proto_ver=proto_ver)\n    suback2_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 4\n    subscribe3_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 2 | opts, proto_ver=proto_ver)\n    suback3_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 2\n    publish_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=2, mid=mid, payload=\"disconnect-message\", proto_ver=proto_ver)\n    publish_dup_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=2, mid=mid, payload=\"disconnect-message\", dup=True, proto_ver=proto_ver)\n    pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\n    pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(40)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n    try:\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"subscribe\", subscribe_packet)\n        bridge.send(suback_packet)\n\n        # Helper\n        helper_connect_packet = mosq_test.gen_connect(\"test-helper\", proto_ver=proto_ver)\n        helper_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n        mid = 312\n        helper_publish_packet = mosq_test.gen_publish(\"bridge/disconnect/test\", qos=2, mid=mid, payload=\"disconnect-message\", proto_ver=proto_ver)\n        helper_pubrec_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\n        helper_pubrel_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\n        helper_pubcomp_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\n        helper_sock = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, port=port2, connack_error=\"helper connack\")\n        mosq_test.do_send_receive(helper_sock, helper_publish_packet, helper_pubrec_packet, \"helper pubrec\")\n        mosq_test.do_send_receive(helper_sock, helper_pubrel_packet, helper_pubcomp_packet, \"helper pubcomp\")\n        helper_sock.close()\n        # End helper\n\n\n        mosq_test.expect_packet(bridge, \"publish\", publish_packet)\n        bridge.close()\n\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"2nd subscribe\", subscribe2_packet)\n        bridge.send(suback2_packet)\n\n        mosq_test.expect_packet(bridge, \"2nd publish\", publish_dup_packet)\n        bridge.send(pubrec_packet)\n\n        mosq_test.expect_packet(bridge, \"pubrel\", pubrel_packet)\n        bridge.close()\n\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"3rd subscribe\", subscribe3_packet)\n        bridge.send(suback3_packet)\n\n        mosq_test.expect_packet(bridge, \"2nd pubrel\", pubrel_packet)\n        bridge.send(pubcomp_packet)\n        rc = 0\n\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n\n"
  },
  {
    "path": "test/broker/06-bridge-br2b-remapping.py",
    "content": "#!/usr/bin/env python3\n\n# Test remapping of topic name for outgoing message\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"bridge_attempt_unsubscribe false\\n\")\n        f.write(\"topic # out 0 local/topic/ remote/topic/\\n\")\n        f.write(\"topic prefix/# out 0 local2/topic/ remote2/topic/\\n\")\n        f.write(\"topic +/value out 0 local3/topic/ remote3/topic/\\n\")\n        f.write(\"topic ic/+ out 0 local4/top remote4/tip\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 5\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n\n\ndef inner_test(bridge, sock, proto_ver):\n    global connect_packet, connack_packet\n\n    if not mosq_test.expect_packet(bridge, \"connect\", connect_packet):\n        return 1\n\n    bridge.send(connack_packet)\n\n    cases = [\n        ('local/topic/something', 'remote/topic/something'),\n        ('local/topic/some/t/h/i/n/g', 'remote/topic/some/t/h/i/n/g'),\n        ('local/topic/value', 'remote/topic/value'),\n        # Don't work, #40 must be fixed before\n        # ('local/topic', 'remote/topic'),\n        ('local2/topic/something', None),  # don't match topic pattern\n        ('local2/topic/prefix/something', 'remote2/topic/prefix/something'),\n        ('local3/topic/something/value', 'remote3/topic/something/value'),\n        ('local4/topic/something', 'remote4/tipic/something'),\n        ('local5/topic/something', None),\n    ]\n\n    mid = 3\n    for (local_topic, remote_topic) in cases:\n        mid += 1\n        local_publish_packet = mosq_test.gen_publish(\n            local_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver\n        )\n        sock.send(local_publish_packet)\n        if remote_topic:\n            remote_publish_packet = mosq_test.gen_publish(\n                remote_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver\n            )\n            match = mosq_test.expect_packet(bridge, \"publish\", remote_publish_packet)\n            if not match:\n                print(\"Fail on cases local_topic=%r, remote_topic=%r\" % (\n                    local_topic, remote_topic,\n                ))\n                return 1\n        else:\n            time.sleep(1)\n            mosq_test.do_ping(bridge,\n                \"FAIL: Received data when nothing is expected\\nFail on cases local_topic=%r, remote_topic=%r\" % (\n                local_topic, remote_topic,\n                ))\n    return 0\n\n\ndef do_test(proto_ver):\n    global connect_packet, connack_packet\n\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    client_id = socket.gethostname()+\".bridge_sample\"\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    client_connect_packet = mosq_test.gen_connect(\"pub-test\", proto_ver=proto_ver)\n    client_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(4)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n    try:\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(2)\n\n        sock = mosq_test.do_client_connect(\n            client_connect_packet, client_connack_packet,\n            port=port2,\n        )\n\n        rc = inner_test(bridge, sock, proto_ver)\n\n        sock.close()\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/06-bridge-clean-session-core.py",
    "content": "#!/usr/bin/env python3\n# Test whether a broker handles cleansession and local_cleansession correctly on bridges\n#\n# Test cases (with settings for broker A (edge). The settings on broker B (core)\n# are irrelevant, though you'll need persistence enabled to test, unless you\n# can simulate network interruptions.\n# Similarly, you'll need persistence on A, _purely_ to simplify the testing with a client\n# t# | LCS | CS | queued from (expected)\n#               | A->B | B->A\n#  1 |  -(t| t  |  no  | no\n#  2 |  -(f| f  |  yes | yes\n#  3 |  t  | t  |  no  | no  (as per #1)\n#  4 |  t  | f  |  no  | yes\n#  5 |  f  | t  |  yes | no\n#  6 |  f  | f  |  yes | yes (as per #2)\n#\n# Test setup is two (real) brokers, so that messages can be published and subscribed in both\n# directions, with two test clients, one at each end.\n\n# Disable on Travis for now, too unreliable\nfrom mosq_test_helper import *\nfrom collections import namedtuple\n\n# Normally we don't want tests to spew debug, but if you're working on a test, it's useful\nVERBOSE_TEST=False\ndef tprint(*args, **kwargs):\n    if VERBOSE_TEST:\n        print(\" \".join(map(str,args)), **kwargs)\n\n# this is our \"A\" broker\ndef write_config_edge(filename, persistence_file, remote_port, listen_port, protocol_version, cs=False, lcs=None):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (listen_port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file %s\\n\" % (persistence_file))\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"local_clientid id_local\\n\")\n        f.write(\"remote_clientid id_remote\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (remote_port))\n        f.write(\"topic br_out/# out 1\\n\")\n        f.write(\"topic br_in/# in 1\\n\")\n        # We need to ensure connections break fast enough to keep test times sane\n        f.write(\"keepalive_interval 5\\n\")\n        f.write(\"restart_timeout 2\\n\")\n        f.write(\"cleansession %s\\n\" % (\"true\" if cs else \"false\"))\n        # Ensure defaults are tested\n        if lcs is not None:\n            f.write(\"local_cleansession %s\\n\" % (\"true\" if lcs else \"false\"))\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n\n\n# this is our \"B\" broker\ndef write_config_core(filename, listen_port, persistence_file):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (listen_port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file %s\\n\" % (persistence_file))\n\n\ndef wait_for_bridge_to_connect(port, clientid):\n    conn = mosq_test.gen_connect(\"helper\", clean_session=True)\n    connack = mosq_test.gen_connack(rc=0)\n\n    sock = mosq_test.do_client_connect(conn, connack, port=port)\n    sub = mosq_test.gen_subscribe(1, f'$SYS/broker/connection/{clientid}/state', 0)\n    suback = mosq_test.gen_suback(1, 0)\n    mosq_test.do_send_receive(sock, sub, suback)\n    pub_r = mosq_test.gen_publish(topic=f\"$SYS/broker/connection/{clientid}/state\", payload=\"1\", qos=0, retain=True)\n    pub_nr = mosq_test.gen_publish(topic=f\"$SYS/broker/connection/{clientid}/state\", payload=\"1\", qos=0)\n    pub_rec = sock.recv(len(pub_r))\n    if pub_rec != pub_r and pub_rec != pub_nr:\n        raise ValueError(mosq_test.to_string(pub_rec))\n    sock.close()\n\n\ndef do_test(proto_ver, cs, lcs=None):\n    tprint(\"Running test with cs:%s, lcs: %s and proto: %d\" % (cs, lcs, proto_ver))\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n    else:\n        bridge_protocol = \"mqttv50\"\n\n    # Match default behaviour of broker\n    expect_queued_ab = True\n    expect_queued_ba = True\n    if lcs is None:\n        lcs = cs\n    if lcs:\n        expect_queued_ab = False\n    if cs:\n        expect_queued_ba = False\n\n\n    (port_a_listen, port_b_listen) = mosq_test.get_port(2)\n    conf_file_a = os.path.basename(__file__).replace('.py', '%d_a_edge.conf'%(port_a_listen))\n    persistence_file_a = os.path.basename(__file__).replace('.py', '%d_a_edge.db'%(port_a_listen))\n    write_config_edge(conf_file_a, persistence_file_a, port_b_listen, port_a_listen, bridge_protocol, cs=cs, lcs=lcs)\n\n    conf_file_b = os.path.basename(__file__).replace('.py', '%d_b_core.conf'%(port_b_listen))\n    persistence_file_b = os.path.basename(__file__).replace('.py', '%d_b_core.db'%(port_b_listen))\n    write_config_core(conf_file_b, port_b_listen, persistence_file_b)\n\n    AckedPair = namedtuple(\"AckedPair\", \"p ack\")\n\n    def make_conn(client_tag, proto, cs, session_present=False):\n        client_id = socket.gethostname() + \".\" + client_tag\n        conn = mosq_test.gen_connect(client_id, clean_session=cs, proto_ver=proto, session_expiry=0 if cs else 5000)\n        connack = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, flags=1 if session_present else 0)\n        return AckedPair(conn, connack)\n\n\n    def make_sub(topic, mid, qos, proto):\n        if proto_ver == 5:\n            opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED\n        else:\n            opts = 0\n        sub = mosq_test.gen_subscribe(mid, topic, qos | opts, proto_ver=proto)\n        suback = mosq_test.gen_suback(mid, qos, proto_ver=proto)\n        return AckedPair(sub, suback)\n\n\n    def make_pub(topic, mid, proto, qos=1, payload_tag=\"message\", rc=-1):\n        # Using the mid automatically makes it hard to verify messages that might have been retransmitted.\n        # encourage users to put sequence numbers in topics instead....\n        pub = mosq_test.gen_publish(topic, mid=mid, qos=qos, retain=False, payload=payload_tag + \"-from-\" + topic, proto_ver=proto)\n        puback = mosq_test.gen_puback(mid, proto_ver=proto, reason_code=rc)\n        return AckedPair(pub, puback)\n\n    # Clients are testing messages in both directions, they need to be durable\n    conn_a = make_conn(\"client_a_edge\", proto_ver, False)\n    conn_b = make_conn(\"client_b_core\", proto_ver, False)\n    # We expect session present when we reconnect\n    reconn_a = make_conn(\"client_a_edge\", proto_ver, False, session_present=True)\n    reconn_b = make_conn(\"client_b_core\", proto_ver, False, session_present=True)\n\n    # remember, mids are from each broker's point of view, not the \"world\"\n    sub_a = make_sub(\"br_in/#\", qos=1, mid=1, proto=proto_ver)\n    sub_b = make_sub(\"br_out/#\", qos=1, mid=1, proto=proto_ver)\n\n    pub_a1 = make_pub(\"br_out/test-queued1\", mid=1, proto=proto_ver)\n    pub_a2 = make_pub(\"br_out/test-queued2\", mid=2, proto=proto_ver)\n    pub_a3 = make_pub(\"br_out/test-queued3\", mid=3, proto=proto_ver)\n    pub_a3r = make_pub(\"br_out/test-queued3\", mid=2, proto=proto_ver) # without queueing, there is no a2\n\n    pub_b1 = make_pub(\"br_in/test-queued1\", mid=1, proto=proto_ver)\n    pub_b2 = make_pub(\"br_in/test-queued2\", mid=2, proto=proto_ver)\n    pub_b3 = make_pub(\"br_in/test-queued3\", mid=3, proto=proto_ver)\n    pub_b3r = make_pub(\"br_in/test-queued3\", mid=2, proto=proto_ver) # without queueing, there is no b2\n\n    success = False\n    broker_termination_success = True\n    stde_a1 = stde_b1 = None\n    try:\n        # b must start first, as it's the destination of a\n        broker_b = mosq_test.start_broker(filename=conf_file_b, port=port_b_listen, use_conf=True)\n        broker_a = mosq_test.start_broker(filename=conf_file_a, port=port_a_listen, use_conf=True)\n\n        client_a = mosq_test.do_client_connect(conn_a.p, conn_a.ack, port=port_a_listen)\n        mosq_test.do_send_receive(client_a, sub_a.p, sub_a.ack, \"suback_a\")\n\n        client_b = mosq_test.do_client_connect(conn_b.p, conn_b.ack, port=port_b_listen)\n        mosq_test.do_send_receive(client_b, sub_b.p, sub_b.ack, \"suback_b\")\n\n        mosq_test.do_send_receive(client_a, pub_a1.p, pub_a1.ack, \"puback_a1\")\n        mosq_test.do_receive_send(client_b, pub_a1.p, pub_a1.ack, \"a->b1 (b-side)\")\n\n        mosq_test.do_send_receive(client_b, pub_b1.p, pub_b1.ack, \"puback_b1\")\n        mosq_test.do_receive_send(client_a, pub_b1.p, pub_b1.ack, \"b->a1 (a-side)\")\n\n        tprint(\"Normal bi-dir bridging works. continuing\")\n\n        broker_b.terminate()\n        if mosq_test.wait_for_subprocess(broker_b):\n            print(\"broker_b not terminated\")\n            broker_termination_success = False\n        (stdo_b1, stde_b1) = broker_b.communicate()\n\n        # as we're _terminating_ the connections should close ~straight away\n        tprint(\"terminated B\", time.time())\n        time.sleep(0.5)\n\n        # should be queued (or not)\n        mosq_test.do_send_receive(client_a, pub_a2.p, pub_a2.ack, \"puback_a2\")\n\n        broker_b = mosq_test.start_broker(filename=conf_file_b, port=port_b_listen, use_conf=True)\n        # client b needs to reconnect now!\n\n        client_b = mosq_test.do_client_connect(reconn_b.p, reconn_b.ack, port=port_b_listen)\n        tprint(\"client b reconnected after restarting broker b at \", time.time())\n        wait_for_bridge_to_connect(port_b_listen, \"id_remote\")\n\n        # should go through\n        tprint(\"(B should be alive again now!) sending (after reconn!) a3 at \", time.time())\n        mosq_test.do_send_receive(client_a, pub_a3.p, pub_a3.ack, \"puback_a3\")\n\n\n        if expect_queued_ab:\n            tprint(\"1.expecting a->b queueing\")\n            mosq_test.do_receive_send(client_b, pub_a2.p, pub_a2.ack, \"a->b_2\")\n            mosq_test.do_receive_send(client_b, pub_a3.p, pub_a3.ack, \"a->b_3\")\n        else:\n            tprint(\"not expecting a->b queueing\")\n            mosq_test.do_receive_send(client_b, pub_a3r.p, pub_a3r.ack, \"a->b_3(r)\")\n\n        tprint(\"Stage 1 complete, repeating in other direction\")\n\n        # ok, now repeat in the other direction...\n        broker_a.terminate()\n        if mosq_test.wait_for_subprocess(broker_a):\n            print(\"broker_a not terminated\")\n            broker_termination_success = False\n        (stdo_a1, stde_a1) = broker_a.communicate()\n        time.sleep(0.5)\n\n        mosq_test.do_send_receive(client_b, pub_b2.p, pub_b2.ack, \"puback_b2\")\n\n        broker_a = mosq_test.start_broker(filename=conf_file_a, port=port_a_listen, use_conf=True)\n        # client a needs to reconnect now!\n        client_a = mosq_test.do_client_connect(reconn_a.p, reconn_a.ack, port=port_a_listen)\n        tprint(\"client A reconnected after restarting broker A at \", time.time())\n        wait_for_bridge_to_connect(port_a_listen, \"id_remote\")\n\n        # should go through\n        client_b.send(pub_b3.p)\n\n        if expect_queued_ba:\n            tprint(\"2.expecting b->a queueueing\")\n            mosq_test.do_receive_send(client_a, pub_b2.p, pub_b2.ack, \"b->a_2\")\n            mosq_test.do_receive_send(client_a, pub_b3.p, pub_b3.ack, \"b->a_3\")\n        else:\n            tprint(\"not expecting message b->a_2\")\n            mosq_test.do_receive_send(client_a, pub_b3r.p, pub_b3r.ack, \"b->a_3(r)\")\n\n        success = broker_termination_success\n\n    except Exception as e:\n        print(e)\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file_a)\n        os.remove(conf_file_b)\n        broker_a.terminate()\n        broker_b.terminate()\n        if mosq_test.wait_for_subprocess(broker_a):\n            print(\"broker_a not terminated\")\n            success = False\n        if mosq_test.wait_for_subprocess(broker_b):\n            print(\"broker_b not terminated\")\n            success = False\n        (stdo_a, stde_a) = broker_a.communicate()\n        (stdo_b, stde_b) = broker_b.communicate()\n        # Must be after terminating!\n        try:\n            os.remove(persistence_file_a)\n        except FileNotFoundError:\n            print(\"persistence file a didn't exist, skipping remove\")\n        try:\n            os.remove(persistence_file_b)\n        except FileNotFoundError:\n            print(\"persistence file b didn't exist, skipping remove\")\n        if not success:\n            print(\"Test failed, dumping broker A logs: \")\n            if stde_a1:\n                print(stde_a1.decode('utf-8'))\n            print(stde_a.decode('utf-8'))\n            print(\"Test failed, dumping broker B logs: \")\n            if stde_b1:\n                print(stde_b1.decode('utf-8'))\n            print(stde_b.decode('utf-8'))\n            exit(1)\n\nif sys.argv[3] == \"True\":\n    cs = True\nelif sys.argv[3] == \"False\":\n    cs = False\nelse:\n    raise ValueError(\"cs\")\n\nif sys.argv[4] == \"True\":\n    lcs = True\nelif sys.argv[4] == \"False\":\n    lcs = False\nelif sys.argv[4] == \"None\":\n    lcs = None\nelse:\n    raise ValueError(\"lcs\")\n\ndo_test(proto_ver=4, cs=cs, lcs=lcs)\n# FIXME - v5 clean session bridging doesn't work: see\n# https://github.com/eclipse/mosquitto/issues/1632\n#do_test(proto_ver=5, cs=cs, lcs=lcs)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/06-bridge-clean-session-csF-lcsF.py",
    "content": "#!/usr/bin/env python3\n# Test whether a broker handles cleansession and local_cleansession correctly on bridges\n\nfrom collections import namedtuple\n\nfrom mosq_test_helper import *\n\n(port_a_listen, port_b_listen) = mosq_test.get_port(2)\nsubprocess.run([f'{Path(__file__).resolve().parent}/06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), \"False\", \"False\"])\n"
  },
  {
    "path": "test/broker/06-bridge-clean-session-csF-lcsN.py",
    "content": "#!/usr/bin/env python3\n# Test whether a broker handles cleansession and local_cleansession correctly on bridges\n\nfrom mosq_test_helper import *\nfrom collections import namedtuple\n\n(port_a_listen, port_b_listen) = mosq_test.get_port(2)\nsubprocess.run([f'{Path(__file__).resolve().parent}/06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), \"False\", \"None\"])\n\n"
  },
  {
    "path": "test/broker/06-bridge-clean-session-csF-lcsT.py",
    "content": "#!/usr/bin/env python3\n# Test whether a broker handles cleansession and local_cleansession correctly on bridges\n\nfrom mosq_test_helper import *\nfrom collections import namedtuple\n\n(port_a_listen, port_b_listen) = mosq_test.get_port(2)\nsubprocess.run([f'{Path(__file__).resolve().parent}/06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), \"False\", \"True\"])\n\n"
  },
  {
    "path": "test/broker/06-bridge-clean-session-csT-lcsF.py",
    "content": "#!/usr/bin/env python3\n# Test whether a broker handles cleansession and local_cleansession correctly on bridges\n\nfrom mosq_test_helper import *\nfrom collections import namedtuple\n\n(port_a_listen, port_b_listen) = mosq_test.get_port(2)\nsubprocess.run([f'{Path(__file__).resolve().parent}/06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), \"True\", \"False\"])\n\n"
  },
  {
    "path": "test/broker/06-bridge-clean-session-csT-lcsN.py",
    "content": "#!/usr/bin/env python3\n# Test whether a broker handles cleansession and local_cleansession correctly on bridges\n\nfrom mosq_test_helper import *\nfrom collections import namedtuple\n\n(port_a_listen, port_b_listen) = mosq_test.get_port(2)\nsubprocess.run([f'{Path(__file__).resolve().parent}/06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), \"True\", \"None\"])\n\n"
  },
  {
    "path": "test/broker/06-bridge-clean-session-csT-lcsT.py",
    "content": "#!/usr/bin/env python3\n# Test whether a broker handles cleansession and local_cleansession correctly on bridges\n\nfrom mosq_test_helper import *\nfrom collections import namedtuple\n\n(port_a_listen, port_b_listen) = mosq_test.get_port(2)\nsubprocess.run([f'{Path(__file__).resolve().parent}/06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), \"True\", \"True\"])\n\n"
  },
  {
    "path": "test/broker/06-bridge-config-reload.py",
    "content": "#!/usr/bin/env python3\n\n# tests that bridge configuration is reloaded on signal\n\nfrom mosq_test_helper import *\nimport signal\n\n\ndef write_config(filename, port1, port2, subtopic, reload_immediate=False):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic # in 0 local/topic/ remote/%s/\\n\" % (subtopic))\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 1\\n\")\n        if reload_immediate:\n            f.write(\"bridge_reload_type immediate\\n\")\n        f.write(\"bridge_max_topic_alias 0\\n\")\n\n\ndef accept_new_connection(sock):\n    conn, _ = sock.accept()\n    conn.settimeout(20)\n\n    client_id = socket.gethostname()+\".bridge_sample\"\n    connect_packet = mosq_test.gen_connect(\n        client_id, clean_session=False, proto_ver=0x84)\n    connack_packet = mosq_test.gen_connack()\n\n    mosq_test.expect_packet(conn, \"connect\", connect_packet)\n    conn.send(connack_packet)\n\n    return conn\n\n\ndef accept_subscription(socket, topic, mid=1, qos=0):\n    subscribe_packet = mosq_test.gen_subscribe(mid, topic, qos)\n    suback_packet = mosq_test.gen_suback(mid, qos)\n\n    mosq_test.expect_packet(socket, \"subscribe\", subscribe_packet)\n    socket.send(suback_packet)\n\n\ndef start_fake_broker(port):\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    sock.settimeout(3)\n    sock.bind(('', port))\n    sock.listen(5)\n    return sock\n\n\ndef expect_no_incoming_connection(sock):\n    try:\n        accept_new_connection(sock) # will timeout if nothing comes in\n        raise mosq_test.TestError # hence, it shouldn't reach this\n    except socket.timeout:\n        pass\n\n\ndef do_test():\n    rc = 1\n\n    port1, port2 = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    try:\n        ssock = start_fake_broker(port1)\n\n        write_config(conf_file, port1, port2, \"topic1\", True)\n\n        broker = mosq_test.start_broker(\n            filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n        bridge = accept_new_connection(ssock)\n        accept_subscription(bridge, \"remote/topic1/#\")\n\n        write_config(conf_file, port1, port2, \"topic2\", True)\n        broker.send_signal(signal.SIGHUP)\n\n        bridge = accept_new_connection(ssock) # immediate reload forces a reconnection\n        accept_subscription(bridge, \"remote/topic2/#\")\n\n        write_config(conf_file, port1, port2, \"topic3\", False)\n        broker.send_signal(signal.SIGHUP)\n\n        expect_no_incoming_connection(ssock) # as it was set to lazy reload\n\n        bridge.close()\n\n        bridge = accept_new_connection(ssock)\n        accept_subscription(bridge, \"remote/topic3/#\")\n\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        try:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            _, stde = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n        except NameError:\n            pass\n\n        try:\n            os.remove(conf_file)\n        except FileNotFoundError:\n            pass\n\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        try:\n            ssock.close()\n        except NameError:\n            pass\n\n        return rc\n\n\nexit_code = do_test()\nexit(exit_code)\n"
  },
  {
    "path": "test/broker/06-bridge-fail-persist-resend-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a bridge can cope with an unknown PUBACK\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"\\n\")\n        f.write(\"connection bridge-u-test\\n\")\n        f.write(\"remote_clientid bridge-u-test\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic bridge/# out\\n\")\n        f.write(\"\\n\")\n        f.write(\"cleansession true\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 5\\n\")\n        f.write(\"try_private false\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n\n\n\ndef do_test(proto_ver):\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"bridge-u-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 180\n    mid_unknown = 2000\n\n    publish_packet = mosq_test.gen_publish(\"bridge/unknown/qos1\", qos=1, payload=\"bridge-message\", mid=mid, proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n    puback_packet_unknown = mosq_test.gen_puback(mid_unknown, proto_ver=proto_ver)\n\n\n    unsubscribe_packet = mosq_test.gen_unsubscribe(1, \"bridge/#\", proto_ver=proto_ver)\n    unsuback_packet = mosq_test.gen_unsuback(1, proto_ver=proto_ver)\n\n\n    if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n        sleep_time = 5\n    else:\n        sleep_time = 0.5\n\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    sock.settimeout(10)\n    sock.bind(('', port1))\n    sock.listen(5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n    time.sleep(sleep_time)\n\n    try:\n        (conn, address) = sock.accept()\n        conn.settimeout(20)\n\n        mosq_test.expect_packet(conn, \"connect\", connect_packet)\n        conn.send(connack_packet)\n\n        mosq_test.expect_packet(conn, \"unsubscribe\", unsubscribe_packet)\n        conn.send(unsuback_packet)\n\n        # Send the unexpected puback packet\n        conn.send(puback_packet_unknown)\n\n        # Send a legitimate publish packet to verify everything is still ok\n        conn.send(publish_packet)\n\n        mosq_test.expect_packet(conn, \"puback\", puback_packet)\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        sock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n\n"
  },
  {
    "path": "test/broker/06-bridge-fail-persist-resend-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a bridge can cope with an unknown PUBACK\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"\\n\")\n        f.write(\"connection bridge-u-test\\n\")\n        f.write(\"remote_clientid bridge-u-test\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic bridge/# out\\n\")\n        f.write(\"\\n\")\n        f.write(\"cleansession true\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 5\\n\")\n        f.write(\"try_private false\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n\n\ndef do_test(proto_ver):\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"bridge-u-test\", proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 180\n    mid_unknown = 2000\n\n    publish_packet = mosq_test.gen_publish(\"bridge/unknown/qos2\", qos=1, payload=\"bridge-message\", mid=mid, proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    pubrec_packet_unknown1 = mosq_test.gen_pubrec(mid_unknown+1, proto_ver=proto_ver)\n    pubrel_packet_unknown1 = mosq_test.gen_pubrel(mid_unknown+1, proto_ver=proto_ver)\n\n    pubrel_packet_unknown2 = mosq_test.gen_pubrel(mid_unknown+2, proto_ver=proto_ver)\n    pubcomp_packet_unknown2 = mosq_test.gen_pubcomp(mid_unknown+2, proto_ver=proto_ver)\n\n    pubcomp_packet_unknown3 = mosq_test.gen_pubcomp(mid_unknown+3, proto_ver=proto_ver)\n\n\n    unsubscribe_packet = mosq_test.gen_unsubscribe(1, \"bridge/#\", proto_ver=proto_ver)\n    unsuback_packet = mosq_test.gen_unsuback(1, proto_ver=proto_ver)\n\n\n    if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n        sleep_time = 5\n    else:\n        sleep_time = 0.5\n\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    sock.settimeout(10)\n    sock.bind(('', port1))\n    sock.listen(5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n    time.sleep(sleep_time)\n\n    try:\n        (conn, address) = sock.accept()\n        conn.settimeout(20)\n\n        mosq_test.expect_packet(conn, \"connect\", connect_packet)\n        conn.send(connack_packet)\n\n        mosq_test.expect_packet(conn, \"unsubscribe\", unsubscribe_packet)\n        conn.send(unsuback_packet)\n\n        # Send the unexpected pubrec packet\n        conn.send(pubrec_packet_unknown1)\n        mosq_test.expect_packet(conn, \"pubrel\", pubrel_packet_unknown1)\n\n        conn.send(pubrel_packet_unknown2)\n        mosq_test.expect_packet(conn, \"pubcomp\", pubcomp_packet_unknown2)\n\n        conn.send(pubcomp_packet_unknown3)\n\n        # Send a legitimate publish packet to verify everything is still ok\n        conn.send(publish_packet)\n\n        mosq_test.expect_packet(conn, \"puback\", puback_packet)\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        sock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n\n"
  },
  {
    "path": "test/broker/06-bridge-no-local.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether an incoming bridge connection receives its own messages. It\n# shouldn't because for v3.1 and v3.1.1 we have no-local set for all bridges.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver_connect, proto_ver_msgs, sub_opts):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"bridge-test\", proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver_msgs)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"loop/test\", 0 | sub_opts, proto_ver=proto_ver_msgs)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver_msgs)\n\n    publish_packet = mosq_test.gen_publish(\"loop/test\", qos=0, payload=\"message\", proto_ver=proto_ver_msgs)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        sock.send(publish_packet)\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, 128+3, 3, 0)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, 128+4, 4, 0)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, 5, 5, mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/06-bridge-outgoing-retain.py",
    "content": "#!/usr/bin/env python3\n\n# Does a bridge with bridge_outgoing_retain set to false not set the retain bit\n# on outgoing messages?\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version, outgoing_retain):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic \\\"bridge with space/#\\\" both 1\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 5\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" %(protocol_version))\n        f.write(\"bridge_outgoing_retain %s\\n\" %(outgoing_retain))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n\ndef do_test(proto_ver, outgoing_retain):\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol, outgoing_retain)\n\n    rc = 1\n    client_id = socket.gethostname()+\".bridge_sample\"\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    if proto_ver == 5:\n        opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED\n    else:\n        opts = 0\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"bridge with space/#\", 1 | opts, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    if outgoing_retain == \"true\":\n        publish_packet = mosq_test.gen_publish(\"bridge with space/retain/test\", qos=0, retain=True, payload=\"message\", proto_ver=proto_ver)\n    else:\n        publish_packet = mosq_test.gen_publish(\"bridge with space/retain/test\", qos=0, retain=False, payload=\"message\", proto_ver=proto_ver)\n\n\n    helper_connect_packet = mosq_test.gen_connect(\"helper\", clean_session=True, proto_ver=proto_ver)\n    helper_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    helper_publish_packet = mosq_test.gen_publish(\"bridge with space/retain/test\", qos=0, retain=True, payload=\"message\", proto_ver=proto_ver)\n\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(40)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"subscribe\", subscribe_packet)\n        bridge.send(suback_packet)\n\n        # Broker is now connected to us on port1.\n        # Connect our client to the broker on port2 and send a publish\n        # message, which we will then receive by way of the bridge\n        helper = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, port=port2)\n        helper.send(helper_publish_packet)\n        helper.close()\n\n        mosq_test.expect_packet(bridge, \"publish\", publish_packet)\n        rc = 0\n\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(proto_ver=4, outgoing_retain=\"true\")\ndo_test(proto_ver=4, outgoing_retain=\"false\")\ndo_test(proto_ver=5, outgoing_retain=\"true\")\ndo_test(proto_ver=5, outgoing_retain=\"false\")\n\nexit(0)\n"
  },
  {
    "path": "test/broker/06-bridge-per-listener-settings.py",
    "content": "#!/usr/bin/env python3\n\n# Test remapping of topic name for incoming message\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings true\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"bridge_attempt_unsubscribe false\\n\")\n        f.write(\"topic # in 0 local/topic/ remote/topic/\\n\")\n        f.write(\"topic prefix/# in 0 local2/topic/ remote2/topic/\\n\")\n        f.write(\"topic +/value in 0 local3/topic/ remote3/topic/\\n\")\n        f.write(\"topic ic/+ in 0 local4/top remote4/tip\\n\")\n        f.write(\"topic clients/total in 0 test/mosquitto/org $SYS/broker/\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 5\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n\n\ndef inner_test(bridge, sock, proto_ver):\n    global connect_packet, connack_packet\n\n    if not mosq_test.expect_packet(bridge, \"connect\", connect_packet):\n        return 1\n    bridge.send(connack_packet)\n\n    if proto_ver == 5:\n        opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED\n    else:\n        opts = 0\n\n    mid = 0\n    patterns = [\n        \"remote/topic/#\",\n        \"remote2/topic/prefix/#\",\n        \"remote3/topic/+/value\",\n        \"remote4/tipic/+\",\n        \"$SYS/broker/clients/total\",\n    ]\n    for pattern in (\"remote/topic/#\", \"remote2/topic/prefix/#\", \"remote3/topic/+/value\"):\n        mid += 1\n        subscribe_packet = mosq_test.gen_subscribe(mid, pattern, 0 | opts, proto_ver=proto_ver)\n        suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n        if not mosq_test.expect_packet(bridge, \"subscribe\", subscribe_packet):\n            return 1\n        bridge.send(suback_packet)\n\n    mid += 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"#\", 0 | opts, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n    sock.send(subscribe_packet)\n    if not mosq_test.expect_packet(sock, \"suback\", suback_packet):\n        return 1\n\n    cases = [\n        ('local/topic/something', 'remote/topic/something'),\n        ('local/topic/some/t/h/i/n/g', 'remote/topic/some/t/h/i/n/g'),\n        ('local/topic/value', 'remote/topic/value'),\n        # Don't work, #40 must be fixed before\n        # ('local/topic', 'remote/topic'),\n        ('local2/topic/prefix/something', 'remote2/topic/prefix/something'),\n        ('local3/topic/something/value', 'remote3/topic/something/value'),\n        ('local4/topic/something', 'remote4/tipic/something'),\n        ('test/mosquitto/orgclients/total', '$SYS/broker/clients/total'),\n    ]\n\n    for (local_topic, remote_topic) in cases:\n        mid += 1\n        remote_publish_packet = mosq_test.gen_publish(\n            remote_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver)\n        local_publish_packet = mosq_test.gen_publish(\n            local_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver)\n\n        bridge.send(remote_publish_packet)\n        match = mosq_test.expect_packet(sock, \"publish\", local_publish_packet)\n        if not match:\n            print(\"Fail on cases local_topic=%r, remote_topic=%r\" % (\n                local_topic, remote_topic,\n            ))\n            return 1\n    return 0\n\n\ndef do_test(proto_ver):\n    global connect_packet, connack_packet\n\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    client_id = socket.gethostname()+\".bridge_sample\"\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    client_connect_packet = mosq_test.gen_connect(\"pub-test\", proto_ver=proto_ver)\n    client_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(4)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n    try:\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(2)\n\n        sock = mosq_test.do_client_connect(\n            client_connect_packet, client_connack_packet,\n            port=port2,\n        )\n\n        rc = inner_test(bridge, sock, proto_ver)\n\n        sock.close()\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/06-bridge-reconnect-local-out.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a bridge topics work correctly after reconnection.\n# Important point here is that persistence is enabled.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file mosquitto-%d.db\" % (port1))\n        f.write(\"\\n\")\n        f.write(\"connection bridge_sample\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic bridge/# out\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n        f.write(\"cleansession false\\n\")\n\n\ndef do_test(proto_ver):\n    if proto_ver == 4:\n        bridge_protocol = \"mqttv311\"\n        proto_ver_connect = 128+4\n    else:\n        bridge_protocol = \"mqttv50\"\n        proto_ver_connect = 5\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = '06-bridge-reconnect-local-out.conf'\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"bridge-reconnect-test\", proto_ver=proto_ver_connect)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 180\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n    publish_packet = mosq_test.gen_publish(\"bridge/reconnect\", qos=0, payload=\"bridge-reconnect-message\", proto_ver=proto_ver)\n\n    try:\n        os.remove('mosquitto-%d.db' % (port1))\n    except OSError:\n        pass\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port1, use_conf=False)\n\n    local_cmd = [mosq_test.get_build_root() + '/src/mosquitto', '-c', '06-bridge-reconnect-local-out.conf']\n    local_broker = mosq_test.start_broker(cmd=local_cmd, filename=os.path.basename(__file__)+'_local1', use_conf=False, port=port2)\n    if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n        time.sleep(5)\n    else:\n        time.sleep(0.5)\n    local_broker.terminate()\n    if mosq_test.wait_for_subprocess(local_broker):\n        print(\"local_broker not terminated\")\n        if rc == 0: rc=1\n    if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n        time.sleep(5)\n    else:\n        time.sleep(0.5)\n    local_broker = mosq_test.start_broker(cmd=local_cmd, filename=os.path.basename(__file__)+'_local2', port=port2)\n    if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n        time.sleep(5)\n    else:\n        time.sleep(0.5)\n\n    pub = None\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port1)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        # Helper\n        helper_connect_packet = mosq_test.gen_connect(\"test-helper\", proto_ver=proto_ver)\n        helper_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n        helper_publish_packet = mosq_test.gen_publish(\"bridge/reconnect\", qos=1, mid=1, payload=\"bridge-reconnect-message\", proto_ver=proto_ver)\n        helper_puback_packet = mosq_test.gen_puback(mid=1, proto_ver=proto_ver)\n        helper_disconnect_packet = mosq_test.gen_disconnect(proto_ver=proto_ver)\n        helper_sock = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, port=port2, connack_error=\"helper connack\")\n        mosq_test.do_send_receive(helper_sock, helper_publish_packet, helper_puback_packet, \"puback\")\n        helper_sock.send(helper_disconnect_packet)\n        helper_sock.close()\n        # End of helper\n\n        # Should have now received a publish command\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        time.sleep(1)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n        local_broker.terminate()\n        if mosq_test.wait_for_subprocess(local_broker):\n            print(\"local_broker not terminated\")\n            if rc == 0: rc=1\n        try:\n            os.remove('mosquitto-%d.db' % (port1))\n        except OSError:\n            pass\n\n        if rc:\n            (stdo, stde) = local_broker.communicate()\n            print(stde.decode('utf-8'))\n            if pub:\n                (stdo, stde) = pub.communicate()\n                print(stdo.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/06-bridge-remap-receive-wildcard.py",
    "content": "#!/usr/bin/env python3\n\n# Does a bridge resend a QoS=1 message correctly after a disconnect?\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(f\"listener {port2}\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"connection bridge1\\n\")\n        f.write(f\"address 127.0.0.1:{port1}\\n\")\n        f.write(\"topic room1/# both 2 sensor/ myhouse/\\n\")\n        f.write(\"topic tst/ba both 2\\n\")\n        f.write(\"topic # both 2\\n\")\n        f.write(\"keepalive_interval 600\\n\")\n        f.write(\"remote_clientid mosquitto\\n\")\n        f.write(\"bridge_protocol_version mqttv50\\n\")\n        f.write(\"notifications false\\n\")\n\ndef do_test(proto_ver):\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2)\n\n    rc = 1\n    keepalive = 600\n    client_id = \"mosquitto\"\n    properties = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10)\n    properties += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n    connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver, properties=properties)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    if proto_ver == 5:\n        opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED\n    else:\n        opts = 0\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"myhouse/room1/#\", 2 | opts, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 2\n    subscribe_packet2 = mosq_test.gen_subscribe(mid, \"tst/ba\", 2 | opts, proto_ver=proto_ver)\n    suback_packet2= mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    mid = 3\n    subscribe_packet3 = mosq_test.gen_subscribe(mid, \"#\", 2 | opts, proto_ver=proto_ver)\n    suback_packet3 = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver)\n\n    ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    ssock.settimeout(40)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n    try:\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"subscribe1\", subscribe_packet)\n        bridge.send(suback_packet)\n\n        mosq_test.expect_packet(bridge, \"subscribe2\", subscribe_packet2)\n        bridge.send(suback_packet2)\n\n        mosq_test.expect_packet(bridge, \"subscribe3\", subscribe_packet3)\n        bridge.send(suback_packet3)\n\n        try:\n            bridge.send(bytes.fromhex(\"320c00062b2b2b2b2b2b00040033\"))\n            #bridge.send(bytes.fromhex(\"320c00062b2b2b2b2b2b00040033\"))\n            #bridge.send(bytes.fromhex(\"320c00062b2b2b2b2b2b00040033\"))\n            bridge.send(bytes.fromhex(\"C000\")) # PING\n            d = bridge.recv(1)\n            if len(d) == 0:\n                rc = 0\n        except (ConnectionResetError, BrokenPipeError, mosq_test.TestError):\n            #expected behaviour\n            rc = 0\n\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        broker.wait()\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(proto_ver=5)\n\nexit(0)\n"
  },
  {
    "path": "test/broker/06-bridge-remote-shutdown.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a bridge topics work correctly after reconnection.\n# Important point here is that persistence is enabled.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2, protocol_version):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"plugin c/plugin_evt_persist_client_update.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"persistent_client_expiration 1d\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection connect_only_bridge\\n\")\n        f.write(\"address 127.0.0.1:%d\\n\" % (port1))\n        f.write(\"topic bridge/# out\\n\")\n        f.write(\"bridge_protocol_version %s\\n\" % (protocol_version))\n        f.write(\"bridge_max_topic_alias 0\\n\")\n        f.write(\"cleansession false\\n\")\n        f.write(\"notification_topic bridge_state\\n\")\n        f.write(\"restart_timeout 300\\n\")\n\n\ndef do_test(proto_ver):\n    bridge_protocol = \"mqttv311\" if proto_ver == 4 else \"mqttv50\"\n\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = '06-bridge-remote-shutdown.conf'\n    write_config(conf_file, port1, port2, bridge_protocol)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"test-client\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    mid = 180\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"#\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n    publish_packet = mosq_test.gen_publish(\"echo_topic\", qos=0, payload=\"sample-message\", proto_ver=proto_ver)\n    bridge_up_packet = mosq_test.gen_publish(\"bridge_state\", qos=0, payload=\"1\", retain=1, proto_ver=proto_ver)\n    bridge_down_packet = mosq_test.gen_publish(\"bridge_state\", qos=0, payload=\"0\", retain=0, proto_ver=proto_ver)\n    \n    remote_broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port1, use_conf=False)\n\n    local_cmd = [mosq_test.get_build_root() + '/src/mosquitto', '-c', conf_file]\n    local_broker = mosq_test.start_broker(cmd=local_cmd, filename=os.path.basename(__file__)+'_local1', use_conf=False, port=port2)    \n\n    rc1 = None\n    \n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port2)\n        sock = mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        mosq_test.expect_packet(sock, \"check for bridge up state\", bridge_up_packet)\n\n        rc1, stde1 = mosq_test.terminate_broker(remote_broker)\n        \n        mosq_test.expect_packet(sock, \"wait for bridge down state\", bridge_down_packet)\n        # If we now get a message published to evt/persist/client/update instead of the expected echo\n        # the bridge connection was most likely added to the expiry list\n        sock = mosq_test.do_send_receive(sock, publish_packet, publish_packet)\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        \n        pass\n    finally:\n        if rc1 is None:\n            rc1, stde1 = mosq_test.terminate_broker(remote_broker)\n        rc2, stde2 = mosq_test.terminate_broker(local_broker)\n        if rc or rc1 or rc2:\n            print(f\"Remote broker first run rc={rc1}\")\n            print(stde1.decode('utf-8'))\n\n            print(f\"Local broker rc={rc2}\")\n            print(stde2.decode('utf-8'))\n        try: \n            os.remove(conf_file)\n        except OSError:\n            pass\n    return rc == 0\n\nif do_test(proto_ver=4) and do_test(proto_ver=5):\n    exit(0)\n\nexit(1)\n\n"
  },
  {
    "path": "test/broker/07-will-control.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client setting a will with $CONTROL in is denied\n\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 1\n    connect_packet = mosq_test.gen_connect(\"will\", will_topic=\"$CONTROL/dynamic-security/v1\", will_payload=b\"will-message\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.client_connect_only(port=port)\n        sock.send(connect_packet)\n        d = sock.recv(1)\n        if d == b\"\":\n            rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        if start_broker:\n            broker.terminate()\n            broker.wait()\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-delay-invalid-573191.py",
    "content": "#!/usr/bin/env python3\n\n# Test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=573191\n# Check under valgrind/asan for leaks.\n\nfrom mosq_test_helper import *\n\ndef do_test():\n    rc = 1\n\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 3)\n    connect_packet = mosq_test.gen_connect(\"will-573191-test\", proto_ver=5, will_topic=\"\", will_properties=props)\n    connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.PROTOCOL_ERROR, proto_ver=5)\n\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=30, port=port)\n        sock.close()\n    except BrokenPipeError:\n        rc = 0\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n    return rc\n\nsys.exit(do_test())\n"
  },
  {
    "path": "test/broker/07-will-delay-reconnect.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client with a will delay handles correctly on the client reconnecting\n# First connection is durable, second is clean session, and without a will, so the will should not be received.\n# MQTT 5\n\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker):\n    rc = 1\n\n    mid = 1\n    connect1_packet = mosq_test.gen_connect(\"will-delay-reconnect-test\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\n    will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 3)\n    connect2a_packet = mosq_test.gen_connect(\"will-delay-reconnect-helper\", proto_ver=5, will_topic=\"will/delay/reconnect/test\", will_payload=b\"will delay\", will_properties=will_props, clean_session=False, properties=props)\n    connack2a_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    connect2b_packet = mosq_test.gen_connect(\"will-delay-reconnect-helper\", proto_ver=5, clean_session=False)\n    connack2b_packet = mosq_test.gen_connack(rc=0, flags=1, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/delay/reconnect/test\", 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port)\n        mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2a_packet, connack2a_packet, timeout=30, port=port)\n        sock2.close()\n\n        time.sleep(1)\n        sock2 = mosq_test.do_client_connect(connect2b_packet, connack2b_packet, timeout=30, port=port)\n        time.sleep(3)\n\n        # The client2 has reconnected within the original will delay interval, which has now\n        # passed, but it should have been deleted anyway. Disconnect and see\n        # whether we get the old will. We should not.\n        sock2.close()\n\n        mosq_test.do_ping(sock1)\n        rc = 0\n\n        sock1.close()\n        sock2.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-delay-recover.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client with a will delay recovers on the client reconnecting\n# MQTT 5\n\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker, clean_session):\n    rc = 1\n\n    mid = 1\n    connect1_packet = mosq_test.gen_connect(\"will-delay-recovery\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    connect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 30)\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 3)\n    connect2_packet = mosq_test.gen_connect(\"will-delay-recovery-helper\", proto_ver=5, will_topic=\"will/delay/recovery/test\", will_payload=b\"will delay\", will_properties=props, clean_session=clean_session, properties=connect_props)\n    connack2a_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n    if clean_session == True:\n        connack2b_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n    else:\n        connack2b_packet = mosq_test.gen_connack(rc=0, proto_ver=5, flags=1)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/delay/recovery/test\", 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    connect2_packet_clear = mosq_test.gen_connect(\"will-delay-recovery-helper\", proto_ver=5)\n\n    will_packet = mosq_test.gen_publish(topic=\"will/delay/recovery/test\", payload=\"will delay\", qos=0, proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port)\n        mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2a_packet, timeout=30, port=port)\n        sock2.close()\n\n        time.sleep(1)\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2b_packet, timeout=30, port=port)\n        time.sleep(3)\n\n        # The client2 has reconnected within the will delay interval, which has now\n        # passed.\n        if clean_session:\n            # The old session has ended, so we should receive the will\n            mosq_test.expect_packet(sock1, \"will\", will_packet)\n        else:\n            # We should not have received the will at this point.\n            mosq_test.do_ping(sock1)\n        rc = 0\n\n        sock1.close()\n        sock2.close()\n\n        sock2 = mosq_test.do_client_connect(connect2_packet_clear, connack1_packet, timeout=30, port=port)\n        sock2.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, clean_session=True)\n    if rc:\n        return rc\n    rc = do_test(start_broker, clean_session=False)\n    if rc:\n        return rc\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-delay-session-expiry-0.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client that connects with a will delay that is longer than\n# their session expiry interval has their will published.\n# MQTT 5\n# https://github.com/eclipse/mosquitto/issues/1401\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n\n    mid = 1\n    connect1_packet = mosq_test.gen_connect(\"will-session-exp\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 60)\n    connect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 0)\n\n    connect2_packet = mosq_test.gen_connect(\"will-session-exp-helper\", proto_ver=5, properties=connect_props, will_topic=\"will/session-expiry/test\", will_payload=b\"will delay\", will_qos=2, will_properties=will_props)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/session-expiry/test\", 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"will/session-expiry/test\", qos=0, payload=\"will delay\", proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port, connack_error=\"connack1\")\n        mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=5, port=port, connack_error=\"connack2\")\n        time.sleep(1)\n        sock2.close()\n\n        # Will should be sent immediately due to session-expiry-interval=0. If not, the read will timeout\n        mosq_test.expect_packet(sock1, \"publish\", publish_packet)\n        rc = 0\n\n        sock1.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-delay-session-expiry.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client that connects with a will delay that is longer than\n# their session expiry interval has their will published.\n# MQTT 5\n# https://github.com/eclipse/mosquitto/issues/1401\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n\n    mid = 1\n    connect1_packet = mosq_test.gen_connect(\"will-session-exp\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 4)\n    connect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 2)\n\n    connect2_packet = mosq_test.gen_connect(\"will-session-exp-helper\", proto_ver=5, properties=connect_props, will_topic=\"will/session-expiry/test\", will_payload=b\"will delay\", will_qos=2, will_properties=will_props)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/session-expiry/test\", 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"will/session-expiry/test\", qos=0, payload=\"will delay\", proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port, connack_error=\"connack1\")\n        mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=30, port=port, connack_error=\"connack2\")\n        time.sleep(1)\n        sock2.close()\n\n        # Wait for session to expire\n        time.sleep(3)\n        mosq_test.expect_packet(sock1, \"publish\", publish_packet)\n        rc = 0\n\n        sock1.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-delay-session-expiry2.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client that connects with a will delay that is shorter than\n# their session expiry interval has their will published.\n# MQTT 5\n# https://github.com/eclipse/mosquitto/issues/1401\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n\n    mid = 1\n    connect1_packet = mosq_test.gen_connect(\"will-session-exp2\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 2)\n    connect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 4)\n\n    connect2_packet = mosq_test.gen_connect(\"will-session-exp2-helper\", proto_ver=5, properties=connect_props, will_topic=\"will/session/expiry2/test\", will_payload=b\"will delay\", will_qos=2, will_properties=will_props)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/session/expiry2/test\", 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"will/session/expiry2/test\", qos=0, payload=\"will delay\", proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port, connack_error=\"connack1\")\n        mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=30, port=port, connack_error=\"connack2\")\n        time.sleep(1)\n        sock2.close()\n\n        # Wait for session to expire\n        time.sleep(3)\n        mosq_test.expect_packet(sock1, \"publish\", publish_packet)\n        rc = 0\n\n        sock1.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-delay.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client will is transmitted with a delay correctly.\n# MQTT 5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, clean_session):\n    rc = 1\n\n    mid = 1\n    connect1_packet = mosq_test.gen_connect(\"will-delay-test\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\n    will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 3)\n    connect2_packet = mosq_test.gen_connect(\"will-delay-helper\", proto_ver=5, properties=props, will_topic=\"will/delay/test\", will_payload=b\"will delay\", will_qos=2, will_properties=will_props, clean_session=clean_session)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/delay/test\", 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"will/delay/test\", qos=0, payload=\"will delay\", proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port)\n        mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=30, port=port)\n        sock2.close()\n\n        t_start = time.time()\n        mosq_test.expect_packet(sock1, \"publish\", publish_packet)\n        t_finish = time.time()\n        if t_finish - t_start > 2 and t_finish - t_start < 5:\n            rc = 0\n\n        sock1.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, clean_session=True)\n    if rc:\n        return rc\n    rc = do_test(start_broker, clean_session=False)\n    if rc:\n        return rc\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-disconnect-with-will.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client will is transmitted when a client disconnects with DISCONNECT with will.\n# MQTT 5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n\n    mid = 1\n    connect1_packet = mosq_test.gen_connect(\"will-with-disconnect-test\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    connect2_packet = mosq_test.gen_connect(\"will-with-disconnect-helper\", proto_ver=5, will_topic=\"will/with/disconnect/test\", will_payload=b\"will delay\", will_qos=2)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n    disconnect_packet = mosq_test.gen_disconnect(reason_code=4, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/with/disconnect/test\", 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"will/with/disconnect/test\", qos=0, payload=\"will delay\", proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port)\n        mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=30, port=port)\n        sock2.send(disconnect_packet)\n\n        mosq_test.expect_packet(sock1, \"publish\", publish_packet)\n        rc = 0\n\n        sock2.close()\n        sock1.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-invalid-utf8.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a will topic with invalid UTF-8 fails\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"will-invalid-utf8\", will_topic=\"will/invalid/utf8\", proto_ver=proto_ver)\n\n    b = list(struct.unpack(\"B\"*len(connect_packet), connect_packet))\n    b[40] = 0 # Topic should never have a 0x0000\n    connect_packet = struct.pack(\"B\"*len(b), *b)\n\n    port = mosq_test.get_port()\n    broker = None\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, b\"\", timeout=30, port=port)\n        sock.close()\n    except BrokenPipeError:\n        rc = 0\n    finally:\n        if broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n    return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc\n    return do_test(start_broker, proto_ver=5)\n\nif __name__ == '__main__':\n    sys.exit(all_tests(True))\n"
  },
  {
    "path": "test/broker/07-will-no-flag.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is disconnected if it sets the will flag but does\n# not provide a will payload.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"will-no-payload\", will_topic=\"will/topic\", will_qos=1, will_retain=True, proto_ver=proto_ver)\n    b = list(struct.unpack(\"B\"*len(connect_packet), connect_packet))\n\n    bmod = b[0:len(b)-2]\n    bmod[1] = bmod[1] - 2 # Reduce remaining length by two to remove final two payload length values\n\n    connect_packet = struct.pack(\"B\"*len(bmod), *bmod)\n    connack_packet = mosq_test.gen_connack(mqtt5_rc.PROTOCOL_ERROR, proto_ver=5)\n\n    port = mosq_test.get_port()\n    broker = None\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n    except BrokenPipeError:\n        rc = 0\n    finally:\n        if broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n    return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc\n    return do_test(start_broker, proto_ver=5)\n\nif __name__ == '__main__':\n    sys.exit(all_tests(True))\n"
  },
  {
    "path": "test/broker/07-will-null-topic.py",
    "content": "#!/usr/bin/env python3\n\nimport struct\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"will-null-topic\", will_topic=\"\", will_payload=struct.pack(\"!4sB7s\", b\"will\", 0, b\"message\"), proto_ver=proto_ver)\n\n    if proto_ver == 5:\n        connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.PROTOCOL_ERROR, proto_ver=5)\n    else:\n        connack_packet = b\"\"\n\n    port = mosq_test.get_port()\n    broker = None\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=30, port=port)\n        sock.close()\n    except BrokenPipeError:\n        rc = 0\n    finally:\n        if broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n    return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc\n    return 0\n\nif __name__ == '__main__':\n    sys.exit(all_tests(True))\n"
  },
  {
    "path": "test/broker/07-will-null.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client will is transmitted correctly with a null payload.\n\nfrom mosq_test_helper import *\n\ndef helper(port, proto_ver):\n    connect_packet = mosq_test.gen_connect(\"07-will-null-helper\", will_topic=\"will/null/test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    sock.close()\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n    mid = 53\n    connect_packet = mosq_test.gen_connect(\"07-will-null-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/null/test\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"will/null/test\", qos=0, proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=30, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        helper(port, proto_ver)\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d\" % (proto_ver))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-oversize-payload.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client will that is too large is handled\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"message_size_limit 1\\n\")\n\ndef do_test(proto_ver, clean_session):\n    rc = 1\n    mid = 53\n    connect_packet = mosq_test.gen_connect(\"will-test\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    connect_packet_ok = mosq_test.gen_connect(\"test-helper\", will_topic=\"will/qos0/test\", will_payload=b\"A\", clean_session=clean_session, proto_ver=proto_ver, session_expiry=60)\n    connack_packet_ok = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    connect_packet_bad = mosq_test.gen_connect(\"test-helper\", will_topic=\"will/qos0/test\", will_payload=b\"AB\", clean_session=clean_session, proto_ver=proto_ver, session_expiry=60)\n    if proto_ver == 5:\n        connack_packet_bad = mosq_test.gen_connack(rc=mqtt5_rc.PACKET_TOO_LARGE, proto_ver=proto_ver, property_helper=False)\n    else:\n        connack_packet_bad = mosq_test.gen_connack(rc=5, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/qos0/test\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"will/qos0/test\", qos=0, payload=\"A\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port, timeout=5)\n        sock2.close()\n\n        sock2 = mosq_test.do_client_connect(connect_packet_ok, connack_packet_ok, port=port, timeout=5)\n        sock2.close()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        # Check there are no more messages\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(4, True)\ndo_test(4, False)\ndo_test(5, True)\ndo_test(5, False)\nexit(0)\n"
  },
  {
    "path": "test/broker/07-will-per-listener.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client will is transmitted correctly, with per_listener_settings enabled\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings true\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n\ndef do_test(proto_ver, clean_session):\n    rc = 1\n    mid = 53\n    connect1_packet = mosq_test.gen_connect(\"will-qos0-test\", proto_ver=proto_ver)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    connect2_packet = mosq_test.gen_connect(\"test-helper\", will_topic=\"will/qos0/test\", will_payload=b\"will-message\", clean_session=clean_session, proto_ver=proto_ver, session_expiry=60)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/qos0/test\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"will/qos0/test\", qos=0, payload=\"will-message\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\n    try:\n        sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port, timeout=5)\n        sock2.close()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(4, True)\ndo_test(4, False)\ndo_test(5, True)\ndo_test(5, False)\nexit(0)\n"
  },
  {
    "path": "test/broker/07-will-properties.py",
    "content": "#!/usr/bin/env python3\n\n# Test for bug #1244. This occurs if a V5 will message is used where the first\n# Will property is one of: content-type, payload-format-indicator,\n# response-topic. These are the properties that are attached to the will for\n# later use, as opposed to e.g.  will-delay-interval which is a value which is\n# read immediately and not passed\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker, will_props, recvd_props):\n    rc = 1\n\n    mid = 1\n    connect1_packet = mosq_test.gen_connect(\"07-will-properties-helper\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"07/will/properties/will/test\", 0, proto_ver=5)\n    suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    connect2_packet = mosq_test.gen_connect(\"07-will-properties\", proto_ver=5, will_topic=\"07/will/properties/will/test\", will_payload=b\"will payload\", will_properties=will_props)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"07/will/properties/will/test\", qos=0, payload=\"will payload\", proto_ver=5, properties=recvd_props)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port)\n        mosq_test.do_send_receive(sock1, subscribe1_packet, suback1_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=30, port=port)\n        sock2.close()\n\n        mosq_test.expect_packet(sock1, \"publish\", publish_packet)\n        rc = 0\n\n        sock1.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    # Single test property\n    will_props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    rc = do_test(start_broker, will_props, will_props)\n    if rc:\n        return rc;\n\n    # Multiple test properties\n    will_props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    will_props += mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 0)\n    rc = do_test(start_broker, will_props, will_props)\n    if rc:\n        return rc;\n\n    # Multiple test properties, with property that is removed\n    will_props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    will_props += mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 0)\n    will_props += mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 0)\n\n    recv_props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    recv_props += mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 0)\n    rc = do_test(start_broker, will_props, recv_props)\n    if rc:\n        return rc;\n\n    # Multiple test properties, with property that is removed *first*\n    will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 0)\n    will_props += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    will_props += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"data\")\n\n    recv_props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    recv_props += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"data\")\n    rc = do_test(start_broker, will_props, recv_props)\n    if rc:\n        return rc;\n\n    # All properties,  plus multiple user properties (excluding\n    # message-expiry-interval, for ease of testing reasons)\n    will_props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key1\", \"value1\")\n    will_props += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    will_props += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"data\")\n    will_props += mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 0)\n    will_props += mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 1)\n    will_props += mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"application/test\")\n    will_props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key2\", \"value2\")\n\n    recv_props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key1\", \"value1\")\n    recv_props += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    recv_props += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"data\")\n    recv_props += mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 1)\n    recv_props += mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"application/test\")\n    recv_props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key2\", \"value2\")\n    rc = do_test(start_broker, will_props, recv_props)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-qos0.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client will is transmitted correctly.\n\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker, proto_ver, clean_session):\n    rc = 1\n    mid = 53\n    connect1_packet = mosq_test.gen_connect(\"will-qos0-test\", proto_ver=proto_ver)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    connect2_packet = mosq_test.gen_connect(\"will-qos0-helper\", will_topic=\"will/qos0/test\", will_payload=b\"will-message\", clean_session=clean_session, proto_ver=proto_ver, session_expiry=60)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/qos0/test\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"will/qos0/test\", qos=0, payload=\"will-message\", proto_ver=proto_ver)\n\n    connect2_packet_clear = mosq_test.gen_connect(\"will-qos0-helper\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port, timeout=5)\n        sock2.close()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = 0\n\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect2_packet_clear, connack1_packet, timeout=5, port=port)\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4, clean_session=True)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=4, clean_session=False)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5, clean_session=True)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5, clean_session=False)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-reconnect-1273.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a persistent client that disconnects with DISCONNECT has its\n# will published when it reconnects. It shouldn't. Bug 1273:\n# https://github.com/eclipse/mosquitto/issues/1273\n\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker, proto_ver):\n    rc = 1\n\n    connect1_packet = mosq_test.gen_connect(\"will-reconnect-helper\", proto_ver=proto_ver)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    subscribe1_packet = mosq_test.gen_subscribe(mid, \"will/reconnect/test\", 0, proto_ver=proto_ver)\n    suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    connect2_packet = mosq_test.gen_connect(\"will-1273\", will_topic=\"will/reconnect/test\", will_payload=b\"will msg\",clean_session=False, proto_ver=proto_ver, session_expiry=60)\n    connack2a_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    connack2b_packet = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"will/reconnect/test\", qos=0, payload=\"alive\", proto_ver=proto_ver)\n\n    connect2_packet_clear = mosq_test.gen_connect(\"will-1273\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        # Connect and subscribe will-sub\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port, connack_error=\"connack1\")\n        mosq_test.do_send_receive(sock1, subscribe1_packet, suback1_packet, \"suback\")\n\n        # Connect will-1273\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2a_packet, timeout=30, port=port)\n        # Publish our \"alive\" message\n        sock2.send(publish_packet)\n        # Clean disconnect\n        sock2.send(disconnect_packet)\n\n        # will-1273 should get the \"alive\"\n        mosq_test.expect_packet(sock1, \"publish1\", publish_packet)\n\n        sock2.close()\n\n        # Reconnect\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2b_packet, timeout=30, port=port, connack_error=\"connack2\")\n        # will-1273 to publish \"alive\" again, and will-sub to receive it.\n        sock2.send(publish_packet)\n        mosq_test.expect_packet(sock1, \"publish2\", publish_packet)\n        # Do a ping to make sure there are no other packets received.\n        mosq_test.do_ping(sock1)\n        rc = 0\n\n        sock1.close()\n        sock2.close()\n\n        sock2 = mosq_test.do_client_connect(connect2_packet_clear, connack1_packet, timeout=30, port=port, connack_error=\"connack clear\")\n        sock2.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, proto_ver=5)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/07-will-takeover.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a will is published when a client takes over an existing session that has a will set.\n#\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker, proto_ver, clean_session1, clean_session2):\n    rc = 1\n\n    mid = 1\n    connect1_packet = mosq_test.gen_connect(\"will-takeover-helper\", proto_ver=proto_ver)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    if proto_ver == 5:\n        if clean_session1 == False:\n            connect_props1 = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\n        else:\n            connect_props1 = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 0)\n\n        if clean_session2 == False:\n            connect_props2 = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\n        else:\n            connect_props2 = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 0)\n    else:\n        connect_props1 = b\"\"\n        connect_props2 = b\"\"\n\n    connect2_packet = mosq_test.gen_connect(\"will-takeover-test\", proto_ver=proto_ver, will_topic=\"will/takeover/test\", will_payload=b\"LWT\", clean_session=clean_session1, properties=connect_props1)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    connect3_packet = mosq_test.gen_connect(\"will-takeover-test\", proto_ver=proto_ver, clean_session=clean_session2, properties=connect_props2)\n    if clean_session1 == False and clean_session2 == False:\n        connack3_packet = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\n    else:\n        connack3_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"will/takeover/test\", 0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(topic=\"will/takeover/test\", qos=0, payload=\"Client ready\", proto_ver=proto_ver)\n    publish_lwt_packet = mosq_test.gen_publish(topic=\"will/takeover/test\", qos=0, payload=\"LWT\", proto_ver=proto_ver)\n\n    connect2_packet_clear = mosq_test.gen_connect(\"will-takeover-test\", proto_ver=proto_ver)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        # Connect helper to look for will being published\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port)\n        mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, \"suback\")\n\n        # Connect client with will\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=5, port=port)\n\n        # Send a \"ready\" message\n        sock2.send(publish_packet)\n        mosq_test.expect_packet(sock1, \"publish 1\", publish_packet)\n\n        # Connect client with will again as a separate connection, this should\n        # take over from the previous one but only trigger a Will if we are taking\n        # over a clean session/session-expiry-interval==0 client\n        sock3 = mosq_test.do_client_connect(connect3_packet, connack3_packet, timeout=5, port=port)\n        sock2.close()\n\n        if clean_session1 == True or clean_session2 == True:\n            mosq_test.expect_packet(sock1, \"publish LWT\", publish_lwt_packet)\n\n        # Send the \"ready\" message again\n        sock3.send(publish_packet)\n        mosq_test.expect_packet(sock1, \"publish 2\", publish_packet)\n        # If the helper has received a will message, then the ping test will fail\n        mosq_test.do_ping(sock1)\n        rc = 0\n\n        sock1.close()\n        sock2.close()\n        sock3.close()\n\n        sock2 = mosq_test.do_client_connect(connect2_packet_clear, connack2_packet, timeout=5, port=port)\n        sock2.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                print(\"proto_ver=%d clean_session1=%d clean_session2=%d\" % (proto_ver, clean_session1, clean_session2))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, proto_ver=4, clean_session1=True, clean_session2=True)\n    if rc:\n        print(\"1\")\n        return rc;\n    rc = do_test(start_broker, proto_ver=4, clean_session1=False, clean_session2=True)\n    if rc:\n        print(\"2\")\n        return rc;\n    rc = do_test(start_broker, proto_ver=4, clean_session1=True, clean_session2=False)\n    if rc:\n        print(\"3\")\n        return rc;\n    rc = do_test(start_broker, proto_ver=4, clean_session1=False, clean_session2=False)\n    if rc:\n        print(\"4\")\n        return rc;\n    rc = do_test(start_broker, proto_ver=5, clean_session1=True, clean_session2=True)\n    if rc:\n        print(\"5\")\n        return rc;\n    rc = do_test(start_broker, proto_ver=5, clean_session1=False, clean_session2=True)\n    if rc:\n        print(\"6\")\n        return rc;\n    rc = do_test(start_broker, proto_ver=5, clean_session1=True, clean_session2=False)\n    if rc:\n        print(\"7\")\n        return rc;\n    rc = do_test(start_broker, proto_ver=5, clean_session1=False, clean_session2=False)\n    if rc:\n        print(\"8\")\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/08-ssl-bridge-helper.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\nport = mosq_test.get_port()\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"test-helper\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\npublish_packet = mosq_test.gen_publish(\"bridge/ssl/test\", qos=0, payload=\"message\")\n\ndisconnect_packet = mosq_test.gen_disconnect()\n\nsock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, connack_error=\"helper connack\")\nsock.send(publish_packet)\nsock.send(disconnect_packet)\nsock.close()\n\nexit(0)\n"
  },
  {
    "path": "test/broker/08-ssl-bridge.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\nsource_dir = Path(__file__).resolve().parent\nssl_dir = source_dir.parent / \"ssl\"\n\ndef write_config(filename, address, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(f\"listener {port2}\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge_test\\n\")\n        f.write(f\"address {address}:{port1}\\n\")\n        f.write(\"topic bridge/# both 0\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"restart_timeout 2\\n\")\n        f.write(\"\\n\")\n        f.write(f\"bridge_cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(\"bridge_insecure true\\n\")\n\ndef do_test(address):\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, address, port1, port2)\n\n    rc = 1\n    client_id = socket.gethostname()+\".bridge_test\"\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=False, proto_ver=128+4)\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"bridge/#\", 0)\n    suback_packet = mosq_test.gen_suback(mid, 0)\n\n    publish_packet = mosq_test.gen_publish(\"bridge/ssl/test\", qos=0, payload=\"message\")\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=f\"{ssl_dir}/all-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    context.load_cert_chain(certfile=f\"{ssl_dir}/server-san.crt\", keyfile=f\"{ssl_dir}/server-san.key\")\n    ssock = context.wrap_socket(sock, server_side=True)\n    ssock.settimeout(20)\n    ssock.bind(('', port1))\n    ssock.listen(5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\n    try:\n        (bridge, address) = ssock.accept()\n        bridge.settimeout(20)\n\n        mosq_test.expect_packet(bridge, \"connect\", connect_packet)\n        bridge.send(connack_packet)\n\n        mosq_test.expect_packet(bridge, \"subscribe\", subscribe_packet)\n        bridge.send(suback_packet)\n\n        pub = subprocess.Popen([f'{source_dir}/08-ssl-bridge-helper.py', str(port2)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        pub_terminated = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminated = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(bridge, \"publish\", publish_packet)\n        rc = pub_terminated\n\n        bridge.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        try:\n            bridge.close()\n        except NameError:\n            pass\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        ssock.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(\"127.0.0.1\")\ndo_test(mosq_test.get_non_loopback_ip()) # tests non-matching certificate hostname with bridge_insecure\n"
  },
  {
    "path": "test/broker/08-ssl-connect-cert-auth-crl.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"require_certificate true\\n\")\n        f.write(f\"crlfile {ssl_dir}/crl.pem\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-success-test\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\ntry:\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-root-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    context.load_cert_chain(certfile=f\"{ssl_dir}/client.crt\", keyfile=f\"{ssl_dir}/client.key\")\n    ssock = context.wrap_socket(sock, server_hostname=\"localhost\")\n    ssock.settimeout(20)\n    ssock.connect((\"localhost\", port1))\n\n    mosq_test.do_send_receive(ssock, connect_packet, connack_packet, \"connack\")\n\n    rc = 0\n\n    ssock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/08-ssl-connect-cert-auth-expired-allowed.py",
    "content": "#!/usr/bin/env python3\n\n# Check the `disable_client_cert_date_checks` option.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"require_certificate true\\n\")\n        f.write(\"disable_client_cert_date_checks true\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-success-test\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\ntry:\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-root-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    context.load_cert_chain(certfile=f\"{ssl_dir}/client-expired.crt\", keyfile=f\"{ssl_dir}/client-expired.key\")\n    ssock = context.wrap_socket(sock, server_hostname=\"localhost\")\n    ssock.settimeout(20)\n    ssock.connect((\"localhost\", port1))\n    mosq_test.do_send_receive(ssock, connect_packet, connack_packet, \"connack\")\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    time.sleep(0.5)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/08-ssl-connect-cert-auth-expired.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a valid CONNECT results in the correct CONNACK packet using an\n# SSL connection with client certificates required.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"require_certificate true\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\nssl_eof = False\ntry:\n    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-root-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    context.load_cert_chain(certfile=f\"{ssl_dir}/client-expired.crt\", keyfile=f\"{ssl_dir}/client-expired.key\")\n    with socket.create_connection((\"localhost\", port1)) as sock:\n        ssock = context.wrap_socket(sock, server_hostname=\"localhost\", suppress_ragged_eofs=True)\n        ssock.settimeout(None)\n        try:\n            ssock.read(1)\n        except ssl.SSLEOFError:\n            # Under load, sometimes the broker closes the connection after the\n            # handshake has failed, but before we have chance to send our\n            # payload and so we get an EOF.\n            ssl_eof = True\n        except ssl.SSLError as err:\n            if err.reason == \"SSLV3_ALERT_CERTIFICATE_EXPIRED\":\n                rc = 0\n            elif err.errno == 8 and \"EOF occurred\" in err.strerror:\n                rc = 0\n            else:\n                broker.terminate()\n                print(err.strerror)\n                raise ValueError(err.errno) from err\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    time.sleep(0.5)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n\n    if ssl_eof:\n        if \"certificate verify failed\" in stde.decode('utf-8'):\n            rc = 0\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/08-ssl-connect-cert-auth-revoked.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"require_certificate true\\n\")\n        f.write(f\"crlfile {ssl_dir}/crl.pem\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\nssl_eof = False\ntry:\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-root-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    context.load_cert_chain(certfile=f\"{ssl_dir}/client-revoked.crt\", keyfile=f\"{ssl_dir}/client-revoked.key\")\n    ssock = context.wrap_socket(sock, server_hostname=\"localhost\")\n    ssock.settimeout(20)\n    try:\n        ssock.connect((\"localhost\", port1))\n        try:\n            ssock.read(1)\n        except ssl.SSLEOFError:\n            # Under load, sometimes the broker closes the connection after the\n            # handshake has failed, but before we have chance to send our\n            # payload and so we get an EOF.\n            ssl_eof = True\n        except ssl.SSLError as err:\n            if err.reason == \"SSLV3_ALERT_CERTIFICATE_REVOKED\":\n                rc = 0\n            elif err.errno == 8 and \"EOF occurred\" in err.strerror:\n                rc = 0\n            else:\n                broker.terminate()\n                print(err.strerror)\n                raise ValueError(err.errno) from err\n    except ssl.SSLError as err:\n        if err.errno == 1 and \"certificate revoked\" in err.strerror:\n            rc = 0\n        elif err.errno == 8 and \"EOF occurred\" in err.strerror:\n            rc = 0\n        else:\n            broker.terminate()\n            print(err.strerror)\n            raise ValueError(err.errno)\n\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    time.sleep(0.5)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if ssl_eof:\n        if \"certificate verify failed\" in stde.decode('utf-8'):\n            rc = 0\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/08-ssl-connect-cert-auth-without.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client can connect without an SSL certificate if one is required.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"require_certificate true\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-cert-test\")\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\nsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ncontext = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)\ncontext.minimum_version = ssl.TLSVersion.TLSv1_2\nssock = context.wrap_socket(sock, server_hostname=\"localhost\")\nssock.settimeout(20)\ntry:\n    ssock.connect((\"localhost\", port1))\n    mosq_test.do_send_receive(ssock, connect_packet, \"\", \"connack\")\nexcept ssl.SSLEOFError as err:\n    rc = 0\nexcept ssl.SSLError as err:\n    if err.errno == 1:\n        rc = 0\n    else:\n        print(\"unexpected SSLError occurred\", err)\nexcept socket.error as err:\n    if err.errno == errno.ECONNRESET:\n        rc = 0\n    else:\n        print(\"unexpected socket.error occurred\", err)\nexcept mosq_test.TestError as err:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/08-ssl-connect-cert-auth.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"require_certificate true\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-success-test\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\ntry:\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-root-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    context.load_cert_chain(certfile=f\"{ssl_dir}/client.crt\", keyfile=f\"{ssl_dir}/client.key\")\n    ssock = context.wrap_socket(sock, server_hostname=\"localhost\")\n    ssock.settimeout(20)\n    ssock.connect((\"localhost\", port1))\n\n    mosq_test.do_send_receive(ssock, connect_packet, connack_packet, \"connack\")\n\n    rc = 0\n\n    ssock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/08-ssl-connect-dhparam.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(f\"dhparamfile {ssl_dir}/dhparam\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-success-test\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\ntry:\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-root-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    ssock = context.wrap_socket(sock, server_hostname=\"localhost\")\n    ssock.settimeout(20)\n    ssock.connect((\"localhost\", port1))\n\n    mosq_test.do_send_receive(ssock, connect_packet, connack_packet, \"connack\")\n\n    rc = 0\n\n    ssock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/08-ssl-connect-identity.py",
    "content": "#!/usr/bin/env python3\n\n# Client connects with a certificate to a server that has use_identity_as_username=true. Shouldn't be rejected.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" %(port1))\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"\\n\")\n        f.write(\"use_identity_as_username true\\n\")\n        f.write(\"require_certificate true\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-identity-test\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\ntry:\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-root-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    context.load_cert_chain(certfile=f\"{ssl_dir}/client.crt\", keyfile=f\"{ssl_dir}/client.key\")\n    ssock = context.wrap_socket(sock, server_hostname=\"localhost\")\n    ssock.settimeout(20)\n    ssock.connect((\"localhost\", port1))\n\n    mosq_test.do_send_receive(ssock, connect_packet, connack_packet, \"connack\")\n\n    rc = 0\n\n    ssock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    time.sleep(0.5)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/08-ssl-connect-no-auth-wrong-ca.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-success-test\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\nsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ncontext = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-alt-ca.crt\")\ncontext.minimum_version = ssl.TLSVersion.TLSv1_2\nssock = context.wrap_socket(sock, server_hostname=\"localhost\")\nssock.settimeout(20)\ntry:\n    ssock.connect((\"localhost\", port1))\nexcept ssl.SSLError as err:\n    if err.errno == 1:\n        rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    ssock.close()\n\ntime.sleep(0.5)\nbroker.terminate()\nif mosq_test.wait_for_subprocess(broker):\n    print(\"broker not terminated\")\n    if rc == 0: rc=1\n(stdo, stde) = broker.communicate()\nif rc:\n    print(stde.decode('utf-8'))\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/08-ssl-connect-no-auth.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-success-test\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\ntry:\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-root-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    ssock = context.wrap_socket(sock, server_hostname=\"localhost\")\n    ssock.settimeout(20)\n    ssock.connect((\"localhost\", port1))\n\n    mosq_test.do_send_receive(ssock, connect_packet, connack_packet, \"connack\")\n\n    rc = 0\n\n    ssock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/08-ssl-connect-no-identity.py",
    "content": "#!/usr/bin/env python3\n\n# Client connects without a certificate to a server that has use_identity_as_username=true. Should be rejected.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"\\n\")\n        f.write(\"use_identity_as_username true\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-no-identity-test\")\nconnack_packet = mosq_test.gen_connack(rc=4)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)\n\ntry:\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-root-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    ssock = context.wrap_socket(sock, server_hostname=\"localhost\")\n    ssock.settimeout(20)\n    ssock.connect((\"localhost\", port1))\n\n    mosq_test.do_send_receive(ssock, connect_packet, connack_packet, \"connack\")\n\n    rc = 0\n\n    ssock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    time.sleep(2)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/08-ssl-hup-disconnect.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client connected with a client certificate when\n# use_identity_as_username is true is then disconnected when a SIGHUP is\n# received.\n# https://github.com/eclipse/mosquitto/issues/1402\n\nfrom mosq_test_helper import *\nimport signal\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config(filename, pw_file, port, option):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"require_certificate true\\n\")\n        f.write(\"%s true\\n\" % (option))\n        f.write(\"password_file %s\\n\" % (pw_file))\n\ndef write_pwfile(filename):\n    with open(filename, 'w') as f:\n        # Username \"test client\", password test\n        f.write('test client:$6$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==\\n')\n\ndef do_test(option):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    pw_file = os.path.basename(__file__).replace('.py', '.pwfile')\n    write_config(conf_file, pw_file, port, option)\n    write_pwfile(pw_file)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-success-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\n    try:\n        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=f\"{ssl_dir}/test-root-ca.crt\")\n        context.minimum_version = ssl.TLSVersion.TLSv1_2\n        context.load_cert_chain(certfile=f\"{ssl_dir}/client.crt\", keyfile=f\"{ssl_dir}/client.key\")\n        ssock = context.wrap_socket(sock, server_hostname=\"localhost\")\n        ssock.settimeout(20)\n        ssock.connect((\"localhost\", port))\n        mosq_test.do_send_receive(ssock, connect_packet, connack_packet, \"connack\")\n\n        broker.send_signal(signal.SIGHUP)\n        time.sleep(1)\n\n        # This will fail if we've been disconnected\n        mosq_test.do_ping(ssock)\n        rc = 0\n\n        ssock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        os.remove(pw_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(\"use_identity_as_username\")\ndo_test(\"use_subject_as_username\")\nexit(0)\n\n"
  },
  {
    "path": "test/broker/08-tls-psk-bridge.psk",
    "content": "psk-test:deadbeef\n"
  },
  {
    "path": "test/broker/08-tls-psk-bridge.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef write_config1(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(f\"psk_file {str(source_dir/'08-tls-psk-bridge.psk')}\\n\")\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"psk_hint hint\\n\")\n\ndef write_config2(filename, port2, port3):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port3))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"connection bridge-psk\\n\")\n        f.write(\"address localhost:%d\\n\" % (port2))\n        f.write(\"topic psk/test out\\n\")\n        f.write(\"bridge_identity psk-test\\n\")\n        f.write(\"bridge_psk deadbeef\\n\")\n\n(port1, port2, port3) = mosq_test.get_port(3)\nconf_file1 = \"08-tls-psk-bridge.conf\"\nconf_file2 = \"08-tls-psk-bridge.conf2\"\nwrite_config1(conf_file1, port1, port2)\nwrite_config2(conf_file2, port2, port3)\n\nenv = mosq_test.env_add_ld_library_path()\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"no-psk-test-client\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 1\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"psk/test\", 0)\nsuback_packet = mosq_test.gen_suback(mid, 0)\n\npublish_packet = mosq_test.gen_publish(topic=\"psk/test\", payload=\"message\", qos=0)\n\nbridge_cmd = [mosq_test.get_build_root() + '/src/mosquitto', '-c', '08-tls-psk-bridge.conf2']\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1)\nbridge = mosq_test.start_broker(filename=os.path.basename(__file__)+'_bridge', cmd=bridge_cmd, port=port3)\n\npub = None\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=30, port=port1)\n\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    pub = subprocess.run(['./c/08-tls-psk-bridge.test', str(port3)], env=env, capture_output=True, encoding='utf-8')\n    if pub.returncode != 0:\n        print(\"d\")\n        print(pub.returncode)\n        raise ValueError\n\n    mosq_test.expect_packet(sock, \"publish\", publish_packet)\n    rc = pub.returncode\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file1)\n    os.remove(conf_file2)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    bridge.terminate()\n    if mosq_test.wait_for_subprocess(bridge):\n        print(\"bridge not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n        (stdo, stde) = bridge.communicate()\n        print(stde.decode('utf-8'))\n        if pub:\n            print(pub.stdout)\n            print(pub.stderr)\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/08-tls-psk-pub.psk",
    "content": "psk-id:deadbeef\n"
  },
  {
    "path": "test/broker/08-tls-psk-pub.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"psk_file {str(source_dir/'08-tls-psk-pub.psk')}\\n\")\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"psk_hint hint\\n\")\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"log_type all\\n\")\n\n(port1, port2) = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port1, port2)\n\nenv = mosq_test.env_add_ld_library_path()\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"no-psk-test-client\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 1\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"psk/test\", 0)\nsuback_packet = mosq_test.gen_suback(mid, 0)\n\npublish_packet = mosq_test.gen_publish(topic=\"psk/test\", payload=\"message\", qos=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port2)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port2)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    pub = subprocess.Popen(['./c/08-tls-psk-pub.test', str(port1)], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    pub_terminate_rc = 0\n    if mosq_test.wait_for_subprocess(pub):\n        print(\"pub not terminated\")\n        pub_terminate_rc = 1\n    if pub.returncode != 0:\n        raise ValueError\n    (stdo, stde) = pub.communicate()\n\n    mosq_test.expect_packet(sock, \"publish\", publish_packet)\n    rc = pub_terminate_rc\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/09-acl-access-variants.py",
    "content": "#!/usr/bin/env python3\n\n# Check access\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, per_listener):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener))\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"acl_file %s\\n\" % (filename.replace('.conf', '.acl')))\n\ndef write_acl(filename, global_en, user_en, pattern_en):\n    with open(filename, 'w') as f:\n        if global_en:\n            f.write('topic readwrite topic/global/#\\n')\n            f.write('topic deny      topic/global/except\\n')\n        if user_en:\n            f.write('user username\\n')\n            f.write('topic readwrite topic/username/#\\n')\n            f.write('topic deny      topic/username/except\\n')\n        if pattern_en:\n            f.write('pattern readwrite pattern/%u/#\\n')\n            f.write('pattern deny      pattern/%u/except\\n')\n\n\n\ndef single_test(port, per_listener, username, topic, expect_deny):\n    connect_packet = mosq_test.gen_connect(\"acl-check\", username=username)\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid=mid, topic=topic, qos=1)\n    suback_packet = mosq_test.gen_suback(mid=mid, qos=1)\n\n    mid = 2\n    publish1s_packet = mosq_test.gen_publish(topic=topic, mid=mid, qos=1, payload=\"message\")\n    puback1s_packet = mosq_test.gen_puback(mid)\n\n    mid=1\n    publish1r_packet = mosq_test.gen_publish(topic=topic, mid=mid, qos=1, payload=\"message\")\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    sock.send(publish1s_packet)\n    if expect_deny:\n        mosq_test.expect_packet(sock, \"puback\", puback1s_packet)\n        mosq_test.do_ping(sock)\n    else:\n        mosq_test.receive_unordered(sock, puback1s_packet, publish1r_packet, \"puback / publish1r\")\n    sock.close()\n\n\ndef acl_test(port, per_listener, global_en, user_en, pattern_en):\n    acl_file = os.path.basename(__file__).replace('.py', '.acl')\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    write_acl(acl_file, global_en=global_en, user_en=user_en, pattern_en=pattern_en)\n    write_config(conf_file, port, per_listener)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = 0\n    try:\n        if global_en:\n            single_test(port, per_listener, username=None,       topic=\"topic/global\", expect_deny=False)\n            single_test(port, per_listener, username=\"username\", topic=\"topic/global\", expect_deny=True)\n            single_test(port, per_listener, username=None,       topic=\"topic/global/except\", expect_deny=True)\n        if user_en:\n            single_test(port, per_listener, username=None,       topic=\"topic/username\", expect_deny=True)\n            single_test(port, per_listener, username=\"username\", topic=\"topic/username\", expect_deny=False)\n            single_test(port, per_listener, username=\"username\", topic=\"topic/username/except\", expect_deny=True)\n        if pattern_en:\n            single_test(port, per_listener, username=None,       topic=\"pattern/username\", expect_deny=True)\n            single_test(port, per_listener, username=\"username\", topic=\"pattern/username\", expect_deny=False)\n            single_test(port, per_listener, username=\"username\", topic=\"pattern/username/except\", expect_deny=True)\n    except mosq_test.TestError:\n        rc = 1\n    finally:\n        os.remove(conf_file)\n        os.remove(acl_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndef do_test(port, per_listener):\n    acl_test(port, per_listener, global_en=False, user_en=False, pattern_en=True)\n    acl_test(port, per_listener, global_en=False, user_en=True, pattern_en=False)\n    acl_test(port, per_listener, global_en=True, user_en=False, pattern_en=False)\n    acl_test(port, per_listener, global_en=False, user_en=True, pattern_en=True)\n    acl_test(port, per_listener, global_en=True, user_en=False, pattern_en=True)\n    acl_test(port, per_listener, global_en=True, user_en=True, pattern_en=True)\n\nport = mosq_test.get_port()\n\ndo_test(port, \"true\")\ndo_test(port, \"false\")\n"
  },
  {
    "path": "test/broker/09-acl-change.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether messages deliver or not after some access is revoked.\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config(filename, port, per_listener):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener))\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"acl_file %s\\n\" % (filename.replace('.conf', '.acl')))\n\ndef write_acl(filename, en):\n    with open(filename, 'w') as f:\n        f.write('user username\\n')\n        f.write('topic readwrite topic/one\\n')\n        if en:\n            f.write('topic readwrite topic/two\\n')\n\nusername = \"username\"\n\nconnect1_packet = mosq_test.gen_connect(\"acl-check\", username=username, clean_session=False)\nconnack1a_packet = mosq_test.gen_connack(rc=0)\nconnack1b_packet = mosq_test.gen_connack(rc=0, flags=1)\n\nmid = 1\nsubscribe1_packet = mosq_test.gen_subscribe(mid=mid, topic=\"topic/one\", qos=1)\nsuback1_packet = mosq_test.gen_suback(mid=mid, qos=1)\n\nmid = 2\nsubscribe2_packet = mosq_test.gen_subscribe(mid=mid, topic=\"topic/two\", qos=1)\nsuback2_packet = mosq_test.gen_suback(mid=mid, qos=1)\n\ndisconnect_packet = mosq_test.gen_disconnect()\n\nconnect2_packet = mosq_test.gen_connect(\"helper\", username=username)\nconnack2_packet = mosq_test.gen_connack(rc=0)\n\nmid = 1\npublish1s_packet = mosq_test.gen_publish(topic=\"topic/one\", mid=mid, qos=1, payload=\"message1\")\npuback1s_packet = mosq_test.gen_puback(mid)\n\nmid = 2\npublish2s_packet = mosq_test.gen_publish(topic=\"topic/two\", mid=mid, qos=1, payload=\"message2\")\npuback2s_packet = mosq_test.gen_puback(mid)\n\nmid = 1\npublish1r_packet = mosq_test.gen_publish(topic=\"topic/one\", mid=mid, qos=1, payload=\"message1\")\npuback1r_packet = mosq_test.gen_puback(mid)\n\nmid = 2\npublish3s_packet = mosq_test.gen_publish(topic=\"topic/one\", mid=mid, qos=1, payload=\"message3\")\npuback3s_packet = mosq_test.gen_puback(mid)\n\nmid = 3\npublish3r_packet = mosq_test.gen_publish(topic=\"topic/one\", mid=mid, qos=1, payload=\"message3\")\npuback3r_packet = mosq_test.gen_puback(mid)\n\nmid = 3\npublish4s_packet = mosq_test.gen_publish(topic=\"topic/two\", mid=mid, qos=1, payload=\"message4\")\npuback4s_packet = mosq_test.gen_puback(mid)\n\nrc = 1\n\nport = mosq_test.get_port()\n\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port, \"false\")\n\nacl_file = os.path.basename(__file__).replace('.py', '.acl')\nwrite_acl(acl_file, True)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    # Connect, subscribe, then disconnect\n    sock = mosq_test.do_client_connect(connect1_packet, connack1a_packet, port=port)\n    mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback1\")\n    mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback2\")\n    sock.send(disconnect_packet)\n    sock.close()\n\n    # Helper publish to topic/one and topic/two, will be queued for other client\n    sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port)\n    mosq_test.do_send_receive(sock, publish1s_packet, puback1s_packet, \"puback1\")\n    mosq_test.do_send_receive(sock, publish2s_packet, puback2s_packet, \"puback2\")\n    sock.close()\n\n    # Reload ACLs with topic/two now disabled\n    write_acl(acl_file, False)\n    broker.send_signal(signal.SIGHUP)\n\n    sock = mosq_test.do_client_connect(connect1_packet, connack1b_packet, port=port)\n    sock.settimeout(10)\n    mosq_test.expect_packet(sock, \"publish1r\", publish1r_packet)\n    # We don't expect messages to topic/two any more, so we don't expect the queued one\n    sock.send(publish3s_packet)\n    mosq_test.receive_unordered(sock, puback3s_packet, publish3r_packet, \"puback3/publish3r\")\n\n    # Send this, don't expect it to succeed\n    mosq_test.do_send_receive(sock, publish4s_packet, puback4s_packet, \"puback4\")\n\n    # Check for non delivery with a ping\n    mosq_test.do_ping(sock)\n\n    sock.close()\n    rc = 0\n\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    os.remove(acl_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n        exit(rc)\n\nport = mosq_test.get_port()\n\n"
  },
  {
    "path": "test/broker/09-acl-empty-file.py",
    "content": "#!/usr/bin/env python3\n\n# Test for CVE-2018-xxxxx\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config(filename, port, per_listener):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener))\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"acl_file %s\\n\" % (filename.replace('.conf', '.acl')))\n\ndef write_acl(filename):\n    with open(filename, 'w') as f:\n        f.write('#comment\\n')\n        f.write('\\n')\n\n\ndef do_test(port, per_listener):\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, per_listener)\n\n    acl_file = os.path.basename(__file__).replace('.py', '.acl')\n    write_acl(acl_file)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"acl-check\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"test/topic\", qos=0, payload=\"message\")\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"test/topic\", 0)\n    suback_packet = mosq_test.gen_suback(mid, 0)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        sock.send(publish_packet)\n\n        # If we receive the message, this will fail.\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        os.remove(acl_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\nport = mosq_test.get_port()\ndo_test(port, \"false\")\ndo_test(port, \"true\")\n"
  },
  {
    "path": "test/broker/09-auth-bad-method.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether sending an Authentication Method produces the correct response\n# when no auth methods are defined.\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"basic\")\n    connect_packet = mosq_test.gen_connect(\"connect-test\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.BAD_AUTHENTICATION_METHOD, proto_ver=5, properties=None)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/09-extended-auth-change-username.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether an extended auth plugin can change the username of a client.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, acl_file, port, per_listener):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener))\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"acl_file %s\\n\" % (acl_file))\n        f.write(\"auth_plugin c/auth_plugin_extended_single.so\\n\")\n\ndef write_acl(filename):\n    with open(filename, 'w') as f:\n        f.write('user new_username\\n')\n        f.write('topic readwrite topic/one\\n')\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nacl_file = os.path.basename(__file__).replace('.py', '.acl')\n\ndef do_test(per_listener):\n    write_config(conf_file, acl_file, port, per_listener)\n    write_acl(acl_file)\n    rc = 1\n\n    # Connect without a username - this means no access\n    connect1_packet = mosq_test.gen_connect(\"client-params-test1\", proto_ver=5)\n    connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"topic/one\", 1, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\n    mid = 2\n    publish1_packet = mosq_test.gen_publish(\"topic/one\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback1_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.NOT_AUTHORIZED)\n\n    # Connect without a username, but have the plugin change it\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"change\")\n    connect2_packet = mosq_test.gen_connect(\"client-params-test2\", proto_ver=5, properties=props)\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"change\")\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n    mid = 2\n    publish2s_packet = mosq_test.gen_publish(\"topic/one\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback2s_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    mid = 1\n    publish2r_packet = mosq_test.gen_publish(\"topic/one\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback1\")\n        mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, \"puback1\")\n        mosq_test.do_ping(sock)\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback2\")\n        sock.send(publish2s_packet)\n        mosq_test.receive_unordered(sock, puback2s_packet, publish2r_packet, \"puback2/publish2\")\n        mosq_test.do_ping(sock)\n        rc = 0\n\n        sock.close()\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        os.remove(acl_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(\"true\")\ndo_test(\"false\")\nexit(0)\n"
  },
  {
    "path": "test/broker/09-extended-auth-multistep-reauth.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"auth_plugin c/auth_plugin_extended_multiple.so\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\n\n# First auth\n# ==========\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"step1\")\nconnect1_packet = mosq_test.gen_connect(\"client-params-test\", proto_ver=5, properties=props)\n\n# Server to client\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"1pets\")\nauth1_1_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n\n# Client to server\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"supercalifragilisticexpialidocious\")\nauth1_2_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nconnack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n# Second auth\n# ===========\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"step1\")\nreauth2_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.REAUTHENTICATE, properties=props)\n\n# Server to client\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"1pets\")\nauth2_1_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n\n# Client to server\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"supercalifragilisticexpialidocious\")\nauth2_2_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nauth2_3_packet = mosq_test.gen_auth(reason_code=0, properties=props)\n\n\n# Third auth - bad due to different method\n# ========================================\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"badmethod\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"step1\")\nreauth3_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.REAUTHENTICATE, properties=props)\n\n# Server to client\ndisconnect3_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.PROTOCOL_ERROR, proto_ver=5)\n\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect1_packet, auth1_1_packet, timeout=20, port=port, connack_error=\"auth1\")\n    mosq_test.do_send_receive(sock, auth1_2_packet, connack1_packet, \"connack1\")\n    mosq_test.do_ping(sock, \"pingresp1\")\n\n    mosq_test.do_send_receive(sock, reauth2_packet, auth2_1_packet, \"auth2_1\")\n    mosq_test.do_send_receive(sock, auth2_2_packet, auth2_3_packet, \"auth2_3\")\n    mosq_test.do_ping(sock, \"pingresp2\")\n\n    mosq_test.do_send_receive(sock, reauth3_packet, disconnect3_packet, \"disconnect3\")\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/09-extended-auth-multistep.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_extended_multiple.so\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\n\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"step1\")\nconnect_packet = mosq_test.gen_connect(\"client-params-test\", proto_ver=5, properties=props)\n\n# Server to client\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"1pets\")\nauth1_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n\n# Client to server\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"supercalifragilisticexpialidocious\")\nauth2_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, auth1_packet, timeout=20, port=port, connack_error=\"auth1\")\n    mosq_test.do_send_receive(sock, auth2_packet, connack_packet)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/09-extended-auth-reauth.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"port %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_extended_reauth.so\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\n\n# First authentication succeeds\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"repeat\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"repeat\")\nconnect_packet = mosq_test.gen_connect(\"client-params-test\", proto_ver=5, properties=props)\n\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"repeat\")\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n# Reauthentication fails\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"repeat\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"repeat\")\nauth_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.REAUTHENTICATE, properties=props)\ndisconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    mosq_test.do_send_receive(sock, auth_packet, disconnect_packet)\n    sock.close()\n\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/09-extended-auth-single.py",
    "content": "#!/usr/bin/env python3\n\n# Multi tests for extended auth with a single step.\n# * Error in plugin\n# * No matching authentication method\n# * Matching authentication method, but auth rejected\n# * Matching authentication method, auth succeeds\n# * Matching authentication method, auth succeeds, new auth data sent back to client\n\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_extended_single.so\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\n\n# Single, error in plugin\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"error\")\nconnect1_packet = mosq_test.gen_connect(\"client-params-test1\", proto_ver=5, properties=props)\n\n# Single, no matching authentication method\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"non-matching\")\nconnect2_packet = mosq_test.gen_connect(\"client-params-test2\", proto_ver=5, properties=props)\nconnack2_packet = mosq_test.gen_connack(rc=mqtt5_rc.BAD_AUTHENTICATION_METHOD, proto_ver=5, properties=None)\n\n# Single step, matching method, failure\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"single\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"baddata\")\nconnect3_packet = mosq_test.gen_connect(\"client-params-test3\", proto_ver=5, properties=props)\nconnack3_packet = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, properties=None)\n\n# Single step, matching method, success\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"single\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"data\")\nconnect4_packet = mosq_test.gen_connect(\"client-params-test5\", proto_ver=5, properties=props)\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"single\")\nconnack4_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n# Single step, matching method, success, auth data back to client\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"somedata\")\nconnect5_packet = mosq_test.gen_connect(\"client-params-test6\", proto_ver=5, properties=props)\nprops = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"atademos\")\nconnack5_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n\ntry:\n    sock = None\n    try:\n        sock = mosq_test.do_client_connect(connect1_packet, b\"\", timeout=20, port=port)\n        sock.close()\n        rc = 2\n    except BrokenPipeError:\n        pass\n\n    sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port)\n    sock.close()\n\n    sock = mosq_test.do_client_connect(connect3_packet, connack3_packet, timeout=20, port=port)\n    sock.close()\n\n    sock = mosq_test.do_client_connect(connect4_packet, connack4_packet, timeout=20, port=port)\n    sock.close()\n\n    sock = mosq_test.do_client_connect(connect5_packet, connack5_packet, timeout=20, port=port)\n    sock.close()\n\n    rc = 0\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nsys.exit(rc)\n"
  },
  {
    "path": "test/broker/09-extended-auth-single2.py",
    "content": "#!/usr/bin/env python3\n\n# Multi tests for extended auth with a single step - multiple plugins at once.\n# * Error in plugin\n# * No matching authentication method\n# * Matching authentication method, but auth rejected\n# * Matching authentication method, auth succeeds\n# * Matching authentication method, auth succeeds, new auth data sent back to client\n\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_extended_single.so\\n\")\n        f.write(\"auth_plugin c/auth_plugin_extended_single2.so\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n\ndef do_test(suffix):\n    write_config(conf_file, port)\n    rc = 1\n    # Single, error in plugin\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"error%s\" % (suffix))\n    connect1_packet = mosq_test.gen_connect(\"client-params-test1\", proto_ver=5, properties=props)\n\n    # Single, no matching authentication method\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"non-matching%s\" % (suffix))\n    connect2_packet = mosq_test.gen_connect(\"client-params-test2\", proto_ver=5, properties=props)\n    connack2_packet = mosq_test.gen_connack(rc=mqtt5_rc.BAD_AUTHENTICATION_METHOD, proto_ver=5, properties=None)\n\n    # Single step, matching method, failure\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"single%s\" % (suffix))\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"baddata\")\n    connect3_packet = mosq_test.gen_connect(\"client-params-test3\", proto_ver=5, properties=props)\n    connack3_packet = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, properties=None)\n\n    # Single step, matching method, success\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"single%s\" % (suffix))\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"data\")\n    connect4_packet = mosq_test.gen_connect(\"client-params-test5\", proto_ver=5, properties=props)\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"single%s\" % (suffix))\n    connack4_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n    # Single step, matching method, success, auth data back to client\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror%s\" % (suffix))\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"somedata\")\n    connect5_packet = mosq_test.gen_connect(\"client-params-test6\", proto_ver=5, properties=props)\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"mirror%s\" % (suffix))\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"atademos\")\n    connack5_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n\n    broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect1_packet, b\"\", timeout=20, port=port)\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port)\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect3_packet, connack3_packet, timeout=20, port=port)\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect4_packet, connack4_packet, timeout=20, port=port)\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect5_packet, connack5_packet, timeout=20, port=port)\n        sock.close()\n\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(\"\")\ndo_test(\"2\")\nexit(0)\n\n"
  },
  {
    "path": "test/broker/09-plugin-acl-access-variants.py",
    "content": "#!/usr/bin/env python3\n\n# Check access\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, per_listener):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener))\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/acl-file/mosquitto_acl_file.so\\n\")\n        f.write(\"plugin_opt_acl_file %s\\n\" % (filename.replace('.conf', '.acl')))\n\ndef write_acl(filename, global_en, user_en, pattern_en):\n    with open(filename, 'w') as f:\n        if global_en:\n            f.write('topic readwrite topic/global/#\\n')\n            f.write('topic deny      topic/global/except\\n')\n        if user_en:\n            f.write('user username\\n')\n            f.write('topic readwrite topic/username/#\\n')\n            f.write('topic deny      topic/username/except\\n')\n        if pattern_en:\n            f.write('pattern readwrite pattern/%u/#\\n')\n            f.write('pattern deny      pattern/%u/except\\n')\n\n\n\ndef single_test(port, per_listener, username, topic, expect_deny):\n    connect_packet = mosq_test.gen_connect(\"acl-check\", username=username)\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid=mid, topic=topic, qos=1)\n    suback_packet = mosq_test.gen_suback(mid=mid, qos=1)\n\n    mid = 2\n    publish1s_packet = mosq_test.gen_publish(topic=topic, mid=mid, qos=1, payload=\"message\")\n    puback1s_packet = mosq_test.gen_puback(mid)\n\n    mid=1\n    publish1r_packet = mosq_test.gen_publish(topic=topic, mid=mid, qos=1, payload=\"message\")\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    sock.send(publish1s_packet)\n    if expect_deny:\n        mosq_test.expect_packet(sock, \"puback\", puback1s_packet)\n        mosq_test.do_ping(sock)\n    else:\n        mosq_test.receive_unordered(sock, puback1s_packet, publish1r_packet, \"puback / publish1r\")\n    sock.close()\n\n\ndef acl_test(port, per_listener, global_en, user_en, pattern_en):\n    acl_file = os.path.basename(__file__).replace('.py', '.acl')\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    write_acl(acl_file, global_en=global_en, user_en=user_en, pattern_en=pattern_en)\n    write_config(conf_file, port, per_listener)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = 0\n    try:\n        if global_en:\n            single_test(port, per_listener, username=None,       topic=\"topic/global\", expect_deny=False)\n            single_test(port, per_listener, username=\"username\", topic=\"topic/global\", expect_deny=True)\n            single_test(port, per_listener, username=None,       topic=\"topic/global/except\", expect_deny=True)\n        if user_en:\n            single_test(port, per_listener, username=None,       topic=\"topic/username\", expect_deny=True)\n            single_test(port, per_listener, username=\"username\", topic=\"topic/username\", expect_deny=False)\n            single_test(port, per_listener, username=\"username\", topic=\"topic/username/except\", expect_deny=True)\n        if pattern_en:\n            single_test(port, per_listener, username=None,       topic=\"pattern/username\", expect_deny=True)\n            single_test(port, per_listener, username=\"username\", topic=\"pattern/username\", expect_deny=False)\n            single_test(port, per_listener, username=\"username\", topic=\"pattern/username/except\", expect_deny=True)\n    except mosq_test.TestError:\n        rc = 1\n    finally:\n        os.remove(conf_file)\n        os.remove(acl_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndef do_test(port, per_listener):\n    acl_test(port, per_listener, global_en=False, user_en=False, pattern_en=True)\n    acl_test(port, per_listener, global_en=False, user_en=True, pattern_en=False)\n    acl_test(port, per_listener, global_en=True, user_en=False, pattern_en=False)\n    acl_test(port, per_listener, global_en=False, user_en=True, pattern_en=True)\n    acl_test(port, per_listener, global_en=True, user_en=False, pattern_en=True)\n    acl_test(port, per_listener, global_en=True, user_en=True, pattern_en=True)\n\nport = mosq_test.get_port()\n\ndo_test(port, \"true\")\ndo_test(port, \"false\")\n"
  },
  {
    "path": "test/broker/09-plugin-acl-change.py",
    "content": "#!/usr/bin/env python3\n\n# A clean start=False client connects, and publishes to a topic it has access\n# to with QoS 2 - but does not send a PUBREL. It closes the connection. The\n# access to the topic is revoked, the client reconnects and it attempts to\n# complete the flow. Is the publish allowed? It should not be.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, plugin_ver):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_acl_change.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\ndef do_test(plugin_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, plugin_ver)\n\n    rc = 1\n    connect1_packet = mosq_test.gen_connect(\"acl-change-test\", clean_session=False)\n    connack1_packet = mosq_test.gen_connack(rc=0)\n\n    connect2_packet = mosq_test.gen_connect(\"acl-change-test\", clean_session=False)\n    connack2_packet = mosq_test.gen_connack(rc=0,flags=1)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"#\", 0)\n    suback_packet = mosq_test.gen_suback(mid, 0)\n\n    mid = 2\n    publish1_packet = mosq_test.gen_publish(\"publish/topic\", qos=2, mid=mid, payload=\"message\")\n    pubrec1_packet = mosq_test.gen_pubrec(mid)\n    pubrel1_packet = mosq_test.gen_pubrel(mid)\n    pubcomp1_packet = mosq_test.gen_pubcomp(mid)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback 1\")\n        mosq_test.do_send_receive(sock, publish1_packet, pubrec1_packet, \"pubrec\")\n        sock.close()\n\n        # ACL has changed\n        sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback 2\")\n        mosq_test.do_send_receive(sock, pubrel1_packet, pubcomp1_packet, \"pubcomp\")\n        mosq_test.do_ping(sock)\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as err:\n        print(err)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(4)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-acl-pub-prop.py",
    "content": "#!/usr/bin/env python3\n\n# Bug specific test - if a QoS2 publish is denied, then we publish again with\n# the same mid to a topic that is allowed, does it work properly?\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/auth_plugin_v5.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\ndef do_test():\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"cnwTICONIURW\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"custom-name\", \"custom-value\")\n    publish_allowed_packet = mosq_test.gen_publish(\"bad-topic\", qos=1, mid=mid, payload=\"message\", properties=props, proto_ver=5)\n    puback_allowed_packet = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS, proto_ver=5)\n\n    mid = 2\n    publish_denied_packet = mosq_test.gen_publish(\"bad-topic\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback_denied_packet = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n\n        mosq_test.do_send_receive(sock, publish_allowed_packet, puback_allowed_packet, \"puback allowed\")\n        mosq_test.do_send_receive(sock, publish_denied_packet, puback_denied_packet, \"puback denied\")\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test()\n"
  },
  {
    "path": "test/broker/09-plugin-auth-acl-pub.py",
    "content": "#!/usr/bin/env python3\n\n# Bug specific test - if a QoS2 publish is denied, then we publish again with\n# the same mid to a topic that is allowed, does it work properly?\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, plugin_ver):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v%d.so\\n\" % (plugin_ver))\n        f.write(\"allow_anonymous false\\n\")\n\ndef do_test(plugin_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, plugin_ver)\n\n    rc = 1\n    connect1_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"readwrite\", clean_session=False)\n    connack1_packet = mosq_test.gen_connack(rc=0)\n\n    connect2_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"readwrite\", clean_session=False)\n    connack2_packet = mosq_test.gen_connack(rc=0,flags=1)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"readonly\", 2)\n    suback_packet = mosq_test.gen_suback(mid, 2)\n\n    mid = 2\n    publish1_packet = mosq_test.gen_publish(\"readonly\", qos=2, mid=mid, payload=\"message\")\n    pubrec1_packet = mosq_test.gen_pubrec(mid)\n    pubrel1_packet = mosq_test.gen_pubrel(mid)\n    pubcomp1_packet = mosq_test.gen_pubcomp(mid)\n\n    mid = 2\n    publish2_packet = mosq_test.gen_publish(\"writeable\", qos=1, mid=mid, payload=\"message\")\n    puback2_packet = mosq_test.gen_puback(mid)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, publish1_packet, pubrec1_packet, \"pubrec1\")\n        sock.close()\n\n        sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, publish2_packet, puback2_packet, \"puback2\")\n\n        mosq_test.do_ping(sock)\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(2)\ndo_test(3)\ndo_test(4)\ndo_test(5)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-acl-sub-denied.py",
    "content": "#!/usr/bin/env python3\n\n# Test topic subscription. All SUBSCRIBE requests are denied. Check this\n# produces the correct response, and check the client isn't disconnected (ref:\n# issue #1016).\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_acl_sub_denied.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"sub-denied-test\", username=\"denied\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 53\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"qos0/test\", 0)\nsuback_packet = mosq_test.gen_suback(mid, 128)\n\nmid_pub = 54\npublish_packet = mosq_test.gen_publish(\"topic\", qos=1, payload=\"test\", mid=mid_pub)\npuback_packet = mosq_test.gen_puback(mid_pub)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-acl-sub.py",
    "content": "#!/usr/bin/env python3\n\n# Test topic subscription. All topic are allowed but not using wildcard in subscribe.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, plugin_ver):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v%d.so\\n\" % (plugin_ver))\n        f.write(\"allow_anonymous false\\n\")\n\ndef do_test(plugin_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, plugin_ver)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"readonly\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 53\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"qos0/test\", 0)\n    suback_packet = mosq_test.gen_suback(mid, 0)\n\n    mid_fail = 54\n    subscribe_packet_fail = mosq_test.gen_subscribe(mid_fail, \"#\", 0)\n    if plugin_ver == 2:\n        suback_packet_fail = mosq_test.gen_suback(mid_fail, 0)\n    else:\n        suback_packet_fail = mosq_test.gen_suback(mid_fail, 0x80)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        mosq_test.do_send_receive(sock, subscribe_packet_fail, suback_packet_fail, \"suback\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(2)\ndo_test(3)\ndo_test(4)\ndo_test(5)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-context-params.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether message parameters are passed to the plugin acl check function.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_context_params.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"client-params-test\", keepalive=42, username=\"client-username\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"param/topic\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\nmid = 3\npublish_packet = mosq_test.gen_publish(topic=\"param/topic\", qos=1, payload=\"payload contents\", retain=1, mid=mid)\npuback_packet = mosq_test.gen_puback(mid)\n\nmid = 1\npublish_packet_recv = mosq_test.gen_publish(topic=\"param/topic\", qos=1, payload=\"payload contents\", retain=0, mid=mid)\n\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/09-plugin-auth-defer-unpwd-fail.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection fail when using a auth_plugin that defer authentication.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, plugin_ver):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v%d.so\\n\" % (plugin_ver))\n        f.write(\"allow_anonymous false\\n\")\n\ndef do_test(plugin_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, plugin_ver)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username@v2\", password=\"doesNotMatter\")\n    connack_packet = mosq_test.gen_connack(rc=5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(4)\ndo_test(5)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-defer-unpwd-success.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a two auth_plugin (first will defer, second will accept).\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, plugin_ver):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v%d.so\\n\" % (plugin_ver))\n        f.write(\"auth_plugin c/auth_plugin_v2.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\ndef do_test(plugin_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, plugin_ver)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username@v2\", password=\"doesNotMatter\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(4)\ndo_test(5)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-msg-params.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether message parameters are passed to the plugin acl check function.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_msg_params.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"msg-param-test\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"param/topic\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\nmid = 3\npublish_packet = mosq_test.gen_publish(topic=\"param/topic\", qos=1, payload=\"payload contents\", retain=1, mid=mid)\npuback_packet = mosq_test.gen_puback(mid)\n\nmid = 1\npublish_packet_recv = mosq_test.gen_publish(topic=\"param/topic\", qos=1, payload=\"payload contents\", retain=0, mid=mid)\n\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    sock.send(publish_packet)\n    mosq_test.receive_unordered(sock, puback_packet, publish_packet_recv, \"puback/publish_receive\")\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/09-plugin-auth-unpwd-fail.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a simple auth_plugin.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, plugin_ver):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v%d.so\\n\" % (plugin_ver))\n        f.write(\"allow_anonymous false\\n\")\n\ndef do_test(plugin_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, plugin_ver)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"wrong\")\n    connack_packet = mosq_test.gen_connack(rc=5)\n\n    connect_packet_binary_pw1 = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"binary-password\", password=\"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x08\")\n    connect_packet_binary_pw2 = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"binary-password\", password=\"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\")\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        sock.close()\n\n        if plugin_ver == 5:\n            sock = mosq_test.do_client_connect(connect_packet_binary_pw1, connack_packet, port=port)\n            sock.close()\n            sock = mosq_test.do_client_connect(connect_packet_binary_pw2, connack_packet, port=port)\n            sock.close()\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(4)\ndo_test(5)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-unpwd-success.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a simple auth_plugin.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, plugin_ver):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v%d.so\\n\" % (plugin_ver))\n        f.write(\"allow_anonymous false\\n\")\n\ndef do_test(plugin_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, plugin_ver)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"cnwTICONIURW\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    connect_packet_binary_pw = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"binary-password\", password=\"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\")\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        sock.close()\n        if plugin_ver == 5:\n            sock = mosq_test.do_client_connect(connect_packet_binary_pw, connack_packet, port=port)\n            sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(4)\ndo_test(5)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-v2-unpwd-fail.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a simple auth_plugin.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v2.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"wrong\")\nconnack_packet = mosq_test.gen_connack(rc=5)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-v2-unpwd-success.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a simple auth_plugin.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v2.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"cnwTICONIURW\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    rc = 0\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-v3-unpwd-fail.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a simple auth_plugin.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v3.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"wrong\")\nconnack_packet = mosq_test.gen_connack(rc=5)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-v3-unpwd-success.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a simple auth_plugin.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v3.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"cnwTICONIURW\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    rc = 0\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-v4-unpwd-fail.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a simple auth_plugin.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v4.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"wrong\")\nconnack_packet = mosq_test.gen_connack(rc=5)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-v4-unpwd-success.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a simple auth_plugin.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v4.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"cnwTICONIURW\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    rc = 0\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-v5-unpwd-fail.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a simple auth_plugin.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v5.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"wrong\")\nconnack_packet = mosq_test.gen_connack(rc=5)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/09-plugin-auth-v5-unpwd-success.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a connection is successful with correct username and password\n# when using a simple auth_plugin.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_v5.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"connect-uname-pwd-test\", username=\"test-username\", password=\"cnwTICONIURW\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    rc = 0\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/09-plugin-bad.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\n# Check whether incomplete plugins are handled ok\n\ndef write_config(filename, port, plugver, num):\n    with open(filename, 'w') as f:\n        f.write(f\"listener {port}\\n\")\n        f.write(f\"auth_plugin c/bad_v{plugver}_{num}.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\ndef do_test(plugver, num):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, plugver, num)\n\n    try:\n        rc = 1\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port, check_port=False)\n        broker.wait(5)\n        broker.terminate()\n        if broker.returncode == 13:\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(\"none\", 1)\n\nfor i in range(1,8):\n    do_test(2, i)\n\nfor i in range(1,8):\n    do_test(3, i)\n\nfor i in range(1,5):\n    do_test(4, i)\n\ndo_test(5, 1)\n"
  },
  {
    "path": "test/broker/09-plugin-change-id.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, plugin_ver):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_id_change.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\ndef do_test(plugin_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, plugin_ver)\n\n    rc = 1\n    connect1_packet = mosq_test.gen_connect(\"already-exists\")\n    connack1_packet = mosq_test.gen_connack(rc=0)\n\n    connect2_packet = mosq_test.gen_connect(\"id-change-test\")\n    connack2_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"#\", 0)\n    # Only subs by client id == allowed is allowed\n    suback_packet_denied = mosq_test.gen_suback(mid, 128)\n    suback_packet_ok = mosq_test.gen_suback(mid, 0)\n\n    mid = 2\n    publish1_packet = mosq_test.gen_publish(\"publish/topic\", qos=2, mid=mid, payload=\"message\")\n    pubrec1_packet = mosq_test.gen_pubrec(mid)\n    pubrel1_packet = mosq_test.gen_pubrel(mid)\n    pubcomp1_packet = mosq_test.gen_pubcomp(mid)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port)\n        sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet_denied, \"suback denied\")\n        mosq_test.do_send_receive(sock2, subscribe_packet, suback_packet_ok, \"suback ok\")\n\n        mosq_test.do_ping(sock1)\n        mosq_test.do_ping(sock2)\n        sock1.close()\n        sock2.close()\n\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    except Exception as err:\n        print(err)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(4)\n"
  },
  {
    "path": "test/broker/09-plugin-delayed-auth.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether message parameters are passed to the plugin acl check function.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_delayed.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\ndef do_test(proto_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"delayed-auth-test\", username=\"delayed-username\", password=\"good\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    connect_packet2 = mosq_test.gen_connect(\"delayed-auth-test\", username=\"delayed-username\", password=\"bad\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        connack_packet2 = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=proto_ver, property_helper=False)\n    else:\n        connack_packet2 = mosq_test.gen_connack(rc=5, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        sock.close()\n        sock = mosq_test.do_client_connect(connect_packet2, connack_packet2, timeout=20, port=port)\n        sock.close()\n\n        # Connect, disconnect, reconnect - try to trigger #3388\n        print(broker.returncode)\n        for i in range(0, 10):\n            try:\n                sock = mosq_test.client_connect_only()\n            except ConnectionRefusedError:\n                time.sleep(0.5)\n        sock.send(connect_packet)\n        sock.close()\n        # Give the tick time to trigger\n        time.sleep(0.5)\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        sock.close()\n\n        rc = 0\n\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(4)\ndo_test(5)\n"
  },
  {
    "path": "test/broker/09-plugin-evt-client-offline.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/plugin_evt_client_offline.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef do_test():\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"plugin-evt-subscribe\", proto_ver=4, clean_session=False)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=4)\n\n    publish_packet = mosq_test.gen_publish(\"evt/client/offline\", qos=0, payload=\"plugin-evt-subscribe\")\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\n    try:\n        sub_sock = mosq_test.sub_helper(port, '#')\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n\n        mosq_test.expect_packet(sub_sock, \"publish\", publish_packet)\n        rc = 0\n\n        sub_sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test()\n"
  },
  {
    "path": "test/broker/09-plugin-evt-message-in.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/plugin_evt_message_in.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef do_test():\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"plugin-evt-message-in\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"fixed-topic\", 2, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    publish_packet1 = mosq_test.gen_publish(\"subpub/qos2/receive/maximum1\", qos=0, payload=\"message1\", proto_ver=5, properties=props)\n\n    props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\n    publish_packet2 = mosq_test.gen_publish(\"fixed-topic\", qos=0, payload=\"new-message\", proto_ver=5, properties=props)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        sock.send(publish_packet1)\n        mosq_test.expect_packet(sock, \"publish2\", publish_packet2)\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test()\n"
  },
  {
    "path": "test/broker/09-plugin-evt-message-out.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/plugin_evt_message_out.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef do_test():\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"plugin-evt-message-out\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"#\", 2, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5)\n\n    publish_packet_deny = mosq_test.gen_publish(\"deny\", qos=0, payload=\"message1\", proto_ver=5)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    publish_packet1 = mosq_test.gen_publish(\"out-topic\", qos=0, payload=\"message1\", proto_ver=5, properties=props)\n\n    props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\n    publish_packet2 = mosq_test.gen_publish(\"new-topic\", qos=0, payload=\"new-message\", proto_ver=5, properties=props)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        sock.send(publish_packet_deny)\n        mosq_test.do_ping(sock)\n\n        sock.send(publish_packet1)\n        mosq_test.expect_packet(sock, \"publish2\", publish_packet2)\n        mosq_test.do_ping(sock)\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test()\n"
  },
  {
    "path": "test/broker/09-plugin-evt-psk-key.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a plugin can subscribe to the tick event\n\nfrom mosq_test_helper import *\n\n\ndef write_config(filename, port, per_listener_settings=\"false\"):\n    with open(filename, \"w\") as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener_settings))\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/plugin_evt_psk_key.so\\n\")\n        f.write(\"psk_hint myhint\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef do_test(per_listener_settings):\n    rc = 1\n    proto_ver = 5\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace(\".py\", \".conf\")\n    write_config(conf_file, port, per_listener_settings)\n\n    broker = mosq_test.start_broker(\n        filename=os.path.basename(__file__),\n        use_conf=True,\n        port=port,\n    )\n\n    try:\n        pub_client_args = [\n            mosq_test.get_build_root() + \"/client/mosquitto_pub\",\n            \"-t\",\n            \"plugin/psk/test\",\n            \"-m\",\n            \"psk-test-pub-client\",\n            \"--psk\",\n            \"297A49\",\n            \"--psk-identity\",\n            \"pubidentity\",\n            \"-p\",\n            str(port),\n            \"-r\",\n        ]\n        pub_client = mosq_test.start_client(\n            filename=sys.argv[0].replace(\"/\", \"-\"), cmd=pub_client_args\n        )\n        pub_client.wait()\n\n        bad_client_args = [\n            mosq_test.get_build_root() + \"/client/mosquitto_pub\",\n            \"-t\",\n            \"plugin/psk/test\",\n            \"-m\",\n            \"psk-test-bad-client\",\n            \"--psk\",\n            \"159445\",\n            \"--psk-identity\",\n            \"badidentity\",\n            \"-p\",\n            str(port),\n            \"-r\",\n        ]\n        bad_client = mosq_test.start_client(\n            filename=sys.argv[0].replace(\"/\", \"-\"), cmd=bad_client_args\n        )\n        bad_client.wait()\n        if bad_client.returncode == 0:\n            raise ValueError(\"bad client should have failed\")\n\n        sub_client_args = [\n            mosq_test.get_build_root() + \"/client/mosquitto_sub\",\n            \"-t\",\n            \"plugin/psk/test\",\n            \"-C\",\n            \"1\",\n            \"-W\",\n            \"2\",\n            \"--psk\",\n            \"159445\",\n            \"--psk-identity\",\n            \"subidentity\",\n            \"-v\",\n            \"-N\",\n            \"-p\",\n            str(port),\n        ]\n        sub_client = mosq_test.start_client(\n            filename=sys.argv[0].replace(\"/\", \"-\"), cmd=sub_client_args\n        )\n        sub_client.wait()\n\n        if pub_client.returncode == 0 and sub_client.returncode == 0:\n            (stdo, _) = sub_client.communicate()\n            if stdo.decode(\"utf-8\") != \"plugin/psk/test psk-test-pub-client\":\n                raise ValueError(stdo.decode(\"utf-8\"))\n        else:\n            raise ValueError(\n                f\"pub_client returned {pub_client.returncode}, sub_client returned {sub_client.returncode}\"\n            )\n\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    except Exception as err:\n        print(err)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        broker.wait()\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode(\"utf-8\"))\n            exit(rc)\n\n\ndo_test(\"false\")\ndo_test(\"true\")\n"
  },
  {
    "path": "test/broker/09-plugin-evt-reload.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a plugin can subscribe to the reload event\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config(filename, port, per_listener_settings=\"false\"):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener_settings))\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/plugin_evt_reload.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\ndef do_test(per_listener_settings):\n    proto_ver = 5\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, per_listener_settings)\n\n    rc = 1\n    keepalive = 10\n    connect_packet = mosq_test.gen_connect(\"plugin-reload-test\", keepalive=keepalive, username=\"readwrite\", clean_session=False, proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    reload_packet = mosq_test.gen_publish(\"topic/reload\", qos=0, payload=\"test-message\", proto_ver=proto_ver)\n\n    print(\"1\")\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=10, port=port)\n        broker.send_signal(signal.SIGHUP)\n\n        mosq_test.expect_packet(sock, \"reload message\", reload_packet)\n        #mosq_test.expect_packet(sock, \"reload message\", reload_packet)\n        #mosq_test.expect_packet(sock, \"reload message\", reload_packet)\n\n        mosq_test.do_ping(sock)\n\n        rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        broker.wait()\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(\"false\")\ndo_test(\"true\")\n"
  },
  {
    "path": "test/broker/09-plugin-evt-subscribe.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/plugin_evt_subscribe.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef do_test():\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"plugin-evt-subscribe\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subscribe-topic\", 1, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\n    mid = 2\n    publish_packet1 = mosq_test.gen_publish(\"new-topic\", mid=mid, qos=1, payload=\"message1\", proto_ver=5)\n    puback_packet1 = mosq_test.gen_puback(mid, proto_ver=5)\n    publish_packet2 = mosq_test.gen_publish(\"new-topic\", qos=0, payload=\"message1\", proto_ver=5)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        sock.send(publish_packet1)\n        mosq_test.receive_unordered(sock, puback_packet1, publish_packet2, \"puback / publish2\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test()\n"
  },
  {
    "path": "test/broker/09-plugin-evt-tick.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a plugin can subscribe to the tick event\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, per_listener_settings=\"false\"):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener_settings))\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/plugin_evt_tick.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\ndef do_test(per_listener_settings):\n    proto_ver = 5\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, per_listener_settings)\n\n    rc = 1\n    keepalive = 10\n    connect_packet = mosq_test.gen_connect(\"plugin-tick-test\", keepalive=keepalive, username=\"readwrite\", clean_session=False, proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    tick_packet = mosq_test.gen_publish(\"topic/tick\", qos=0, payload=\"test-message\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=10, port=port)\n\n        mosq_test.expect_packet(sock, \"tick message\", tick_packet)\n        mosq_test.expect_packet(sock, \"tick message\", tick_packet)\n        mosq_test.expect_packet(sock, \"tick message\", tick_packet)\n\n        mosq_test.do_ping(sock)\n\n        rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        broker.wait()\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(\"false\")\ndo_test(\"true\")\n"
  },
  {
    "path": "test/broker/09-plugin-evt-unsubscribe.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/plugin_evt_unsubscribe.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef do_test():\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"plugin-evt-unsubscribe\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"unsubscribe-topic\", 1, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\n    mid = 2\n    unsubscribe_packet = mosq_test.gen_unsubscribe(mid, \"unsubscribe-topic\", proto_ver=5)\n    unsuback_packet = mosq_test.gen_unsuback(mid, mqtt5_rc.NO_SUBSCRIPTION_EXISTED, proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"unsubscribe-topic\", qos=0, payload=\"message1\", proto_ver=5)\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, \"unsuback\")\n        mosq_test.do_send_receive(sock, publish_packet, publish_packet, \"publish\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test()\n"
  },
  {
    "path": "test/broker/09-plugin-load-acl.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a plugin can subscribe to the tick event\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config(filename, ports, per_listener_settings):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener_settings))\n        f.write(\"plugin_load acl c/plugin_load_acl.so\\n\")\n\n        f.write(\"listener %d\\n\" % (ports[0]))\n        f.write(\"listener_allow_anonymous true\\n\")\n        f.write(\"plugin_use acl\\n\")\n\n        f.write(\"listener %d\\n\" % (ports[1]))\n        f.write(\"listener_allow_anonymous true\\n\")\n\ndef client_check(topic, rc, port):\n    connect_packet = mosq_test.gen_connect(client_id=\"id\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n\n    publish_packet = mosq_test.gen_publish(topic=topic, qos=1, mid=1, payload=\"message\", proto_ver=5)\n    puback_packet = mosq_test.gen_puback(mid=1, reason_code=rc, proto_ver=5)\n\n    mosq_test.do_send_receive(sock, publish_packet, puback_packet, f\"puback {topic}\")\n\n    sock.close()\n\n\ndef do_test(per_listener_settings):\n    proto_ver = 5\n    ports = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, ports, per_listener_settings)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=ports[0])\n\n    rc = 1\n    try:\n        # Plugin loaded\n        client_check(\"denied-topic\", mqtt5_rc.NOT_AUTHORIZED, ports[0]) # Should fail\n        client_check(\"allowed-topic\", mqtt5_rc.NO_MATCHING_SUBSCRIBERS, ports[0]) # Should succeed\n        # No plugin\n        client_check(\"denied-topic\", mqtt5_rc.NO_MATCHING_SUBSCRIBERS, ports[1]) # Should succeed\n        client_check(\"allowed-topic\", mqtt5_rc.NO_MATCHING_SUBSCRIBERS, ports[1]) # Should succeed\n\n        rc = 0\n    except Exception as err:\n        print(err)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        broker.wait()\n        if rc:\n            print(f\"per_listener_settings:{per_listener_settings}\")\n            (stdo, stde) = broker.communicate()\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(\"false\")\ndo_test(\"true\")\n"
  },
  {
    "path": "test/broker/09-plugin-load-basic-auth.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a plugin can subscribe to the tick event\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config1(filename, ports, per_listener_settings, plugver):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener_settings))\n        f.write(\"plugin_load auth c/auth_plugin_v%d.so\\n\" % (plugver))\n        f.write(\"listener %d\\n\" % (ports[0]))\n        f.write(\"plugin_use auth\\n\")\n        f.write(\"listener %d\\n\" % (ports[1]))\n        f.write(\"allow_anonymous false\\n\")\n        f.write(\"listener %d\\n\" % (ports[2]))\n        f.write(\"plugin_use auth\\n\")\n\ndef write_config2(filename, ports, per_listener_settings, plugver):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener_settings))\n        f.write(\"plugin_load auth c/auth_plugin_v%d.so\\n\" % (plugver))\n        f.write(\"listener %d\\n\" % (ports[0]))\n        f.write(\"listener %d\\n\" % (ports[1]))\n        f.write(\"plugin_use auth\\n\")\n        f.write(\"listener %d\\n\" % (ports[2]))\n        f.write(\"plugin_use auth\\n\")\n\ndef client_check(username, password, rc, port):\n    connect_packet = mosq_test.gen_connect(client_id=\"id\", username=username, password=password)\n    connack_packet = mosq_test.gen_connack(rc=rc)\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    sock.close()\n\n\ndef do_test(per_listener_settings, plugver):\n    proto_ver = 5\n    ports = mosq_test.get_port(3)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config1(conf_file, ports, per_listener_settings, plugver)\n\n    plugin_reload_fixed = False # FIXME - set to true then remove once plugin reloading is working\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=ports[0])\n\n    rc = 1\n    try:\n        client_check(\"test-username\", \"cnwTICONIURW\", 0, ports[0]) # Should succeed\n        client_check(\"test-username\", \"cnwTICONIURW\", 5, ports[1]) # Should fail\n        client_check(\"test-username\", \"cnwTICONIURW\", 0, ports[2]) # Should succeed\n\n        client_check(\"bad-actor\", \"nope\", 5, ports[0]) # Should fail\n        client_check(\"bad-actor\", \"nope\", 5, ports[1]) # Should fail\n        client_check(\"bad-actor\", \"nope\", 5, ports[2]) # Should fail\n\n        client_check(None, None, 5, ports[0]) # Should fail\n        client_check(None, None, 5, ports[1]) # Should fail\n        client_check(None, None, 5, ports[2]) # Should fail\n\n        if plugin_reload_fixed:\n            # Now swap auth around so ports[0] has no plugin but ports[1] does\n            write_config2(conf_file, ports, per_listener_settings, plugver)\n\n            broker.send_signal(signal.SIGHUP)\n\n            client_check(\"test-username\", \"cnwTICONIURW\", 5, ports[0]) # Should fail\n            client_check(\"test-username\", \"cnwTICONIURW\", 0, ports[1]) # Should succeed\n            client_check(\"test-username\", \"cnwTICONIURW\", 0, ports[2]) # Should succeed\n\n            client_check(\"bad-actor\", \"nope\", 5, ports[0]) # Should fail\n            client_check(\"bad-actor\", \"nope\", 5, ports[1]) # Should fail\n            client_check(\"bad-actor\", \"nope\", 5, ports[2]) # Should fail\n\n            client_check(None, None, 5, ports[0]) # Should fail\n            client_check(None, None, 5, ports[1]) # Should fail\n            client_check(None, None, 5, ports[2]) # Should fail\n        else:\n            # Now swap auth around so ports[0] has no plugin but ports[1] does\n            write_config2(conf_file, ports, per_listener_settings, plugver)\n\n            # Check config works as before - plugin reloading disabled\n\n            broker.send_signal(signal.SIGHUP)\n\n            client_check(\"test-username\", \"cnwTICONIURW\", 0, ports[0]) # Should succeed\n            client_check(\"test-username\", \"cnwTICONIURW\", 5, ports[1]) # Should fail\n            client_check(\"test-username\", \"cnwTICONIURW\", 0, ports[2]) # Should succeed\n\n            client_check(\"bad-actor\", \"nope\", 5, ports[0]) # Should fail\n            client_check(\"bad-actor\", \"nope\", 5, ports[1]) # Should fail\n            client_check(\"bad-actor\", \"nope\", 5, ports[2]) # Should fail\n\n            client_check(None, None, 5, ports[0]) # Should fail\n            client_check(None, None, 5, ports[1]) # Should fail\n            client_check(None, None, 5, ports[2]) # Should fail\n\n        rc = 0\n    except Exception as err:\n        print(err)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        broker.wait()\n        if rc:\n            print(f\"per_listener_settings:{per_listener_settings} plugver:{plugver}\")\n            (stdo, stde) = broker.communicate()\n            print(stde.decode('utf-8'))\n            exit(rc)\n\nprint(\"T1\")\ndo_test(\"false\", 2)\nprint(\"T2\")\ndo_test(\"true\", 2)\nprint(\"T3\")\ndo_test(\"false\", 3)\nprint(\"T4\")\ndo_test(\"true\", 3)\nprint(\"T5\")\ndo_test(\"false\", 4)\nprint(\"T6\")\ndo_test(\"true\", 4)\nprint(\"T7\")\ndo_test(\"false\", 5)\nprint(\"T8\")\ndo_test(\"true\", 5)\n"
  },
  {
    "path": "test/broker/09-plugin-load-extended-auth.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a plugin can subscribe to the tick event\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config(filename, ports, per_listener_settings):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener_settings))\n        f.write(\"plugin_load auth c/plugin_load_extended_auth.so\\n\")\n\n        f.write(\"listener %d\\n\" % (ports[0]))\n        f.write(\"plugin_use auth\\n\")\n\n        f.write(\"listener %d\\n\" % (ports[1]))\n\ndef client_check_start_denied(start_data, rc, port):\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"test\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, start_data)\n    connect_packet = mosq_test.gen_connect(client_id=\"id\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=rc, proto_ver=5)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n    except BrokenPipeError:\n        if rc == mqtt5_rc.NOT_AUTHORIZED or rc == mqtt5_rc.BAD_AUTHENTICATION_METHOD:\n            return\n        else:\n            raise\n        return\n\n\ndef client_check_start_allowed(start_data, cont_data, rc, port):\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"test\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, start_data)\n    connect_packet = mosq_test.gen_connect(client_id=\"id\", proto_ver=5, properties=props)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"test\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"start-ok\")\n    auth_packet_recv = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"test\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, cont_data)\n    auth_packet_send = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"test\")\n    connack_packet = mosq_test.gen_connack(rc=rc, proto_ver=5, properties=props)\n\n    sock = mosq_test.do_client_connect(connect_packet, auth_packet_recv, port=port)\n    try:\n        mosq_test.do_send_receive(sock, auth_packet_send, connack_packet, f\"connack {cont_data}\")\n        sock.close()\n    except BrokenPipeError:\n        if rc == mqtt5_rc.NOT_AUTHORIZED:\n            return\n        else:\n            raise\n\n\ndef do_test(per_listener_settings):\n    proto_ver = 5\n    ports = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, ports, per_listener_settings)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=ports[0])\n\n    rc = 1\n    try:\n        # Plugin loaded\n        client_check_start_denied(\"denied-start\", mqtt5_rc.NOT_AUTHORIZED, ports[0]) # Should fail\n        client_check_start_allowed(\"allowed-start\", \"denied-continue\", mqtt5_rc.NOT_AUTHORIZED, ports[0]) # Should fail\n        client_check_start_allowed(\"allowed-start\", \"allowed-continue\", mqtt5_rc.SUCCESS, ports[0]) # Should succeed\n        # No plugin\n        client_check_start_denied(\"denied-topic\", mqtt5_rc.BAD_AUTHENTICATION_METHOD, ports[1]) # Should fail\n        client_check_start_denied(\"allowed-topic\", mqtt5_rc.BAD_AUTHENTICATION_METHOD, ports[1]) # Should fail\n\n        rc = 0\n    except Exception as err:\n        print(err)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        broker.wait()\n        if rc:\n            print(f\"per_listener_settings:{per_listener_settings}\")\n            (stdo, stde) = broker.communicate()\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(\"false\")\ndo_test(\"true\")\n"
  },
  {
    "path": "test/broker/09-plugin-publish.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"auth_plugin c/auth_plugin_publish.so\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\nproto_ver = 5\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect1_packet = mosq_test.gen_connect(\"test-client\", username=\"readwrite\", clean_session=False, proto_ver=proto_ver)\nconnack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\npublish_packet = mosq_test.gen_publish(\"init\", qos=0, proto_ver=proto_ver)\n\npublish0_packet = mosq_test.gen_publish(\"topic/0\", qos=0, payload=\"test-message-0\", proto_ver=proto_ver)\n\nmid = 1\npublish1_packet = mosq_test.gen_publish(\"topic/1\", qos=1, mid=mid, payload=\"test-message-1\", proto_ver=proto_ver)\npuback1_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\nmid = 2\npublish2_packet = mosq_test.gen_publish(\"topic/2\", qos=2, mid=mid, payload=\"test-message-2\", proto_ver=proto_ver)\npubrec2_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\npubrel2_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\npubcomp2_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\n\nprops = mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 1)\npublish0p_packet = mosq_test.gen_publish(\"topic/0\", qos=0, payload=\"test-message-0\", proto_ver=proto_ver, properties=props)\n\nmid = 3\npublish1p_packet = mosq_test.gen_publish(\"topic/1\", qos=1, mid=mid, payload=\"test-message-1\", proto_ver=proto_ver, properties=props)\npuback1p_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\nmid = 4\npublish2p_packet = mosq_test.gen_publish(\"topic/2\", qos=2, mid=mid, payload=\"test-message-2\", proto_ver=proto_ver, properties=props)\npubrec2p_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver)\npubrel2p_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver)\npubcomp2p_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port)\n\n    # Trigger the plugin to send us some messages\n    sock.send(publish_packet)\n\n    mosq_test.expect_packet(sock, \"publish0\", publish0_packet)\n    mosq_test.expect_packet(sock, \"publish1\", publish1_packet)\n    sock.send(puback1_packet)\n\n    mosq_test.expect_packet(sock, \"publish2\", publish2_packet)\n    mosq_test.do_send_receive(sock, pubrec2_packet, pubrel2_packet, \"pubrel1\")\n    sock.send(pubcomp2_packet)\n\n    # And trigger the second set of messages, with properties\n    sock.send(publish_packet)\n    mosq_test.expect_packet(sock, \"publish0p\", publish0p_packet)\n    mosq_test.expect_packet(sock, \"publish1p\", publish1p_packet)\n    sock.send(puback1_packet)\n\n    mosq_test.expect_packet(sock, \"publish2p\", publish2p_packet)\n    mosq_test.do_send_receive(sock, pubrec2p_packet, pubrel2p_packet, \"pubrel1p\")\n    sock.send(pubcomp2p_packet)\n\n    mosq_test.do_ping(sock)\n\n    rc = 0\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/09-plugin-unsupported.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\n# Check whether unsupported plugin versions are handled ok\n\ndef write_config(filename, port, plugver):\n    with open(filename, 'w') as f:\n        f.write(f\"listener {port}\\n\")\n        f.write(f\"auth_plugin c/bad_v{plugver}.so\\n\")\n        f.write(\"allow_anonymous false\\n\")\n\ndef do_test(plugver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port, plugver)\n\n    try:\n        rc = 1\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port, check_port=False)\n        broker.wait(5)\n        broker.terminate()\n        if broker.returncode == 13:\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(1)\ndo_test(6)\n"
  },
  {
    "path": "test/broker/09-pwfile-parse-invalid.py",
    "content": "#!/usr/bin/env python3\n\n# Test for CVE-2018-xxxxx.\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config(filename, port, per_listener):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener))\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"password_file %s\\n\" % (filename.replace('.conf', '.pwfile')))\n        f.write(\"allow_anonymous false\")\n\ndef write_pwfile(filename, bad_line1, bad_line2):\n    with open(filename, 'w') as f:\n        if bad_line1 is not None:\n            f.write('%s\\n' % (bad_line1))\n        # Username test, password test\n        f.write('test:$6$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==\\n')\n        # Username empty, password 0 length\n        f.write('empty:$6$o+53eGXtmlfHeYrg$FY7X9DNQ4uU1j0NiPmGOOSU05ZSzhqNmNhXIof/0nLpVb1zDhcRHdaC72E3YryH7dtTiG/r6jH6C8J+30cZBgA==\\n')\n        if bad_line2 is not None:\n            f.write('%s\\n' % (bad_line2))\n\ndef do_test(port, connack_rc, username, password):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"username-password-check\", username=username, password=password)\n    connack_packet = mosq_test.gen_connack(rc=connack_rc)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if rc:\n            raise AssertionError\n\n\ndef username_password_tests(port):\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        do_test(port, connack_rc=0, username='test', password='test')\n        do_test(port, connack_rc=5, username='test', password='bad')\n        do_test(port, connack_rc=5, username='test', password='')\n        do_test(port, connack_rc=5, username='test', password=None)\n        do_test(port, connack_rc=5, username='empty', password='test')\n        do_test(port, connack_rc=5, username='empty', password='bad')\n        do_test(port, connack_rc=5, username='empty', password='')\n        do_test(port, connack_rc=5, username='empty', password=None)\n        do_test(port, connack_rc=5, username='bad', password='test')\n        do_test(port, connack_rc=5, username='bad', password='bad')\n        do_test(port, connack_rc=5, username='bad', password='')\n        do_test(port, connack_rc=5, username='bad', password=None)\n        do_test(port, connack_rc=5, username='', password='test')\n        do_test(port, connack_rc=5, username='', password='bad')\n        do_test(port, connack_rc=5, username='', password='')\n        do_test(port, connack_rc=5, username='', password=None)\n        do_test(port, connack_rc=5, username=None, password='test')\n        do_test(port, connack_rc=5, username=None, password='bad')\n        do_test(port, connack_rc=5, username=None, password='')\n        do_test(port, connack_rc=5, username=None, password=None)\n    except ValueError:\n        pass\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n\n\ndef expect_broker_fail_test(port, expect_fail_log):\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port, expect_fail=True, expect_fail_log=expect_fail_log)\n\n\ndef all_tests(port):\n    # Valid file, single user\n    write_pwfile(pw_file, bad_line1=None, bad_line2=None)\n    username_password_tests(port)\n\n    # Invalid file, first line blank\n    write_pwfile(pw_file, bad_line1='', bad_line2=None)\n    username_password_tests(port)\n\n    # Invalid file, last line blank\n    write_pwfile(pw_file, bad_line1=None, bad_line2='')\n    username_password_tests(port)\n\n    # Invalid file, first and last line blank\n    write_pwfile(pw_file, bad_line1='', bad_line2='')\n    username_password_tests(port)\n\n    # Invalid file, first line 'comment'\n    write_pwfile(pw_file, bad_line1='#comment', bad_line2=None)\n    username_password_tests(port)\n\n    # Invalid file, last line 'comment'\n    write_pwfile(pw_file, bad_line1=None, bad_line2='#comment')\n    username_password_tests(port)\n\n    # Invalid file, first and last line 'comment'\n    write_pwfile(pw_file, bad_line1='#comment', bad_line2='#comment')\n    username_password_tests(port)\n\n    # Invalid file, first line blank and last line 'comment'\n    write_pwfile(pw_file, bad_line1='', bad_line2='#comment')\n    username_password_tests(port)\n\n    # Invalid file, first line incomplete\n    write_pwfile(pw_file, bad_line1='bad:', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Unable to decode line in password file\")\n\n    # Invalid file, first line incomplete, but with \"password\"\n    write_pwfile(pw_file, bad_line1='bad:bad', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Unable to decode line in password file\")\n\n    # Invalid file, first line incomplete, partial password hash\n    write_pwfile(pw_file, bad_line1='bad:$', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Unable to decode line in password file\")\n\n    # Invalid file, first line incomplete, partial password hash\n    write_pwfile(pw_file, bad_line1='bad:$6', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Unable to decode line in password file\")\n\n    # Invalid file, first line incomplete, partial password hash\n    write_pwfile(pw_file, bad_line1='bad:$6$', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Unable to decode line in password file\")\n\n    # Valid file, first line incomplete, has valid salt but no password hash\n    write_pwfile(pw_file, bad_line1='bad:$6$njERlZMi/7DzNB9E', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Unable to decode line in password file\")\n\n    # Valid file, first line incomplete, has valid salt but no password hash\n    write_pwfile(pw_file, bad_line1='bad:$6$njERlZMi/7DzNB9E$', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Unable to decode line in password file\")\n\n    # Valid file, first line has invalid hash designator\n    write_pwfile(pw_file, bad_line1='bad:$5$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Unable to decode line in password file\")\n\n    # Invalid file, missing username but valid password hash\n    write_pwfile(pw_file, bad_line1=':$6$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Invalid line in password file\")\n\n    # Valid file, valid username but password salt not base64\n    write_pwfile(pw_file, bad_line1='bad:$6$njER{ZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Unable to decode line in password file\")\n\n    # Valid file, valid username but password hash not base64\n    write_pwfile(pw_file, bad_line1='bad:$6$njERlZMi/7DzNB9E$iiavfuXv{}8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None)\n    expect_broker_fail_test(port, \"Error: Unable to decode line in password file\")\n\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npw_file = os.path.basename(__file__).replace('.py', '.pwfile')\n\ntry:\n    write_config(conf_file, port, \"false\")\n    all_tests(port)\n\n    write_config(conf_file, port, \"true\")\n    all_tests(port)\nfinally:\n    os.remove(conf_file)\n    os.remove(pw_file)\n\nsys.exit(0)\n"
  },
  {
    "path": "test/broker/10-listener-mount-point.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port1))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"listener %d\\n\" % (port2))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"mount_point mount/\\n\")\n        f.write(\"\\n\")\n        f.write(\"log_type debug\\n\")\n\n\ndef helper(port, proto_ver):\n    connect_packet = mosq_test.gen_connect(\"10-listener-mount-helper\", proto_ver=proto_ver)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(\"10/listener/mount/test\", qos=0, payload=\"mount point\", proto_ver=proto_ver)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, connack_error=\"helper connack\")\n    sock.send(publish_packet)\n    sock.close()\n\n\ndef do_test(proto_ver):\n    (port1, port2) = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port1, port2)\n\n    rc = 1\n    mid = 1\n\n    # Subscriber for listener with mount point\n    connect_packet1 = mosq_test.gen_connect(\"test1\", proto_ver=proto_ver)\n    connack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    subscribe_packet1 = mosq_test.gen_subscribe(mid, \"#\", 0, proto_ver=proto_ver)\n    suback_packet1 = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n    publish_packet1 = mosq_test.gen_publish(\"mount/10/listener/mount/test\", qos=0, payload=\"mount point\", proto_ver=proto_ver)\n\n    # Subscriber for listener without mount point\n    connect_packet2 = mosq_test.gen_connect(\"test2\", proto_ver=proto_ver)\n    connack_packet2 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    subscribe_packet2 = mosq_test.gen_subscribe(mid, \"#\", 0, proto_ver=proto_ver)\n    suback_packet2 = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)\n    publish_packet2 = mosq_test.gen_publish(\"10/listener/mount/test\", qos=0, payload=\"mount point\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect_packet1, connack_packet1, timeout=20, port=port1)\n        mosq_test.do_send_receive(sock1, subscribe_packet1, suback_packet1, \"suback1\")\n\n        sock2 = mosq_test.do_client_connect(connect_packet2, connack_packet2, timeout=20, port=port2)\n        mosq_test.do_send_receive(sock2, subscribe_packet2, suback_packet2, \"suback2\")\n\n        helper(port2, proto_ver)\n        # Should have now received a publish command\n\n        mosq_test.expect_packet(sock1, \"publish1\", publish_packet1)\n        mosq_test.expect_packet(sock2, \"publish2\", publish_packet2)\n        rc = 0\n\n        sock1.close()\n        sock2.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n"
  },
  {
    "path": "test/broker/11-message-expiry.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether the broker reduces the message expiry interval when republishing.\n# MQTT v5, with a broker restart and persistence.\n\n# Client connects with clean session set false, subscribes with qos=1, then disconnects\n# Helper publishes two messages, one with a short expiry and one with a long expiry\n# We wait until the short expiry will have expired but the long one not.\n# Client reconnects, expects delivery of the long expiry message with a reduced\n# expiry interval property.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file mosquitto-%d.db\\n\" % (port))\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"subpub-qos0-test\", proto_ver=5, clean_session=False, session_expiry=60)\nconnack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\nconnack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5, flags=1)\n\nmid = 53\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos1\", 1, proto_ver=5)\nsuback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\n\n\nhelper_connect = mosq_test.gen_connect(\"helper\", proto_ver=5)\nhelper_connack = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nmid=1\nprops = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 5)\npublish1s_packet = mosq_test.gen_publish(\"subpub/qos1\", mid=mid, qos=1, payload=\"message1\", proto_ver=5, properties=props)\npuback1s_packet = mosq_test.gen_puback(mid)\n\nmid=2\nprops = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 100)\npublish2s_packet = mosq_test.gen_publish(\"subpub/qos1\", mid=mid, qos=1, payload=\"message2\", proto_ver=5, properties=props)\npuback2s_packet = mosq_test.gen_puback(mid)\n\nmid=3\npublish3_packet = mosq_test.gen_publish(\"subpub/qos1\", mid=mid, qos=1, payload=\"message3\", proto_ver=5)\npuback3_packet = mosq_test.gen_puback(mid)\n\n\nif os.path.exists('mosquitto-%d.db' % (port)):\n    os.unlink('mosquitto-%d.db' % (port))\n\nport = mosq_test.get_port()\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack1_packet, timeout=20, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    sock.close()\n\n    helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port)\n    mosq_test.do_send_receive(helper, publish1s_packet, puback1s_packet, \"puback 1\")\n    mosq_test.do_send_receive(helper, publish2s_packet, puback2s_packet, \"puback 2\")\n    mosq_test.do_send_receive(helper, publish3_packet, puback3_packet, \"puback 3\")\n\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo1, stde1) = broker.communicate()\n    sock.close()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    time.sleep(7)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=20, port=port)\n    packet = sock.recv(len(publish2s_packet))\n    for i in range(100, 1, -1):\n        props = mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, i)\n        publish2r_packet = mosq_test.gen_publish(\"subpub/qos1\", mid=2, qos=1, payload=\"message2\", proto_ver=5, properties=props)\n        if packet == publish2r_packet:\n            mosq_test.expect_packet(sock, \"publish3\", publish3_packet)\n            rc = 0\n            break\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n    if os.path.exists('mosquitto-%d.db' % (port)):\n        os.unlink('mosquitto-%d.db' % (port))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/11-persistence-autosave-changes.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic receives its own message sent to that topic.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file mosquitto-%d.db\\n\" % (port))\n        f.write(\"autosave_interval 1\\n\")\n        f.write(\"autosave_on_changes true\\n\")\n\ndef do_test():\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"persistent-test\", clean_session=True)\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    publish_packet = mosq_test.gen_publish(\"subpub/qos1\", qos=1, mid=1, payload=\"message\", retain=True)\n    puback_packet = mosq_test.gen_puback(1)\n\n    if os.path.exists('mosquitto-%d.db' % (port)):\n        os.unlink('mosquitto-%d.db' % (port))\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n        sock.close()\n\n        time.sleep(0.5)\n        if os.path.exists('mosquitto-%d.db' % (port)):\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if os.path.exists('mosquitto-%d.db' % (port)):\n            os.unlink('mosquitto-%d.db' % (port))\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test()\nexit(0)\n\n"
  },
  {
    "path": "test/broker/11-persistent-subscription-no-local.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic receives its own message sent to that topic.\n# And whether the no-local option is persisted.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file mosquitto-%d.db\\n\" % (port))\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\n    \"persistent-subscription-test\", clean_session=False, proto_ver=5, session_expiry=60\n)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\nconnack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=5)  # session present\n\nmid = 1\nsubscribe1_packet = mosq_test.gen_subscribe(mid, \"subpub/nolocal\", 5, proto_ver=5)\nsuback1_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\nmid = 2\nsubscribe2_packet = mosq_test.gen_subscribe(mid, \"subpub/local\", 1, proto_ver=5)\nsuback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\nmid = 1\npublish1_packet = mosq_test.gen_publish(\"subpub/nolocal\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\npuback1_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\nmid = 2\npublish2s_packet = mosq_test.gen_publish(\"subpub/local\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\npuback2s_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\nmid = 1\npublish2a_packet = mosq_test.gen_publish(\"subpub/local\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\npuback2a_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\nmid = 2\npublish2b_packet = mosq_test.gen_publish(\"subpub/local\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\npuback2b_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\nif os.path.exists('mosquitto-%d.db' % (port)):\n    os.unlink('mosquitto-%d.db' % (port))\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n(stdo1, stde1) = (None, None)\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, \"suback1\")\n    mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, \"suback2\")\n\n    mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, \"puback1a\")\n    sock.send(publish2s_packet)\n    mosq_test.receive_unordered(sock, puback2s_packet, publish2a_packet, \"puback2a/publish2a\")\n\n    sock.send(puback2a_packet)\n\n    # Send a ping and wait for the the response to make sure the puback2a_packet was processed by the broker\n    mosq_test.do_ping(sock)\n\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo1, stde1) = broker.communicate()\n    sock.close()\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=20, port=port)\n\n    mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, \"puback1b\")\n    sock.send(publish2s_packet)\n    mosq_test.receive_unordered(sock, puback2s_packet, publish2b_packet, \"puback2b/publish2b\")\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    if rc and stde1:\n        print(stde1.decode('utf-8'))\n\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n    if os.path.exists('mosquitto-%d.db' % (port)):\n        os.unlink('mosquitto-%d.db' % (port))\n\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/11-persistent-subscription.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic receives its own message sent to that topic.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file mosquitto-%d.db\\n\" % (port))\n\ndef do_test(proto_ver):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    rc = 1\n    mid = 530\n    connect_packet = mosq_test.gen_connect(\n        \"persistent-subscription-test\", clean_session=False, proto_ver=proto_ver, session_expiry=60\n    )\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    connack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)  # session present\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos1\", 1, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver)\n\n    mid = 300\n    publish_packet = mosq_test.gen_publish(\"subpub/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    mid = 1\n    publish_packet2 = mosq_test.gen_publish(\"subpub/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n\n    if os.path.exists('mosquitto-%d.db' % (port)):\n        os.unlink('mosquitto-%d.db' % (port))\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    (stdo1, stde1) = (\"\", \"\")\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo1, stde1) = broker.communicate()\n        sock.close()\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=20, port=port)\n\n        sock.send(publish_packet)\n        mosq_test.receive_unordered(sock, puback_packet, publish_packet2, \"puback/publish2\")\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if os.path.exists('mosquitto-%d.db' % (port)):\n            os.unlink('mosquitto-%d.db' % (port))\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\nexit(0)\n\n"
  },
  {
    "path": "test/broker/11-pub-props.py",
    "content": "#!/usr/bin/env python3\n\n# Does a persisted PUBLISH keep its properties?\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file mosquitto-%d.db\\n\" % (port))\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\n    \"persistent-props-test\", clean_session=True, proto_ver=5\n)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nmid = 1\nprops = mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 1)\nprops += mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"plain/text\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"/dev/null\")\nprops += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"2357289375902345\")\nprops += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name\", \"value\")\npublish_packet = mosq_test.gen_publish(\"subpub/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=5, properties=props, retain=True)\npuback_packet = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS, proto_ver=5)\n\npublish2_packet = mosq_test.gen_publish(\"subpub/qos1\", qos=0, payload=\"message\", proto_ver=5, properties=props, retain=True)\n\nmid = 1\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos1\", 0, proto_ver=5)\nsuback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\nif os.path.exists('mosquitto-%d.db' % (port)):\n    os.unlink('mosquitto-%d.db' % (port))\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n(stdo1, stde1) = (\"\", \"\")\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    mosq_test.expect_packet(sock, \"publish2\", publish2_packet)\n\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo1, stde1) = broker.communicate()\n    sock.close()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    mosq_test.expect_packet(sock, \"publish2\", publish2_packet)\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n    if os.path.exists('mosquitto-%d.db' % (port)):\n        os.unlink('mosquitto-%d.db' % (port))\n\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/11-subscription-id.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client message maintains its subscription id when persisted and restored.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(\"persistence_file mosquitto-%d.db\\n\" % (port))\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\n\nconnect_packet = mosq_test.gen_connect(\n    \"persistent-subscription-test\", clean_session=False, proto_ver=5, session_expiry=60\n)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\nconnack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=5)  # session present\n\nmid = 1\nprops = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 53)\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"subpub/qos1\", 1, proto_ver=5, properties=props)\nsuback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\nmid = 1\nprops = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 53)\npublish_packet2 = mosq_test.gen_publish(\"subpub/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=5, properties=props)\n\n\nhelper_connect_packet = mosq_test.gen_connect(\"helper\", clean_session=True, proto_ver=5)\nhelper_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nmid = 1\nhelper_publish_packet = mosq_test.gen_publish(\"subpub/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\nhelper_puback_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n\nif os.path.exists('mosquitto-%d.db' % (port)):\n    os.unlink('mosquitto-%d.db' % (port))\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n(stdo1, stde1) = (\"\", \"\")\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    sock.close()\n\n    sock = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, timeout=20, port=port)\n    mosq_test.do_send_receive(sock, helper_publish_packet, helper_puback_packet, \"helper puback\")\n    sock.close()\n\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo1, stde1) = broker.communicate()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=20, port=port)\n\n    mosq_test.expect_packet(sock, \"publish2\", publish_packet2)\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n    if os.path.exists('mosquitto-%d.db' % (port)):\n        os.unlink('mosquitto-%d.db' % (port))\n        pass\n\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/12-prop-assigned-client-identifier.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether sending a non zero session expiry interval in DISCONNECT after\n# having sent a zero session expiry interval is treated correctly in MQTT v5.\n\nfrom mosq_test_helper import *\n\n\ndef do_test(start_broker, clean_start):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(None, proto_ver=5, clean_session=clean_start)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.ASSIGNED_CLIENT_IDENTIFIER, \"auto-00000000-0000-0000-0000-000000000000\")\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 1)\n    disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props)\n\n    disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        sock.settimeout(10)\n        sock.connect((\"localhost\", port))\n\n        sock.send(connect_packet)\n        connack_recvd = sock.recv(len(connack_packet))\n\n        if connack_recvd[0:12] == connack_packet[0:12]:\n            # FIXME - this test could be tightened up a lot\n            rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    rc = do_test(start_broker, True)\n    if rc:\n        return rc;\n    rc = do_test(start_broker, False)\n    if rc:\n        return rc;\n    return 0\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/12-prop-maximum-packet-size-broker.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether the broker disconnects a client nicely when they send a too large packet.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"max_packet_size 50\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\n\nconnect_packet = mosq_test.gen_connect(\"12-max-packet-broker\", proto_ver=5)\nprops = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10)\nprops += mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 50)\nprops += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False)\n\npublish_packet_ok = mosq_test.gen_publish(\"12/max/packet/size/broker/test/topic\", qos=0, payload=\"012345678\", proto_ver=5)\npublish_packet_bad = mosq_test.gen_publish(\"12/max/packet/size/broker/test/topic\", qos=0, payload=\"0123456789\", proto_ver=5)\ndisconnect_packet = mosq_test.gen_disconnect(reason_code=149, proto_ver=5)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    sock.send(publish_packet_ok)\n    mosq_test.do_ping(sock)\n    mosq_test.do_send_receive(sock, publish_packet_bad, disconnect_packet, \"disconnect\")\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    os.remove(conf_file)\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n\n"
  },
  {
    "path": "test/broker/12-prop-maximum-packet-size-publish-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether maximum packet size is honoured on a PUBLISH to a client\n# MQTTv5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 40)\n    connect_packet = mosq_test.gen_connect(\"12-max-publish-qos1\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"12/max/publish/qos1/test/topic\", 1, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\n    mid=1\n    publish1_packet = mosq_test.gen_publish(topic=\"12/max/publish/qos1/test/topic\", mid=mid, qos=1, payload=\"1234\", proto_ver=5)\n    puback1_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    mid=2\n    props = mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 1)\n    publish2_packet = mosq_test.gen_publish(topic=\"12/max/publish/qos1/test/topic\", mid=mid, qos=1, payload=\"56\", proto_ver=5, properties=props)\n    puback2_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    mid=3\n    publish3_packet = mosq_test.gen_publish(topic=\"12/max/publish/qos1/test/topic\", mid=mid, qos=1, payload=\"789\", proto_ver=5)\n    puback3_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet)\n\n        mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, \"puback 1\")\n        # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE\n        mosq_test.do_ping(sock)\n\n        mosq_test.do_send_receive(sock, publish2_packet, puback2_packet, \"puback 2\")\n        # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE\n        mosq_test.do_ping(sock)\n\n        sock.send(publish3_packet)\n        mosq_test.receive_unordered(sock, puback3_packet, publish3_packet, \"puback 3/publish3\")\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc;\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/12-prop-maximum-packet-size-publish-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether maximum packet size is honoured on a PUBLISH to a client\n# MQTTv5\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 40)\n    connect_packet = mosq_test.gen_connect(\"12-max-publish-qos2\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"12/max/publish/qos2/test/topic\", 2, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5)\n\n    mid=1\n    publish1_packet = mosq_test.gen_publish(topic=\"12/max/publish/qos2/test/topic\", mid=mid, qos=2, payload=\"1234\", proto_ver=5)\n    pubrec1_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel1_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp1_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid=2\n    publish2_packet = mosq_test.gen_publish(topic=\"12/max/publish/qos2/test/topic\", mid=mid, qos=2, payload=\"789\", proto_ver=5)\n    pubrec2_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel2_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet)\n\n        mosq_test.do_send_receive(sock, publish1_packet, pubrec1_packet, \"pubrec 1\")\n        mosq_test.do_send_receive(sock, pubrel1_packet, pubcomp1_packet, \"pubcomp 1\")\n\n        # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE\n        mosq_test.do_ping(sock)\n\n        mosq_test.do_send_receive(sock, publish2_packet, pubrec2_packet, \"pubrec 2\")\n        sock.send(pubrel2_packet)\n        mosq_test.receive_unordered(sock, pubcomp2_packet, publish2_packet, \"pubcomp 2/publish2\")\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/12-prop-response-topic-correlation-data.py",
    "content": "#!/usr/bin/env python3\n\n# client 1 subscribes to normal-topic\n# client 2 susbscribes to response-topic\n# client 2 publishes message to normal-topic with response-topic property and correlation-data property\n# client 1 receives message, publishes a response on response-topic\n# client 2 receives message, checks payload\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n\n    connect_packet1 = mosq_test.gen_connect(\"client1\", proto_ver=5)\n    connect_packet2 = mosq_test.gen_connect(\"client2\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet1 = mosq_test.gen_subscribe(mid=1, topic=\"normal/topic\", qos=0, proto_ver=5)\n    subscribe_packet2 = mosq_test.gen_subscribe(mid=1, topic=\"response/topic\", qos=0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid=1, qos=0, proto_ver=5)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"response/topic\")\n    props = mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"45vyvynq30q3vt4 nuy893b4v3\")\n    publish_packet2 = mosq_test.gen_publish(topic=\"normal/topic\", qos=0, payload=\"2\", proto_ver=5, properties=props)\n\n    publish_packet1 = mosq_test.gen_publish(topic=\"response/topic\", qos=0, payload=\"22\", proto_ver=5)\n\n    disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props)\n\n    disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect_packet1, connack_packet, port=port)\n        sock2 = mosq_test.do_client_connect(connect_packet2, connack_packet, port=port)\n\n        mosq_test.do_send_receive(sock1, subscribe_packet1, suback_packet, \"subscribe1\")\n        mosq_test.do_send_receive(sock2, subscribe_packet2, suback_packet, \"subscribe2\")\n\n        sock2.send(publish_packet2)\n        mosq_test.expect_packet(sock1, \"publish1\", publish_packet2)\n        # FIXME - it would be better to extract the property and payload, even though we know them\n        sock1.send(publish_packet1)\n        mosq_test.expect_packet(sock2, \"publish2\", publish_packet1)\n        rc = 0\n\n        sock1.close()\n        sock2.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/12-prop-response-topic.py",
    "content": "#!/usr/bin/env python3\n\n# client 1 subscribes to normal-topic\n# client 2 susbscribes to response-topic\n# client 2 publishes message to normal-topic with response-topic property\n# client 1 receives message, publishes a response on response-topic\n# client 2 receives message, checks payload\n\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    rc = 1\n\n    connect_packet1 = mosq_test.gen_connect(\"12-response-topic-client1\", proto_ver=5)\n    connect_packet2 = mosq_test.gen_connect(\"12-response-topic-client2\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet1 = mosq_test.gen_subscribe(mid=1, topic=\"12/response/topic/normal/topic\", qos=0, proto_ver=5)\n    subscribe_packet2 = mosq_test.gen_subscribe(mid=1, topic=\"12/response/topic/response/topic\", qos=0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid=1, qos=0, proto_ver=5)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"12/response/topic/response/topic\")\n    publish_packet2 = mosq_test.gen_publish(topic=\"12/response/topic/normal/topic\", qos=0, payload=\"2\", proto_ver=5, properties=props)\n\n    publish_packet1 = mosq_test.gen_publish(topic=\"12/response/topic/response/topic\", qos=0, payload=\"22\", proto_ver=5)\n\n    disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props)\n\n    disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock1 = mosq_test.do_client_connect(connect_packet1, connack_packet, port=port)\n        sock2 = mosq_test.do_client_connect(connect_packet2, connack_packet, port=port)\n\n        mosq_test.do_send_receive(sock1, subscribe_packet1, suback_packet, \"subscribe1\")\n        mosq_test.do_send_receive(sock2, subscribe_packet2, suback_packet, \"subscribe2\")\n\n        sock2.send(publish_packet2)\n        mosq_test.expect_packet(sock1, \"publish1\", publish_packet2)\n        # FIXME - it would be better to extract the property and payload, even though we know them\n        sock1.send(publish_packet1)\n        mosq_test.expect_packet(sock2, \"publish2\", publish_packet1)\n        rc = 0\n\n        sock1.close()\n        sock2.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/12-prop-server-keepalive.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether sending a non zero session expiry interval in DISCONNECT after\n# having sent a zero session expiry interval is treated correctly in MQTT v5.\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"\\n\")\n        f.write(\"max_keepalive 60\\n\")\n\nport = mosq_test.get_port(1)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\n\nrc = 1\n\nkeepalive = 61\nconnect_packet = mosq_test.gen_connect(\"12-server-keepalive\", proto_ver=5, keepalive=keepalive)\n\nprops = mqtt5_props.gen_uint16_prop(mqtt5_props.SERVER_KEEP_ALIVE, 60) \\\n        + mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10) \\\n        + mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 2000000) \\\n        + mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    sock.close()\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/12-prop-subpub-content-type.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic receives its own message sent to that topic.\n# Does the Content Type property get sent through?\n# MQTT v5\n\nimport prop_subpub_helper as helper\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    props_out = mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"text\")\n    props_out = props_out+mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS, 1)\n\n    props_in = mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"text\")\n\n    return helper.prop_subpub_helper(start_broker, \"12-prop-subpub-content-type\", props_out, props_in)\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/12-prop-subpub-payload-format.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic receives its own message sent to that topic.\n# Does the Payload Format Indicator property get sent through?\n# MQTT v5\n\nimport prop_subpub_helper as helper\nfrom mosq_test_helper import *\n\ndef do_test(start_broker):\n    props_out = mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 0xed)\n    props_out = props_out+mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS, 1)\n\n    props_in = mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 0xed)\n\n    return helper.prop_subpub_helper(start_broker, \"12-prop-subpub-payload-format\", props_out, props_in, expect_proto_error=True)\n\n\ndef all_tests(start_broker=False):\n    return do_test(start_broker)\n\nif __name__ == '__main__':\n    all_tests(True)\n"
  },
  {
    "path": "test/broker/13-websocket-bad-origin.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\nrc = 1\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"protocol websockets\\n\")\n        f.write(\"websockets_origin example.org\\n\")\n\ndef do_test(publish_packet, reason_code, error_string):\n    global rc\n\n    rc = 1\n\n    connect_packet = mosq_test.gen_connect(\"13-malformed-publish-v5\", proto_ver=5)\n\n    connack_props = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10)\n    connack_props += mqtt5_props.gen_byte_prop(mqtt5_props.RETAIN_AVAILABLE, 0)\n    connack_props += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n    connack_props += mqtt5_props.gen_byte_prop(mqtt5_props.MAXIMUM_QOS, 1)\n\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=connack_props, property_helper=False)\n\n    mid = 0\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=reason_code)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, error_string=error_string)\n    rc = 0\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    websocket_req_bad = b\"GET /mqtt HTTP/1.1\\r\\n\" \\\n        + b\"Host: localhost\\r\\n\" \\\n        + b\"Upgrade: websocket\\r\\n\" \\\n        + b\"Connection: Upgrade\\r\\n\" \\\n        + B\"Sec-WebSocket-Key: 1JaITHdgDZVd/4OE2AzTTA==\\r\\n\" \\\n        + b\"Sec-WebSocket-Protocol: mqtt\\r\\n\" \\\n        + b\"Sec-WebSocket-Version: 13\\r\\n\" \\\n        + b\"Origin: localhost\\r\\n\" \\\n        + b\"\\r\\n\"\n\n    sock = mosq_test.do_client_connect(websocket_req_bad, b\"\", port=port)\n    sock.close()\nexcept BrokenPipeError:\n    pass\n\ntry:\n    websocket_req_good = b\"GET /mqtt HTTP/1.1\\r\\n\" \\\n        + b\"Host: localhost\\r\\n\" \\\n        + b\"Upgrade: websocket\\r\\n\" \\\n        + b\"Connection: Upgrade\\r\\n\" \\\n        + B\"Sec-WebSocket-Key: 1JaITHdgDZVd/4OE2AzTTA==\\r\\n\" \\\n        + b\"Sec-WebSocket-Protocol: mqtt\\r\\n\" \\\n        + b\"Sec-WebSocket-Version: 13\\r\\n\" \\\n        + b\"Origin: example.org\\r\\n\" \\\n        + b\"\\r\\n\"\n\n    websocket_resp_good = b\"HTTP/1.1 101 Switching Protocols\\r\\n\" \\\n        + b\"Upgrade: WebSocket\\r\\n\" \\\n        + b\"Connection: Upgrade\\r\\n\" \\\n        + b\"Sec-WebSocket-Accept: Ako91O0lxiq8gN0+b9YCijMx8lk=\\r\\n\" \\\n        + b\"Sec-WebSocket-Protocol: mqtt\\r\\n\" \\\n        + b\"\\r\\n\"\n\n    sock = mosq_test.do_client_connect(websocket_req_good, websocket_resp_good, port=port)\n    sock.close()\n\n    rc = 0\nexcept Exception as err:\n    print(err)\nfinally:\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    os.remove(conf_file)\n    if rc:\n        print(stde.decode('utf-8'))\n        exit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-acl.py",
    "content": "#!/usr/bin/env python3\n\n# Test ACL for allow/deny. This does not consider ACL priority and the ACLs do not overlap.\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous false\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nadd_client_command_with_id = { \"commands\": [{\n    \"command\": \"createClient\", \"username\": \"user_one\",\n    \"password\": \"password\", \"clientid\": \"cid\",\n    \"correlationData\": \"2\" }]\n}\nadd_client_response_with_id = {'responses': [{'command': 'createClient', 'correlationData': '2'}]}\n\n\nadd_client_group_role_command = {\"commands\":[\n    { \"command\": \"createGroup\", \"groupname\": \"mygroup\" },\n    { \"command\": \"createRole\", \"rolename\": \"myrole\" },\n    { \"command\": \"addGroupRole\", \"groupname\": \"mygroup\", \"rolename\": \"myrole\" },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"subscribeLiteral\", \"topic\": \"simple/topic\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"subscribePattern\", \"topic\": \"single-wildcard/+/topic\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"subscribePattern\", \"topic\": \"multilevel-wildcard/#\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"unsubscribeLiteral\", \"topic\": \"simple/topic\", \"allow\": False },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"subscribePattern\", \"topic\": \"pattern/#\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"subscribePattern\", \"topic\": \"subscribe/pattern/c/%c/allowed\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"subscribePattern\", \"topic\": \"subscribe/pattern/c/%c/denied\", \"allow\": False },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"subscribePattern\", \"topic\": \"subscribe/pattern/u/%u/allowed\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"subscribePattern\", \"topic\": \"subscribe/pattern/u/%u/denied\", \"allow\": False },\n    { \"command\": \"addGroupClient\", \"groupname\": \"mygroup\", \"username\": \"user_one\" }\n    ]}\n\nadd_client_group_role_response = {'responses': [\n    {'command': 'createGroup'},\n    {'command': 'createRole'},\n    {'command': 'addGroupRole'},\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'},\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'},\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'},\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'},\n    {'command': 'addRoleACL'},\n    {'command': 'addGroupClient'}\n    ]}\n\nadd_publish_acl_command = {\"commands\":[\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientSend\", \"topic\": \"simple/topic\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientSend\", \"topic\": \"single-wildcard/deny/deny\", \"priority\":10, \"allow\": False },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientSend\", \"topic\": \"single-wildcard/+/+\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientSend\", \"topic\": \"multilevel-wildcard/topic/#\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientSend\", \"topic\": \"pattern/u/%u/topic/#\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientSend\", \"topic\": \"pattern/u/%u/denied\", \"allow\": False },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientSend\", \"topic\": \"pattern/c/%c/topic/#\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientSend\", \"topic\": \"pattern/c/%c/denied\", \"allow\": False },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientReceive\", \"topic\": \"single-wildcard/bob/bob\", \"allow\": False },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientReceive\", \"topic\": \"multilevel-wildcard/topic/topic/denied\", \"allow\": False },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientReceive\", \"topic\": \"pattern/u/%u/topic/#\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientReceive\", \"topic\": \"pattern/u/%u/denied\", \"allow\": False },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientReceive\", \"topic\": \"pattern/c/%c/topic/#\", \"allow\": True },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"publishClientReceive\", \"topic\": \"pattern/c/%c/denied\", \"allow\": False },\n    ]}\n\nadd_publish_acl_response = {'responses': [\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'},\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'},\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'},\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'},\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'},\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'},\n    {'command': 'addRoleACL'}, {'command': 'addRoleACL'}\n    ]}\n\ndelete_role_command = {\"commands\":[\n    { \"command\": \"deleteRole\", \"rolename\": \"myrole\"}\n    ]}\ndelete_role_response = {'responses': [{'command': 'deleteRole'}]}\n\nrc = 1\nconnect_packet_admin = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet_admin = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet_admin = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet_admin = mosq_test.gen_suback(mid, 1)\n\n# Success\nconnect_packet_with_id1 = mosq_test.gen_connect(\"cid\", username=\"user_one\", password=\"password\", proto_ver=5)\nconnack_packet_with_id1 = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nmid = 4\nsubscribe_simple_packet = mosq_test.gen_subscribe(mid, \"simple/topic\", 0, proto_ver=5)\nsuback_simple_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5)\nsuback_simple_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 5\nsubscribe_single_packet = mosq_test.gen_subscribe(mid, \"single-wildcard/bob/topic\", 0, proto_ver=5)\nsuback_single_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5)\nsuback_single_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 6\nsubscribe_multi_packet = mosq_test.gen_subscribe(mid, \"multilevel-wildcard/topic/topic/#\", 0, proto_ver=5)\nsuback_multi_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5)\nsuback_multi_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 7\npublish_simple_packet = mosq_test.gen_publish(mid=mid, topic=\"simple/topic\", qos=1, payload=\"message\", proto_ver=5)\npuback_simple_packet_success = mosq_test.gen_puback(mid, proto_ver=5)\npuback_simple_packet_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\npublish_simple_packet_r = mosq_test.gen_publish(topic=\"simple/topic\", qos=0, payload=\"message\", proto_ver=5)\n\n# This message is in single-wildcard/+/+ so could be allowed, but the single-wildcard/deny/deny with higher priority should override\nmid = 9\npublish_single_packet_denied = mosq_test.gen_publish(mid=mid, topic=\"single-wildcard/deny/deny\", qos=1, payload=\"message\", proto_ver=5)\npuback_single_packet_denied_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 8\npublish_single_packet = mosq_test.gen_publish(mid=mid, topic=\"single-wildcard/bob/topic\", qos=1, payload=\"message\", proto_ver=5)\npuback_single_packet_success = mosq_test.gen_puback(mid, proto_ver=5)\npuback_single_packet_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\npublish_single_packet_r = mosq_test.gen_publish(topic=\"single-wildcard/bob/topic\", qos=0, payload=\"message\", proto_ver=5)\n\nmid = 9\npublish_multi_packet = mosq_test.gen_publish(mid=mid, topic=\"multilevel-wildcard/topic/topic/allowed\", qos=1, payload=\"message\", proto_ver=5)\npuback_multi_packet_success = mosq_test.gen_puback(mid, proto_ver=5)\npuback_multi_packet_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 10\npublish_multi_denied_packet = mosq_test.gen_publish(mid=mid, topic=\"multilevel-wildcard/topic/topic/denied\", qos=1, payload=\"message\", proto_ver=5)\npuback_multi_denied_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\npublish_multi_packet_r = mosq_test.gen_publish(topic=\"multilevel-wildcard/topic/topic/allowed\", qos=0, payload=\"message\", proto_ver=5)\n\nmid = 11\nunsubscribe_simple_packet = mosq_test.gen_unsubscribe(mid, \"simple/topic\", proto_ver=5)\nunsuback_simple_packet_fail = mosq_test.gen_unsuback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 12\nunsubscribe_single_packet = mosq_test.gen_unsubscribe(mid, \"single-wildcard/bob/topic\", proto_ver=5)\nunsuback_single_packet_success = mosq_test.gen_unsuback(mid, 0, proto_ver=5)\n\nmid = 13\nunsubscribe_multi_packet = mosq_test.gen_unsubscribe(mid, \"multilevel-wildcard/topic/topic/#\", proto_ver=5)\nunsuback_multi_packet_success = mosq_test.gen_unsuback(mid, 0, proto_ver=5)\n\nmid = 14\nsubscribe_pattern_packet = mosq_test.gen_subscribe(mid, \"pattern/#\", 0, proto_ver=5)\nsuback_pattern_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\ndisconnect_kick_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.ADMINISTRATIVE_ACTION, proto_ver=5)\n\nmid = 15\npublish_u_pattern_packet = mosq_test.gen_publish(mid=mid, topic=\"pattern/u/user_one/topic\", qos=1, proto_ver=5, payload=\"test\")\npuback_u_packet_success = mosq_test.gen_puback(mid=mid, proto_ver=5)\npublish_u_pattern_packet_r = mosq_test.gen_publish(topic=\"pattern/u/user_one/topic\", qos=0, proto_ver=5, payload=\"test\")\n\nmid = 16\npublish_u_pattern_packet_denied = mosq_test.gen_publish(mid=mid, topic=\"pattern/u/user_one/denied\", qos=1, proto_ver=5, payload=\"test\")\npuback_u_packet_denied = mosq_test.gen_puback(mid=mid, reason_code=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 17\npublish_c_pattern_packet = mosq_test.gen_publish(mid=mid, topic=\"pattern/c/cid/topic\", qos=1, proto_ver=5, payload=\"test\")\npuback_c_packet_success = mosq_test.gen_puback(mid=mid, proto_ver=5)\npublish_c_pattern_packet_r = mosq_test.gen_publish(topic=\"pattern/c/cid/topic\", qos=0, proto_ver=5, payload=\"test\")\n\nmid = 18\npublish_c_pattern_packet_denied = mosq_test.gen_publish(mid=mid, topic=\"pattern/c/cid/denied\", qos=1, proto_ver=5, payload=\"test\")\npuback_c_packet_denied = mosq_test.gen_puback(mid=mid, reason_code=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 19\nsubscribe_u_pattern_packet_allowed_success = mosq_test.gen_subscribe(mid, \"subscribe/pattern/u/user_one/allowed\", qos=1, proto_ver=5)\nsuback_u_pattern_packet_allowed_success = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\nmid = 20\nsubscribe_u_pattern_packet_allowed_fail = mosq_test.gen_subscribe(mid, \"subscribe/pattern/u/bad/allowed\", qos=1, proto_ver=5)\nsuback_u_pattern_packet_allowed_fail = mosq_test.gen_suback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 21\nsubscribe_u_pattern_packet_denied_success = mosq_test.gen_subscribe(mid, \"subscribe/pattern/u/user_one/denied\", qos=1, proto_ver=5)\nsuback_u_pattern_packet_denied_success = mosq_test.gen_suback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 22\nsubscribe_c_pattern_packet_allowed_success = mosq_test.gen_subscribe(mid, \"subscribe/pattern/c/cid/allowed\", qos=1, proto_ver=5)\nsuback_c_pattern_packet_allowed_success = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\nmid = 23\nsubscribe_c_pattern_packet_allowed_fail = mosq_test.gen_subscribe(mid, \"subscribe/pattern/c/bad/allowed\", qos=1, proto_ver=5)\nsuback_c_pattern_packet_allowed_fail = mosq_test.gen_suback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\nmid = 24\nsubscribe_c_pattern_packet_denied_success = mosq_test.gen_subscribe(mid, \"subscribe/pattern/c/cid/denied\", qos=1, proto_ver=5)\nsuback_c_pattern_packet_denied_success = mosq_test.gen_suback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\n\ntry:\n    if not os.path.exists(str(port)):\n        os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent) + \"/dynamic-security-init.json\", \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet_admin, connack_packet_admin, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet_admin, suback_packet_admin, \"suback\")\n\n    # Add client\n    command_check(sock, add_client_command_with_id, add_client_response_with_id)\n\n    # Client with username, password, and client id\n    csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error=\"connack 1\")\n\n    # Subscribe to \"simple/topic\" - not allowed\n    mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_fail, \"suback simple 1\")\n\n    # Subscribe to \"single-wildcard/bob/topic\" - not allowed\n    mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_fail, \"suback single 1\")\n\n    # Subscribe to \"multilevel-wildcard/topic/topic/topic\" - not allowed\n    mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_fail, \"suback multi 1\")\n\n    # Publish to \"simple/topic\" - not allowed\n    mosq_test.do_send_receive(csock, publish_simple_packet, puback_simple_packet_fail, \"puback simple 1\")\n\n    # Publish to \"single-wildcard/bob/topic\" - not allowed\n    mosq_test.do_send_receive(csock, publish_single_packet, puback_single_packet_fail, \"puback single 1\")\n\n    # Publish to \"multilevel-wildcard/topic/topic/topic\" - not allowed\n    mosq_test.do_send_receive(csock, publish_multi_packet, puback_multi_packet_fail, \"puback multi 1\")\n\n    # Create a group, add a role to the group, add the client to the group\n    # Add some subscribe/unsubscribe ACLs - this will kick the client\n    command_check(sock, add_client_group_role_command, add_client_group_role_response)\n\n    mosq_test.expect_packet(csock, \"disconnect kick 1\", disconnect_kick_packet)\n    csock.close()\n\n    # Reconnect\n    csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error=\"connack 2\")\n\n    # Subscribe to \"simple/topic\" - this is now allowed\n    mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_success, \"suback simple 2\")\n\n    # Subscribe to \"single-wildcard/bob/topic\" - this is now allowed\n    mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_success, \"suback single 2\")\n\n    # Subscribe to \"multilevel-wildcard/topic/topic/topic\" - this is now allowed\n    mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_success, \"suback multi 2\")\n\n    # Publish to \"simple/topic\" - not allowed\n    mosq_test.do_send_receive(csock, publish_simple_packet, puback_simple_packet_fail, \"puback 2\")\n\n    # Publish to \"single-wildcard/bob/topic\" - not allowed\n    mosq_test.do_send_receive(csock, publish_single_packet, puback_single_packet_fail, \"puback single 2\")\n\n    # Publish to \"multilevel-wildcard/topic/topic/topic\" - not allowed\n    mosq_test.do_send_receive(csock, publish_multi_packet, puback_multi_packet_fail, \"puback multi 2\")\n\n    # Add some publish ACLs - this will kick the client\n    command_check(sock, add_publish_acl_command, add_publish_acl_response)\n\n    mosq_test.expect_packet(csock, \"disconnect kick 2\", disconnect_kick_packet)\n    csock.close()\n\n    # Reconnect\n    csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error=\"connack 3\")\n\n    # Subscribe to \"simple/topic\" - this is now allowed\n    mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_success, \"suback simple 3\")\n\n    # Subscribe to \"single-wildcard/bob/topic\" - this is now allowed\n    mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_success, \"suback single 3\")\n\n    # Subscribe to \"multilevel-wildcard/topic/topic/allowed\" - this is now allowed\n    mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_success, \"suback multi 3\")\n\n    # Subscribe to \"pattern/#\" - allowed\n    mosq_test.do_send_receive(csock, subscribe_pattern_packet, suback_pattern_packet, \"suback\")\n\n    # Publish to \"simple/topic\" - this is now allowed\n    csock.send(publish_simple_packet)\n    mosq_test.receive_unordered(csock, publish_simple_packet_r, puback_simple_packet_success, \"puback simple 3 / publish r\")\n\n    # Publish to \"single-wildcard/bob/topic\" - this is now allowed\n    csock.send(publish_single_packet)\n    mosq_test.receive_unordered(csock, publish_single_packet_r, puback_single_packet_success, \"puback single 3 / publish r\")\n\n    # Publish to \"single-wildcard/deny/deny\" - this is stillnot allowed\n    mosq_test.do_send_receive(csock, publish_single_packet_denied, puback_single_packet_denied_fail, \"puback single denied 1\")\n\n    # Publish to \"multilevel-wildcard/topic/topic/allowed\" - this is now allowed\n    csock.send(publish_multi_packet)\n    mosq_test.receive_unordered(csock, publish_multi_packet_r, puback_multi_packet_success, \"puback multi 3 / publish r\")\n\n    # Publish to \"multilevel-wildcard/topic/topic/denied\" - receiving is denied by publishClientReceive\n    mosq_test.do_send_receive(csock, publish_multi_denied_packet, puback_multi_denied_packet, \"puback multi denied\")\n    mosq_test.do_ping(csock)\n\n    # Simple unsubscribe should be denied\n    mosq_test.do_send_receive(csock, unsubscribe_simple_packet, unsuback_simple_packet_fail, \"unsuback simple 1\")\n\n    # Single unsubscribe should be allowed\n    mosq_test.do_send_receive(csock, unsubscribe_single_packet, unsuback_single_packet_success, \"unsuback single 1\")\n\n    # Multi unsubscribe should be allowed\n    mosq_test.do_send_receive(csock, unsubscribe_multi_packet, unsuback_multi_packet_success, \"unsuback multi 1\")\n\n    # Publish to \"pattern/u/user_one/topic\" - this is allowed\n    csock.send(publish_u_pattern_packet)\n    mosq_test.receive_unordered(csock, publish_u_pattern_packet_r, puback_u_packet_success, \"puback pattern 1 / publish r\")\n\n    # Publish to \"pattern/u/user_one/denied\" - this is not allowed\n    mosq_test.do_send_receive(csock, publish_u_pattern_packet_denied, puback_u_packet_denied, \"puback pattern 2\")\n\n    # Publish to \"pattern/c/cid/topic\" - this is allowed\n    csock.send(publish_c_pattern_packet)\n    mosq_test.receive_unordered(csock, publish_c_pattern_packet_r, puback_c_packet_success, \"puback pattern 3 / publish r\")\n\n    # Publish to \"pattern/c/cid/denied\" - this is not allowed\n    mosq_test.do_send_receive(csock, publish_c_pattern_packet_denied, puback_c_packet_denied, \"puback pattern 4\")\n\n    # Subscribe to \"subscribe/pattern/u/user_one/allowed\" - this is allowed\n    mosq_test.do_send_receive(csock, subscribe_u_pattern_packet_allowed_success, suback_u_pattern_packet_allowed_success, \"suback pattern 1\")\n\n    # Subscribe to \"subscribe/pattern/u/bad/allowed\" - this is not allowed\n    mosq_test.do_send_receive(csock, subscribe_u_pattern_packet_allowed_fail, suback_u_pattern_packet_allowed_fail, \"suback pattern 2\")\n\n    # Subscribe to \"subscribe/pattern/u/user_one/denied\" - this is not allowed\n    mosq_test.do_send_receive(csock, subscribe_u_pattern_packet_denied_success, suback_u_pattern_packet_denied_success, \"suback pattern 3\")\n\n    # Subscribe to \"subscribe/pattern/c/cid/allowed\" - this is allowed\n    mosq_test.do_send_receive(csock, subscribe_c_pattern_packet_allowed_success, suback_c_pattern_packet_allowed_success, \"suback pattern 4\")\n\n    # Subscribe to \"subscribe/pattern/c/bad/allowed\" - this is not allowed\n    mosq_test.do_send_receive(csock, subscribe_c_pattern_packet_allowed_fail, suback_c_pattern_packet_allowed_fail, \"suback pattern 5\")\n\n    # Subscribe to \"subscribe/pattern/c/cid/denied\" - this is not allowed\n    mosq_test.do_send_receive(csock, subscribe_c_pattern_packet_denied_success, suback_c_pattern_packet_denied_success, \"suback pattern 6\")\n\n\n    # Delete the role, client should be kicked\n    command_check(sock, delete_role_command, delete_role_response)\n\n    mosq_test.expect_packet(csock, \"disconnect kick 3\", disconnect_kick_packet)\n    csock.close()\n\n    # Reconnect - these should all be denied again.\n    csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error=\"connack 4\")\n\n    # Subscribe to \"simple/topic\" - not allowed\n    mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_fail, \"suback simple 4\")\n\n    # Subscribe to \"single-wildcard/bob/topic\" - not allowed\n    mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_fail, \"suback single 4\")\n\n    # Subscribe to \"multilevel-wildcard/topic/topic/topic\" - not allowed\n    mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_fail, \"suback multi 4\")\n\n    # Publish to \"simple/topic\" - not allowed\n    mosq_test.do_send_receive(csock, publish_simple_packet, puback_simple_packet_fail, \"puback simple 4\")\n\n    # Publish to \"single-wildcard/bob/topic\" - not allowed\n    mosq_test.do_send_receive(csock, publish_single_packet, puback_single_packet_fail, \"puback single 4\")\n\n    # Publish to \"multilevel-wildcard/topic/topic/topic\" - not allowed\n    mosq_test.do_send_receive(csock, publish_multi_packet, puback_multi_packet_fail, \"puback multi 4\")\n\n    check_details(sock, 2, 1, 1, 29)\n\n    csock.close()\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    shutil.rmtree(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-allow-wildcard.py",
    "content": "#!/usr/bin/env python3\n\n# Test for allowwildcardsubs behaviour\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous false\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nadd_client_command_with_id = { \"commands\": [{\n    \"command\": \"createClient\", \"username\": \"user_one\",\n    \"password\": \"password\", \"clientid\": \"cid\",\n    \"correlationData\": \"2\" }]\n}\nadd_client_response_with_id = {'responses': [{'command': 'createClient', 'correlationData': '2'}]}\n\n\nadd_client_group_role_command = {\"commands\":[\n    { \"command\": \"createGroup\", \"groupname\": \"mygroup\" },\n    { \"command\": \"createRole\", \"rolename\": \"myrole\", \"allowwildcardsubs\": True},\n    { \"command\": \"addGroupRole\", \"groupname\": \"mygroup\", \"rolename\": \"myrole\" },\n    { \"command\": \"addRoleACL\", \"rolename\": \"myrole\", \"acltype\": \"subscribePattern\", \"topic\": \"multilevel-wildcard/#\", \"allow\": True },\n    { \"command\": \"addGroupClient\", \"groupname\": \"mygroup\", \"username\": \"user_one\" }\n    ]}\n\nadd_client_group_role_response = {'responses': [\n    {'command': 'createGroup'},\n    {'command': 'createRole'},\n    {'command': 'addGroupRole'},\n    {'command': 'addRoleACL'},\n    {'command': 'addGroupClient'}\n    ]}\n\nmodify_role_command = {\"commands\":[\n    { \"command\": \"modifyRole\", \"rolename\": \"myrole\", \"allowwildcardsubs\": False}\n    ]}\n\nmodify_role_response = {\"responses\":[\n    { \"command\": \"modifyRole\"}\n    ]}\n\nrc = 1\nconnect_packet_admin = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet_admin = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet_admin = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet_admin = mosq_test.gen_suback(mid, 1)\n\n# Success\nconnect_packet = mosq_test.gen_connect(\"cid\", username=\"user_one\", password=\"password\", proto_ver=5)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nmid = 4\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"multilevel-wildcard/#\", 0, proto_ver=5)\nsuback_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5)\nsuback_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\n\ndisconnect_kick_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.ADMINISTRATIVE_ACTION, proto_ver=5)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet_admin, connack_packet_admin, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet_admin, suback_packet_admin, \"suback\")\n\n    # Add client\n    command_check(sock, add_client_command_with_id, add_client_response_with_id)\n\n    # Create a group, add a role to the group, add the client to the group\n    command_check(sock, add_client_group_role_command, add_client_group_role_response)\n\n    # Client with username, password, and client id\n    csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port, connack_error=\"connack 1\")\n\n    # Subscribe to \"multilevel-wildcard/#\" - allowed\n    mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_success, \"suback # allowed\")\n\n    # Modify role - this will kick the client and remove the ability to subscribe to wildcards\n    command_check(sock, modify_role_command, modify_role_response)\n\n    mosq_test.expect_packet(csock, \"disconnect kick 1\", disconnect_kick_packet)\n    csock.close()\n\n    # Reconnect\n    csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port, connack_error=\"connack 2\")\n\n    # Subscribe to \"multilevel-wildcard/#\" - not allowed\n    mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_fail, \"suback # not allowed\")\n\n    csock.close()\n\n    check_details(sock, 2, 1, 2, 7)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    shutil.rmtree(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-anon-group.py",
    "content": "#!/usr/bin/env python3\n\n# Test the anonymous group support by adding a group, setting the anon group, adding a role to the group and checking a subscription.\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nget_anon_group_none_command = { \"commands\": [{\n    \"command\": \"getAnonymousGroup\",\n    \"correlationData\": \"2\" }]\n}\nget_anon_group_none_response = {'responses': [{'command': 'getAnonymousGroup',\n    'data': {'group': {'groupname': ''}},\n    'correlationData': '2'}]}\n\ncreate_group_set_anon_command = { \"commands\": [\n    { \"command\": \"createGroup\", \"groupname\": \"anon-clients\", \"correlationData\": \"3\" },\n    { \"command\": \"setAnonymousGroup\", \"groupname\": \"anon-clients\", \"correlationData\": \"4\" }\n    ]\n}\ncreate_group_set_anon_response = {'responses': [\n    {'command': 'createGroup', 'correlationData': '3'},\n    {'command': 'setAnonymousGroup', 'correlationData': '4'},\n    ]}\n\n\nget_anon_group_command = { \"commands\": [{\n    \"command\": \"getAnonymousGroup\",\n    \"correlationData\": \"3\" }]\n}\nget_anon_group_response = {'responses': [{'command': 'getAnonymousGroup',\n    'data': {'group': {'groupname': 'anon-clients'}},\n    'correlationData': '3'}]}\n\n\ncreate_role_apply_command = { \"commands\": [\n    { \"command\": \"createRole\", \"rolename\": \"anon\", \"correlationData\": \"4\" },\n    { \"command\": \"addRoleACL\", \"rolename\": \"anon\",\n        \"acltype\": \"subscribeLiteral\", \"topic\": \"anon/topic\", \"allow\": True,\n        \"correlationData\": \"5\" },\n    { \"command\": \"addGroupRole\", \"groupname\": \"anon-clients\",\n        \"rolename\": \"anon\", \"correlationData\": \"6\"}\n    ]\n}\ncreate_role_apply_response = {'responses': [\n    {'command': 'createRole', 'correlationData': '4'},\n    {'command': 'addRoleACL', 'correlationData': '5'},\n    {'command': 'addGroupRole', 'correlationData': '6'}\n    ]}\n\ndelete_anon_group_command = { \"commands\": [\n    { \"command\": \"deleteGroup\", \"groupname\": \"anon-clients\", \"correlationData\": \"40\" }\n    ]\n}\ndelete_anon_group_response = {'responses': [\n    {'command': 'deleteGroup', \"error\":'Deleting the anonymous group is forbidden', 'correlationData': '40'}\n    ]}\n\ndelete_anon_group_command = { \"commands\": [\n    { \"command\": \"deleteGroup\", \"groupname\": \"anon-clients\", \"correlationData\": \"40\" }\n    ]\n}\ndelete_anon_group_response = {'responses': [\n    {'command': 'deleteGroup', \"error\":'Deleting the anonymous group is forbidden', 'correlationData': '40'}\n    ]}\n\n\n\nrc = 1\n\n# Admin\nconnect_packet_admin = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet_admin = mosq_test.gen_connack(rc=0)\n\nmid = 1\nsubscribe_packet_admin = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet_admin = mosq_test.gen_suback(mid, 1)\n\n# Client\nconnect_packet = mosq_test.gen_connect(\"cid\", proto_ver=5)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nmid = 1\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"anon/topic\", qos=1, proto_ver=5)\nsuback_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\nsuback_packet_success = mosq_test.gen_suback(mid, 1, proto_ver=5)\n\ndisconnect_packet_kick = mosq_test.gen_disconnect(reason_code=mqtt5_rc.ADMINISTRATIVE_ACTION, proto_ver=5)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet_admin, connack_packet_admin, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet_admin, suback_packet_admin, \"suback admin\")\n\n    # Add client\n    command_check(sock, get_anon_group_none_command, get_anon_group_none_response)\n\n    # Client is anon, there is no anon group, so subscribe should fail\n    csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_fail, \"suback 1\")\n\n    # Add group, and set to anon\n    command_check(sock, create_group_set_anon_command, create_group_set_anon_response)\n    command_check(sock, get_anon_group_command, get_anon_group_response)\n\n    # Anon group is changed, so we are kicked\n    mosq_test.expect_packet(csock, \"disconnect 1\", disconnect_packet_kick)\n    csock.close()\n\n    # Reconnect, subscribe should still fail\n    csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_fail, \"suback 2\")\n\n    # Add role with subscribe ACL, and apply to anon group\n    command_check(sock, create_role_apply_command, create_role_apply_response)\n\n    # Anon group is changed, so we are kicked\n    mosq_test.expect_packet(csock, \"disconnect 2\", disconnect_packet_kick)\n    csock.close()\n\n    # Reconnect, subscribe should now succeed\n    csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_success, \"suback 3\")\n\n    # Try to delete anon group, this should fail\n    command_check(sock, delete_anon_group_command, delete_anon_group_response)\n\n    check_details(sock, 1, 1, 2, 5)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-auth.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous false\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nadd_client_command_with_id = { \"commands\": [{\n    \"command\": \"createClient\", \"username\": \"user_one\",\n    \"password\": \"password\", \"clientid\": \"cid\",\n    \"correlationData\": \"2\" }]\n}\nadd_client_response_with_id = {'responses': [{'command': 'createClient', 'correlationData': '2'}]}\n\nadd_client_command_without_id = { \"commands\": [{\n    \"command\": \"createClient\", \"username\": \"user_two\",\n    \"password\": \"asdfgh\",\n    \"correlationData\": \"3\" }]\n}\nadd_client_response_without_id = {'responses': [{'command': 'createClient', 'correlationData': '3'}]}\n\nset_client_id_command = { \"commands\": [{\n    \"command\": \"setClientId\", \"username\": \"user_two\", \"clientid\": \"new-cid\",\n    \"correlationData\": \"5\" }]\n}\nset_client_id_response = {'responses': [{'command': 'setClientId', 'correlationData': '5'}]}\n\n# No password defined, this client should never be able to connect.\nadd_client_command_without_pw = { \"commands\": [{\n    \"command\": \"createClient\", \"username\": \"user_three\",\n    \"correlationData\": \"4\" }]\n}\nadd_client_response_without_pw = {'responses': [{'command': 'createClient', 'correlationData': '4'}]}\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\n# Success\nconnect_packet_with_id1 = mosq_test.gen_connect(\"cid\", username=\"user_one\", password=\"password\", proto_ver=5)\nconnack_packet_with_id1 = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n# Fail - bad client id\nconnect_packet_with_id2 = mosq_test.gen_connect(\"bad-cid\", username=\"user_one\", password=\"password\", proto_ver=5)\nconnack_packet_with_id2 = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, property_helper=False)\n\n# Fail - bad password\nconnect_packet_with_id3 = mosq_test.gen_connect(\"cid\", username=\"user_one\", password=\"ttt\", proto_ver=5)\nconnack_packet_with_id3 = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, property_helper=False)\n\n# Fail - no password\nconnect_packet_with_id4 = mosq_test.gen_connect(\"cid\", username=\"user_one\", proto_ver=5)\nconnack_packet_with_id4 = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, property_helper=False)\n\n# Success\nconnect_packet_without_id1 = mosq_test.gen_connect(\"no-cid\", username=\"user_two\", password=\"asdfgh\", proto_ver=5)\nconnack_packet_without_id1 = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n# Fail - bad password\nconnect_packet_without_id2 = mosq_test.gen_connect(\"no-cid\", username=\"user_two\", password=\"pass\", proto_ver=5)\nconnack_packet_without_id2 = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, property_helper=False)\n\n# Fail - no password\nconnect_packet_without_id3 = mosq_test.gen_connect(\"no-cid\", username=\"user_two\", proto_ver=5)\nconnack_packet_without_id3 = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, property_helper=False)\n\n# Success\nconnect_packet_set_id1 = mosq_test.gen_connect(\"new-cid\", username=\"user_two\", password=\"asdfgh\", proto_ver=5)\nconnack_packet_set_id1 = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n# Fail - bad client id\nconnect_packet_set_id2 = mosq_test.gen_connect(\"bad-cid\", username=\"user_two\", password=\"asdfgh\", proto_ver=5)\nconnack_packet_set_id2 = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, property_helper=False)\n\n\n# Fail - bad password\nconnect_packet_without_pw1 = mosq_test.gen_connect(\"cid2\", username=\"user_three\", password=\"pass\", proto_ver=5)\nconnack_packet_without_pw1 = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, property_helper=False)\n\n# Fail - no password\nconnect_packet_without_pw2 = mosq_test.gen_connect(\"cid2\", username=\"user_three\", proto_ver=5)\nconnack_packet_without_pw2 = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, property_helper=False)\n\n# Fail - no username\nconnect_packet_without_un = mosq_test.gen_connect(\"cid3\", proto_ver=5)\nconnack_packet_without_un = mosq_test.gen_connack(rc=mqtt5_rc.NOT_AUTHORIZED, proto_ver=5, property_helper=False)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Add client\n    command_check(sock, add_client_command_with_id, add_client_response_with_id)\n    command_check(sock, add_client_command_without_id, add_client_response_without_id)\n    command_check(sock, add_client_command_without_pw, add_client_response_without_pw)\n\n    # Client with username, password, and client id\n    csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error=\"with id 1\")\n    csock.close()\n\n    csock = mosq_test.do_client_connect(connect_packet_with_id2, connack_packet_with_id2, timeout=5, port=port, connack_error=\"with id 2\")\n    csock.close()\n\n    csock = mosq_test.do_client_connect(connect_packet_with_id3, connack_packet_with_id3, timeout=5, port=port, connack_error=\"with id 3\")\n    csock.close()\n\n    csock = mosq_test.do_client_connect(connect_packet_with_id4, connack_packet_with_id4, timeout=5, port=port, connack_error=\"with id 4\")\n    csock.close()\n\n    # Client with just username and password\n    csock = mosq_test.do_client_connect(connect_packet_without_id1, connack_packet_without_id1, timeout=5, port=port, connack_error=\"without id 1\")\n    csock.close()\n\n    csock = mosq_test.do_client_connect(connect_packet_without_id2, connack_packet_without_id2, timeout=5, port=port, connack_error=\"without id 2\")\n    csock.close()\n\n    csock = mosq_test.do_client_connect(connect_packet_without_id3, connack_packet_without_id3, timeout=5, port=port, connack_error=\"without id 3\")\n    csock.close()\n\n    # Client with no password set\n    csock = mosq_test.do_client_connect(connect_packet_without_pw1, connack_packet_without_pw1, timeout=5, port=port, connack_error=\"without pw 1\")\n    csock.close()\n\n    csock = mosq_test.do_client_connect(connect_packet_without_pw2, connack_packet_without_pw2, timeout=5, port=port, connack_error=\"without pw 2\")\n    csock.close()\n\n    # Add client id to \"user_two\"\n    command_check(sock, set_client_id_command, set_client_id_response)\n\n    csock = mosq_test.do_client_connect(connect_packet_set_id1, connack_packet_set_id1, timeout=5, port=port, connack_error=\"set id 1\")\n    csock.close()\n\n    csock = mosq_test.do_client_connect(connect_packet_set_id2, connack_packet_set_id2, timeout=5, port=port, connack_error=\"set id 2\")\n    csock.close()\n\n    # No username, anon disabled\n    csock = mosq_test.do_client_connect(connect_packet_without_un, connack_packet_without_un, timeout=5, port=port, connack_error=\"without username\")\n    csock.close()\n\n    check_details(sock, 4, 0, 1, 4)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-client-invalid.py",
    "content": "#!/usr/bin/env python3\n\n# Check invalid inputs for client commands\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\n# ==========================================================================\n# Create client\n# ==========================================================================\n\n# No username\ncreate_client1_command = { 'commands': [{'command': 'createClient' }] }\ncreate_client1_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\ncreate_client2_command = { 'commands': [{'command': 'createClient', 'username': 5 }] }\ncreate_client2_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\ncreate_client3_command = { 'commands': [{'command': 'createClient', 'username': '￿LO' }] }\ncreate_client3_response = {'responses': [{'command': 'createClient', 'error': 'Username not valid UTF-8'}]}\n\n# Password not a string\ncreate_client4_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':5 }] }\ncreate_client4_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing password'}]}\n\n# Client id not a string\ncreate_client5_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'clientid':5}] }\ncreate_client5_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing client id'}]}\n\n# Client id not UTF-8\ncreate_client6_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'clientid':'￿LO' }] }\ncreate_client6_response = {'responses': [{'command': 'createClient', 'error': 'Client ID not valid UTF-8'}]}\n\n# Text name not a string\ncreate_client7_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'textname':5}] }\ncreate_client7_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing textname'}]}\n\n# Text description not a string\ncreate_client8_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'textdescription':5}] }\ncreate_client8_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing textdescription'}]}\n\n# Client already exists\ncreate_client9_command = { 'commands': [{'command': 'createClient', 'username': 'admin', 'password':'5'}]}\ncreate_client9_response = {'responses': [{'command': 'createClient', 'error': 'Client already exists'}]}\n\n# Roles not an array\ncreate_client10_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'roles':'bad'}] }\ncreate_client10_response = {'responses': [{'command': 'createClient', 'error': \"'roles' not an array or missing/invalid rolename\"}]}\n\n# Role not found\ncreate_client11_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'roles':[{'rolename':'notfound'}]}] }\ncreate_client11_response = {'responses': [{'command': 'createClient', 'error': 'Role not found'}]}\n\n# Group not found\ncreate_client12_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'groups':[{'groupname':'notfound'}]}] }\ncreate_client12_response = {'responses': [{'command': 'createClient', 'error': 'Group not found'}]}\n\n\n# ==========================================================================\n# Delete client\n# ==========================================================================\n\n# No username\ndelete_client1_command = { 'commands': [{'command': 'deleteClient'}]}\ndelete_client1_response = {'responses': [{'command': 'deleteClient', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\ndelete_client2_command = { 'commands': [{'command': 'deleteClient', 'username':5}]}\ndelete_client2_response = {'responses': [{'command': 'deleteClient', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\ndelete_client3_command = { 'commands': [{'command': 'deleteClient', 'username': '￿LO' }] }\ndelete_client3_response = {'responses': [{'command': 'deleteClient', 'error': 'Username not valid UTF-8'}]}\n\n# Client not found\ndelete_client4_command = { 'commands': [{'command': 'deleteClient', 'username':'notfound'}]}\ndelete_client4_response = {'responses': [{'command': 'deleteClient', 'error': 'Client not found'}]}\n\n# ==========================================================================\n# Disable client\n# ==========================================================================\n\n# No username\ndisable_client1_command = { 'commands': [{'command': 'disableClient'}]}\ndisable_client1_response = {'responses': [{'command': 'disableClient', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\ndisable_client2_command = { 'commands': [{'command': 'disableClient', 'username':5}]}\ndisable_client2_response = {'responses': [{'command': 'disableClient', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\ndisable_client3_command = { 'commands': [{'command': 'disableClient', 'username': '￿LO' }] }\ndisable_client3_response = {'responses': [{'command': 'disableClient', 'error': 'Username not valid UTF-8'}]}\n\n# Client not found\ndisable_client4_command = { 'commands': [{'command': 'disableClient', 'username':'notfound'}]}\ndisable_client4_response = {'responses': [{'command': 'disableClient', 'error': 'Client not found'}]}\n\n\n# ==========================================================================\n# Enable client\n# ==========================================================================\n\n# No username\nenable_client1_command = { 'commands': [{'command': 'enableClient'}]}\nenable_client1_response = {'responses': [{'command': 'enableClient', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\nenable_client2_command = { 'commands': [{'command': 'enableClient', 'username':5}]}\nenable_client2_response = {'responses': [{'command': 'enableClient', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\nenable_client3_command = { 'commands': [{'command': 'enableClient', 'username': '￿LO' }] }\nenable_client3_response = {'responses': [{'command': 'enableClient', 'error': 'Username not valid UTF-8'}]}\n\n# Client not found\nenable_client4_command = { 'commands': [{'command': 'enableClient', 'username':'notfound'}]}\nenable_client4_response = {'responses': [{'command': 'enableClient', 'error': 'Client not found'}]}\n\n\n# ==========================================================================\n# Set client id\n# ==========================================================================\n\n# No username\nset_client_id1_command = { 'commands': [{'command': 'setClientId'}]}\nset_client_id1_response = {'responses': [{'command': 'setClientId', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\nset_client_id2_command = { 'commands': [{'command': 'setClientId', 'username':5}]}\nset_client_id2_response = {'responses': [{'command': 'setClientId', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\nset_client_id3_command = { 'commands': [{'command': 'setClientId', 'username': '￿LO' }] }\nset_client_id3_response = {'responses': [{'command': 'setClientId', 'error': 'Username not valid UTF-8'}]}\n\n# No client id\nset_client_id4_command = { 'commands': [{'command': 'setClientId', 'username':'user'}]}\nset_client_id4_response = {'responses': [{'command': 'setClientId', 'error': 'Client not found'}]}\n\n# Client id not a string\nset_client_id5_command = { 'commands': [{'command': 'setClientId', 'username':'user', 'clientid':5}]}\nset_client_id5_response = {'responses': [{'command': 'setClientId', 'error': 'Invalid/missing client ID'}]}\n\n# Client id not UTF-8\nset_client_id6_command = { 'commands': [{'command': 'setClientId', 'username':'user', 'clientid': '￿LO' }] }\nset_client_id6_response = {'responses': [{'command': 'setClientId', 'error': 'Client ID not valid UTF-8'}]}\n\n# Client not found\nset_client_id7_command = { 'commands': [{'command': 'setClientId', 'username':'notfound', 'clientid':'newid'}]}\nset_client_id7_response = {'responses': [{'command': 'setClientId', 'error': 'Client not found'}]}\n\n\n# ==========================================================================\n# Set password\n# ==========================================================================\n\n# No username\nset_password1_command = { 'commands': [{'command': 'setClientPassword'}]}\nset_password1_response = {'responses': [{'command': 'setClientPassword', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\nset_password2_command = { 'commands': [{'command': 'setClientPassword', 'username':5}]}\nset_password2_response = {'responses': [{'command': 'setClientPassword', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\nset_password3_command = { 'commands': [{'command': 'setClientPassword', 'username':'￿LO' }] }\nset_password3_response = {'responses': [{'command': 'setClientPassword', 'error': 'Username not valid UTF-8'}]}\n\n# No password\nset_password4_command = { 'commands': [{'command': 'setClientPassword', 'username':'user'}]}\nset_password4_response = {'responses': [{'command': 'setClientPassword', 'error': 'Invalid/missing password'}]}\n\n# password not a string\nset_password5_command = { 'commands': [{'command': 'setClientPassword', 'username':'user', 'password':5}]}\nset_password5_response = {'responses': [{'command': 'setClientPassword', 'error': 'Invalid/missing password'}]}\n\n# password is empty\nset_password6_command = { 'commands': [{'command': 'setClientPassword', 'username':'user', 'password':''}]}\nset_password6_response = {'responses': [{'command': 'setClientPassword', 'error': 'Empty password is not allowed'}]}\n\n# Client not found\nset_password7_command = { 'commands': [{'command': 'setClientPassword', 'username':'notfound', 'password':'newpw'}]}\nset_password7_response = {'responses': [{'command': 'setClientPassword', 'error': 'Client not found'}]}\n\n\n# ==========================================================================\n# Get client\n# ==========================================================================\n\n# No username\nget_client1_command = { 'commands': [{'command': 'getClient'}]}\nget_client1_response = {'responses': [{'command': 'getClient', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\nget_client2_command = { 'commands': [{'command': 'getClient', 'username':5}]}\nget_client2_response = {'responses': [{'command': 'getClient', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\nget_client3_command = { 'commands': [{'command': 'getClient', 'username':'￿LO' }] }\nget_client3_response = {'responses': [{'command': 'getClient', 'error': 'Username not valid UTF-8'}]}\n\n# Client not found\nget_client4_command = { 'commands': [{'command': 'getClient', 'username':'notfound'}]}\nget_client4_response = {'responses': [{'command': 'getClient', 'error': 'Client not found'}]}\n\n\n# ==========================================================================\n# Add role\n# ==========================================================================\n\n# No username\nadd_role1_command = { 'commands': [{'command': 'addClientRole'}]}\nadd_role1_response = {'responses': [{'command': 'addClientRole', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\nadd_role2_command = { 'commands': [{'command': 'addClientRole', 'username':5}]}\nadd_role2_response = {'responses': [{'command': 'addClientRole', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\nadd_role3_command = { 'commands': [{'command': 'addClientRole', 'username':'￿LO' }] }\nadd_role3_response = {'responses': [{'command': 'addClientRole', 'error': 'Username not valid UTF-8'}]}\n\n# No rolename\nadd_role4_command = { 'commands': [{'command': 'addClientRole', 'username':'user'}]}\nadd_role4_response = {'responses': [{'command': 'addClientRole', 'error': 'Invalid/missing rolename'}]}\n\n# rolename not a string\nadd_role5_command = { 'commands': [{'command': 'addClientRole', 'username':'user', 'rolename':5}]}\nadd_role5_response = {'responses': [{'command': 'addClientRole', 'error': 'Invalid/missing rolename'}]}\n\n# rolename not UTF-8\nadd_role6_command = { 'commands': [{'command': 'addClientRole', 'username':'user', 'rolename':'￿LO' }] }\nadd_role6_response = {'responses': [{'command': 'addClientRole', 'error': 'Role name not valid UTF-8'}]}\n\n# Client not found\nadd_role7_command = { 'commands': [{'command': 'addClientRole', 'username':'notfound', 'rolename':'notfound'}]}\nadd_role7_response = {'responses': [{'command': 'addClientRole', 'error': 'Client not found'}]}\n\n# Role not found\nadd_role8_command = { 'commands': [{'command': 'addClientRole', 'username':'admin', 'rolename':'notfound'}]}\nadd_role8_response = {'responses': [{'command': 'addClientRole', 'error': 'Role not found'}]}\n\n\n# ==========================================================================\n# Remove role\n# ==========================================================================\n\n# No username\nremove_role1_command = { 'commands': [{'command': 'removeClientRole'}]}\nremove_role1_response = {'responses': [{'command': 'removeClientRole', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\nremove_role2_command = { 'commands': [{'command': 'removeClientRole', 'username':5}]}\nremove_role2_response = {'responses': [{'command': 'removeClientRole', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\nremove_role3_command = { 'commands': [{'command': 'removeClientRole', 'username':'￿LO' }] }\nremove_role3_response = {'responses': [{'command': 'removeClientRole', 'error': 'Username not valid UTF-8'}]}\n\n# No rolename\nremove_role4_command = { 'commands': [{'command': 'removeClientRole', 'username':'user'}]}\nremove_role4_response = {'responses': [{'command': 'removeClientRole', 'error': 'Invalid/missing rolename'}]}\n\n# rolename not a string\nremove_role5_command = { 'commands': [{'command': 'removeClientRole', 'username':'user', 'rolename':5}]}\nremove_role5_response = {'responses': [{'command': 'removeClientRole', 'error': 'Invalid/missing rolename'}]}\n\n# rolename not UTF-8\nremove_role6_command = { 'commands': [{'command': 'removeClientRole', 'username':'user', 'rolename':'￿LO' }] }\nremove_role6_response = {'responses': [{'command': 'removeClientRole', 'error': 'Role name not valid UTF-8'}]}\n\n# Client not found\nremove_role7_command = { 'commands': [{'command': 'removeClientRole', 'username':'notfound', 'rolename':'notfound'}]}\nremove_role7_response = {'responses': [{'command': 'removeClientRole', 'error': 'Client not found'}]}\n\n# Role not found\nremove_role8_command = { 'commands': [{'command': 'removeClientRole', 'username':'admin', 'rolename':'notfound'}]}\nremove_role8_response = {'responses': [{'command': 'removeClientRole', 'error': 'Role not found'}]}\n\n\n# ==========================================================================\n# Modify client\n# ==========================================================================\n\n# Create a client to modify\nmodify_client0_command = { 'commands': [{'command': 'createClient', 'username':'user'}]}\nmodify_client0_response = {'responses': [{'command': 'createClient'}]}\n\n# No username\nmodify_client1_command = { 'commands': [{'command': 'modifyClient'}]}\nmodify_client1_response = {'responses': [{'command': 'modifyClient', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\nmodify_client2_command = { 'commands': [{'command': 'modifyClient', 'username':5}]}\nmodify_client2_response = {'responses': [{'command': 'modifyClient', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\nmodify_client3_command = { 'commands': [{'command': 'modifyClient', 'username':'￿LO' }] }\nmodify_client3_response = {'responses': [{'command': 'modifyClient', 'error': 'Username not valid UTF-8'}]}\n\n# roles not a list\nmodify_client4_command = { 'commands': [{'command': 'modifyClient', 'username':'user', 'password':'test', 'roles':'string'}]}\nmodify_client4_response = {'responses': [{'command': 'modifyClient', 'error': \"'roles' not an array or missing/invalid rolename\"}]}\n\n# No rolename\nmodify_client5_command = { 'commands': [{'command': 'modifyClient', 'username':'user', 'roles':[{'rolename':5}]}]}\nmodify_client5_response = {'responses': [{'command': 'modifyClient', 'error': \"'roles' not an array or missing/invalid rolename\"}]}\n\n# rolename not UTF-8\n#modify_client6_command = { 'commands': [{'command': 'modifyClient', 'username':'user', 'rolename':'￿LO' }] }\n#modify_client6_response = {'responses': [{'command': 'modifyClient', 'error': 'Username not valid UTF-8'}]}\n\n# Client not found\nmodify_client7_command = { 'commands': [{'command': 'modifyClient', 'username':'notfound', 'rolename':'notfound'}]}\nmodify_client7_response = {'responses': [{'command': 'modifyClient', 'error': 'Client not found'}]}\n\n# Role not found\nmodify_client8_command = { 'commands': [{'command': 'modifyClient', 'username':'user', 'roles':[{'rolename':'notfound'}]}]}\nmodify_client8_response = {'responses': [{'command': 'modifyClient', 'error': 'Role not found'}]}\n\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    command_check(sock, create_client1_command, create_client1_response, \"1\")\n    command_check(sock, create_client2_command, create_client2_response, \"2\")\n    command_check(sock, create_client3_command, create_client3_response, \"3\")\n    command_check(sock, create_client4_command, create_client4_response, \"4\")\n    command_check(sock, create_client5_command, create_client5_response, \"5\")\n    command_check(sock, create_client6_command, create_client6_response, \"6\")\n    command_check(sock, create_client7_command, create_client7_response, \"7\")\n    command_check(sock, create_client8_command, create_client8_response, \"8\")\n    command_check(sock, create_client9_command, create_client9_response, \"9\")\n    command_check(sock, create_client10_command, create_client10_response, \"10\")\n    command_check(sock, create_client11_command, create_client11_response, \"11\")\n    command_check(sock, create_client12_command, create_client12_response, \"12\")\n\n    command_check(sock, delete_client1_command, delete_client1_response, \"1\")\n    command_check(sock, delete_client2_command, delete_client2_response, \"2\")\n    #command_check(sock, delete_client3_command, delete_client3_response, \"3\")\n    command_check(sock, delete_client4_command, delete_client4_response, \"4\")\n\n    command_check(sock, disable_client1_command, disable_client1_response, \"1\")\n    command_check(sock, disable_client2_command, disable_client2_response, \"2\")\n    command_check(sock, disable_client3_command, disable_client3_response, \"3\")\n    command_check(sock, disable_client4_command, disable_client4_response, \"4\")\n\n    command_check(sock, enable_client1_command, enable_client1_response, \"1\")\n    command_check(sock, enable_client2_command, enable_client2_response, \"2\")\n    command_check(sock, enable_client3_command, enable_client3_response, \"3\")\n    command_check(sock, enable_client4_command, enable_client4_response, \"4\")\n\n    command_check(sock, set_client_id1_command, set_client_id1_response, \"1\")\n    command_check(sock, set_client_id2_command, set_client_id2_response, \"2\")\n    command_check(sock, set_client_id3_command, set_client_id3_response, \"3\")\n    command_check(sock, set_client_id4_command, set_client_id4_response, \"4\")\n    command_check(sock, set_client_id5_command, set_client_id5_response, \"5\")\n    command_check(sock, set_client_id6_command, set_client_id6_response, \"6\")\n    command_check(sock, set_client_id7_command, set_client_id7_response, \"7\")\n\n    command_check(sock, set_password1_command, set_password1_response, \"1\")\n    command_check(sock, set_password2_command, set_password2_response, \"2\")\n    command_check(sock, set_password3_command, set_password3_response, \"3\")\n    command_check(sock, set_password4_command, set_password4_response, \"4\")\n    command_check(sock, set_password5_command, set_password5_response, \"5\")\n    command_check(sock, set_password6_command, set_password6_response, \"6\")\n    command_check(sock, set_password7_command, set_password7_response, \"7\")\n\n    command_check(sock, get_client1_command, get_client1_response, \"1\")\n    command_check(sock, get_client2_command, get_client2_response, \"2\")\n    command_check(sock, get_client3_command, get_client3_response, \"3\")\n    command_check(sock, get_client4_command, get_client4_response, \"4\")\n\n    command_check(sock, add_role1_command, add_role1_response, \"1\")\n    command_check(sock, add_role2_command, add_role2_response, \"2\")\n    command_check(sock, add_role3_command, add_role3_response, \"3\")\n    command_check(sock, add_role4_command, add_role4_response, \"4\")\n    command_check(sock, add_role5_command, add_role5_response, \"5\")\n    command_check(sock, add_role6_command, add_role6_response, \"6\")\n    command_check(sock, add_role7_command, add_role7_response, \"7\")\n    command_check(sock, add_role8_command, add_role8_response, \"8\")\n\n    command_check(sock, remove_role1_command, remove_role1_response, \"1\")\n    command_check(sock, remove_role2_command, remove_role2_response, \"2\")\n    command_check(sock, remove_role3_command, remove_role3_response, \"3\")\n    command_check(sock, remove_role4_command, remove_role4_response, \"4\")\n    command_check(sock, remove_role5_command, remove_role5_response, \"5\")\n    command_check(sock, remove_role6_command, remove_role6_response, \"6\")\n    command_check(sock, remove_role7_command, remove_role7_response, \"7\")\n    command_check(sock, remove_role8_command, remove_role8_response, \"8\")\n\n    command_check(sock, modify_client0_command, modify_client0_response, \"1\")\n    command_check(sock, modify_client1_command, modify_client1_response, \"1\")\n    command_check(sock, modify_client2_command, modify_client2_response, \"2\")\n    command_check(sock, modify_client3_command, modify_client3_response, \"3\")\n    command_check(sock, modify_client4_command, modify_client4_response, \"4\")\n    command_check(sock, modify_client5_command, modify_client5_response, \"5\")\n    #command_check(sock, modify_client6_command, modify_client6_response, \"6\")\n    command_check(sock, modify_client7_command, modify_client7_response, \"7\")\n    command_check(sock, modify_client8_command, modify_client8_response, \"8\")\n\n    check_details(sock, 2, 0, 1, 1)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-client.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nadd_client_command = { \"commands\": [{\n            \"command\": \"createClient\", \"username\": \"user_one\",\n            \"password\": \"password\", \"clientid\": \"cid\",\n            \"textname\": \"Name\", \"textdescription\": \"Description\",\n            \"rolename\": \"\", \"correlationData\": \"2\" }]\n}\nadd_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]}\nadd_client_repeat_response = {'responses':[{\"command\":\"createClient\",\"error\":\"Client already exists\", \"correlationData\":\"2\"}]}\n\nlist_clients_command = { \"commands\": [{\n            \"command\": \"listClients\", \"verbose\": False, \"correlationData\": \"10\"}]\n}\nlist_clients_response = {'responses': [{\"command\": \"listClients\", \"data\":{\"totalCount\":2, \"clients\":[\"admin\", \"user_one\"]},\"correlationData\":\"10\"}]}\n\nlist_clients_verbose_command = { \"commands\": [{\n            \"command\": \"listClients\", \"verbose\": True, \"correlationData\": \"20\"}]\n}\nlist_clients_verbose_response = {'responses':[{\"command\": \"listClients\", \"data\":{\"totalCount\":2, \"clients\":[\n    {'username': 'admin', 'textname': 'Dynsec admin user', 'roles': [{'rolename': 'admin'}], 'groups': [], 'connections': [{'address': '127.0.0.1'}]},\n    {\"username\":\"user_one\", \"clientid\":\"cid\", \"textname\":\"Name\", \"textdescription\":\"Description\",\n        \"roles\":[], \"groups\":[], 'connections': []}]}, \"correlationData\":\"20\"}]}\n\n\nget_client_command = { \"commands\": [{\n    \"command\": \"getClient\", \"username\": \"user_one\", \"correlationData\": \"42\"}]}\nget_client_response = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one', 'clientid': 'cid',\n    'textname': 'Name', 'textdescription': 'Description', 'groups': [], 'connections': [], 'roles': []}}, \"correlationData\":\"42\"}]}\n\nset_client_password_command = {\"commands\": [{\n    \"command\": \"setClientPassword\", \"username\": \"user_one\", \"password\": \"password\"}]}\nset_client_password_response = {\"responses\": [{\"command\":\"setClientPassword\"}]}\n\ndelete_client_command = { \"commands\": [{\n            \"command\": \"deleteClient\", \"username\": \"user_one\"}]}\ndelete_client_response = {'responses':[{'command': 'deleteClient'}]}\n\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nanon_connect_packet = mosq_test.gen_connect(\"anon-helper\")\nanon_connack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    # The anon user is used to ensure that when the commands are run they are also valid if an anon user is present.\n    anon_sock = mosq_test.do_client_connect(anon_connect_packet, anon_connack_packet, timeout=5, port=port)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Add client\n    command_check(sock, add_client_command, add_client_response)\n\n    # List clients non-verbose\n    command_check(sock, list_clients_command, list_clients_response)\n\n    # List clients verbose\n    command_check(sock, list_clients_verbose_command, list_clients_verbose_response)\n\n    # Kill broker and restart, checking whether our changes were saved.\n    broker.terminate()\n    broker_terminate_rc = 0\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        broker_terminate_rc = 1\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Get client\n    command_check(sock, get_client_command, get_client_response)\n\n    # List clients non-verbose\n    command_check(sock, list_clients_command, list_clients_response)\n\n    # List clients verbose\n    command_check(sock, list_clients_verbose_command, list_clients_verbose_response)\n\n    # Add duplicate client\n    command_check(sock, add_client_command, add_client_repeat_response)\n\n    # Set client password\n    command_check(sock, set_client_password_command, set_client_password_response)\n\n    # Delete client\n    command_check(sock, delete_client_command, delete_client_response)\n\n    # Check number of changes is correct\n    check_details(sock, 1, 0, 1, 3)\n\n    rc = broker_terminate_rc\n\n    sock.close()\n    anon_sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-config-init-env.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous false\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\ntry:\n    os.mkdir(str(port))\nexcept FileExistsError:\n    pass\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"adminadminadmin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nenv = os.environ\nenv[\"MOSQUITTO_DYNSEC_PASSWORD\"] = \"adminadminadmin\"\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port, env=env, timeout=3)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    rc = 0\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    try:\n        os.remove(f\"{port}/dynamic-security.json.pw\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    broker.wait()\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-config-init-file.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous false\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n        f.write(\"plugin_opt_password_init_file %d/init\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\ntry:\n    os.mkdir(str(port))\n    with open(f\"{port}/init\", \"w\") as f:\n        f.write(\"adminadminadmin\\n\")\nexcept FileExistsError:\n    pass\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"adminadminadmin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port, timeout=3)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    rc = 0\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    try:\n        os.remove(f\"{port}/init\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    broker.wait()\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-config-init-random.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous false\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\ntry:\n    os.mkdir(str(port))\nexcept FileExistsError:\n    pass\n\nrc = 1\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port, timeout=3)\n\nwith open(f\"{port}/dynamic-security.json.pw\", \"r\") as f:\n    data = f.readlines()\n\nadmin_pw = data[0].split(\" \")[1].strip()\nuser_pw = data[1].split(\" \")[1].strip()\n\ntry:\n    # Admin user\n    connect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=admin_pw)\n    connack_packet = mosq_test.gen_connack(rc=0)\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n\n    # Subscribe should be allowed\n    mid = 2\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\n    suback_packet = mosq_test.gen_suback(mid, 1)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"admin suback\")\n    sock.close()\n\n    # Basic user\n    connect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"democlient\", password=user_pw)\n    connack_packet = mosq_test.gen_connack(rc=0)\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n\n    # Subscribe should not be allowed\n    mid = 2\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\n    suback_packet = mosq_test.gen_suback(mid, 128)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"user suback\")\n    sock.close()\n\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    try:\n        os.remove(f\"{port}/dynamic-security.json.pw\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    broker.wait()\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-default-access.py",
    "content": "#!/usr/bin/env python3\n\n# This tests the default ACL type access behaviour for when no ACL matches.\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous false\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nadd_client_command = { \"commands\": [{\n    \"command\": \"createClient\", \"username\": \"user_one\",\n    \"password\": \"password\", \"clientid\": \"cid\",\n    \"correlationData\": \"2\" }]\n}\nadd_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]}\n\nget_access_command = { \"commands\": [{\"command\": \"getDefaultACLAccess\", \"correlationData\": \"3\" }]}\nget_access_response = {'responses': [\n    {\n        \"command\": \"getDefaultACLAccess\",\n        'data': {'acls': [\n            {'acltype': 'publishClientSend', 'allow': False},\n            {'acltype': 'publishClientReceive', 'allow': True},\n            {'acltype': 'subscribe', 'allow': False},\n            {'acltype': 'unsubscribe', 'allow': True}\n        ]},\n        \"correlationData\": \"3\"\n    }]\n}\n\nallow_subscribe_command = { \"commands\": [\n    {\n        \"command\": \"setDefaultACLAccess\",\n        \"acls\":[\n            { \"acltype\": \"subscribe\", \"allow\": True }\n\t\t],\n        \"correlationData\": \"4\" }\n    ]\n}\nallow_subscribe_response = {'responses': [{'command': 'setDefaultACLAccess', 'correlationData': '4'}]}\n\nallow_publish_send_command = { \"commands\": [\n    {\n        \"command\": \"setDefaultACLAccess\",\n        \"acls\":[\n            { \"acltype\": \"publishClientSend\", \"allow\": True }\n\t\t],\n        \"correlationData\": \"5\" }\n    ]\n}\nallow_publish_send_response = {'responses': [{'command': 'setDefaultACLAccess', 'correlationData': '5'}]}\n\nallow_publish_recv_command = { \"commands\": [\n    {\n        \"command\": \"setDefaultACLAccess\",\n        \"acls\":[\n            { \"acltype\": \"publishClientReceive\", \"allow\": False }\n\t\t],\n        \"correlationData\": \"6\" }\n    ]\n}\nallow_publish_recv_response = {'responses': [{'command': 'setDefaultACLAccess', 'correlationData': '6'}]}\n\nallow_unsubscribe_command = { \"commands\": [\n    {\n        \"command\": \"setDefaultACLAccess\",\n        \"acls\":[\n            { \"acltype\": \"unsubscribe\", \"allow\": False }\n\t\t],\n        \"correlationData\": \"7\" }\n    ]\n}\nallow_unsubscribe_response = {'responses': [{'command': 'setDefaultACLAccess', 'correlationData': '7'}]}\n\nrc = 1\nconnect_packet_admin = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet_admin = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet_admin = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet_admin = mosq_test.gen_suback(mid, 1)\n\nconnect_packet = mosq_test.gen_connect(\"cid\", username=\"user_one\", password=\"password\", proto_ver=5)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nmid = 3\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"topic\", 0, proto_ver=5)\nsuback_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\nsuback_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\nmid = 4\nunsubscribe_packet = mosq_test.gen_unsubscribe(mid, \"topic\", proto_ver=5)\nunsuback_packet_fail = mosq_test.gen_unsuback(mid, mqtt5_rc.NOT_AUTHORIZED, proto_ver=5)\nunsuback_packet_success = mosq_test.gen_unsuback(mid, proto_ver=5)\n\nmid = 5\npublish_packet = mosq_test.gen_publish(topic=\"topic\", mid=mid, qos=1, payload=\"message\", proto_ver=5)\npuback_packet_fail = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.NOT_AUTHORIZED)\npuback_packet_success = mosq_test.gen_puback(mid, proto_ver=5)\n\npublish_packet_recv = mosq_test.gen_publish(topic=\"topic\", qos=0, payload=\"message\", proto_ver=5)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet_admin, connack_packet_admin, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet_admin, suback_packet_admin, \"admin suback\")\n\n    # Add client\n    command_check(sock, add_client_command, add_client_response)\n    command_check(sock, get_access_command, get_access_response)\n\n    csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n\n    # Subscribe should fail because default access is deny\n    mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_fail, \"suback fail\")\n\n    # Set default subscribe access to allow\n    command_check(sock, allow_subscribe_command, allow_subscribe_response)\n\n    # Subscribe should succeed because default access is now allowed\n    mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_success, \"suback success\")\n\n    # Publish should fail because publishClientSend default is denied\n    mosq_test.do_send_receive(csock, publish_packet, puback_packet_fail, \"puback fail\")\n\n    # Set default publish send access to allow\n    command_check(sock, allow_publish_send_command, allow_publish_send_response)\n\n    # Publish should now succeed because publishClientSend default is allow\n    # We also receive the message because publishClientReceive default is allow.\n    csock.send(publish_packet)\n    mosq_test.receive_unordered(csock, puback_packet_success, publish_packet_recv, \"puback success / publish recv\")\n\n    # Set default publish receive access to deny\n    command_check(sock, allow_publish_recv_command, allow_publish_recv_response)\n\n    # Publish should succeed because publishClientSend default is allow\n    # We should *not* receive the publish because it has been disabled.\n    mosq_test.do_send_receive(csock, publish_packet, puback_packet_success, \"puback success\")\n    mosq_test.do_ping(csock)\n\n    # Unsubscribe should succeed because default access is allowed\n    mosq_test.do_send_receive(csock, unsubscribe_packet, unsuback_packet_success, \"unsuback success\")\n\n    # Set default unsubscribe access to allow\n    command_check(sock, allow_unsubscribe_command, allow_unsubscribe_response)\n\n    # Subscribe should succeed because default access is allowed\n    mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_success, \"suback success 2\")\n\n    # Unsubscribe should fail because default access is no longer allowed\n    mosq_test.do_send_receive(csock, unsubscribe_packet, unsuback_packet_fail, \"unsuback fail\")\n\n    csock.close()\n\n    check_details(sock, 2, 0, 1, 5)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-disable-client.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nadd_client_command = { \"commands\": [{\n            \"command\": \"createClient\", \"username\": \"user_one\",\n            \"password\": \"password\", \"clientid\": \"cid\",\n            \"textname\": \"Name\", \"textdescription\": \"Description\",\n            \"rolename\": \"\", \"correlationData\": \"2\" }]\n}\nadd_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]}\nadd_client_repeat_response = {'responses':[{\"command\":\"createClient\",\"error\":\"Client already exists\", \"correlationData\":\"2\"}]}\n\nget_client_command = { \"commands\": [{\n            \"command\": \"getClient\", \"username\": \"user_one\"}]}\nget_client_response1 = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one', 'clientid': 'cid',\n            'textname': 'Name', 'textdescription': 'Description', 'groups': [], 'roles': [], 'connections': []}}}]}\nget_client_response2 = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one', 'clientid': 'cid',\n            'textname': 'Name', 'textdescription': 'Description', 'disabled':True, 'groups': [], 'roles': [], 'connections': []}}}]}\n\ndisable_client_command = { \"commands\": [{\n            \"command\": \"disableClient\", \"username\": \"user_one\"}]}\ndisable_client_response = {'responses':[{'command': 'disableClient'}]}\n\nenable_client_command = { \"commands\": [{\n            \"command\": \"enableClient\", \"username\": \"user_one\"}]}\nenable_client_response = {'responses':[{'command': 'enableClient'}]}\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nclient_connect_packet = mosq_test.gen_connect(\"cid\", username=\"user_one\", password=\"password\")\nclient_connack_packet1 = mosq_test.gen_connack(rc=5)\nclient_connack_packet2 = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Add client\n    command_check(sock, add_client_command, add_client_response)\n\n    # Get client\n    command_check(sock, get_client_command, get_client_response1)\n\n    # Disable client\n    command_check(sock, disable_client_command, disable_client_response)\n\n    # Get client - should be disabled\n    command_check(sock, get_client_command, get_client_response2)\n\n    # Try to log in - should fail\n    client_sock = mosq_test.do_client_connect(client_connect_packet, client_connack_packet1, timeout=5, port=port)\n\n    # Enable client\n    command_check(sock, enable_client_command, enable_client_response)\n\n    # Get client - should be enabled\n    command_check(sock, get_client_command, get_client_response1)\n\n    # Try to log in - should succeed\n    client_sock = mosq_test.do_client_connect(client_connect_packet, client_connack_packet2, timeout=5, port=port)\n    client_sock.close()\n\n    check_details(sock, 2, 0, 1, 3)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-group-invalid.py",
    "content": "#!/usr/bin/env python3\n\n# Check invalid inputs for group commands\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\n# Create client for modifying\ncreate_client0_command = { 'commands': [{'command': 'createClient', 'username':'validclient' }] }\ncreate_client0_response = {'responses': [{'command': 'createClient'}]}\n\n# Create group for modifying\ncreate_group0_command = { 'commands': [{'command': 'createGroup', 'groupname':'validgroup' }] }\ncreate_group0_response = {'responses': [{'command': 'createGroup'}]}\n\n# Create role for modifying\ncreate_role0_command = { 'commands': [{'command': 'createRole', 'rolename':'validrole' }] }\ncreate_role0_response = {'responses': [{'command': 'createRole'}]}\n\n# ==========================================================================\n# Create group\n# ==========================================================================\n\n# No groupname\ncreate_group1_command = { 'commands': [{'command': 'createGroup' }] }\ncreate_group1_response = {'responses': [{'command': 'createGroup', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not a string\ncreate_group2_command = { 'commands': [{'command': 'createGroup', 'groupname':5 }] }\ncreate_group2_response = {'responses': [{'command': 'createGroup', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not UTF-8\ncreate_group3_command = { 'commands': [{'command': 'createGroup', 'groupname': '￿LO' }] }\ncreate_group3_response = {'responses': [{'command': 'createGroup', 'error': 'Group name not valid UTF-8'}]}\n\n# textname not a string\ncreate_group4_command = { 'commands': [{'command': 'createGroup', 'groupname':'g', 'textname':5 }] }\ncreate_group4_response = {'responses': [{'command': 'createGroup', 'error': 'Invalid/missing textname'}]}\n\n# textdescription not a string\ncreate_group5_command = { 'commands': [{'command': 'createGroup', 'groupname':'g', 'textdescription':5 }] }\ncreate_group5_response = {'responses': [{'command': 'createGroup', 'error': 'Invalid/missing textdescription'}]}\n\n# Group already exists\ncreate_group6_command = { 'commands': [{'command': 'createGroup', 'groupname': 'validgroup'}]}\ncreate_group6_response = {'responses': [{'command': 'createGroup', 'error': 'Group already exists'}]}\n\n# Role not found\ncreate_group7_command = { 'commands': [{'command': 'createGroup', 'groupname': 'group', 'roles':[{'rolename':'notfound'}]}] }\ncreate_group7_response = {'responses': [{'command': 'createGroup', 'error': 'Role not found'}]}\n\n# ==========================================================================\n# Delete group\n# ==========================================================================\n\n# No groupname\ndelete_group1_command = { 'commands': [{'command': 'deleteGroup' }] }\ndelete_group1_response = {'responses': [{'command': 'deleteGroup', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not a string\ndelete_group2_command = { 'commands': [{'command': 'deleteGroup', 'groupname':5 }] }\ndelete_group2_response = {'responses': [{'command': 'deleteGroup', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not UTF-8\ndelete_group3_command = { 'commands': [{'command': 'deleteGroup', 'groupname': '￿LO' }] }\ndelete_group3_response = {'responses': [{'command': 'deleteGroup', 'error': 'Group name not valid UTF-8'}]}\n\n# Group not found\ndelete_group4_command = { 'commands': [{'command': 'deleteGroup', 'groupname': 'group'}]}\ndelete_group4_response = {'responses': [{'command': 'deleteGroup', 'error': 'Group not found'}]}\n\n# ==========================================================================\n# Add role\n# ==========================================================================\n\n# No groupname\nadd_role1_command = { 'commands': [{'command': 'addGroupRole' }] }\nadd_role1_response = {'responses': [{'command': 'addGroupRole', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not a string\nadd_role2_command = { 'commands': [{'command': 'addGroupRole', 'groupname':5 }] }\nadd_role2_response = {'responses': [{'command': 'addGroupRole', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not UTF-8\nadd_role3_command = { 'commands': [{'command': 'addGroupRole', 'groupname': '￿LO' }] }\nadd_role3_response = {'responses': [{'command': 'addGroupRole', 'error': 'Group name not valid UTF-8'}]}\n\n# No rolename\nadd_role4_command = { 'commands': [{'command': 'addGroupRole', 'groupname':'g' }] }\nadd_role4_response = {'responses': [{'command': 'addGroupRole', 'error': 'Invalid/missing rolename'}]}\n\n# Rolename not a string\nadd_role5_command = { 'commands': [{'command': 'addGroupRole', 'groupname':'g', 'rolename':5 }] }\nadd_role5_response = {'responses': [{'command': 'addGroupRole', 'error': 'Invalid/missing rolename'}]}\n\n# Rolename not UTF-8\nadd_role6_command = { 'commands': [{'command': 'addGroupRole', 'groupname':'g', 'rolename':'￿LO' }] }\nadd_role6_response = {'responses': [{'command': 'addGroupRole', 'error': 'Role name not valid UTF-8'}]}\n\n# Group not found\nadd_role7_command = { 'commands': [{'command': 'addGroupRole', 'groupname':'notfound', 'rolename':'notfound' }] }\nadd_role7_response = {'responses': [{'command': 'addGroupRole', 'error': 'Group not found'}]}\n\n# Role not found\nadd_role8_command = { 'commands': [{'command': 'addGroupRole', 'groupname':'validgroup', 'rolename':'notfound' }] }\nadd_role8_response = {'responses': [{'command': 'addGroupRole', 'error': 'Role not found'}]}\n\n\n# ==========================================================================\n# Remove role\n# ==========================================================================\n\n# No groupname\nremove_role1_command = { 'commands': [{'command': 'removeGroupRole' }] }\nremove_role1_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not a string\nremove_role2_command = { 'commands': [{'command': 'removeGroupRole', 'groupname':5 }] }\nremove_role2_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not UTF-8\nremove_role3_command = { 'commands': [{'command': 'removeGroupRole', 'groupname': '￿LO' }] }\nremove_role3_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Group name not valid UTF-8'}]}\n\n# No rolename\nremove_role4_command = { 'commands': [{'command': 'removeGroupRole', 'groupname':'g' }] }\nremove_role4_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Invalid/missing rolename'}]}\n\n# Rolename not a string\nremove_role5_command = { 'commands': [{'command': 'removeGroupRole', 'groupname':'g', 'rolename':5 }] }\nremove_role5_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Invalid/missing rolename'}]}\n\n# Rolename not UTF-8\nremove_role6_command = { 'commands': [{'command': 'removeGroupRole', 'groupname': 'g', 'rolename':'￿LO' }] }\nremove_role6_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Role name not valid UTF-8'}]}\n\n# Group not found\nremove_role7_command = { 'commands': [{'command': 'removeGroupRole', 'groupname':'notfound', 'rolename':'notfound' }] }\nremove_role7_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Group not found'}]}\n\n# Role not found\nremove_role8_command = { 'commands': [{'command': 'removeGroupRole', 'groupname':'validgroup', 'rolename':'notfound' }] }\nremove_role8_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Role not found'}]}\n\n\n# ==========================================================================\n# Add client\n# ==========================================================================\n\n# No groupname\nadd_client1_command = { 'commands': [{'command': 'addGroupClient', 'username':'g' }] }\nadd_client1_response = {'responses': [{'command': 'addGroupClient', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not a string\nadd_client2_command = { 'commands': [{'command': 'addGroupClient', 'groupname':5, 'username':'g' }] }\nadd_client2_response = {'responses': [{'command': 'addGroupClient', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not UTF-8\nadd_client3_command = { 'commands': [{'command': 'addGroupClient', 'groupname': '￿LO', 'username':'g' }] }\nadd_client3_response = {'responses': [{'command': 'addGroupClient', 'error': 'Group name not valid UTF-8'}]}\n\n# No username\nadd_client4_command = { 'commands': [{'command': 'addGroupClient', 'groupname':'g' }] }\nadd_client4_response = {'responses': [{'command': 'addGroupClient', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\nadd_client5_command = { 'commands': [{'command': 'addGroupClient', 'groupname':'g', 'username':5 }] }\nadd_client5_response = {'responses': [{'command': 'addGroupClient', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\nadd_client6_command = { 'commands': [{'command': 'addGroupClient', 'groupname':'g', 'username': '￿LO' }] }\nadd_client6_response = {'responses': [{'command': 'addGroupClient', 'error': 'Username not valid UTF-8'}]}\n\n# Group not found\nadd_client7_command = { 'commands': [{'command': 'addGroupClient', 'groupname':'notfound', 'username':'validclient' }] }\nadd_client7_response = {'responses': [{'command': 'addGroupClient', 'error': 'Group not found'}]}\n\n# Client not found\nadd_client8_command = { 'commands': [{'command': 'addGroupClient', 'groupname':'validgroup', 'username':'notfound' }] }\nadd_client8_response = {'responses': [{'command': 'addGroupClient', 'error': 'Client not found'}]}\n\n\n# ==========================================================================\n# Remove client\n# ==========================================================================\n\n# No groupname\nremove_client1_command = { 'commands': [{'command': 'removeGroupClient', 'username':'g' }] }\nremove_client1_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not a string\nremove_client2_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':5, 'username':'g' }] }\nremove_client2_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not UTF-8\nremove_client3_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'￿LO', 'username':'g' }] }\nremove_client3_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Group name not valid UTF-8'}]}\n\n# No username\nremove_client4_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'g' }] }\nremove_client4_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Invalid/missing username'}]}\n\n# Username not a string\nremove_client5_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'g', 'username':5 }] }\nremove_client5_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Invalid/missing username'}]}\n\n# Username not UTF-8\nremove_client6_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'g', 'username': '￿LO' }] }\nremove_client6_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Username not valid UTF-8'}]}\n\n# Group not found\nremove_client7_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'notfound', 'username':'validclient' }] }\nremove_client7_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Group not found'}]}\n\n# Client not found\nremove_client8_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'validgroup', 'username':'notfound' }] }\nremove_client8_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Client not found'}]}\n\n\n# ==========================================================================\n# Get group\n# ==========================================================================\n\n# No groupname\nget_group1_command = { 'commands': [{'command': 'getGroup'}] }\nget_group1_response = {'responses': [{'command': 'getGroup', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not a string\nget_group2_command = { 'commands': [{'command': 'getGroup', 'groupname':5}] }\nget_group2_response = {'responses': [{'command': 'getGroup', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not UTF-8\nget_group3_command = { 'commands': [{'command': 'getGroup', 'groupname':'￿LO' }] }\nget_group3_response = {'responses': [{'command': 'getGroup', 'error': 'Group name not valid UTF-8'}]}\n\n# Group not found\nget_group4_command = { 'commands': [{'command': 'getGroup', 'groupname':\"missing\"}] }\nget_group4_response = {'responses': [{'command': 'getGroup', 'error': 'Group not found'}]}\n\n# ==========================================================================\n# Set anon group\n# ==========================================================================\n\n# No groupname\nset_anon_group1_command = { 'commands': [{'command': 'setAnonymousGroup'}] }\nset_anon_group1_response = {'responses': [{'command': 'setAnonymousGroup', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not a string\nset_anon_group2_command = { 'commands': [{'command': 'setAnonymousGroup', 'groupname':5}] }\nset_anon_group2_response = {'responses': [{'command': 'setAnonymousGroup', 'error': 'Invalid/missing groupname'}]}\n\n# Groupname not UTF-8\nset_anon_group3_command = { 'commands': [{'command': 'setAnonymousGroup', 'groupname':'￿LO' }] }\nset_anon_group3_response = {'responses': [{'command': 'setAnonymousGroup', 'error': 'Group name not valid UTF-8'}]}\n\n# Group not found\nset_anon_group4_command = { 'commands': [{'command': 'setAnonymousGroup', 'groupname':'notfound' }] }\nset_anon_group4_response = {'responses': [{'command': 'setAnonymousGroup', 'error': 'Group not found'}]}\n\n# ==========================================================================\n# Modify group\n# ==========================================================================\n\n# No groupname\nmodify_group1_command = { 'commands': [{'command': 'modifyGroup'}]}\nmodify_group1_response = {'responses': [{'command': 'modifyGroup', 'error': 'Invalid/missing groupname'}]}\n\n# Group name not a string\nmodify_group2_command = { 'commands': [{'command': 'modifyGroup', 'groupname':5}]}\nmodify_group2_response = {'responses': [{'command': 'modifyGroup', 'error': 'Invalid/missing groupname'}]}\n\n# Group name not UTF-8\nmodify_group3_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'￿LO' }] }\nmodify_group3_response = {'responses': [{'command': 'modifyGroup', 'error': 'Group name not valid UTF-8'}]}\n\n# roles not a list\nmodify_group4_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'validgroup', 'password':'test', 'roles':'string'}]}\nmodify_group4_response = {'responses': [{'command': 'modifyGroup', 'error': \"'roles' not an array or missing/invalid rolename\"}]}\n\n# No rolename\nmodify_group5_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'validgroup', 'roles':[{}]}]}\nmodify_group5_response = {'responses': [{'command': 'modifyGroup', 'error': \"'roles' not an array or missing/invalid rolename\"}]}\n\n# rolename not a string\nmodify_group6_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'validgroup', 'roles':[{'rolename':5}]}]}\nmodify_group6_response = {'responses': [{'command': 'modifyGroup', 'error': \"'roles' not an array or missing/invalid rolename\"}]}\n\n# rolename not UTF-8\n#modify_group7_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'validgroup','roles':[{'rolename':'￿LO'}] }] }\n#modify_group7_response = {'responses': [{'command': 'modifyGroup', 'error': 'Role name not valid UTF-8'}]}\n\n# Group not found\nmodify_group8_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'notfound', 'rolename':'notfound'}]}\nmodify_group8_response = {'responses': [{'command': 'modifyGroup', 'error': 'Group not found'}]}\n\n# Role not found\nmodify_group9_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'validgroup', 'roles':[{'rolename':'notfound'}]}]}\nmodify_group9_response = {'responses': [{'command': 'modifyGroup', 'error': 'Role not found'}]}\n\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    command_check(sock, create_client0_command, create_client0_response, \"0\")\n    command_check(sock, create_group0_command, create_group0_response, \"0\")\n    command_check(sock, create_role0_command, create_role0_response, \"0\")\n\n    command_check(sock, create_group1_command, create_group1_response, \"1\")\n    command_check(sock, create_group2_command, create_group2_response, \"2\")\n    command_check(sock, create_group3_command, create_group3_response, \"3\")\n    command_check(sock, create_group4_command, create_group4_response, \"4\")\n    command_check(sock, create_group5_command, create_group5_response, \"5\")\n    command_check(sock, create_group6_command, create_group6_response, \"6\")\n    command_check(sock, create_group7_command, create_group7_response, \"7\")\n\n    command_check(sock, delete_group1_command, delete_group1_response, \"1\")\n    command_check(sock, delete_group2_command, delete_group2_response, \"2\")\n    command_check(sock, delete_group3_command, delete_group3_response, \"3\")\n    command_check(sock, delete_group4_command, delete_group4_response, \"4\")\n\n    command_check(sock, add_role1_command, add_role1_response, \"1\")\n    command_check(sock, add_role2_command, add_role2_response, \"2\")\n    command_check(sock, add_role3_command, add_role3_response, \"3\")\n    command_check(sock, add_role4_command, add_role4_response, \"4\")\n    command_check(sock, add_role5_command, add_role5_response, \"5\")\n    command_check(sock, add_role6_command, add_role6_response, \"6\")\n    command_check(sock, add_role7_command, add_role7_response, \"7\")\n    command_check(sock, add_role8_command, add_role8_response, \"8\")\n\n    command_check(sock, remove_role1_command, remove_role1_response, \"1\")\n    command_check(sock, remove_role2_command, remove_role2_response, \"2\")\n    command_check(sock, remove_role3_command, remove_role3_response, \"3\")\n    command_check(sock, remove_role4_command, remove_role4_response, \"4\")\n    command_check(sock, remove_role5_command, remove_role5_response, \"5\")\n    command_check(sock, remove_role6_command, remove_role6_response, \"6\")\n    command_check(sock, remove_role7_command, remove_role7_response, \"7\")\n    command_check(sock, remove_role8_command, remove_role8_response, \"8\")\n\n    command_check(sock, add_client1_command, add_client1_response, \"1\")\n    command_check(sock, add_client2_command, add_client2_response, \"2\")\n    command_check(sock, add_client3_command, add_client3_response, \"3\")\n    command_check(sock, add_client4_command, add_client4_response, \"4\")\n    command_check(sock, add_client5_command, add_client5_response, \"5\")\n    command_check(sock, add_client6_command, add_client6_response, \"6\")\n    command_check(sock, add_client7_command, add_client7_response, \"7\")\n    command_check(sock, add_client8_command, add_client8_response, \"8\")\n\n    command_check(sock, remove_client1_command, remove_client1_response, \"1\")\n    command_check(sock, remove_client2_command, remove_client2_response, \"2\")\n    command_check(sock, remove_client3_command, remove_client3_response, \"3\")\n    command_check(sock, remove_client4_command, remove_client4_response, \"4\")\n    command_check(sock, remove_client5_command, remove_client5_response, \"5\")\n    command_check(sock, remove_client6_command, remove_client6_response, \"6\")\n    command_check(sock, remove_client7_command, remove_client7_response, \"7\")\n    command_check(sock, remove_client8_command, remove_client8_response, \"8\")\n\n    command_check(sock, get_group1_command, get_group1_response, \"1\")\n    command_check(sock, get_group2_command, get_group2_response, \"2\")\n    command_check(sock, get_group3_command, get_group3_response, \"3\")\n    command_check(sock, get_group4_command, get_group4_response, \"4\")\n\n    command_check(sock, set_anon_group1_command, set_anon_group1_response, \"1\")\n    command_check(sock, set_anon_group2_command, set_anon_group2_response, \"2\")\n    command_check(sock, set_anon_group3_command, set_anon_group3_response, \"3\")\n    command_check(sock, set_anon_group4_command, set_anon_group4_response, \"4\")\n\n    command_check(sock, modify_group1_command, modify_group1_response, \"1\")\n    command_check(sock, modify_group2_command, modify_group2_response, \"2\")\n    command_check(sock, modify_group3_command, modify_group3_response, \"3\")\n    command_check(sock, modify_group4_command, modify_group4_response, \"4\")\n    command_check(sock, modify_group5_command, modify_group5_response, \"5\")\n    command_check(sock, modify_group6_command, modify_group6_response, \"6\")\n    #command_check(sock, modify_group7_command, modify_group7_response, \"7\")\n    command_check(sock, modify_group8_command, modify_group8_response, \"8\")\n    command_check(sock, modify_group9_command, modify_group9_response, \"9\")\n\n    check_details(sock, 2, 1, 2, 3)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-group.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\ncreate_role_command = { \"commands\": [{'command': 'createRole', 'correlationData': '3',\n    \"rolename\": \"basic\", \"acls\":[\n    {\"acltype\":\"publishClientSend\", \"topic\": \"out/#\", \"priority\":3, \"allow\": True}], \"textname\":\"name\", \"textdescription\":\"desc\"\n    }]}\ncreate_role_response = {'responses': [{'command': 'createRole', 'correlationData': '3'}]}\n\nadd_role_to_group_command = { \"commands\": [{'command': 'addGroupRole', 'correlationData': '4',\n    \"groupname\": \"group_one\", \"rolename\": \"basic\"\n    }]}\nadd_role_to_group_response = {'responses': [{'command': 'addGroupRole', 'correlationData': '4'}]}\n\ncreate_client_command = { \"commands\": [{\n            \"command\": \"createClient\", \"username\": \"user_one\",\n            \"password\": \"password\", \"clientid\": \"cid\",\n            \"textname\": \"Name\", \"textdescription\": \"description\",\n            \"rolename\": \"\", \"correlationData\": \"2\" }]}\ncreate_client_response = {'responses':[{\"command\":\"createClient\",\"correlationData\":\"2\"}]}\n\ncreate_client2_command = { \"commands\": [{\n            \"command\": \"createClient\", \"username\": \"user_two\",\n            \"password\": \"password\",\n            \"textname\": \"Name\", \"textdescription\": \"description\",\n            \"rolename\": \"\", \"correlationData\": \"1\" }]}\ncreate_client2_response = {'responses':[{\"command\":\"createClient\",\"correlationData\":\"1\"}]}\n\ncreate_group_command = { \"commands\": [{\n            \"command\": \"createGroup\", \"groupname\": \"group_one\",\n            \"textname\": \"Name\", \"textdescription\": \"description\",\n            \"correlationData\":\"3\"}]}\ncreate_group_response = {'responses':[{\"command\":\"createGroup\",\"correlationData\":\"3\"}]}\ncreate_group_repeat_response = {'responses':[{\"command\":\"createGroup\",\"error\":\"Group already exists\",\"correlationData\":\"3\"}]}\n\ncreate_group2_command = { \"commands\": [{\n            \"command\": \"createGroup\", \"groupname\": \"group_two\",\n            \"textname\": \"Name\", \"textdescription\": \"description\",\n            \"correlationData\":\"30\"}]}\ncreate_group2_response = {'responses':[{\"command\":\"createGroup\",\"correlationData\":\"30\"}]}\n\nlist_groups_command = { \"commands\": [{\n            \"command\": \"listGroups\", \"verbose\": False, \"correlationData\": \"10\"}]}\nlist_groups_response = {'responses':[{\"command\": \"listGroups\", \"data\":{\"totalCount\":2, \"groups\":[\"group_one\",\"group_two\"]},\"correlationData\":\"10\"}]}\n\nlist_groups_verbose_command = { \"commands\": [{\n            \"command\": \"listGroups\", \"verbose\": True, \"correlationData\": \"15\"}]}\nlist_groups_verbose_response = {'responses':[{'command': 'listGroups', 'data': {\"totalCount\":2, 'groups':[\n    {'groupname': 'group_one', 'textname': 'Name', 'textdescription': 'description', 'clients': [\n    {\"username\":\"user_one\"}, {\"username\":\"user_two\"}], \"roles\":[{'rolename':'basic'}]},\n    {'groupname': 'group_two', 'textname': 'Name', 'textdescription': 'description', 'clients': [\n    {\"username\":\"user_one\"}], \"roles\":[]}\n    ]},\n    'correlationData': '15'}]}\n\nlist_clients_verbose_command = { \"commands\": [{\n            \"command\": \"listClients\", \"verbose\": True, \"correlationData\": \"20\"}]}\nlist_clients_verbose_response = {'responses':[{\"command\": \"listClients\", \"data\":{\"totalCount\":3, \"clients\":[\n            {'username': 'admin', 'textname': 'Dynsec admin user', 'roles': [{'rolename': 'admin'}], 'groups': [], 'connections': [{'address': '127.0.0.1'}]},\n            {\"username\":\"user_one\", \"clientid\":\"cid\", \"textname\":\"Name\", \"textdescription\":\"description\",\n             \"groups\":[{\"groupname\":\"group_one\"}, {\"groupname\":\"group_two\"}], \"roles\":[], 'connections': []},\n            {\"username\":\"user_two\", \"textname\":\"Name\", \"textdescription\":\"description\",\n             \"groups\":[{\"groupname\":\"group_one\"}], \"roles\":[],  'connections': []},\n            ]}, \"correlationData\":\"20\"}]}\n\nget_group_command = { \"commands\": [{\"command\": \"getGroup\", \"groupname\":\"group_one\"}]}\nget_group_response = {'responses':[{'command': 'getGroup', 'data': {'group': {'groupname': 'group_one',\n    'textname':'Name', 'textdescription':'description', 'clients': [{\"username\":\"user_one\"}, {\"username\":\"user_two\"}], 'roles': [{'rolename':'basic'}]\n        }}}]}\n\nadd_client_to_group_command = {\"commands\": [{\"command\":\"addGroupClient\", \"username\":\"user_one\",\n            \"groupname\": \"group_one\", \"correlationData\":\"1234\"}]}\nadd_client_to_group_response = {'responses':[{'command': 'addGroupClient', 'correlationData': '1234'}]}\nadd_duplicate_client_to_group_response = {'responses':[{'command': 'addGroupClient', 'error':'Client is already in this group', 'correlationData': '1234'}]}\n\nadd_client_to_group2_command = {\"commands\": [{\"command\":\"addGroupClient\", \"username\":\"user_one\",\n            \"groupname\": \"group_two\", \"correlationData\":\"1234\"}]}\nadd_client_to_group2_response = {'responses':[{'command': 'addGroupClient', 'correlationData': '1234'}]}\n\nadd_client2_to_group_command = {\"commands\": [{\"command\":\"addGroupClient\", \"username\":\"user_two\",\n            \"groupname\": \"group_one\", \"correlationData\":\"1235\"}]}\nadd_client2_to_group_response = {'responses':[{'command': 'addGroupClient', 'correlationData': '1235'}]}\n\nremove_client_from_group_command = {\"commands\": [{\"command\":\"removeGroupClient\", \"username\":\"user_one\",\n            \"groupname\": \"group_one\", \"correlationData\":\"4321\"}]}\nremove_client_from_group_response = {'responses':[{'command': 'removeGroupClient', 'correlationData': '4321'}]}\n\ndelete_group_command = {\"commands\": [{\"command\":\"deleteGroup\", \"groupname\":\"group_two\", \"correlationData\":\"5678\"}]}\ndelete_group_response = {'responses':[{\"command\":\"deleteGroup\", \"correlationData\":\"5678\"}]}\n\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Add role\n    command_check(sock, create_role_command, create_role_response)\n\n    # Add client\n    command_check(sock, create_client_command, create_client_response)\n    command_check(sock, create_client2_command, create_client2_response)\n\n    # Add group\n    command_check(sock, create_group2_command, create_group2_response)\n    command_check(sock, create_group_command, create_group_response)\n\n    # Add client to group\n    command_check(sock, add_client_to_group_command, add_client_to_group_response)\n    command_check(sock, add_client_to_group2_command, add_client_to_group2_response)\n    command_check(sock, add_client2_to_group_command, add_client2_to_group_response)\n    command_check(sock, add_client_to_group_command, add_duplicate_client_to_group_response)\n\n    # Add role to group\n    command_check(sock, add_role_to_group_command, add_role_to_group_response)\n\n    # Get group\n    command_check(sock, get_group_command, get_group_response)\n\n    # List groups non-verbose\n    command_check(sock, list_groups_command, list_groups_response)\n\n    # List groups verbose\n    command_check(sock, list_groups_verbose_command, list_groups_verbose_response, \"list groups\")\n\n    # List clients verbose\n    command_check(sock, list_clients_verbose_command, list_clients_verbose_response)\n\n    # Kill broker and restart, checking whether our changes were saved.\n    broker.terminate()\n    broker_terminate_rc = 0\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        broker_terminate_rc = 1\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Add duplicate group\n    command_check(sock, create_group_command, create_group_repeat_response)\n\n    # Remove client from group\n    command_check(sock, remove_client_from_group_command, remove_client_from_group_response)\n\n    # Add client back to group\n    command_check(sock, add_client_to_group_command, add_client_to_group_response)\n\n    # Delete group entirely\n    command_check(sock, delete_group_command, delete_group_response)\n\n    check_details(sock, 3, 1, 2, 12)\n\n    rc = broker_terminate_rc\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-modify-client.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\ncreate_client_command = { \"commands\": [{\n            \"command\": \"createClient\", \"username\": \"user_one\",\n            \"password\": \"password\", \"clientid\": \"cid\",\n            \"textname\": \"Name\", \"textdescription\": \"Description\",\n            \"correlationData\": \"2\" }]\n}\ncreate_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]}\n\ncreate_groups_command = { \"commands\": [\n    {\n        \"command\": \"createGroup\", \"groupname\": \"group_one\",\n        \"textname\": \"Name\", \"textdescription\": \"Description\",\n        \"correlationData\": \"12\"\n    },\n    {\n        \"command\": \"createGroup\", \"groupname\": \"group_two\",\n        \"textname\": \"Name\", \"textdescription\": \"Description\",\n        \"correlationData\": \"13\"\n    }\n    ]\n}\ncreate_groups_response = {'responses': [\n    {'command': 'createGroup', 'correlationData': '12'},\n    {'command': 'createGroup', 'correlationData': '13'}\n    ]}\n\ncreate_roles_command = { \"commands\": [\n    {\n        \"command\": \"createRole\", \"rolename\": \"role_one\",\n        \"textname\": \"Name\", \"textdescription\": \"Description\",\n        \"acls\":[], \"correlationData\": \"21\"\n    },\n    {\n        \"command\": \"createRole\", \"rolename\": \"role_two\",\n        \"textname\": \"Name\", \"textdescription\": \"Description\",\n        \"acls\":[], \"correlationData\": \"22\"\n    },\n    {\n        \"command\": \"createRole\", \"rolename\": \"role_three\",\n        \"textname\": \"Name\", \"textdescription\": \"Description\",\n        \"acls\":[], \"correlationData\": \"23\"\n    }\n    ]\n}\ncreate_roles_response = {'responses': [\n    {'command': 'createRole', 'correlationData': '21'},\n    {'command': 'createRole', 'correlationData': '22'},\n    {'command': 'createRole', 'correlationData': '23'}\n    ]}\n\nmodify_client_command1 = { \"commands\": [{\n    \"command\": \"modifyClient\", \"username\": \"user_one\",\n    \"textname\": \"Modified name\", \"textdescription\": \"Modified description\",\n    \"clientid\": \"\",\n    \"roles\":[\n        {'rolename':'role_one', 'priority':2},\n        {'rolename':'role_two'},\n        {'rolename':'role_three', 'priority':10}\n    ],\n    \"groups\":[\n        {'groupname':'group_one', 'priority':3},\n        {'groupname':'group_two', 'priority':8}\n    ],\n    \"correlationData\": \"3\" }]\n}\nmodify_client_response1 = {'responses': [{'command': 'modifyClient', 'correlationData': '3'}]}\n\nmodify_client_command2 = { \"commands\": [{\n    \"command\": \"modifyClient\", \"username\": \"user_one\",\n    \"textname\": \"Modified name\", \"textdescription\": \"Modified description\",\n    \"groups\":[],\n    \"correlationData\": \"4\" }]\n}\nmodify_client_response2 = {'responses': [{'command': 'modifyClient', 'correlationData': '4'}]}\n\n\nget_client_command1 = { \"commands\": [{\n    \"command\": \"getClient\", \"username\": \"user_one\"}]}\nget_client_response1 = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one', 'clientid': 'cid',\n    'textname': 'Name', 'textdescription': 'Description',\n    'roles': [],\n    'groups': [],\n    'connections': []\n    }}}]}\n\nget_client_command2 = { \"commands\": [{\n    \"command\": \"getClient\", \"username\": \"user_one\"}]}\nget_client_response2 = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one',\n    'textname': 'Modified name', 'textdescription': 'Modified description',\n    'roles': [\n        {'rolename':'role_three', 'priority':10},\n        {'rolename':'role_one', 'priority':2},\n        {'rolename':'role_two'}\n    ],\n    'groups': [\n        {'groupname':'group_two', 'priority':8},\n        {'groupname':'group_one', 'priority':3}],\n    'connections': []\n    }}}]}\n\nget_client_command3 = { \"commands\": [{\n            \"command\": \"getClient\", \"username\": \"user_one\"}]}\nget_client_response3 = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one',\n    'textname': 'Modified name', 'textdescription': 'Modified description',\n    'groups': [],\n    'roles': [\n        {'rolename':'role_three', 'priority':10},\n        {'rolename':'role_one', 'priority':2},\n        {'rolename':'role_two'}],\n     'connections': []\n    }}}]}\n\n\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Create client\n    command_check(sock, create_client_command, create_client_response)\n\n    # Create groups\n    command_check(sock, create_groups_command, create_groups_response)\n\n    # Create role\n    command_check(sock, create_roles_command, create_roles_response)\n\n    # Get client\n    command_check(sock, get_client_command1, get_client_response1, \"get client 1\")\n\n    # Modify client - with groups\n    command_check(sock, modify_client_command1, modify_client_response1)\n\n    # Get client\n    command_check(sock, get_client_command2, get_client_response2, \"get client 2a\")\n\n    # Kill broker and restart, checking whether our changes were saved.\n    broker.terminate()\n    broker_terminate_rc = 0\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        broker_terminate_rc = 1\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Get client\n    command_check(sock, get_client_command2, get_client_response2, \"get client 2b\")\n\n    # Modify client - without groups\n    command_check(sock, modify_client_command2, modify_client_response2)\n\n    # Get client\n    command_check(sock, get_client_command3, get_client_response3, \"get client 3\")\n\n    check_details(sock, 2, 2, 4, 8)\n\n    rc = broker_terminate_rc\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n        pass\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-modify-group.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\ncreate_client_command = { \"commands\": [{\n            \"command\": \"createClient\", \"username\": \"user_one\",\n            \"password\": \"password\", \"clientid\": \"cid\",\n            \"textname\": \"Name\", \"textdescription\": \"Description\",\n            \"correlationData\": \"2\" }]\n}\ncreate_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]}\n\ncreate_group_command = { \"commands\": [{\n    \"command\": \"createGroup\", \"groupname\": \"group_one\",\n    \"textname\": \"Name\", \"textdescription\": \"Description\",\n    \"rolename\": \"\", \"correlationData\": \"2\" }]\n}\ncreate_group_response = {'responses': [{'command': 'createGroup', 'correlationData': '2'}]}\n\ncreate_role_command = { \"commands\": [\n    {\n        \"command\": \"createRole\", \"rolename\": \"role_one\",\n        \"textname\": \"Name\", \"textdescription\": \"Description\",\n        \"acls\":[], \"correlationData\": \"2\"\n    },\n    {\n        \"command\": \"createRole\", \"rolename\": \"role_two\",\n        \"textname\": \"Name\", \"textdescription\": \"Description\",\n        \"acls\":[], \"correlationData\": \"3\"\n    }\n    ]\n}\ncreate_role_response = {'responses': [\n    {'command': 'createRole', 'correlationData': '2'},\n    {'command': 'createRole', 'correlationData': '3'}\n    ]}\n\nmodify_group_command1 = { \"commands\": [{\n    \"command\": \"modifyGroup\", \"groupname\": \"group_one\",\n    \"textname\": \"Modified name\", \"textdescription\": \"Modified description\",\n    \"roles\":[{'rolename':'role_one'}],\n    \"clients\":[{'username':'user_one'}],\n    \"correlationData\": \"3\" }]\n}\nmodify_group_response1 = {'responses': [{'command': 'modifyGroup', 'correlationData': '3'}]}\n\nmodify_group_command2 = { \"commands\": [{\n    \"command\": \"modifyGroup\", \"groupname\": \"group_one\",\n    \"textname\": \"Modified name\", \"textdescription\": \"Modified description\",\n    \"roles\":[\n        {'rolename':'role_one', 'priority':99},\n        {'rolename':'role_two', 'priority':87}\n    ],\n    \"clients\":[],\n    \"correlationData\": \"3\" }]\n}\nmodify_group_response2 = {'responses': [{'command': 'modifyGroup', 'correlationData': '3'}]}\n\nmodify_group_command3 = { \"commands\": [{\n    \"command\": \"modifyGroup\", \"groupname\": \"group_one\",\n    \"textname\": \"Modified name\", \"textdescription\": \"Modified description\",\n    \"roles\":[],\n    \"clients\":[],\n    \"correlationData\": \"3\" }]\n}\nmodify_group_response3 = {'responses': [{'command': 'modifyGroup', 'correlationData': '3'}]}\n\n\nget_group_command1 = { \"commands\": [{\n    \"command\": \"getGroup\", \"groupname\": \"group_one\"}]}\nget_group_response1 = {'responses':[{'command': 'getGroup', 'data': {'group': {'groupname': 'group_one',\n    'textname': 'Name', 'textdescription': 'Description',\n    'clients':[],\n    'roles': []}}}]}\n\nget_group_command2 = { \"commands\": [{\n    \"command\": \"getGroup\", \"groupname\": \"group_one\"}]}\nget_group_response2 = {'responses':[{'command': 'getGroup', 'data': {'group': {'groupname': 'group_one',\n    'textname': 'Modified name', 'textdescription': 'Modified description',\n    'clients':[{'username':'user_one'}],\n    'roles': [{'rolename':'role_one'}]}}}]}\n\nget_group_command3 = { \"commands\": [{\n    \"command\": \"getGroup\", \"groupname\": \"group_one\"}]}\nget_group_response3 = {'responses':[{'command': 'getGroup', 'data': {'group': {'groupname': 'group_one',\n    'textname': 'Modified name', 'textdescription': 'Modified description',\n    'clients':[],\n    'roles': [\n        {'rolename':'role_one', 'priority':99},\n        {'rolename':'role_two', 'priority':87}\n    ]}}}]}\n\nget_group_command4 = { \"commands\": [{\n    \"command\": \"getGroup\", \"groupname\": \"group_one\"}]}\nget_group_response4 = {'responses':[{'command': 'getGroup', 'data': {'group': {'groupname': 'group_one',\n    'textname': 'Modified name', 'textdescription': 'Modified description',\n    'clients':[],\n    'roles': []}}}]}\n\n\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Create client\n    command_check(sock, create_client_command, create_client_response)\n\n    # Create group\n    command_check(sock, create_group_command, create_group_response)\n\n    # Create role\n    command_check(sock, create_role_command, create_role_response)\n\n    # Get group\n    command_check(sock, get_group_command1, get_group_response1, \"get group 1\")\n\n    # Modify group\n    command_check(sock, modify_group_command1, modify_group_response1)\n\n    # Get group\n    command_check(sock, get_group_command2, get_group_response2, \"get group 2a\")\n\n    # Kill broker and restart, checking whether our changes were saved.\n    broker.terminate()\n    broker_terminate_rc = 0\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        broker_terminate_rc = 1\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Get group\n    command_check(sock, get_group_command2, get_group_response2, \"get group 2b\")\n\n    # Modify group\n    command_check(sock, modify_group_command2, modify_group_response2)\n\n    # Get group\n    command_check(sock, get_group_command3, get_group_response3, \"get group 3\")\n\n    # Modify group\n    command_check(sock, modify_group_command3, modify_group_response3)\n\n    # Get group\n    command_check(sock, get_group_command4, get_group_response4, \"get group 4\")\n\n    check_details(sock, 2, 1, 3, 7)\n\n    rc = broker_terminate_rc\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n        pass\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-modify-role.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\ncreate_role_command = { \"commands\": [{\n    \"command\": \"createRole\", \"rolename\": \"role_one\",\n    \"textname\": \"Name\", \"textdescription\": \"Description\",\n    \"acls\":[\n        {\n            \"acltype\": \"publishClientSend\",\n            \"allow\": True,\n            \"topic\": \"topic/#\",\n            \"priority\": 8\n        },\n        {\n            \"acltype\": \"publishClientSend\",\n            \"allow\": True,\n            \"topic\": \"topic/2/#\",\n            \"priority\": 9\n        }\n    ], \"correlationData\": \"2\" }]\n}\ncreate_role_response = {'responses': [{'command': 'createRole', 'correlationData': '2'}]}\n\nmodify_role_command = { \"commands\": [{\n    \"command\": \"modifyRole\", \"rolename\": \"role_one\",\n    \"textname\": \"Modified name\", \"textdescription\": \"Modified description\", 'allowwildcardsubs': False,\n    \"acls\":[\n        {\n            \"acltype\": \"publishClientReceive\",\n            \"allow\": True,\n            \"topic\": \"topic/#\",\n            \"priority\": 2\n        },\n        {\n            \"acltype\": \"publishClientReceive\",\n            \"allow\": True,\n            \"topic\": \"topic/2/#\",\n            \"priority\": 1\n        }\n    ],\n    \"correlationData\": \"3\" }]\n}\nmodify_role_response = {'responses': [{'command': 'modifyRole', 'correlationData': '3'}]}\n\n\nget_role_command1 = { \"commands\": [{\"command\": \"getRole\", \"rolename\": \"role_one\"}]}\nget_role_response1 = {'responses':[{'command': 'getRole', 'data': {'role': {'rolename': 'role_one',\n    'textname': 'Name', 'textdescription': 'Description', 'allowwildcardsubs': True,\n    'acls': [\n        {\n            \"acltype\": \"publishClientSend\",\n            \"topic\": \"topic/2/#\",\n            \"allow\": True,\n            \"priority\": 9\n        },\n        {\n            \"acltype\": \"publishClientSend\",\n            \"topic\": \"topic/#\",\n            \"allow\": True,\n            \"priority\": 8\n        }\n    ]}}}]}\n\nget_role_command2 = { \"commands\": [{\n    \"command\": \"getRole\", \"rolename\": \"role_one\"}]}\nget_role_response2 = {'responses':[{'command': 'getRole', 'data': {'role': {'rolename': 'role_one',\n    'textname': 'Modified name', 'textdescription': 'Modified description', 'allowwildcardsubs': False,\n    'acls': [\n        {\n            \"acltype\": \"publishClientReceive\",\n            \"topic\": \"topic/#\",\n            \"allow\": True,\n            \"priority\": 2\n        },\n        {\n            \"acltype\": \"publishClientReceive\",\n            \"topic\": \"topic/2/#\",\n            \"allow\": True,\n            \"priority\": 1\n        }\n    ]}}}]}\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Add role\n    command_check(sock, create_role_command, create_role_response)\n\n    # Get role\n    command_check(sock, get_role_command1, get_role_response1)\n\n    # Modify role\n    command_check(sock, modify_role_command, modify_role_response)\n\n    # Get role\n    command_check(sock, get_role_command2, get_role_response2)\n\n    # Kill broker and restart, checking whether our changes were saved.\n    broker.terminate()\n    broker_terminate_rc = 0\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        broker_terminate_rc = 1\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Get role\n    command_check(sock, get_role_command2, get_role_response2)\n\n    check_details(sock, 1, 0, 2, 2)\n\n    rc = broker_terminate_rc\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-plugin-invalid.py",
    "content": "#!/usr/bin/env python3\n\n# Check invalid inputs for plugin commands\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\ndef command_check_text(sock, command_payload, expected_response, msg=\"\"):\n    command_packet = mosq_test.gen_publish(topic=\"$CONTROL/dynamic-security/v1\", qos=0, payload=command_payload)\n    sock.send(command_packet)\n    response = json.loads(mosq_test.read_publish(sock))\n    if response != expected_response:\n        print(expected_response)\n        print(response)\n        if msg != \"\":\n            print(msg)\n        raise ValueError(response)\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\n# ==========================================================================\n# Bad commands\n# ==========================================================================\n\n# Invalid JSON\nbad1_command = 'not json'\nbad1_response = {'responses': [{'command': 'Unknown command', 'error': 'Invalid/missing commands'}]}\n\n# No commands\nbad2_command = {}\nbad2_response = {'responses': [{'command': 'Unknown command', 'error': 'Invalid/missing commands'}]}\n\n# Commands not an array\nbad3_command = {'commands': 'test'}\nbad3_response = {'responses': [{'command': 'Unknown command', 'error': 'Invalid/missing commands'}]}\n\n# Empty commands array\nbad4_command = {'commands': []}\nbad4_response = {'responses': []}\n\n# Empty command\nbad5_command = {'commands': ['bad']}\nbad5_response = {'responses': [{'command': 'Unknown command', 'error': 'Command not an object'}]}\n\n# Bad array type\nbad6_command = {'commands': [{}]}\nbad6_response = {'responses': [{'command': 'Unknown command', 'error': 'Missing command'}]}\n\n# Bad command type\nbad7_command = {'commands': [{'command':6}]}\nbad7_response = {'responses': [{'command': 'Unknown command', 'error': 'Missing command'}]}\n\n# Bad correlationData type\nbad8_command = {'commands': [{'command':'command', 'correlationData':6}]}\nbad8_response = {'responses': [{'command': 'command', 'error': 'Invalid correlationData data type.'}]}\n\n# Unknown command\nbad9_command = {'commands': [{'command':'command'}]}\nbad9_response = {'responses': [{'command': 'command', 'error': 'Unknown command'}]}\n\n# ==========================================================================\n# setDefaultACLAccess\n# ==========================================================================\n\n# Missing actions array\nset_default1_command = {'commands': [{'command':'setDefaultACLAccess'}]}\nset_default1_response = {'responses': [{'command': 'setDefaultACLAccess', 'error': 'Missing/invalid actions array'}]}\n\n# Actions array not an array\nset_default2_command = {'commands': [{'command':'setDefaultACLAccess', 'actions':'bad'}]}\nset_default2_response = {'responses': [{'command': 'setDefaultACLAccess', 'error': 'Missing/invalid actions array'}]}\n\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    command_check(sock, bad1_command, bad1_response, \"1\")\n    command_check(sock, bad2_command, bad2_response, \"2\")\n    command_check(sock, bad3_command, bad3_response, \"3\")\n    command_check(sock, bad4_command, bad4_response, \"4\")\n    command_check(sock, bad5_command, bad5_response, \"5\")\n    command_check(sock, bad6_command, bad6_response, \"6\")\n    command_check(sock, bad7_command, bad7_response, \"7\")\n    command_check(sock, bad8_command, bad8_response, \"8\")\n    command_check(sock, bad9_command, bad9_response, \"9\")\n\n    command_check(sock, set_default1_command, set_default1_response, \"1\")\n    command_check(sock, set_default2_command, set_default2_response, \"2\")\n\n    check_details(sock, 1, 0, 1, 0)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-role-invalid.py",
    "content": "#!/usr/bin/env python3\n\n# Check invalid inputs for role commands\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\n# Create client for modifying\ncreate_client0_command = { 'commands': [{'command': 'createClient', 'username':'validclient' }] }\ncreate_client0_response = {'responses': [{'command': 'createClient'}]}\n\n# Create group for modifying\ncreate_group0_command = { 'commands': [{'command': 'createGroup', 'groupname':'validgroup' }] }\ncreate_group0_response = {'responses': [{'command': 'createGroup'}]}\n\n# Create role for modifying\ncreate_role0_command = { 'commands': [{'command': 'createRole', 'rolename':'validrole' }] }\ncreate_role0_response = {'responses': [{'command': 'createRole'}]}\n\n# Add ACL for modifying\nadd_role_acl0_command = { 'commands': [{'command': 'addRoleACL', 'rolename':'validrole', 'acltype':'unsubscribePattern', 'topic':'validtopic', 'allow':True }] }\nadd_role_acl0_response = {'responses': [{'command': 'addRoleACL'}]}\n\n# ==========================================================================\n# Create role\n# ==========================================================================\n\n# No rolename\ncreate_role1_command = { 'commands': [{'command': 'createRole' }] }\ncreate_role1_response = {'responses': [{'command': 'createRole', 'error': 'Invalid/missing rolename'}]}\n\n# Rolename not a string\ncreate_role2_command = { 'commands': [{'command': 'createRole', 'rolename':5 }] }\ncreate_role2_response = {'responses': [{'command': 'createRole', 'error': 'Invalid/missing rolename'}]}\n\n# Rolename not UTF-8\ncreate_role3_command = { 'commands': [{'command': 'createRole', 'rolename': '￿LO' }] }\ncreate_role3_response = {'responses': [{'command': 'createRole', 'error': 'Role name not valid UTF-8'}]}\n\n# textname not a string\ncreate_role4_command = { 'commands': [{'command': 'createRole', 'rolename':'g', 'textname':5 }] }\ncreate_role4_response = {'responses': [{'command': 'createRole', 'error': 'Invalid/missing textname'}]}\n\n# textdescription not a string\ncreate_role5_command = { 'commands': [{'command': 'createRole', 'rolename':'g', 'textdescription':5 }] }\ncreate_role5_response = {'responses': [{'command': 'createRole', 'error': 'Invalid/missing textdescription'}]}\n\n# Role already exists\ncreate_role6_command = { 'commands': [{'command': 'createRole', 'rolename': 'validrole'}]}\ncreate_role6_response = {'responses': [{'command': 'createRole', 'error': 'Role already exists'}]}\n\n# Bad ACLs\n#create_role7_command = { 'commands': [{'command': 'createRole', 'rolename': 'role', 'roles':[{'rolename':'notfound'}]}] }\n#create_role7_response = {'responses': [{'command': 'createRole', 'error': 'Role not found'}]}\n\n# ==========================================================================\n# Delete role\n# ==========================================================================\n\n# No rolename\ndelete_role1_command = { 'commands': [{'command': 'deleteRole' }] }\ndelete_role1_response = {'responses': [{'command': 'deleteRole', 'error': 'Invalid/missing rolename'}]}\n\n# Rolename not a string\ndelete_role2_command = { 'commands': [{'command': 'deleteRole', 'rolename':5 }] }\ndelete_role2_response = {'responses': [{'command': 'deleteRole', 'error': 'Invalid/missing rolename'}]}\n\n# Rolename not UTF-8\ndelete_role3_command = { 'commands': [{'command': 'deleteRole', 'rolename': '￿LO' }] }\ndelete_role3_response = {'responses': [{'command': 'deleteRole', 'error': 'Role name not valid UTF-8'}]}\n\n# Role not found\ndelete_role4_command = { 'commands': [{'command': 'deleteRole', 'rolename': 'role'}]}\ndelete_role4_response = {'responses': [{'command': 'deleteRole', 'error': 'Role not found'}]}\n\n# ==========================================================================\n# Get role\n# ==========================================================================\n\n# No rolename\nget_role1_command = { 'commands': [{'command': 'getRole'}] }\nget_role1_response = {'responses': [{'command': 'getRole', 'error': 'Invalid/missing rolename'}]}\n\n# rolename not a string\nget_role2_command = { 'commands': [{'command': 'getRole', 'rolename':5}] }\nget_role2_response = {'responses': [{'command': 'getRole', 'error': 'Invalid/missing rolename'}]}\n\n# rolename not UTF-8\nget_role3_command = { 'commands': [{'command': 'getRole', 'rolename': '￿LO' }] }\nget_role3_response = {'responses': [{'command': 'getRole', 'error': 'Role name not valid UTF-8'}]}\n\n# role not found\nget_role4_command = { 'commands': [{'command': 'getRole', 'rolename':\"notfound\"}] }\nget_role4_response = {'responses': [{'command': 'getRole', 'error': 'Role not found'}]}\n\n\n# ==========================================================================\n# Add role ACL\n# ==========================================================================\n\nadd_role_acl1_command = { 'commands': [{'command': 'addRoleACL'}]}\nadd_role_acl1_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid/missing rolename'}]}\n\nadd_role_acl2_command = { 'commands': [{'command': 'addRoleACL', 'rolename':5}]}\nadd_role_acl2_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid/missing rolename'}]}\n\nadd_role_acl3_command = { 'commands': [{'command': 'addRoleACL', 'rolename': '￿LO' }] }\nadd_role_acl3_response = {'responses': [{'command': 'addRoleACL', 'error': 'Role name not valid UTF-8'}]}\n\nadd_role_acl4_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'notvalid' }] }\nadd_role_acl4_response = {'responses': [{'command': 'addRoleACL', 'error': 'Role not found'}]}\n\nadd_role_acl5_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole' }] }\nadd_role_acl5_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid/missing acltype'}]}\n\nadd_role_acl6_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole', 'acltype':'notvalid' }] }\nadd_role_acl6_response = {'responses': [{'command': 'addRoleACL', 'error': 'Unknown acltype'}]}\n\nadd_role_acl7_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribePattern' }] }\nadd_role_acl7_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid/missing topic'}]}\n\nadd_role_acl8_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole', 'acltype':'subscribePattern', 'topic':5 }] }\nadd_role_acl8_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid/missing topic'}]}\n\nadd_role_acl9_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribeLiteral', 'topic':'￿LO' }] }\nadd_role_acl9_response = {'responses': [{'command': 'addRoleACL', 'error': 'Topic not valid UTF-8'}]}\n\nadd_role_acl10_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribeLiteral', 'topic':'not/#/valid' }] }\nadd_role_acl10_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid ACL topic'}]}\n\n\n# ==========================================================================\n# Remove role ACL\n# ==========================================================================\n\nremove_role_acl1_command = { 'commands': [{'command': 'removeRoleACL'}]}\nremove_role_acl1_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Invalid/missing rolename'}]}\n\nremove_role_acl2_command = { 'commands': [{'command': 'removeRoleACL', 'rolename':5}]}\nremove_role_acl2_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Invalid/missing rolename'}]}\n\nremove_role_acl3_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': '￿LO' }] }\nremove_role_acl3_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Role name not valid UTF-8'}]}\n\nremove_role_acl4_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'notvalid' }] }\nremove_role_acl4_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Role not found'}]}\n\nremove_role_acl5_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'validrole' }] }\nremove_role_acl5_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Invalid/missing acltype'}]}\n\nremove_role_acl6_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'validrole', 'acltype':'notvalid' }] }\nremove_role_acl6_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Unknown acltype'}]}\n\nremove_role_acl7_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribePattern' }] }\nremove_role_acl7_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Invalid/missing topic'}]}\n\nremove_role_acl8_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribePattern', 'topic':5 }] }\nremove_role_acl8_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Invalid/missing topic'}]}\n\nremove_role_acl9_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribePattern', 'topic':'￿LO' }] }\nremove_role_acl9_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Topic not valid UTF-8'}]}\n\n\n# ==========================================================================\n# Modify role\n# ==========================================================================\n\n# No groupname\nmodify_group1_command = { 'commands': [{'command': 'modifyRole'}]}\nmodify_group1_response = {'responses': [{'command': 'modifyRole', 'error': 'Invalid/missing groupname'}]}\n\n# Username not a string\nmodify_group2_command = { 'commands': [{'command': 'modifyRole', 'groupname':5}]}\nmodify_group2_response = {'responses': [{'command': 'modifyRole', 'error': 'Invalid/missing groupname'}]}\n\n# Username not UTF-8\nmodify_group3_command = { 'commands': [{'command': 'modifyRole', 'rolename': '￿LO' }] }\nmodify_group3_response = {'responses': [{'command': 'modifyRole', 'error': 'Role name not valid UTF-8'}]}\n\n# roles not a list\nmodify_group4_command = { 'commands': [{'command': 'modifyRole', 'groupname':'validgroup', 'password':'test', 'roles':'string'}]}\nmodify_group4_response = {'responses': [{'command': 'modifyRole', 'error': \"'roles' not an array or missing/invalid rolename\"}]}\n\n# No rolename\nmodify_group5_command = { 'commands': [{'command': 'modifyRole', 'groupname':'validgroup', 'roles':[{}]}]}\nmodify_group5_response = {'responses': [{'command': 'modifyRole', 'error': \"'roles' not an array or missing/invalid rolename\"}]}\n\n# rolename not a string\nmodify_group6_command = { 'commands': [{'command': 'modifyRole', 'groupname':'validgroup', 'roles':[{'rolename':5}]}]}\nmodify_group6_response = {'responses': [{'command': 'modifyRole', 'error': \"'roles' not an array or missing/invalid rolename\"}]}\n\n# rolename not UTF-8\nmodify_group3_command = { 'commands': [{'command': 'modifyRole', 'rolename': '￿LO' }] }\n#modify_group7_command = { 'commands': [{'command': 'modifyRole', 'groupname':'validgroup'}]}\n#modify_group7_response = {'responses': [{'command': 'modifyRole', 'error': 'Invalid/missing rolename'}]}\n\n# Role not found\nmodify_group8_command = { 'commands': [{'command': 'modifyRole', 'groupname':'notfound', 'rolename':'notfound'}]}\nmodify_group8_response = {'responses': [{'command': 'modifyRole', 'error': 'Role not found'}]}\n\n# Role not found\nmodify_group9_command = { 'commands': [{'command': 'modifyRole', 'groupname':'validgroup', 'roles':[{'rolename':'notfound'}]}]}\nmodify_group9_response = {'responses': [{'command': 'modifyRole', 'error': 'Role not found'}]}\n\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    command_check(sock, create_client0_command, create_client0_response, \"0\")\n    command_check(sock, create_group0_command, create_group0_response, \"0\")\n    command_check(sock, create_role0_command, create_role0_response, \"0\")\n    command_check(sock, add_role_acl0_command, add_role_acl0_response, \"0\")\n\n    command_check(sock, create_role1_command, create_role1_response, \"1\")\n    command_check(sock, create_role2_command, create_role2_response, \"2\")\n    command_check(sock, create_role3_command, create_role3_response, \"3\")\n    command_check(sock, create_role4_command, create_role4_response, \"4\")\n    command_check(sock, create_role5_command, create_role5_response, \"5\")\n    command_check(sock, create_role6_command, create_role6_response, \"6\")\n    #command_check(sock, create_role7_command, create_role7_response, \"7\")\n\n    command_check(sock, delete_role1_command, delete_role1_response, \"1\")\n    command_check(sock, delete_role2_command, delete_role2_response, \"2\")\n    command_check(sock, delete_role3_command, delete_role3_response, \"3\")\n    command_check(sock, delete_role4_command, delete_role4_response, \"4\")\n\n    command_check(sock, get_role1_command, get_role1_response, \"1\")\n    command_check(sock, get_role2_command, get_role2_response, \"2\")\n    command_check(sock, get_role3_command, get_role3_response, \"3\")\n    command_check(sock, get_role4_command, get_role4_response, \"4\")\n\n    command_check(sock, add_role_acl1_command, add_role_acl1_response, \"1\")\n    command_check(sock, add_role_acl2_command, add_role_acl2_response, \"2\")\n    command_check(sock, add_role_acl3_command, add_role_acl3_response, \"3\")\n    command_check(sock, add_role_acl4_command, add_role_acl4_response, \"4\")\n    command_check(sock, add_role_acl5_command, add_role_acl5_response, \"5\")\n    command_check(sock, add_role_acl6_command, add_role_acl6_response, \"6\")\n    command_check(sock, add_role_acl7_command, add_role_acl7_response, \"7\")\n    command_check(sock, add_role_acl8_command, add_role_acl8_response, \"8\")\n    command_check(sock, add_role_acl9_command, add_role_acl9_response, \"9\")\n    command_check(sock, add_role_acl10_command, add_role_acl10_response, \"10\")\n\n    command_check(sock, remove_role_acl1_command, remove_role_acl1_response, \"1\")\n    command_check(sock, remove_role_acl2_command, remove_role_acl2_response, \"2\")\n    command_check(sock, remove_role_acl3_command, remove_role_acl3_response, \"3\")\n    command_check(sock, remove_role_acl4_command, remove_role_acl4_response, \"4\")\n    command_check(sock, remove_role_acl5_command, remove_role_acl5_response, \"5\")\n    command_check(sock, remove_role_acl6_command, remove_role_acl6_response, \"6\")\n    command_check(sock, remove_role_acl7_command, remove_role_acl7_response, \"7\")\n    command_check(sock, remove_role_acl8_command, remove_role_acl8_response, \"8\")\n    command_check(sock, remove_role_acl9_command, remove_role_acl9_response, \"9\")\n\n    #command_check(sock, modify_role1_command, modify_role1_response, \"1\")\n    #command_check(sock, modify_role2_command, modify_role2_response, \"2\")\n    ##command_check(sock, modify_role3_command, modify_role3_response, \"3\")\n    #command_check(sock, modify_role4_command, modify_role4_response, \"4\")\n    #command_check(sock, modify_role5_command, modify_role5_response, \"5\")\n    #command_check(sock, modify_role6_command, modify_role6_response, \"6\")\n    ##command_check(sock, modify_role7_command, modify_role7_response, \"7\")\n    #command_check(sock, modify_role8_command, modify_role8_response, \"8\")\n    #command_check(sock, modify_role9_command, modify_role9_response, \"9\")\n\n    check_details(sock, 2, 1, 2, 4)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/14-dynsec-role.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom dynsec_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\\n\")\n        f.write(\"plugin_opt_config_file %d/dynamic-security.json\\n\" % (port))\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\ncreate_client_command = { \"commands\": [{\n    \"command\": \"createClient\", \"username\": \"user_one\",\n    \"password\": \"password\", \"clientid\": \"cid\",\n    \"textname\": \"Name\", \"textdescription\": \"Description\",\n    \"rolename\": \"\", \"correlationData\": \"2\" }]\n}\ncreate_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]}\n\ncreate_client2_command = { \"commands\": [{\n    \"command\": \"createClient\", \"username\": \"user_two\",\n    \"password\": \"password\",\n    \"textname\": \"Name\", \"textdescription\": \"Description\",\n    \"rolename\": \"\", \"correlationData\": \"3\" }]\n}\ncreate_client2_response = {'responses': [{'command': 'createClient', 'correlationData': '3'}]}\n\ncreate_group_command = { \"commands\": [{\n            \"command\": \"createGroup\", \"groupname\": \"group_one\",\n            \"textname\": \"Name\", \"textdescription\": \"Description\",\n            \"correlationData\":\"3\"}]}\ncreate_group_response = {'responses':[{\"command\":\"createGroup\",\"correlationData\":\"3\"}]}\n\ncreate_role_command = { \"commands\": [{'command': 'createRole', 'correlationData': '3',\n    \"rolename\": \"basic\", \"acls\":[\n    {\"acltype\":\"publishClientSend\", \"topic\": \"out/#\", \"priority\":3, \"allow\": True}], \"textname\":\"name\", \"textdescription\":\"desc\"\n    }]}\ncreate_role_response = {'responses': [{'command': 'createRole', 'correlationData': '3'}]}\n\ncreate_role2_command = { \"commands\": [{'command': 'createRole', 'correlationData': '3',\n    \"rolename\": \"basic2\", \"acls\":[\n    {\"acltype\":\"publishClientSend\", \"topic\": \"out/#\", \"priority\":3, \"allow\": True}], \"textname\":\"name\", \"textdescription\":\"desc\"\n    }]}\ncreate_role2_response = {'responses': [{'command': 'createRole', 'correlationData': '3'}]}\n\n\nadd_role_to_client_command = {\"commands\": [{'command': 'addClientRole', \"username\": \"user_one\",\n    \"rolename\": \"basic\"}]}\nadd_role_to_client_response = {'responses': [{'command': 'addClientRole'}]}\n\nadd_role_to_client2_command = {\"commands\": [{'command': 'addClientRole', \"username\": \"user_one\",\n    \"rolename\": \"basic2\"}]}\nadd_role_to_client2_response = {'responses': [{'command': 'addClientRole'}]}\n\nadd_role_to_group_command = {\"commands\": [{'command': 'addGroupRole', \"groupname\": \"group_one\",\n    \"rolename\": \"basic\"}]}\nadd_role_to_group_response = {'responses': [{'command': 'addGroupRole'}]}\n\n\nlist_roles_verbose_command1 = { \"commands\": [{\n    \"command\": \"listRoles\", \"verbose\": True, \"correlationData\": \"21\"}]\n}\nlist_roles_verbose_response1 = {'responses': [{'command': 'listRoles', 'data':\n    {'totalCount':3, 'roles': [\n    {\"rolename\":\"admin\",\"allowwildcardsubs\": True, \"acls\":[\n    {\"acltype\": \"publishClientSend\", \"topic\": \"$CONTROL/dynamic-security/#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"publishClientReceive\", \"topic\": \"$CONTROL/dynamic-security/#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"publishClientReceive\", \"topic\": \"$SYS/#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"publishClientReceive\", \"topic\": \"#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"subscribePattern\", \"topic\": \"$CONTROL/dynamic-security/#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"subscribePattern\", \"topic\": \"$SYS/#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"subscribePattern\", \"topic\": \"#\", \"priority\":0, \"allow\": True},\n    {\"acltype\": \"unsubscribePattern\", \"topic\": \"#\", \"priority\":0, \"allow\": True}]},\n    {'rolename': 'basic', \"textname\": \"name\", \"textdescription\": \"desc\", \"allowwildcardsubs\": True,\n    'acls': [{'acltype':'publishClientSend', 'topic': 'out/#', 'priority': 3, 'allow': True}]},\n    {'rolename': 'basic2', \"textname\": \"name\", \"textdescription\": \"desc\", \"allowwildcardsubs\": True,\n    'acls': [{'acltype':'publishClientSend', 'topic': 'out/#', 'priority': 3, 'allow': True}]\n    }]}, 'correlationData': '21'}]}\n\nadd_acl_command = {\"commands\": [{'command': \"addRoleACL\", \"rolename\":\"basic\", \"acltype\":\"subscribeLiteral\",\n    \"topic\":\"basic/out\", \"priority\":1, \"allow\":True}]}\nadd_acl_response = {'responses': [{'command': 'addRoleACL'}]}\n\nadd_acl2_command = {\"commands\": [{'command': \"addRoleACL\", \"rolename\":\"basic\", \"acltype\":\"subscribeLiteral\",\n    \"topic\":\"basic/out\", \"priority\":1, \"allow\":True}]}\nadd_acl2_response = {'responses': [{'command': 'addRoleACL', 'error':'ACL with this topic already exists'}]}\n\nlist_roles_verbose_command2 = { \"commands\": [{\n    \"command\": \"listRoles\", \"verbose\": True, \"correlationData\": \"22\"}]\n}\nlist_roles_verbose_response2 = {'responses': [{'command': 'listRoles', 'data': {'totalCount':3, 'roles':\n    [{\"rolename\":\"admin\",'allowwildcardsubs': True, \"acls\":[\n    {\"acltype\": \"publishClientSend\", \"topic\": \"$CONTROL/dynamic-security/#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"publishClientReceive\", \"topic\": \"$CONTROL/dynamic-security/#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"publishClientReceive\", \"topic\": \"$SYS/#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"publishClientReceive\", \"topic\": \"#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"subscribePattern\", \"topic\": \"$CONTROL/dynamic-security/#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"subscribePattern\", \"topic\": \"$SYS/#\", \"priority\":0, \"allow\": True },\n    {\"acltype\": \"subscribePattern\", \"topic\": \"#\", \"priority\":0, \"allow\": True},\n    {\"acltype\": \"unsubscribePattern\", \"topic\": \"#\", \"priority\":0, \"allow\": True}]},\n    {'rolename': 'basic', 'textname': 'name', 'textdescription': 'desc', 'allowwildcardsubs': True, 'acls':\n    [{'acltype':'publishClientSend', 'topic': 'out/#', 'priority': 3, 'allow': True},\n    {'acltype':'subscribeLiteral', 'topic': 'basic/out', 'priority': 1, 'allow': True}]},\n    {'rolename': 'basic2', \"textname\": \"name\", \"textdescription\": \"desc\", 'allowwildcardsubs': True,\n    'acls': [{'acltype':'publishClientSend', 'topic': 'out/#', 'priority': 3, 'allow': True}]\n    }]}, 'correlationData': '22'}]}\n\nget_role_command = {\"commands\": [{'command': \"getRole\", \"rolename\":\"basic\"}]}\nget_role_response = {'responses': [{'command': 'getRole', 'data': {'role':\n    {'rolename': 'basic', 'textname': 'name', 'textdescription': 'desc', 'allowwildcardsubs': True, 'acls':\n    [{'acltype':'publishClientSend', 'topic': 'out/#', 'priority': 3, 'allow': True},\n    {'acltype':'subscribeLiteral', 'topic': 'basic/out', 'priority': 1, 'allow': True}],\n    }}}]}\n\nremove_acl_command = {\"commands\": [{'command': \"removeRoleACL\", \"rolename\":\"basic\", \"acltype\":\"subscribeLiteral\",\n    \"topic\":\"basic/out\"}]}\nremove_acl_response = {'responses': [{'command': 'removeRoleACL'}]}\n\nremove_acl2_command = {\"commands\": [{'command': \"removeRoleACL\", \"rolename\":\"basic\", \"acltype\":\"subscribeLiteral\",\n    \"topic\":\"basic/out\"}]}\nremove_acl2_response = {'responses': [{'command': 'removeRoleACL', 'error':'ACL not found'}]}\n\ndelete_role_command = {\"commands\": [{'command': \"deleteRole\", \"rolename\":\"basic\"}]}\ndelete_role_response = {\"responses\": [{\"command\": \"deleteRole\"}]}\n\ndelete_role2_command = {\"commands\": [{'command': \"deleteRole\", \"rolename\":\"basic\"}]}\ndelete_role2_response = {\"responses\": [{\"command\": \"deleteRole\"}]}\n\nlist_clients_verbose_command = { \"commands\": [{\n    \"command\": \"listClients\", \"verbose\": True, \"correlationData\": \"20\"}]\n}\nlist_clients_verbose_response = {'responses':[{\"command\": \"listClients\", \"data\":{'totalCount':3, \"clients\":[\n    {'username': 'admin', 'textname': 'Dynsec admin user', 'roles': [{'rolename': 'admin'}], 'groups': [],  'connections': [{'address': '127.0.0.1'}]},\n    {\"username\":\"user_one\", \"clientid\":\"cid\", \"textname\":\"Name\", \"textdescription\":\"Description\",\n     \"groups\":[], \"roles\":[{'rolename':'basic'}, {'rolename':'basic2'}],  'connections': []},\n    {\"username\":\"user_two\", \"textname\":\"Name\", \"textdescription\":\"Description\",\n     \"groups\":[], \"roles\":[], 'connections': []}]}, \"correlationData\":\"20\"}]}\n\nlist_groups_verbose_command = { \"commands\": [{\n    \"command\": \"listGroups\", \"verbose\": True, \"correlationData\": \"20\"}]\n}\nlist_groups_verbose_response = {'responses':[{\"command\": \"listGroups\", \"data\":{'totalCount':1, \"groups\":[\n    {\"groupname\":\"group_one\", \"textname\":\"Name\", \"textdescription\":\"Description\",\n    \"clients\":[], \"roles\":[{'rolename':'basic'}]}]}, \"correlationData\":\"20\"}]}\n\nremove_role_from_client_command = {\"commands\": [{'command': 'removeClientRole', \"username\": \"user_one\",\n    \"rolename\": \"basic\"}]}\nremove_role_from_client_response = {'responses': [{'command': 'removeClientRole'}]}\n\nremove_role_from_group_command = {\"commands\": [{'command': 'removeGroupRole', \"groupname\": \"group_one\",\n    \"rolename\": \"basic\"}]}\nremove_role_from_group_response = {'responses': [{'command': 'removeGroupRole'}]}\n\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"ctrl-test\", username=\"admin\", password=\"admin\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/dynamic-security/#\", 1)\nsuback_packet = mosq_test.gen_suback(mid, 1)\n\ntry:\n    os.mkdir(str(port))\n    shutil.copyfile(str(Path(__file__).resolve().parent / \"dynamic-security-init.json\"), \"%d/dynamic-security.json\" % (port))\nexcept FileExistsError:\n    pass\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # Create client\n    command_check(sock, create_client2_command, create_client2_response)\n    command_check(sock, create_client_command, create_client_response)\n\n    # Create group\n    command_check(sock, create_group_command, create_group_response)\n\n    # Create role\n    command_check(sock, create_role_command, create_role_response)\n    command_check(sock, create_role2_command, create_role2_response)\n\n    # Add role to client\n    command_check(sock, add_role_to_client2_command, add_role_to_client2_response)\n    command_check(sock, add_role_to_client_command, add_role_to_client_response)\n\n    # Add role to group\n    command_check(sock, add_role_to_group_command, add_role_to_group_response)\n\n    # List clients verbose\n    command_check(sock, list_clients_verbose_command, list_clients_verbose_response)\n\n    # List groups verbose\n    command_check(sock, list_groups_verbose_command, list_groups_verbose_response)\n\n    # List roles verbose 1\n    command_check(sock, list_roles_verbose_command1, list_roles_verbose_response1, \"list roles verbose 1a\")\n\n    # Add ACL\n    command_check(sock, add_acl_command, add_acl_response)\n    command_check(sock, add_acl2_command, add_acl2_response)\n\n    # List roles verbose 2\n    command_check(sock, list_roles_verbose_command2, list_roles_verbose_response2, \"list roles verbose 2a\")\n\n    # Kill broker and restart, checking whether our changes were saved.\n    broker.terminate()\n    broker_terminate_rc = 0\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        broker_terminate_rc = 1\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    # List roles verbose 2\n    command_check(sock, list_roles_verbose_command2, list_roles_verbose_response2, \"list roles verbose 2b\")\n\n    # Get role\n    command_check(sock, get_role_command, get_role_response)\n\n    # Remove ACL\n    command_check(sock, remove_acl_command, remove_acl_response)\n    command_check(sock, remove_acl2_command, remove_acl2_response)\n\n    # List roles verbose 1\n    command_check(sock, list_roles_verbose_command1, list_roles_verbose_response1, \"list roles verbose 1b\")\n\n    # Remove role from client\n    command_check(sock, remove_role_from_client_command, remove_role_from_client_response)\n\n    # Remove role from group\n    command_check(sock, remove_role_from_group_command, remove_role_from_group_response)\n\n    # Delete role\n    command_check(sock, delete_role_command, delete_role_response)\n\n    check_details(sock, 3, 1, 2, 13)\n\n    rc = broker_terminate_rc\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{port}/dynamic-security.json\")\n    except FileNotFoundError:\n        pass\n    os.rmdir(f\"{port}\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-bridge-queue.py",
    "content": "#!/usr/bin/env python3\n\n# Start two brokers one bridging forward to the second one (the bridge target).\n# Once brokers are up and running we stop the bridge target broker, connect\n# a publisher an publish a number of qos1 messages.\n# Check all the messages should bestored in the persistence store.\n# Afterwards start the bridge target broker, connect a client and\n# add a matching subscription. Then start the bridging broker and check,\n# if all messages are forwarded from the persistence store to bridge target\n# and further to the subscriber.\n# Finally check the persistence store contains zero messages.\n\nfrom mosq_test_helper import *\n\npersist_help = persist_module()\n\nnum_messages = 100\ntopic = \"test/bridge-out\"\n\nport, bridge_target_port = mosq_test.get_port(2)\n\nconf_file = os.path.basename(__file__).replace(\".py\", \".conf\")\nconf_file_bridge_target = os.path.basename(__file__).replace(\n    \".py\", \"_bridge_target.conf\"\n)\n\n\ndef do_test(test_case_name: str, bridging_add_config: dict, target_add_config: dict):\n    persist_help.write_config(\n        conf_file, port, additional_config_entries=bridging_add_config\n    )\n\n    persist_help.write_config(\n        conf_file_bridge_target,\n        bridge_target_port,\n        additional_config_entries=target_add_config,\n    )\n\n    rc = 1\n\n    persist_help.init(port)\n    persist_help.init(bridge_target_port)\n\n    client_id = \"persist-subscriber\"\n\n    qos = 1\n    source_id = \"persist-bridge-test-publisher\"\n    proto_ver = 4\n\n    def gen_pub_packets(idx: int, mid_offset: int):\n        payload = f\"queued message {idx:3}\"\n        publish_packet = mosq_test.gen_publish(\n            topic,\n            mid=mid_offset + idx,\n            qos=qos,\n            payload=payload.encode(\"UTF-8\"),\n            proto_ver=proto_ver,\n        )\n        puback_packet = mosq_test.gen_puback(mid=mid_offset + idx, proto_ver=proto_ver)\n        return publish_packet, puback_packet\n\n    connect_packet = mosq_test.gen_connect(\n        client_id, proto_ver=proto_ver, clean_session=False\n    )\n    connack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    connack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, topic, qos, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, qos=qos, proto_ver=proto_ver)\n\n    connect2_packet = mosq_test.gen_connect(source_id, proto_ver=proto_ver)\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    num_messages = 100\n\n    broker = None\n\n    try:\n        bridge_target_broker = mosq_test.start_broker(\n            filename=conf_file_bridge_target, use_conf=True, port=bridge_target_port\n        )\n\n        # Connect to the bridge target broker and make a qos1 subscription\n        sock_bridge_target = mosq_test.do_client_connect(\n            connect_packet, connack_packet1, timeout=5, port=bridge_target_port\n        )\n        mosq_test.do_send_receive(\n            sock_bridge_target,\n            subscribe_packet,\n            suback_packet,\n            \"suback from bridge target\",\n        )\n\n        # Now start the broker with the bridge\n        broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n\n        # Connect and send a single message forwarded to the bridge target\n        sock = mosq_test.do_client_connect(\n            connect2_packet, connack2_packet, timeout=5, port=port\n        )\n        publish_packet, puback_packet = gen_pub_packets(0, mid_offset=3)\n        mosq_test.do_send_receive(\n            sock, publish_packet, puback_packet, \"puback for first message\"\n        )\n\n        # Wait until we have received the message from the bridge target\n        publish_packet, puback_packet = gen_pub_packets(0, mid_offset=1)\n        mosq_test.do_receive_send(\n            sock_bridge_target, publish_packet, puback_packet, \"first published message\"\n        )\n\n        # Wait for a ping response to make sure the target broker has processed the PUBACK\n        mosq_test.do_ping(sock_bridge_target)\n        sock_bridge_target.close()\n\n        # Make sure the bridging broker processes a ping, which means the PUBACK from the bridge target for the\n        # first message was processed as well\n        mosq_test.do_ping(sock)\n\n        # Stop the bridge target broker\n        (broker_terminate_rc, stde2) = mosq_test.terminate_broker(bridge_target_broker)\n        bridge_target_broker = None\n\n        # Publish messages\n        for i in range(num_messages):\n            publish_packet, puback_packet = gen_pub_packets(idx=i, mid_offset=10)\n            mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n        sock.close()\n\n        # Terminate the bridging broker\n        (broker_terminate_rc, stde3) = mosq_test.terminate_broker(broker)\n        broker = None\n\n        persist_help.check_counts(\n            port,\n            clients=1,\n            client_msgs_out=num_messages,\n            base_msgs=num_messages,\n            subscriptions=0,\n        )\n\n        # Check stored message\n        for i in range(num_messages):\n            payload = f\"queued message {i:3}\"\n            payload_b = payload.encode(\"UTF-8\")\n            mid = 10 + i\n            store_id = persist_help.check_base_msg(\n                port,\n                0,\n                topic,\n                payload_b,\n                source_id,\n                None,\n                len(payload_b),\n                mid,\n                port,\n                qos,\n                retain=0,\n                idx=i,\n            )\n\n            # Check client msg\n            subscriber_mid = 4 + i\n            cmsg_id = 2 + i\n            persist_help.check_client_msg(\n                port,\n                \"upstream-bridge\",\n                cmsg_id,\n                store_id,\n                0,\n                persist_help.dir_out,\n                subscriber_mid,\n                qos,\n                0,\n                persist_help.ms_queued,\n            )\n\n        # Start the bridge target broker\n        bridge_target_broker = mosq_test.start_broker(\n            filename=conf_file_bridge_target, use_conf=True, port=bridge_target_port\n        )\n\n        # Reconnect to the bridge target broker\n        sock = mosq_test.do_client_connect(\n            connect_packet, connack_packet2, timeout=5, port=bridge_target_port\n        )\n\n        # Restart bridging broker\n        broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n\n        # Check, if all message got forwarded through the bridge\n        for i in range(num_messages):\n            publish_packet, puback_packet = gen_pub_packets(idx=i, mid_offset=1)\n            mosq_test.do_receive_send(\n                sock,\n                publish_packet,\n                puback_packet,\n                f\"Receive publish {i} after reconnect\",\n            )\n\n        # Send ping and wait for the PINGRESP to make sure the broker has processed all sent puback\n        mosq_test.do_ping(sock)\n        sock.close()\n\n        # Reconnect to the bridging broker and send ping to make sure the broker has process\n        # PUBACK from bridge target before getting shut down\n        sock = mosq_test.do_client_connect(\n            connect2_packet, connack2_packet, timeout=5, port=port\n        )\n        mosq_test.do_ping(sock)\n        sock.close()\n\n        # Stop both brokers\n        (broker_terminate_rc, stde2) = mosq_test.terminate_broker(bridge_target_broker)\n        bridge_target_broker = None\n        (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n        broker = None\n\n        persist_help.check_counts(\n            port,\n            clients=1,\n            client_msgs_out=0,\n            base_msgs=0,\n            subscriptions=0,\n        )\n\n        rc = broker_terminate_rc\n    finally:\n        if broker is not None:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                if rc == 0:\n                    rc = 1\n            (_, stde) = broker.communicate()\n        if bridge_target_broker is not None:\n            bridge_target_broker.terminate()\n            if mosq_test.wait_for_subprocess(bridge_target_broker):\n                if rc == 0:\n                    rc = 1\n            (_, stde2) = bridge_target_broker.communicate()\n        os.remove(conf_file_bridge_target)\n        os.remove(conf_file)\n        rc += persist_help.cleanup(bridge_target_port)\n        rc += persist_help.cleanup(port)\n\n        print(f\"{test_case_name}\")\n        if rc:\n            if stde2 is not None:\n                print(\"Bridge target brocker log:\")\n                print(stde2.decode(\"utf-8\"))\n            if stde3 is not None:\n                print(\"Bridging brocker log (first run):\")\n                print(stde3.decode(\"utf-8\"))\n            if stde is not None:\n                print(\"Bridging brocker log:\")\n                print(stde.decode(\"utf-8\"))\n        assert rc == 0, f\"rc: {rc}\"\n\n\nin_bridge_config = {\n    \"connection\": \"in-bridge\",\n    \"address\": f\"localhost:{port}\",\n    \"local_clientid\": \"bridge-test\",\n    \"remote_clientid\": \"upstream-bridge\",\n    \"topic\": f\"{topic} in 2\",\n}\n\nout_bridge_config = {\n    \"connection\": \"out-bridge\",\n    \"address\": f\"localhost:{bridge_target_port}\",\n    \"local_clientid\": \"upstream-bridge\",\n    \"remote_clientid\": \"bridge-test\",\n    \"topic\": f\"{topic} out 2\",\n}\n\nmemory_queue_config = {\n    \"max_queued_messages\": num_messages,\n    \"max_inflight_messages\": num_messages // 20,\n}\n\ndo_test(\n    \"memory queue out bridge\",\n    bridging_add_config=memory_queue_config | out_bridge_config,\n    target_add_config={},\n)\n\n# For a test of a push bridge we need to modify the start order\n# of the brokers...\n# do_test(\n#     \"memory queue in bridge\",\n#     bridging_add_config= memory_queue_config,\n#     target_add_config=in_bridge_config\n# )\n\nexit(0)\n"
  },
  {
    "path": "test/broker/15-persist-client-drop-expired-messages.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, send a message with a\n# different client, restore, reconnect, check it is received.\n\nfrom mosq_test_helper import *\nfrom persist_module_helper import *\n\npersist_help = persist_module()\n\nport = mosq_test.get_port()\n\nnum_messages = 100\n\nproto_ver = 5\nqos = 1\ntopic = \"test-expired-msgs\"\nusername = \"test-message-expiry\"\n\nsubscriber_id = \"test-subscriber\"\nsecond_subscriber_id = \"second-subscriber\"\npublisher_id = \"test-publisher\"\n\n\ndef do_test(\n    test_case_name: str,\n    additional_config_entries: dict,\n    resubscribe: bool,\n    num_messages_two_subscribers: int = 0,\n    num_retain_messages : int = 0,\n):\n    print(\n        f\"{test_case_name}, resubscribe = {resubscribe}, two_subscribers = {'True' if num_messages_two_subscribers > 0 else 'False'}, num_retain_messages = {num_retain_messages} \"\n    )\n\n    conf_file = os.path.basename(__file__).replace(\".py\", f\"_{port}.conf\")\n    persist_help.write_config(\n        conf_file,\n        port,\n        additional_config_entries=additional_config_entries,\n    )\n    persist_help.init(port)\n\n    connect2_packet = mosq_test.gen_connect(\n        publisher_id, username=username, proto_ver=proto_ver\n    )\n\n    rc = 1\n\n    broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n\n    con = None\n    try:\n        msg_counts = {subscriber_id: num_messages}\n\n        connect_client(\n            port,\n            subscriber_id,\n            username,\n            proto_ver,\n            session_expiry=60,\n            subscribe_topic=topic,\n        ).close()\n\n        publisher_sock = connect_client(\n            port, publisher_id, username, proto_ver, session_expiry=0\n        )\n        publish_messages(\n            publisher_sock,\n            proto_ver,\n            topic,\n            0,\n            num_messages - num_messages_two_subscribers,\n            message_expiry=60,\n            retain_end=num_retain_messages,\n        )\n\n        if num_messages_two_subscribers > 0:\n            msg_counts[second_subscriber_id] = num_messages_two_subscribers\n            connect_client(\n                port,\n                second_subscriber_id,\n                username,\n                proto_ver,\n                session_expiry=60,\n                subscribe_topic=topic,\n            ).close()\n            publish_messages(\n                publisher_sock,\n                proto_ver,\n                topic,\n                num_messages - num_messages_two_subscribers,\n                num_messages,\n                message_expiry=60,\n                retain_end=num_retain_messages,\n            )\n        publisher_sock.close()\n\n        # Terminate the broker\n        (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n        broker = None\n\n        check_db(\n            persist_help,\n            port,\n            username,\n            subscription_topic=topic,\n            client_msg_counts=msg_counts,\n            publisher_id=publisher_id,\n            num_published_msgs=num_messages,\n            retain_end = num_retain_messages,\n            message_expiry=60,\n        )\n\n        # Put session expiry_time into the past\n        assert persist_help.modify_base_msgs(port, sub_expiry_time=120) == num_messages\n\n        # Restart broker\n        broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n\n        # Reconnect client(s), it should have a session, but all queued messages should be dropped\n        for client_id in msg_counts.keys():\n            subscriber_sock = connect_client(\n                port,\n                client_id,\n                username,\n                proto_ver,\n                session_expiry=60,\n                session_present=True,\n                subscribe_topic=topic if resubscribe else None,\n            )\n        # Send ping and wait for the PINGRESP to make sure the broker will not send a queued message instead\n        mosq_test.do_ping(subscriber_sock)\n        subscriber_sock.close()\n\n        (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n        broker = None\n\n        for client_id in msg_counts.keys():\n            # None for subscriber with subscriber_id means no subscription\n            msg_counts[client_id] = 0\n        check_db(\n            persist_help,\n            port,\n            username,\n            subscription_topic=topic,\n            client_msg_counts=msg_counts,\n            publisher_id=publisher_id,\n            num_published_msgs=num_messages,\n            retain_end = 0,\n        )\n\n        rc = broker_terminate_rc\n    finally:\n        if broker is not None:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                if rc == 0:\n                    rc = 1\n            (_, stde) = broker.communicate()\n        os.remove(conf_file)\n        rc += persist_help.cleanup(port)\n\n        if rc:\n            print(stde.decode(\"utf-8\"))\n        assert rc == 0, f\"rc: {rc}\"\n\n\nmemory_queue_config = {\n    \"log_type\": \"all\",\n    \"max_queued_messages\": num_messages,\n}\n\ndo_test(\n    \"memory queue\",\n    additional_config_entries=memory_queue_config,\n    resubscribe=False,\n)\ndo_test(\n    \"memory queue\",\n    additional_config_entries=memory_queue_config,\n    resubscribe=True,\n)\ndo_test(\n    \"memory queue\",\n    additional_config_entries=memory_queue_config,\n    resubscribe=False,\n    num_messages_two_subscribers=30,\n)\ndo_test(\n    \"memory queue\",\n    additional_config_entries=memory_queue_config,\n    resubscribe=False,\n    num_retain_messages=40,\n)\n"
  },
  {
    "path": "test/broker/15-persist-client-expired-session.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, send a message with a\n# different client, restore, reconnect, check it is received.\n\nfrom mosq_test_helper import *\nfrom persist_module_helper import *\n\npersist_help = persist_module()\n\nport = mosq_test.get_port()\n\nnum_messages = 100\nproto_ver = 5\nqos = 1\ntopic = \"test-session-expiry\"\nusername = \"test-session-expiry\"\n\nsubscriber_id = \"test-expired-session-subscriber\"\nsecond_subscriber_id = \"second-subscriber\"\npublisher_id = \"test-expired-session-publisher\"\n\n\ndef do_test(\n    test_case_name: str,\n    additional_config_entries: dict,\n    resubscribe: bool,\n    num_messages_two_subscribers: int = 0,\n    num_retain_messages : int = 0,\n):\n    print(\n        f\"{test_case_name}, resubscribe = {resubscribe}, two_subscribers = {'True' if num_messages_two_subscribers > 0 else 'False'}, num_retain_messages = {num_retain_messages} \"\n    )\n\n    conf_file = os.path.basename(__file__).replace(\".py\", f\"_{port}.conf\")\n    persist_help.write_config(\n        conf_file,\n        port,\n        additional_config_entries=additional_config_entries,\n    )\n    persist_help.init(port)\n\n    connect2_packet = mosq_test.gen_connect(\n        publisher_id, username=username, proto_ver=proto_ver\n    )\n\n    rc = 1\n\n    broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n\n    con = None\n    try:\n        msg_counts = {subscriber_id: num_messages}\n\n        connect_client(\n            port,\n            subscriber_id,\n            username,\n            proto_ver,\n            session_expiry=60,\n            subscribe_topic=topic,\n        ).close()\n\n        publisher_sock = connect_client(\n            port, publisher_id, username, proto_ver, session_expiry=0\n        )\n        publish_messages(\n            publisher_sock,\n            proto_ver,\n            topic,\n            0,\n            num_messages - num_messages_two_subscribers,\n            retain_end=num_retain_messages,\n        )\n\n        if num_messages_two_subscribers > 0:\n            msg_counts[second_subscriber_id] = num_messages_two_subscribers\n            connect_client(\n                port,\n                second_subscriber_id,\n                username,\n                proto_ver,\n                session_expiry=60,\n                subscribe_topic=topic,\n            ).close()\n            publish_messages(\n                publisher_sock,\n                proto_ver,\n                topic,\n                num_messages - num_messages_two_subscribers,\n                num_messages,\n                retain_end=num_retain_messages,\n            )\n        publisher_sock.close()\n\n        # Terminate the broker\n        (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n        broker = None\n\n        check_db(\n            persist_help,\n            port,\n            username,\n            subscription_topic=topic,\n            client_msg_counts=msg_counts,\n            publisher_id=publisher_id,\n            num_published_msgs=num_messages,\n            retain_end = num_retain_messages,\n        )\n\n        # Put session expiry_time into the past\n        assert persist_help.modify_client(port, subscriber_id, sub_expiry_time=120) == 1\n\n        # Restart broker\n        broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n\n        # Reconnect client, it should have a session, but all queued messages should be dropped\n        subscriber_sock = connect_client(\n            port,\n            subscriber_id,\n            username,\n            proto_ver,\n            session_expiry=60,\n            subscribe_topic=topic if resubscribe else None,\n        )\n        # Send ping and wait for the PINGRESP to make sure the broker will not send a queued message instead\n        mosq_test.do_ping(subscriber_sock)\n        subscriber_sock.close()\n\n        (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n        broker = None\n\n        # None for subscriber with subscriber_id means no subscription\n        msg_counts[subscriber_id] = 0 if resubscribe else None\n        check_db(\n            persist_help,\n            port,\n            username,\n            subscription_topic=topic,\n            client_msg_counts=msg_counts,\n            publisher_id=publisher_id,\n            num_published_msgs=num_messages,\n            retain_end=num_retain_messages,\n        )\n\n        if num_messages_two_subscribers > 0:\n            # Put session expiry_time into the past\n            assert (\n                persist_help.modify_client(\n                    port, second_subscriber_id, sub_expiry_time=120\n                )\n                == 1\n            )\n            # Restart broker\n            broker = mosq_test.start_broker(\n                filename=conf_file, use_conf=True, port=port\n            )\n            # Reconnect client, it should have a session, but all queued messages should be dropped\n            subscriber_sock = connect_client(\n                port,\n                second_subscriber_id,\n                username,\n                proto_ver,\n                session_expiry=60,\n                subscribe_topic=topic if resubscribe else None,\n            )\n            # Send ping and wait for the PINGRESP to make sure the broker will not send a queued message instead\n            mosq_test.do_ping(subscriber_sock)\n            subscriber_sock.close()\n\n            (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n            broker = None\n\n            msg_counts[second_subscriber_id] = 0 if resubscribe else None\n            check_db(\n                persist_help,\n                port,\n                username,\n                subscription_topic=topic,\n                client_msg_counts=msg_counts,\n                publisher_id=publisher_id,\n                num_published_msgs=num_messages,\n                retain_end=num_retain_messages,\n            )\n\n        rc = broker_terminate_rc\n    finally:\n        if broker is not None:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                if rc == 0:\n                    rc = 1\n            (_, stde) = broker.communicate()\n        os.remove(conf_file)\n        rc += persist_help.cleanup(port)\n\n        if rc:\n            print(stde.decode(\"utf-8\"))\n        assert rc == 0, f\"rc: {rc}\"\n\n\nmemory_queue_config = {\n    \"log_type\": \"all\",\n    \"max_queued_messages\": num_messages,\n}\n\n\ndo_test(\n    \"memory queue\",\n    additional_config_entries=memory_queue_config,\n    resubscribe=False,\n)\ndo_test(\n    \"memory queue\",\n    additional_config_entries=memory_queue_config,\n    resubscribe=True,\n)\ndo_test(\n    \"memory queue\",\n    additional_config_entries=memory_queue_config,\n    resubscribe=False,\n    num_messages_two_subscribers=20,\n)\ndo_test(\n    \"memory queue\",\n    additional_config_entries=memory_queue_config,\n    resubscribe=True,\n    num_messages_two_subscribers=20,\n)\ndo_test(\n    \"memory queue\",\n    additional_config_entries=memory_queue_config,\n    resubscribe=False,\n    num_retain_messages=30,\n)\n# The following test case is open for discussion as adapting\n# the check routines will be hard and some observations\n# are unclear right now\n# do_test(\n#     \"memory queue\",\n#     additional_config_entries=memory_queue_config,\n#     resubscribe=False,\n#     num_messages_two_subscribers=40,\n#     num_retain_messages=30,\n# )\n# do_test(\n#     \"memory queue\",\n#     additional_config_entries=memory_queue_config,\n#     resubscribe=False,\n#     num_messages_two_subscribers=50,\n#     num_retain_messages=60,\n# )\n\n"
  },
  {
    "path": "test/broker/15-persist-client-msg-in-v3-1-1.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, start a QoS 2 flow, disconnect, restore, carry on with the\n# QoS 2 flow. Is it received?\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\npersist_help.init(port)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\n\nclient_id = \"persist-client-msg-in-v3-1-1\"\nproto_ver = 4\n\nhelper_id = \"persist-client-msg-in-v3-1-1-helper\"\ntopic = \"client-msg-in/2\"\nqos = 2\nstde = b\"\"\n\nconnect_packet = mosq_test.gen_connect(client_id, proto_ver=proto_ver, clean_session=False)\nconnack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\nconnack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\n\nmid = 1\npublish1_packet = mosq_test.gen_publish(topic=topic, qos=qos, payload=\"message1\", mid=mid, proto_ver=proto_ver)\npubrec1_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\npubrel1_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\npubcomp1_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\nmid = 2\npublish2_packet = mosq_test.gen_publish(topic=topic, qos=qos, payload=\"message2\", mid=mid, proto_ver=proto_ver)\npubrec2_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\npubrel2_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\npubcomp2_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\nconnect_packet_helper = mosq_test.gen_connect(helper_id, proto_ver=proto_ver, clean_session=True)\nsubscribe_packet = mosq_test.gen_subscribe(mid, topic, qos=qos, proto_ver=proto_ver)\nsuback_packet = mosq_test.gen_suback(mid=mid, qos=qos, proto_ver=proto_ver)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    # Connect client, start flow, disconnect\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet1, timeout=5, port=port, connack_error=\"connack 1\")\n    mosq_test.do_send_receive(sock, publish1_packet, pubrec1_packet, \"pubrec1 send\")\n    mosq_test.do_send_receive(sock, publish2_packet, pubrec2_packet, \"pubrec2 send\")\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port, clients=1, client_msgs_in=2, base_msgs=2)\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect helper and subscribe\n    helper = mosq_test.do_client_connect(connect_packet_helper, connack_packet1, timeout=5, port=port, connack_error=\"helper connack\")\n    mosq_test.do_send_receive(helper, subscribe_packet, suback_packet, \"suback helper\")\n\n    # Complete the flow\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port, connack_error=\"connack 2\")\n    mosq_test.do_send_receive(sock, pubrel1_packet, pubcomp1_packet, \"pubrel1 send\")\n    mosq_test.do_send_receive(sock, pubrel2_packet, pubcomp2_packet, \"pubrel2 send\")\n\n    mosq_test.expect_packet(helper, \"publish1 receive\", publish1_packet)\n    mosq_test.expect_packet(helper, \"publish2 receive\", publish2_packet)\n    helper.send(pubrec1_packet)\n    mosq_test.do_receive_send(helper, pubrel1_packet, pubcomp1_packet, \"pubcomp1 receive\")\n\n    helper.send(pubrec2_packet)\n    mosq_test.do_receive_send(helper, pubrel2_packet, pubcomp2_packet, \"pubcomp2 receive\")\n\n    # Send ping and wait for the PINGRESP to make sure the broker has processed all sent puback\n    mosq_test.do_ping(helper)\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port, clients=1)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-client-msg-in-v5-0.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, start a QoS 2 flow, disconnect, restore, carry on with the\n# QoS 2 flow. Is it received?\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\nclient_id = \"persist-client-msg-in-v5-0\"\nproto_ver = 5\n\nhelper_id = \"persist-client-msg-in-v5-0-helper\"\ntopic = \"client-msg-in/2\"\nqos = 2\n\nconnect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\nconnect_packet = mosq_test.gen_connect(client_id, proto_ver=proto_ver, clean_session=False, properties=connect_props)\nconnack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\nconnack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\n\nmid = 1\npublish1_packet = mosq_test.gen_publish(topic=topic, qos=qos, payload=\"message1\", mid=mid, proto_ver=proto_ver)\npubrec1_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\npubrel1_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\npubcomp1_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\nmid = 2\npublish2_packet = mosq_test.gen_publish(topic=topic, qos=qos, payload=\"message2\", mid=mid, proto_ver=proto_ver)\npubrec2_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\npubrel2_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\npubcomp2_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\nconnect_packet_helper = mosq_test.gen_connect(helper_id, proto_ver=proto_ver, clean_session=True)\nsubscribe_packet = mosq_test.gen_subscribe(mid, topic, qos=qos, proto_ver=proto_ver)\nsuback_packet = mosq_test.gen_suback(mid=mid, qos=qos, proto_ver=proto_ver)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    # Connect client, start flow, disconnect\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet1, timeout=5, port=port, connack_error=\"connack 1\")\n    mosq_test.do_send_receive(sock, publish1_packet, pubrec1_packet, \"pubrec1 send\")\n    mosq_test.do_send_receive(sock, publish2_packet, pubrec2_packet, \"pubrec2 send\")\n    sock.close()\n\n    # Kill broker\n    broker_terminate_rc = mosq_test.terminate_broker(broker)\n\n    persist_help.check_counts(port, clients=1, client_msgs_in=2, base_msgs=2)\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect helper and subscribe\n    helper = mosq_test.do_client_connect(connect_packet_helper, connack_packet1, timeout=5, port=port, connack_error=\"helper connack\")\n    mosq_test.do_send_receive(helper, subscribe_packet, suback_packet, \"suback helper\")\n\n    # Complete the flow\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port, connack_error=\"connack 2\")\n    mosq_test.do_send_receive(sock, pubrel1_packet, pubcomp1_packet, \"pubrel1 send\")\n    mosq_test.do_send_receive(sock, pubrel2_packet, pubcomp2_packet, \"pubrel2 send\")\n\n    mosq_test.expect_packet(helper, \"publish1 receive\", publish1_packet)\n    mosq_test.expect_packet(helper, \"publish2 receive\", publish2_packet)\n    helper.send(pubrec1_packet)\n    mosq_test.do_receive_send(helper, pubrel1_packet, pubcomp1_packet, \"pubcomp1 receive\")\n\n    helper.send(pubrec2_packet)\n    mosq_test.do_receive_send(helper, pubrel2_packet, pubcomp2_packet, \"pubcomp2 receive\")\n\n    # Send ping and wait for the PINGRESP to make sure the broker has processed all sent puback\n    mosq_test.do_ping(helper)\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port, clients=1)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-client-msg-modify-acl.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, send a message with a\n# different client, restore, reconnect, check it is received.\n\nfrom mosq_test_helper import *\n\npersist_help = persist_module()\n\nport = mosq_test.get_port()\n\nnum_messages = 100\n\n\ndef do_test(test_case_name: str, additional_config_entries: dict):\n    conf_file = os.path.basename(__file__).replace(\".py\", \".conf\")\n    acl_file = os.path.basename(__file__).replace(\".py\", \".acl\")\n    persist_help.write_config(\n        conf_file,\n        port,\n        additional_config_entries={\"acl_file\": f\"{acl_file}\"}\n        | additional_config_entries,\n    )\n    persist_help.init(port)\n\n    client_id = \"test-change-acl-subscriber\"\n    username = \"test-acl-change\"\n\n    qos = 1\n    topic = \"client-msg/test\"\n    source_id = \"test-change-acl-publisher\"\n    proto_ver = 4\n\n    connect_packet = mosq_test.gen_connect(\n        client_id, username=username, proto_ver=proto_ver, clean_session=False\n    )\n    connack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    connack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, topic, qos, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid, qos=qos, proto_ver=proto_ver)\n\n    connect2_packet = mosq_test.gen_connect(\n        source_id, proto_ver=proto_ver, username=username\n    )\n    connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\n    with open(acl_file, \"w\") as f:\n        f.write(f\"user {username}\\n\")\n        f.write(f\"pattern readwrite {topic}\\n\")\n    os.chmod(f\"{acl_file}\", 0o644)\n\n    rc = 1\n\n    broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n\n    con = None\n    try:\n        sock = mosq_test.do_client_connect(\n            connect_packet, connack_packet1, timeout=5, port=port\n        )\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        sock.close()\n\n        sock = mosq_test.do_client_connect(\n            connect2_packet, connack2_packet, timeout=5, port=port\n        )\n        for i in range(num_messages):\n            payload = f\"queued message {i:3}\"\n            mid = 10 + i\n            publish_packet = mosq_test.gen_publish(\n                topic,\n                mid=mid,\n                qos=qos,\n                payload=payload.encode(\"UTF-8\"),\n                proto_ver=proto_ver,\n            )\n            puback_packet = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\n            mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n        sock.close()\n\n        # Terminate the broker\n        (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n        broker = None\n\n        persist_help.check_counts(\n            port,\n            clients=1,\n            client_msgs_out=num_messages,\n            base_msgs=num_messages,\n            subscriptions=1,\n        )\n\n        # Check client\n        persist_help.check_client(\n            port,\n            client_id,\n            username=username,\n            will_delay_time=0,\n            session_expiry_time=0,\n            listener_port=port,\n            max_packet_size=0,\n            max_qos=2,\n            retain_available=1,\n            session_expiry_interval=4294967295,\n            will_delay_interval=0,\n        )\n\n        # Check subscription\n        persist_help.check_subscription(port, client_id, topic, qos, 0)\n\n        # Check stored message\n        for i in range(num_messages):\n            payload = f\"queued message {i:3}\"\n            payload_b = payload.encode(\"UTF-8\")\n            mid = 10 + i\n            store_id = persist_help.check_base_msg(\n                port,\n                0,\n                topic,\n                payload_b,\n                source_id,\n                username,\n                len(payload_b),\n                mid,\n                port,\n                qos,\n                retain=0,\n                idx=i,\n            )\n\n            # Check client msg\n            subscriber_mid = 1 + i\n            cmsg_id = 1 + i\n            persist_help.check_client_msg(\n                port,\n                client_id,\n                cmsg_id,\n                store_id,\n                0,\n                persist_help.dir_out,\n                subscriber_mid,\n                qos,\n                0,\n                persist_help.ms_queued,\n            )\n\n        # Remove any permission for the test topic and the test user\n        with open(acl_file, \"w\") as f:\n            f.write(f\"user {username}\\n\")\n            f.write(f\"pattern deny {topic}\\n\")\n        os.chmod(f\"{acl_file}\", 0o644)\n\n        # Restart broker\n        broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n\n        # Connect client again, it should have a session, but all queued messages should be dropped\n        sock = mosq_test.do_client_connect(\n            connect_packet, connack_packet2, timeout=5, port=port\n        )\n\n        # Send ping and wait for the PINGRESP to make sure the broker will not send a queued message instead\n        mosq_test.do_ping(sock)\n        sock.close()\n\n        (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n        broker = None\n\n        persist_help.check_counts(\n            port,\n            clients=1,\n            client_msgs_out=0,\n            base_msgs=0,\n            subscriptions=1,\n        )\n\n        rc = broker_terminate_rc\n    finally:\n        if broker is not None:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                if rc == 0:\n                    rc = 1\n            (_, stde) = broker.communicate()\n        os.remove(acl_file)\n        os.remove(conf_file)\n        rc += persist_help.cleanup(port)\n\n        print(f\"{test_case_name}\")\n        if rc:\n            print(stde.decode(\"utf-8\"))\n        assert rc == 0, f\"rc: {rc}\"\n\n\ndo_test(\n    \"memory queue\",\n    additional_config_entries={\n        \"max_queued_messages\": num_messages,\n        \"max_inflight_messages\": num_messages // 20,\n    },\n)\n"
  },
  {
    "path": "test/broker/15-persist-client-msg-out-clear-v3-1-1.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, send a message with a\n# different client, restore, reconnect with clear start, check it is not received.\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\nkeepalive = 10\nclient_id = \"persist-client-msg-v3-1-1\"\nproto_ver = 4\n\nhelper_id = \"persist-client-msg-v3-1-1-helper\"\ntopic0 = \"client-msg/0\"\ntopic1 = \"client-msg/1\"\ntopic2 = \"client-msg/2\"\n\nconnect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, proto_ver=proto_ver, clean_session=False)\nconnect_packet_clear = mosq_test.gen_connect(client_id, keepalive=keepalive, proto_ver=proto_ver, clean_session=True)\nconnack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\nconnack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\nmid = 1\nsubscribe_packet0 = mosq_test.gen_subscribe(mid, topic0, qos=0, proto_ver=proto_ver)\nsuback_packet0 = mosq_test.gen_suback(mid=mid, qos=0, proto_ver=proto_ver)\nsubscribe_packet1 = mosq_test.gen_subscribe(mid, topic1, qos=1, proto_ver=proto_ver)\nsuback_packet1 = mosq_test.gen_suback(mid=mid, qos=1, proto_ver=proto_ver)\nsubscribe_packet2 = mosq_test.gen_subscribe(mid, topic2, qos=2, proto_ver=proto_ver)\nsuback_packet2 = mosq_test.gen_suback(mid=mid, qos=2, proto_ver=proto_ver)\n\nconnect_packet_helper = mosq_test.gen_connect(helper_id, keepalive=keepalive, proto_ver=proto_ver, clean_session=True)\npublish_packet0 = mosq_test.gen_publish(topic=topic0, qos=0, payload=\"message\", proto_ver=proto_ver)\nmid = 1\npublish_packet1 = mosq_test.gen_publish(topic=topic1, qos=1, payload=\"message\", mid=mid, proto_ver=proto_ver)\npuback_packet = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\nmid = 2\npublish_packet2 = mosq_test.gen_publish(topic=topic2, qos=2, payload=\"message\", mid=mid, proto_ver=proto_ver)\npubrec_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\npubrel_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\npubcomp_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    # Connect client, subscribe, disconnect\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet1, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet0, suback_packet0, \"suback 0\")\n    mosq_test.do_send_receive(sock, subscribe_packet1, suback_packet1, \"suback 1\")\n    mosq_test.do_send_receive(sock, subscribe_packet2, suback_packet2, \"suback 2\")\n    sock.close()\n\n    # Connect helper and publish\n    helper = mosq_test.do_client_connect(connect_packet_helper, connack_packet1, timeout=5, port=port)\n    helper.send(publish_packet0)\n    mosq_test.do_send_receive(helper, publish_packet1, puback_packet, \"puback helper\")\n    mosq_test.do_send_receive(helper, publish_packet2, pubrec_packet, \"pubrec helper\")\n    mosq_test.do_send_receive(helper, pubrel_packet, pubcomp_packet, \"pubcomp helper\")\n    helper.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port, clients=1, client_msgs_out=2, base_msgs=2, subscriptions=3)\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port)\n\n    # Does the client get the messages - don't complete the flows\n    mosq_test.expect_packet(sock, \"publish 1\", publish_packet1)\n    mosq_test.expect_packet(sock, \"publish 2\", publish_packet2)\n    sock.close()\n\n    # Connect client again and clear the session\n    sock = mosq_test.do_client_connect(connect_packet_clear, connack_packet1, timeout=5, port=port)\n    # If there are messages, the ping will fail\n    mosq_test.do_ping(sock)\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-client-msg-out-dup-v3-1-1.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\nkeepalive = 10\n\npersist_help.init(port)\n\nclient_id = \"persist-cmsg-out-dup-v3-1-1\"\npayload = \"queued message 1\"\npayload_b = payload.encode(\"UTF-8\")\nqos = 2\ntopic = \"client-msg/test\"\nsource_id = \"persist-cmsg-v3-1-1-helper\"\nproto_ver = 4\n\nkeepalive = 10\nconnect1_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, proto_ver=proto_ver, clean_session=False)\nconnack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\nconnack1_packet2 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, flags=1)\n\nmid = 1\nsubscribe_packet = mosq_test.gen_subscribe(mid, topic, qos, proto_ver=proto_ver)\nsuback_packet = mosq_test.gen_suback(mid, qos=qos, proto_ver=proto_ver)\n\nconnect2_packet = mosq_test.gen_connect(source_id, keepalive=keepalive, proto_ver=proto_ver)\nconnack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\nsource_mid = 18\npublish_packet = mosq_test.gen_publish(topic, mid=source_mid, qos=qos, payload=payload, proto_ver=proto_ver)\npubrec_packet = mosq_test.gen_pubrec(mid=source_mid, proto_ver=proto_ver)\npubrel_packet = mosq_test.gen_pubrel(mid=source_mid, proto_ver=proto_ver)\npubcomp_packet = mosq_test.gen_pubcomp(mid=source_mid, proto_ver=proto_ver)\n\nmid = 1\npublish_packet_r1 = mosq_test.gen_publish(topic, mid=mid, qos=qos, payload=payload, proto_ver=proto_ver)\npublish_packet_r2 = mosq_test.gen_publish(topic, mid=mid, qos=qos, payload=payload, proto_ver=proto_ver, dup=1)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    # Connect and set up subscription, then disconnect\n    sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    sock.close()\n\n    #persist_help.check_counts(port, clients=1, subscriptions=1)\n\n    # Helper - send message then disconnect\n    sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, \"pubrec\")\n    mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, \"pubcomp\")\n    sock.close()\n\n    #persist_help.check_counts(port, clients=1, client_msgs=1, base_msgs=1, subscriptions=1)\n\n    # Reconnect, receive publish, disconnect\n    sock = mosq_test.do_client_connect(connect1_packet, connack1_packet2, timeout=5, port=port)\n    mosq_test.expect_packet(sock, \"publish 1\", publish_packet_r1)\n\n    #persist_help.check_counts(port, clients=1, client_msgs=1, base_msgs=1, subscriptions=1)\n\n    # Reconnect, receive publish, disconnect - dup should now be set\n    sock = mosq_test.do_client_connect(connect1_packet, connack1_packet2, timeout=5, port=port)\n    mosq_test.expect_packet(sock, \"publish 2\", publish_packet_r2)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port, clients=1, client_msgs_out=1, base_msgs=1, subscriptions=1)\n\n    # Check client\n    persist_help.check_client(port, client_id, None, 0, 0, port, 0, 2, 1, 4294967295, 0)\n\n    # Check subscription\n    persist_help.check_subscription(port, client_id, topic, qos, 0)\n\n    # Check stored message\n    store_id = persist_help.check_base_msg(port, 0, topic, payload_b, source_id, None, len(payload_b), source_mid, port, qos, 0)\n\n    # Check client msg\n    persist_help.check_client_msg(port, client_id, 1, store_id, 1, persist_help.dir_out, 1, qos, 0, persist_help.ms_wait_for_pubrec)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (2)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-client-msg-out-queue-v3-1-1.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, send a message with a\n# different client, restore, reconnect, check it is received.\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\nkeepalive = 10\nclient_id = \"persist-client-msg-v3-1-1\"\nproto_ver = 4\n\nhelper_id = \"persist-client-msg-v3-1-1-helper\"\ntopic0 = \"client-msg/0\"\ntopic1 = \"client-msg/1\"\ntopic2 = \"client-msg/2\"\n\nconnect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, proto_ver=proto_ver, clean_session=False)\nconnack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\nconnack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\nmid = 1\nsubscribe_packet0 = mosq_test.gen_subscribe(mid, topic0, qos=0, proto_ver=proto_ver)\nsuback_packet0 = mosq_test.gen_suback(mid=mid, qos=0, proto_ver=proto_ver)\nsubscribe_packet1 = mosq_test.gen_subscribe(mid, topic1, qos=1, proto_ver=proto_ver)\nsuback_packet1 = mosq_test.gen_suback(mid=mid, qos=1, proto_ver=proto_ver)\nsubscribe_packet2 = mosq_test.gen_subscribe(mid, topic2, qos=2, proto_ver=proto_ver)\nsuback_packet2 = mosq_test.gen_suback(mid=mid, qos=2, proto_ver=proto_ver)\n\nconnect_packet_helper = mosq_test.gen_connect(helper_id, keepalive=keepalive, proto_ver=proto_ver, clean_session=True)\npublish_packet0 = mosq_test.gen_publish(topic=topic0, qos=0, payload=\"message\", proto_ver=proto_ver)\nmid = 1\npublish_packet1 = mosq_test.gen_publish(topic=topic1, qos=1, payload=\"message\", mid=mid, proto_ver=proto_ver)\npuback_packet = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\nmid = 2\npublish_packet2 = mosq_test.gen_publish(topic=topic2, qos=2, payload=\"message\", mid=mid, proto_ver=proto_ver)\npubrec_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\npubrel_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\npubcomp_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    # Connect client, subscribe, disconnect\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet1, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet0, suback_packet0, \"suback 0\")\n    mosq_test.do_send_receive(sock, subscribe_packet1, suback_packet1, \"suback 1\")\n    mosq_test.do_send_receive(sock, subscribe_packet2, suback_packet2, \"suback 2\")\n    sock.close()\n\n    # Connect helper and publish\n    helper = mosq_test.do_client_connect(connect_packet_helper, connack_packet1, timeout=5, port=port)\n    helper.send(publish_packet0)\n    mosq_test.do_send_receive(helper, publish_packet1, puback_packet, \"puback helper\")\n    mosq_test.do_send_receive(helper, publish_packet2, pubrec_packet, \"pubrec helper\")\n    mosq_test.do_send_receive(helper, pubrel_packet, pubcomp_packet, \"pubcomp helper\")\n    helper.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port, clients=1, client_msgs_out=2, base_msgs=2, subscriptions=3)\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port)\n\n    # Does the client get the messages\n    mosq_test.do_receive_send(sock, publish_packet1, puback_packet, \"publish 1\")\n    mosq_test.do_receive_send(sock, publish_packet2, pubrec_packet, \"publish 2\")\n    mosq_test.do_receive_send(sock, pubrel_packet, pubcomp_packet, \"pubrel 2\")\n    # Send ping and wait for the PINGRESP to make sure the broker has processed all sent pubcomp\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port)\n    # If there are messages, the ping will fail\n    mosq_test.do_ping(sock)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port, clients=1, subscriptions=3)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-client-msg-out-v3-1-1-db.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, send a message with a\n# different client, restore, reconnect, check it is received.\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\nclient_id = \"persist-cmsg-v3-1-1\"\npayload = \"queued message 1\"\npayload_b = payload.encode(\"UTF-8\")\nqos = 1\ntopic = \"client-msg/test\"\nsource_id = \"persist-cmsg-v3-1-1-helper\"\nproto_ver = 4\n\nconnect_packet = mosq_test.gen_connect(client_id, proto_ver=proto_ver, clean_session=False)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\nmid = 1\nsubscribe_packet = mosq_test.gen_subscribe(mid, topic, qos, proto_ver=proto_ver)\nsuback_packet = mosq_test.gen_suback(mid, qos=qos, proto_ver=proto_ver)\n\nconnect2_packet = mosq_test.gen_connect(source_id, proto_ver=proto_ver)\nconnack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\nmid = 18\npublish_packet = mosq_test.gen_publish(topic, mid=mid, qos=qos, payload=payload, proto_ver=proto_ver)\npuback_packet = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    sock.close()\n\n    sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n    sock.close()\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port, clients=1, client_msgs_out=1, base_msgs=1, subscriptions=1)\n\n    # Check client\n    persist_help.check_client(port, client_id, None, 0, 0, port, 0, 2, 1, 4294967295, 0)\n\n    # Check subscription\n    persist_help.check_subscription(port, client_id, topic, qos, 0)\n\n    # Check stored message\n    store_id = persist_help.check_base_msg(port, 0, topic, payload_b, source_id, None, len(payload_b), mid, port, qos, 0)\n\n    # Check client msg\n    persist_help.check_client_msg(port, client_id, 1, store_id, 0, persist_help.dir_out, 1, qos, 0, persist_help.ms_queued)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (2)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-client-msg-out-v3-1-1.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, send a message with a\n# different client, restore, reconnect, check it is received.\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\nclient_id = \"persist-client-msg-v3-1-1\"\nproto_ver = 4\n\nhelper_id = \"persist-client-msg-v3-1-1-helper\"\ntopic0 = \"client-msg/0\"\ntopic1 = \"client-msg/1\"\ntopic2 = \"client-msg/2\"\n\nconnect_packet = mosq_test.gen_connect(client_id, proto_ver=proto_ver, clean_session=False)\nconnack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\nconnack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\nmid = 1\nsubscribe_packet0 = mosq_test.gen_subscribe(mid, topic0, qos=0, proto_ver=proto_ver)\nsuback_packet0 = mosq_test.gen_suback(mid=mid, qos=0, proto_ver=proto_ver)\nsubscribe_packet1 = mosq_test.gen_subscribe(mid, topic1, qos=1, proto_ver=proto_ver)\nsuback_packet1 = mosq_test.gen_suback(mid=mid, qos=1, proto_ver=proto_ver)\nsubscribe_packet2 = mosq_test.gen_subscribe(mid, topic2, qos=2, proto_ver=proto_ver)\nsuback_packet2 = mosq_test.gen_suback(mid=mid, qos=2, proto_ver=proto_ver)\n\nconnect_packet_helper = mosq_test.gen_connect(helper_id, proto_ver=proto_ver, clean_session=True)\npublish_packet0 = mosq_test.gen_publish(topic=topic0, qos=0, payload=\"message\", proto_ver=proto_ver)\nmid = 1\npublish_packet1 = mosq_test.gen_publish(topic=topic1, qos=1, payload=\"message\", mid=mid, proto_ver=proto_ver)\npuback_packet = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\nmid = 2\npublish_packet2 = mosq_test.gen_publish(topic=topic2, qos=2, payload=\"message\", mid=mid, proto_ver=proto_ver)\npubrec_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\npubrel_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\npubcomp_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    # Connect client, subscribe, disconnect\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet1, timeout=5, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet0, suback_packet0, \"suback 0\")\n    mosq_test.do_send_receive(sock, subscribe_packet1, suback_packet1, \"suback 1\")\n    mosq_test.do_send_receive(sock, subscribe_packet2, suback_packet2, \"suback 2\")\n    sock.close()\n\n    # Connect helper and publish\n    helper = mosq_test.do_client_connect(connect_packet_helper, connack_packet1, timeout=5, port=port)\n    helper.send(publish_packet0)\n    mosq_test.do_send_receive(helper, publish_packet1, puback_packet, \"puback helper\")\n    mosq_test.do_send_receive(helper, publish_packet2, pubrec_packet, \"pubrec helper\")\n    mosq_test.do_send_receive(helper, pubrel_packet, pubcomp_packet, \"pubcomp helper\")\n    helper.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port)\n\n    # Does the client get the messages\n    mosq_test.do_receive_send(sock, publish_packet1, puback_packet, \"publish 1\")\n    mosq_test.do_receive_send(sock, publish_packet2, pubrec_packet, \"publish 2\")\n    mosq_test.do_receive_send(sock, pubrel_packet, pubcomp_packet, \"pubrel 2\")\n    # Send ping and wait for the PINGRESP to make sure the broker has processed all sent pubcomp\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port)\n    # If there are messages, the ping will fail\n    mosq_test.do_ping(sock)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n    persist_help.check_counts(port, clients=1, subscriptions=3)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (2)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-client-msg-out-v5-0.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, send a message with a\n# different client, restore, reconnect, check it is received.\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\nclient_id = \"persist-client-msg-v5-0\"\nproto_ver = 5\n\nhelper_id = \"persist-client-msg-v5-0-helper\"\ntopic0 = \"client-msg/0\"\ntopic1 = \"client-msg/1\"\ntopic2 = \"client-msg/2\"\n\nconnect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\nconnect_packet = mosq_test.gen_connect(client_id, proto_ver=proto_ver, clean_session=False, properties=connect_props)\nconnack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\nconnack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\nmid = 1\n\nsub_props0 = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 1)\nsubscribe_packet0 = mosq_test.gen_subscribe(mid, topic0, qos=0, proto_ver=proto_ver, properties=sub_props0)\nsuback_packet0 = mosq_test.gen_suback(mid=mid, qos=0, proto_ver=proto_ver)\n\nsub_props1 = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 2)\nsubscribe_packet1 = mosq_test.gen_subscribe(mid, topic1, qos=1, proto_ver=proto_ver, properties=sub_props1)\nsuback_packet1 = mosq_test.gen_suback(mid=mid, qos=1, proto_ver=proto_ver)\n\nsub_props2 = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 3)\nsubscribe_packet2 = mosq_test.gen_subscribe(mid, topic2, qos=2, proto_ver=proto_ver, properties=sub_props2)\nsuback_packet2 = mosq_test.gen_suback(mid=mid, qos=2, proto_ver=proto_ver)\n\nconnect_packet_helper = mosq_test.gen_connect(helper_id, proto_ver=proto_ver, clean_session=True)\npublish_packet0 = mosq_test.gen_publish(topic=topic0, qos=0, payload=\"message\", proto_ver=proto_ver)\nmid = 1\npublish_packet1 = mosq_test.gen_publish(topic=topic1, qos=1, payload=\"message\", mid=mid, proto_ver=proto_ver)\npuback_packet = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\npublish_received1 = mosq_test.gen_publish(topic=topic1, qos=1, payload=\"message\", mid=mid, proto_ver=proto_ver, properties=sub_props1)\n\nmid = 2\npubrec_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\npublish_packet2 = mosq_test.gen_publish(topic=topic2, qos=2, payload=\"message\", mid=mid, proto_ver=proto_ver)\npublish_received2 = mosq_test.gen_publish(topic=topic2, qos=2, payload=\"message\", mid=mid, proto_ver=proto_ver, properties=sub_props2)\npubrel_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\npubcomp_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    # Connect client, subscribe, disconnect\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet1, timeout=5, port=port, connack_error=\"connack 1\")\n    mosq_test.do_send_receive(sock, subscribe_packet0, suback_packet0, \"suback 0\")\n    mosq_test.do_send_receive(sock, subscribe_packet1, suback_packet1, \"suback 1\")\n    mosq_test.do_send_receive(sock, subscribe_packet2, suback_packet2, \"suback 2\")\n    sock.close()\n\n    # Connect helper and publish\n    helper = mosq_test.do_client_connect(connect_packet_helper, connack_packet1, timeout=5, port=port, connack_error=\"helper connack\")\n    helper.send(publish_packet0)\n    mosq_test.do_send_receive(helper, publish_packet1, puback_packet, \"puback helper\")\n    mosq_test.do_send_receive(helper, publish_packet2, pubrec_packet, \"pubrec helper\")\n    mosq_test.do_send_receive(helper, pubrel_packet, pubcomp_packet, \"pubcomp helper\")\n    helper.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port, connack_error=\"connack 2\")\n\n    # Does the client get the messages\n    mosq_test.do_receive_send(sock, publish_received1, puback_packet, \"publish received 1\")\n    mosq_test.do_receive_send(sock, publish_received2, pubrec_packet, \"publish received 2\")\n    mosq_test.do_receive_send(sock, pubrel_packet, pubcomp_packet, \"pubrel 2\")\n    # Send ping and wait for the PINGRESP to make sure the broker has processed all sent pubcomp\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port, connack_error=\"connack 3\")\n    # If there are messages, the ping will fail\n    mosq_test.do_ping(sock)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port, clients=1, subscriptions=3)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-client-v3-1-1.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, check it is restored, clear the client, check it is not there.\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\nclient_id = \"persist-client-v3-1-1\"\nproto_ver = 4\n\nconnect_packet = mosq_test.gen_connect(client_id, proto_ver=proto_ver, clean_session=False)\nconnack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\nconnack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\n\nconnect_packet_clean = mosq_test.gen_connect(client_id, proto_ver=proto_ver, clean_session=True)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet1, timeout=5, port=port, connack_error=\"connack 1\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port, connack_error=\"connack 2\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port, connack_error=\"connack 3\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Clear the client\n    sock = mosq_test.do_client_connect(connect_packet_clean, connack_packet1, timeout=5, port=port, connack_error=\"connack 4\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Connect client, it should not have a session\n    sock = mosq_test.do_client_connect(connect_packet_clean, connack_packet1, timeout=5, port=port, connack_error=\"connack 5\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client, it should not have a session\n    sock = mosq_test.do_client_connect(connect_packet_clean, connack_packet1, timeout=5, port=port, connack_error=\"connack 6\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n    persist_help.check_counts(port)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (3)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-client-v5-0.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, check it is restored, clear the client, check it is not there.\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\nkeepalive = 10\nclient_id = \"persist-client-v5-0\"\nproto_ver = 5\n\nconnect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\nconnect_props += mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 10000)\nconnect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, proto_ver=proto_ver, clean_session=False, properties=connect_props)\nconnack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\nconnack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\n\nconnect_packet_clean = mosq_test.gen_connect(client_id, keepalive=keepalive, proto_ver=proto_ver, clean_session=True)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet1, timeout=5, port=port, connack_error=\"connack 1\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Kill broker\n    broker_terminate_rc = mosq_test.terminate_broker(broker)\n\n    persist_help.check_counts(port, clients=1)\n    # FIXME - port persist_help.check_client(port, \"persist-client-v5-0\", None, 0, 1, port, 10000, 2, 1, 60, 0)\n    persist_help.check_client(port, \"persist-client-v5-0\", None, 0, 1, None, 10000, 2, 1, 60, 0)\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=5, port=port, connack_error=\"connack 2\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Clear the client\n    sock = mosq_test.do_client_connect(connect_packet_clean, connack_packet1, timeout=5, port=port, connack_error=\"connack 3\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Connect client, it should not have a session\n    sock = mosq_test.do_client_connect(connect_packet_clean, connack_packet1, timeout=5, port=port, connack_error=\"connack 4\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    persist_help.check_counts(port)\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client, it should not have a session\n    sock = mosq_test.do_client_connect(connect_packet_clean, connack_packet1, timeout=5, port=port, connack_error=\"connack 5\")\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n    persist_help.check_counts(port)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (2)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-client-will.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, send a message with a\n# different client, restore, reconnect, check it is received.\n\nfrom mosq_test_helper import *\nfrom persist_module_helper import *\nfrom typing import Optional\n\nimport mqtt5_rc\nimport mqtt5_props\n\npersist_help = persist_module()\n\nport = mosq_test.get_port()\n\nproto_ver = 5\nqos = 1\ntopic = \"test-will-msg\"\nusername = \"test-will-msg\"\n\nsubscriber_id = \"test-will-subscriber\"\npublisher_id = \"test-will-publisher\"\nhelper_id = \"test-helper\"\n\nwill_properties = mqtt5_props.gen_properties(\n    [\n        {\"identifier\": mqtt5_props.PAYLOAD_FORMAT_INDICATOR, \"value\": 1},\n        {\"identifier\": mqtt5_props.CONTENT_TYPE, \"value\": \"text\"},\n        {\n            \"identifier\": mqtt5_props.USER_PROPERTY,\n            \"name\": \"test-user-property\",\n            \"value\": \"nothing important\",\n        },\n    ]\n)\n\nwill_properties_in_db = (\n    \"[\"\n    '{\"identifier\":\"payload-format-indicator\",\"value\":1}'\n    + ',{\"identifier\":\"content-type\",\"value\":\"text\"}'\n    + ',{\"identifier\":\"user-property\",\"name\":\"test-user-property\",\"value\":\"nothing important\"}'\n    + \"]\"\n)\n\nsend_will_disconnect_rc = mqtt5_rc.DISCONNECT_WITH_WILL_MSG\nnormal_disconnect_rc = mqtt5_rc.NORMAL_DISCONNECTION\n\n\ndef do_test(\n    session_expiry: int,\n    will_qos: int,\n    will_retain: bool,\n    will_delay: int = 0,\n    disconnect_rc: Optional[int] = None,\n):\n    mid = 1\n\n    print(\n        f\" {'persistent' if session_expiry > 0 else 'non persistent'} client\"\n        f\" with will qos = {will_qos}\"\n        f\", will_retain = {will_retain}\"\n        f\" and will delay = {will_delay}\"\n        + (f\" and disconnect rc = {disconnect_rc}\" if disconnect_rc is not None else \"\")\n    )\n\n    def check_will_received(do_subscribe: bool, expect_will_publish=bool):\n        nonlocal mid\n        # Reconnect client, it should have a session\n        subscriber_sock = connect_client(\n            port,\n            subscriber_id,\n            username,\n            proto_ver,\n            session_expiry=60,\n            session_present=True,\n            subscribe_topic=topic if do_subscribe else None,\n        )\n        if expect_will_publish:\n            will_publish = mosq_test.gen_publish(\n                topic,\n                will_qos,\n                will_payload,\n                will_retain and do_subscribe,\n                mid=mid,\n                proto_ver=proto_ver,\n                properties=will_properties,\n            )\n            if will_qos > 0:\n                pub_ack = mosq_test.gen_puback(mid=1, proto_ver=proto_ver)\n                mosq_test.do_receive_send(subscriber_sock, will_publish, pub_ack)\n            else:\n                mosq_test.expect_packet(subscriber_sock, \"will message\", will_publish)\n            mid += 1\n        # Always send a ping. Either to make sure a potential puback is processed by the server\n        # or to make sure the server has not send an unexpected publish\n        mosq_test.do_ping(subscriber_sock)\n        subscriber_sock.close()\n\n    expect_will_received = will_qos > 0\n\n    conf_file = os.path.basename(__file__).replace(\".py\", f\"_{port}.conf\")\n    persist_help.write_config(\n        conf_file,\n        port,\n        additional_config_entries={\"plugin_opt_flush_period\": 0, \"log_type\": \"all\"},\n    )\n    persist_help.init(port)\n\n    rc = 1\n\n    will_payload = b\"My simple will message\"\n    if will_delay:\n        will_delay_property = mqtt5_props.gen_uint32_prop(\n            mqtt5_props.WILL_DELAY_INTERVAL, will_delay\n        )\n    else:\n        will_delay_property = b\"\"\n    will_delayed = will_delay > 0 and session_expiry > 0\n\n    broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n    stde = None\n    stde2 = None\n    try:\n        subscriber_sock = connect_client(\n            port,\n            subscriber_id,\n            username,\n            proto_ver,\n            session_expiry=60,\n            subscribe_topic=topic,\n        )  # .close()\n\n        publisher_sock = connect_client(\n            port,\n            publisher_id,\n            username,\n            proto_ver,\n            session_expiry=session_expiry,\n            will_topic=topic,\n            will_qos=will_qos,\n            will_retain=will_retain,\n            will_payload=will_payload,\n            will_properties=will_properties + will_delay_property,\n        )\n        if disconnect_rc:\n            if disconnect_rc == mqtt5_rc.SESSION_TAKEN_OVER:\n                publisher_sock = connect_client(\n                    port,\n                    publisher_id,\n                    username,\n                    proto_ver,\n                    session_expiry=session_expiry,\n                    session_present=session_expiry > 0,\n                )\n                # Do a ping to make sure the new connection is functional\n                mosq_test.do_ping(publisher_sock)\n            else:\n                mosq_test.do_send(\n                    publisher_sock,\n                    mosq_test.gen_disconnect(\n                        reason_code=disconnect_rc, proto_ver=proto_ver\n                    ),\n                )\n            will_sent = disconnect_rc != normal_disconnect_rc and not will_delayed\n        else:\n            will_sent = False\n\n        # Send an additional ping to make sure the commit to the DB has happened\n        helper_sock = connect_client(\n            port, helper_id, username, proto_ver, session_expiry=0\n        )\n        mosq_test.do_ping(helper_sock)\n        helper_sock.close()\n\n        # Kill the broker\n        broker.kill()\n        (_, stde) = broker.communicate()\n        broker = None\n\n        if will_sent and will_qos > 0:\n            num_client_msgs = 1\n        else:\n            num_client_msgs = 0\n        num_retain = 1 if will_retain > 0 and will_sent else 0\n        num_base_msgs = max(num_client_msgs, num_retain)\n        num_wills = 0 if will_sent else 1\n        persist_help.check_counts(\n            port,\n            clients=1 if session_expiry == 0 else 2,\n            base_msgs=num_base_msgs,\n            client_msgs_out=num_client_msgs,\n            retain_msgs=num_retain,\n            subscriptions=1,\n            wills=0 if will_sent else 1,\n        )\n        if not will_sent:\n            persist_help.check_will(\n                port,\n                publisher_id,\n                will_payload,\n                topic,\n                will_qos,\n                will_retain,\n                properties=will_properties_in_db,\n            )\n\n        # Restart broker\n        broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)\n\n        check_will_received(\n            do_subscribe=False, expect_will_publish=will_qos > 0 and not will_delayed\n        )\n        check_will_received(\n            do_subscribe=True, expect_will_publish=will_retain and not will_delayed\n        )\n\n        (broker_terminate_rc, stde2) = mosq_test.terminate_broker(broker)\n        broker = None\n\n        rc = broker_terminate_rc\n    finally:\n        if broker is not None:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                if rc == 0:\n                    rc = 1\n            (_, stde3) = broker.communicate()\n            if not stde:\n                stde = stde3\n            else:\n                stde2 = stde3\n        os.remove(conf_file)\n        rc += persist_help.cleanup(port)\n\n        if rc:\n            if stde:\n                print(stde.decode(\"utf-8\"))\n            if stde2:\n                print(\"Broker after restart\")\n                print(stde2.decode(\"utf-8\"))\n        # assert rc == 0, f\"rc: {rc}\"\n\n\n# Run test with different parameters:\n# If disconnect_rc is not set the client will not disconnect\n# session_expiry, will qos, will retain, disconnect_rc\n\nsend_will_disconnect_rc = mqtt5_rc.DISCONNECT_WITH_WILL_MSG\n\n# non persistent client connected during crash\ndo_test(0, 0, 0)\ndo_test(0, 1, 0)\ndo_test(0, 0, 1)\ndo_test(0, 1, 1)\n\n# non persistent client connected during crash, will delay does not matter\ndo_test(0, 0, 0, will_delay=30)\ndo_test(0, 1, 0, will_delay=30)\ndo_test(0, 0, 1, will_delay=30)\ndo_test(0, 1, 1, will_delay=30)\n\n# non persistent client disconnecting with will sent before crash\ndo_test(0, 0, 0, disconnect_rc=send_will_disconnect_rc)\ndo_test(0, 1, 0, disconnect_rc=send_will_disconnect_rc)\ndo_test(0, 0, 1, disconnect_rc=send_will_disconnect_rc)\ndo_test(0, 1, 1, disconnect_rc=send_will_disconnect_rc)\n\n# non persistent client disconnecting without will sent before crash\ndo_test(0, 0, 0, disconnect_rc=normal_disconnect_rc)\ndo_test(0, 1, 0, disconnect_rc=normal_disconnect_rc)\ndo_test(0, 0, 1, disconnect_rc=normal_disconnect_rc)\ndo_test(0, 1, 1, disconnect_rc=normal_disconnect_rc)\n\n# persistent client connected during crash\ndo_test(60, 0, 0)\ndo_test(60, 1, 0)\ndo_test(60, 0, 1)\ndo_test(60, 1, 1)\n\n# persistent client connected during crash with a will delay of 30 seconds\ndo_test(60, 0, 0, will_delay=30)\ndo_test(60, 1, 0, will_delay=30)\ndo_test(60, 0, 1, will_delay=30)\ndo_test(60, 1, 1, will_delay=30)\n\n# persistent client disconnecting with will sent before crash\ndo_test(60, 0, 0, disconnect_rc=send_will_disconnect_rc)\ndo_test(60, 1, 0, disconnect_rc=send_will_disconnect_rc)\ndo_test(60, 0, 1, disconnect_rc=send_will_disconnect_rc)\ndo_test(60, 1, 1, disconnect_rc=send_will_disconnect_rc)\n\n# persistent client disconnecting without will sent before crash\ndo_test(60, 0, 0, disconnect_rc=normal_disconnect_rc)\ndo_test(60, 1, 0, disconnect_rc=normal_disconnect_rc)\ndo_test(60, 0, 1, disconnect_rc=normal_disconnect_rc)\ndo_test(60, 1, 1, disconnect_rc=normal_disconnect_rc)\n\n# persistent client disconnecting with will sent, but will delay\ndo_test(60, 0, 0, will_delay=30, disconnect_rc=send_will_disconnect_rc)\ndo_test(60, 1, 0, will_delay=30, disconnect_rc=send_will_disconnect_rc)\ndo_test(60, 0, 1, will_delay=30, disconnect_rc=send_will_disconnect_rc)\ndo_test(60, 1, 1, will_delay=30, disconnect_rc=send_will_disconnect_rc)\n\n# Remove will msg by session takeover through reconnect\ndo_test(0, 1, 1, disconnect_rc=mqtt5_rc.SESSION_TAKEN_OVER)\n# do_test(60, 1, 1, disconnect_rc=mqtt5_rc.SESSION_TAKEN_OVER)\n"
  },
  {
    "path": "test/broker/15-persist-migrate-db.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, start a QoS 2 flow, disconnect, restore, carry on with the\n# QoS 2 flow. Is it received?\n\nfrom mosq_test_helper import *\n\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace(\".py\", \".conf\")\npersist_help.write_config(conf_file, port)\n\n\ndef do_success_test(create_db_of_version: list[int]):\n    print(f\"db migration success test from db {create_db_of_version}\")\n    persist_help.init(port, create_db_of_version=create_db_of_version)\n\n    rc = 1\n\n    broker = mosq_test.start_broker(\n        filename=os.path.basename(__file__), use_conf=True, port=port\n    )\n    try:\n        # Kill broker\n        (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n        broker = None\n\n        persist_help.check_version_infos(\n            port,\n            database_schema_version=[\n                1,\n                1,\n                create_db_of_version[2] if create_db_of_version else 0,\n            ],\n        )\n\n        rc = 0\n    except Exception as err:\n        print(f\"{err}\")\n    finally:\n        if broker is not None:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0:\n                    rc = 1\n                (_, stde) = broker.communicate()\n\n        rc += persist_help.cleanup(port)\n\n        if rc:\n            print(stde.decode(\"utf-8\"))\n            exit(rc)\n\n\ndef do_failure_test(create_db_of_version: list[int]):\n    print(f\"db migration failure test for db {create_db_of_version}\")\n    persist_help.init(port, create_db_of_version=create_db_of_version)\n\n    rc = 1\n    try:\n        rc = do_test_broker_failure(\n            conf_file=conf_file,\n            config=[],\n            port=port,\n            rc_expected=3,\n            error_log_entry=f\"Unknown database_schema version {'.'.join([str(i) for i in create_db_of_version])}\",\n            with_test_config=False,\n        )\n    except Exception as exc:\n        print(f\"Exception: {str(exc)}\")\n    finally:\n        rc += persist_help.cleanup(port)\n        if rc:\n            exit(rc)\n\n\ndo_success_test(create_db_of_version=None)\ndo_success_test(create_db_of_version=[0, 9, 0])\ndo_success_test(create_db_of_version=[1, 0, 0])\ndo_success_test(create_db_of_version=[1, 1, 0])\ndo_success_test(create_db_of_version=[1, 1, 2])\n\ndo_failure_test(create_db_of_version=[1, 2, 0])\ndo_failure_test(create_db_of_version=[2, 0, 0])\n\nos.remove(conf_file)\n"
  },
  {
    "path": "test/broker/15-persist-publish-properties-v5-0.py",
    "content": "#!/usr/bin/env python3\n\n# Publish a retained messages, check they are restored, with properties attached\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\ntopic = \"test/retainprop\"\nsource_id = \"persist-retain-properties-v5-0\"\nqos = 0\nproto_ver = 5\nconnect_packet = mosq_test.gen_connect(source_id, proto_ver=proto_ver, clean_session=True)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\nbase_props = mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 1)\nbase_props += mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"plain/text\")\nbase_props += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"/dev/null\")\nbase_props += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"2357289375902345\")\nbase_props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name\", \"value4\")\nbase_props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name\", \"value3\")\nbase_props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name\", \"value2\")\nbase_props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name\", \"value1\")\nprops = base_props + mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 60)\npublish_packet = mosq_test.gen_publish(topic, qos=qos, payload=\"retained message 1\", retain=True, proto_ver=proto_ver, properties=props)\n\nmid = 1\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"test/retainprop\", 0, proto_ver=proto_ver)\nsuback_packet = mosq_test.gen_suback(mid, qos=0, proto_ver=proto_ver)\n\nmid = 2\nunsubscribe_packet = mosq_test.gen_unsubscribe(mid, \"test/retainprop\", proto_ver=proto_ver)\nunsuback_packet = mosq_test.gen_unsuback(mid, proto_ver=proto_ver)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    # Check no retained messages exist\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    # Ping will fail if a PUBLISH is received\n    mosq_test.do_ping(sock)\n    # Unsubscribe, so we don't receive the messages\n    mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, \"unsuback\")\n\n    # Send some retained messages\n    sock.send(publish_packet)\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    # Subscribe\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    # Check retained messages exist\n    packet = sock.recv(len(publish_packet))\n    for i in range(60, 1, -1):\n        props = base_props + mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, i)\n        publish_packet = mosq_test.gen_publish(topic, qos=qos, payload=\"retained message 1\", retain=True, proto_ver=proto_ver, properties=props)\n        if packet == publish_packet:\n            rc = 0\n            break\n\n    mosq_test.do_ping(sock)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n    persist_help.check_counts(port, base_msgs=1, retain_msgs=1)\n\n    if rc == 0:\n        rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (2)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-retain-clear.py",
    "content": "#!/usr/bin/env python3\n\n# Publish retained messages, check they are restored, delete one\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\ntopic1 = \"test/retain1\"\ntopic2 = \"test/retain2\"\nsource_id = \"persist-retain-delete\"\nqos = 0\npayload1 = \"retained message 1\"\npayload2 = \"retained message 2\"\nproto_ver = 4\nconnect_packet = mosq_test.gen_connect(source_id, proto_ver=proto_ver, clean_session=True)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\npublish1_packet = mosq_test.gen_publish(topic1, qos=qos, payload=payload1, retain=True, proto_ver=proto_ver)\npublish2_packet = mosq_test.gen_publish(topic2, qos=qos, payload=payload2, retain=True, proto_ver=proto_ver)\n\npublish2_clear_packet = mosq_test.gen_publish(topic2, qos=qos, retain=True, proto_ver=proto_ver)\npublish2_clear_echo = mosq_test.gen_publish(topic2, qos=qos, retain=False, proto_ver=proto_ver)\n\nmid = 1\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"#\", 0, proto_ver=4)\nsuback_packet = mosq_test.gen_suback(mid, qos=0, proto_ver=4)\n\nmid = 2\nunsubscribe_packet = mosq_test.gen_unsubscribe(mid, \"#\", proto_ver=4)\nunsuback_packet = mosq_test.gen_unsuback(mid, proto_ver=4)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    # Check no retained messages exist\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    # Ping will fail if a PUBLISH is received\n    mosq_test.do_ping(sock)\n    # Unsubscribe, so we don't receive the messages\n    mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, \"unsuback\")\n\n    # Send some retained messages\n    sock.send(publish1_packet)\n    mosq_test.do_ping(sock)\n    sock.send(publish2_packet)\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    # Subscribe\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    # Check retained messages exist\n    mosq_test.receive_unordered(sock, publish1_packet, publish2_packet, \"publish 1a / 2\")\n    mosq_test.do_ping(sock)\n\n    # Clear retained (and wait for the publish to avoid race condition)\n    mosq_test.do_send_receive(sock, publish2_clear_packet, publish2_clear_echo, \"clear retain flag\")\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    # Subscribe\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    # Check retained message exist\n    mosq_test.expect_packet(sock, \"publish 1b\", publish1_packet)\n    # If the other retained message still exists, this will cause an error\n    mosq_test.do_ping(sock)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n    persist_help.check_counts(port, base_msgs=1, retain_msgs=1)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (2)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-retain-v3-1-1.py",
    "content": "#!/usr/bin/env python3\n\n# Publish a retained messages, check they are restored\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\ntopic1 = \"test/retain1\"\ntopic2 = \"test/retain2\"\ntopic3 = \"test/retain3\"\nsource_id = \"persist-retain-v3-1-1\"\nqos = 0\npayload2 = \"retained message 2\"\npayload3 = \"retained message 3\"\nproto_ver = 4\nconnect_packet = mosq_test.gen_connect(source_id, proto_ver=proto_ver, clean_session=True)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\npublish1_packet = mosq_test.gen_publish(topic1, qos=qos, payload=\"retained message 1\", retain=True, proto_ver=proto_ver)\npublish2_packet = mosq_test.gen_publish(topic2, qos=qos, payload=payload2, retain=False, proto_ver=proto_ver)\npublish3_packet = mosq_test.gen_publish(topic3, qos=qos, payload=payload3, retain=True, proto_ver=proto_ver)\n\nmid = 1\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"#\", 0, proto_ver=4)\nsuback_packet = mosq_test.gen_suback(mid, qos=0, proto_ver=4)\n\nmid = 2\nunsubscribe_packet = mosq_test.gen_unsubscribe(mid, \"#\", proto_ver=4)\nunsuback_packet = mosq_test.gen_unsuback(mid, proto_ver=4)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    # Check no retained messages exist\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    # Ping will fail if a PUBLISH is received\n    mosq_test.do_ping(sock)\n    # Unsubscribe, so we don't receive the messages\n    mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, \"unsuback\")\n\n    # Send some retained messages\n    sock.send(publish1_packet)\n    mosq_test.do_ping(sock)\n    sock.send(publish2_packet) # Not retained\n    mosq_test.do_ping(sock)\n    sock.send(publish3_packet)\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    # Subscribe\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    # Check retained messages exist\n    mosq_test.receive_unordered(sock, publish1_packet, publish3_packet, \"publish 1 / 3\")\n    mosq_test.do_ping(sock)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n    persist_help.check_counts(port, base_msgs=2, retain_msgs=2)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (2)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-retain-v5-0.py",
    "content": "#!/usr/bin/env python3\n\n# Publish a retained messages, check they are restored\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\ntopic1 = \"test/retain1\"\ntopic2 = \"test/retain2\"\ntopic3 = \"test/retain3\"\nsource_id = \"persist-retain-v5-0\"\nqos = 0\npayload2 = \"retained message 2\"\npayload3 = \"retained message 3\"\nproto_ver = 5\nconnect_packet = mosq_test.gen_connect(source_id, proto_ver=proto_ver, clean_session=True)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n\npublish1_packet = mosq_test.gen_publish(topic1, qos=qos, payload=\"retained message 1\", retain=True, proto_ver=proto_ver)\npublish2_packet = mosq_test.gen_publish(topic2, qos=qos, payload=payload2, retain=False, proto_ver=proto_ver)\npublish3_packet = mosq_test.gen_publish(topic3, qos=qos, payload=payload3, retain=True, proto_ver=proto_ver)\n\nmid = 1\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"#\", 0, proto_ver=proto_ver)\nsuback_packet = mosq_test.gen_suback(mid, qos=0, proto_ver=proto_ver)\n\nmid = 2\nunsubscribe_packet = mosq_test.gen_unsubscribe(mid, \"#\", proto_ver=proto_ver)\nunsuback_packet = mosq_test.gen_unsuback(mid, proto_ver=proto_ver)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    # Check no retained messages exist\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    # Ping will fail if a PUBLISH is received\n    mosq_test.do_ping(sock)\n    # Unsubscribe, so we don't receive the messages\n    mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, \"unsuback\")\n\n    # Send some retained messages\n    sock.send(publish1_packet)\n    mosq_test.do_ping(sock)\n    sock.send(publish2_packet) # Not retained\n    mosq_test.do_ping(sock)\n    sock.send(publish3_packet)\n    mosq_test.do_ping(sock)\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)\n    # Subscribe\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    # Check retained messages exist\n    mosq_test.receive_unordered(sock, publish1_packet, publish3_packet, \"publish 1 / 3\")\n    mosq_test.do_ping(sock)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n    persist_help.check_counts(port, base_msgs=2, retain_msgs=2)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (2)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-subscription-v3-1-1.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, restore, reconnect, send a\n# message with a different client, check it is received.\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\ndef helper(port, packets):\n    helper_id = \"persist-subscription-v3-1-1-helper\"\n    connect_packet_helper = mosq_test.gen_connect(helper_id, proto_ver=4, clean_session=True)\n\n    # Connect helper and publish\n    helper = mosq_test.do_client_connect(connect_packet_helper, packets[\"connack1\"], timeout=5, port=port)\n    helper.send(packets[\"publish0\"])\n    mosq_test.do_send_receive(helper, packets[\"publish1\"], packets[\"puback1\"], \"puback helper\")\n    mosq_test.do_send_receive(helper, packets[\"publish2\"], packets[\"pubrec2\"], \"pubrec helper\")\n    mosq_test.do_send_receive(helper, packets[\"pubrel2\"], packets[\"pubcomp2\"], \"pubcomp helper\")\n    helper.close()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\nclient_id = \"persist-subscription-v3-1-1\"\nproto_ver = 4\n\ntopic0 = \"subscription/0\"\ntopic1 = \"subscription/1\"\ntopic2 = \"subscription/2\"\n\npackets = {}\npackets[\"connect\"] = mosq_test.gen_connect(client_id, proto_ver=proto_ver, clean_session=False)\npackets[\"connect_clear\"] = mosq_test.gen_connect(client_id, proto_ver=proto_ver, clean_session=True)\npackets[\"connack1\"] = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\npackets[\"connack2\"] = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\nmid = 1\npackets[\"subscribe0\"] = mosq_test.gen_subscribe(mid, topic0, qos=0, proto_ver=proto_ver)\npackets[\"suback0\"] = mosq_test.gen_suback(mid=mid, qos=0, proto_ver=proto_ver)\npackets[\"subscribe1\"] = mosq_test.gen_subscribe(mid, topic1, qos=1, proto_ver=proto_ver)\npackets[\"suback1\"] = mosq_test.gen_suback(mid=mid, qos=1, proto_ver=proto_ver)\npackets[\"subscribe2\"] = mosq_test.gen_subscribe(mid, topic2, qos=2, proto_ver=proto_ver)\npackets[\"suback2\"] = mosq_test.gen_suback(mid=mid, qos=2, proto_ver=proto_ver)\n\npackets[\"unsubscribe2\"] = mosq_test.gen_unsubscribe(mid, topic2, proto_ver=proto_ver)\npackets[\"unsuback2\"] = mosq_test.gen_unsuback(mid=mid, proto_ver=proto_ver)\n\npackets[\"publish0\"] = mosq_test.gen_publish(topic=topic0, qos=0, payload=\"message\", proto_ver=proto_ver)\nmid = 1\npackets[\"publish1\"] = mosq_test.gen_publish(topic=topic1, qos=1, payload=\"message\", mid=mid, proto_ver=proto_ver)\npackets[\"puback1\"] = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\nmid = 2\npackets[\"publish2\"] = mosq_test.gen_publish(topic=topic2, qos=2, payload=\"message\", mid=mid, proto_ver=proto_ver)\npackets[\"pubrec2\"] = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\npackets[\"pubrel2\"] = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\npackets[\"pubcomp2\"] = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    # Connect client\n    sock = mosq_test.do_client_connect(packets[\"connect\"], packets[\"connack1\"], timeout=5, port=port)\n    mosq_test.do_send_receive(sock, packets[\"subscribe0\"], packets[\"suback0\"], \"suback 0\")\n    mosq_test.do_send_receive(sock, packets[\"subscribe1\"], packets[\"suback1\"], \"suback 1\")\n    mosq_test.do_send_receive(sock, packets[\"subscribe2\"], packets[\"suback2\"], \"suback 2\")\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(packets[\"connect\"], packets[\"connack2\"], timeout=5, port=port)\n    mosq_test.do_ping(sock)\n\n    helper(port, packets)\n\n    # Does the client get the messages\n    mosq_test.expect_packet(sock, \"publish 0\", packets[\"publish0\"])\n    mosq_test.do_receive_send(sock, packets[\"publish1\"], packets[\"puback1\"], \"publish 1\")\n    mosq_test.do_receive_send(sock, packets[\"publish2\"], packets[\"pubrec2\"], \"publish 2\")\n    mosq_test.do_receive_send(sock, packets[\"pubrel2\"], packets[\"pubcomp2\"], \"pubrel 2\")\n\n    # Unsubscribe\n    mosq_test.do_send_receive(sock, packets[\"unsubscribe2\"], packets[\"unsuback2\"], \"unsuback 2\")\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(packets[\"connect\"], packets[\"connack2\"], timeout=5, port=port)\n    mosq_test.do_ping(sock)\n\n    # Connect helper and publish\n    helper(port, packets)\n\n    # Does the client get the messages\n    mosq_test.expect_packet(sock, \"publish 0\", packets[\"publish0\"])\n    mosq_test.do_receive_send(sock, packets[\"publish1\"], packets[\"puback1\"], \"publish 1\")\n    mosq_test.do_ping(sock)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n    persist_help.check_counts(port, clients=1, subscriptions=2)\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, but clear the session\n    sock = mosq_test.do_client_connect(packets[\"connect_clear\"], packets[\"connack1\"], timeout=5, port=port)\n    mosq_test.do_ping(sock)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n    persist_help.check_counts(port)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (3)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/15-persist-subscription-v5-0.py",
    "content": "#!/usr/bin/env python3\n\n# Connect a client, add a subscription, disconnect, restore, reconnect, send a\n# message with a different client, check it is received.\n\nfrom mosq_test_helper import *\npersist_help = persist_module()\n\ndef helper(port, packets):\n    helper_id = \"persist-subscription-v5-0-helper\"\n    connect_packet_helper = mosq_test.gen_connect(helper_id, proto_ver=5, clean_session=True)\n    connack_packet_helper = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    # Connect helper and publish\n    helper = mosq_test.do_client_connect(connect_packet_helper, connack_packet_helper, timeout=5, port=port, connack_error=\"helper connack\")\n    helper.send(packets[\"publish0-helper\"])\n    mosq_test.do_send_receive(helper, packets[\"publish1-helper\"], packets[\"puback1\"], \"puback helper\")\n    mosq_test.do_send_receive(helper, packets[\"publish2-helper\"], packets[\"pubrec2\"], \"pubrec helper\")\n    mosq_test.do_send_receive(helper, packets[\"pubrel2\"], packets[\"pubcomp2\"], \"pubcomp helper\")\n    helper.close()\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\npersist_help.write_config(conf_file, port)\n\nrc = 1\n\npersist_help.init(port)\n\nclient_id = \"persist-subscription-v5-0\"\nproto_ver = 5\n\ntopic0 = \"subscription/0\"\ntopic1 = \"subscription/1\"\ntopic2 = \"subscription/2\"\n\npackets = {}\nconnect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)\npackets[\"connect\"] = mosq_test.gen_connect(client_id, proto_ver=proto_ver, clean_session=False, properties=connect_props)\npackets[\"connack1\"] = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\npackets[\"connack2\"] = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver)\nmid = 1\n\npublish_props0 = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 100)\npackets[\"subscribe0\"] = mosq_test.gen_subscribe(mid, topic0, qos=0, proto_ver=proto_ver, properties=publish_props0)\npackets[\"suback0\"] = mosq_test.gen_suback(mid=mid, qos=0, proto_ver=proto_ver)\n\npublish_props1 = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 101)\npackets[\"subscribe1\"] = mosq_test.gen_subscribe(mid, topic1, qos=1, proto_ver=proto_ver, properties=publish_props1)\npackets[\"suback1\"] = mosq_test.gen_suback(mid=mid, qos=1, proto_ver=proto_ver)\n\npublish_props2 = mqtt5_props.gen_varint_prop(mqtt5_props.SUBSCRIPTION_IDENTIFIER, 102)\npackets[\"subscribe2\"] = mosq_test.gen_subscribe(mid, topic2, qos=2, proto_ver=proto_ver, properties=publish_props2)\npackets[\"suback2\"] = mosq_test.gen_suback(mid=mid, qos=2, proto_ver=proto_ver)\n\npackets[\"unsubscribe2\"] = mosq_test.gen_unsubscribe(mid, topic2, proto_ver=proto_ver)\npackets[\"unsuback2\"] = mosq_test.gen_unsuback(mid=mid, proto_ver=proto_ver)\n\npackets[\"publish0-helper\"] = mosq_test.gen_publish(topic=topic0, qos=0, payload=\"message\", proto_ver=proto_ver)\npackets[\"publish0\"] = mosq_test.gen_publish(topic=topic0, qos=0, payload=\"message\", proto_ver=proto_ver, properties=publish_props0)\nmid = 1\npackets[\"publish1-helper\"] = mosq_test.gen_publish(topic=topic1, qos=1, payload=\"message\", mid=mid, proto_ver=proto_ver)\npackets[\"publish1\"] = mosq_test.gen_publish(topic=topic1, qos=1, payload=\"message\", mid=mid, proto_ver=proto_ver, properties=publish_props1)\npackets[\"puback1\"] = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\nmid = 2\npackets[\"publish2-helper\"] = mosq_test.gen_publish(topic=topic2, qos=2, payload=\"message\", mid=mid, proto_ver=proto_ver)\npackets[\"publish2\"] = mosq_test.gen_publish(topic=topic2, qos=2, payload=\"message\", mid=mid, proto_ver=proto_ver, properties=publish_props2)\npackets[\"pubrec2\"] = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver)\npackets[\"pubrel2\"] = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver)\npackets[\"pubcomp2\"] = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver)\n\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ncon = None\ntry:\n    # Connect client\n    sock = mosq_test.do_client_connect(packets[\"connect\"], packets[\"connack1\"], timeout=5, port=port, connack_error=\"connack1\")\n    mosq_test.do_send_receive(sock, packets[\"subscribe0\"], packets[\"suback0\"], \"suback 0\")\n    mosq_test.do_send_receive(sock, packets[\"subscribe1\"], packets[\"suback1\"], \"suback 1\")\n    mosq_test.do_send_receive(sock, packets[\"subscribe2\"], packets[\"suback2\"], \"suback 2\")\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(packets[\"connect\"], packets[\"connack2\"], timeout=5, port=port)\n    mosq_test.do_ping(sock)\n\n    helper(port, packets)\n\n    # Does the client get the messages\n    mosq_test.expect_packet(sock, \"publish 0\", packets[\"publish0\"])\n    mosq_test.do_receive_send(sock, packets[\"publish1\"], packets[\"puback1\"], \"publish 1\")\n    mosq_test.do_receive_send(sock, packets[\"publish2\"], packets[\"pubrec2\"], \"publish 2\")\n    mosq_test.do_receive_send(sock, packets[\"pubrel2\"], packets[\"pubcomp2\"], \"pubrel 2\")\n\n    # Unsubscribe\n    mosq_test.do_send_receive(sock, packets[\"unsubscribe2\"], packets[\"unsuback2\"], \"unsuback 2\")\n    sock.close()\n\n    # Kill broker\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n\n    # Restart broker\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    # Connect client again, it should have a session\n    sock = mosq_test.do_client_connect(packets[\"connect\"], packets[\"connack2\"], timeout=5, port=port, connack_error=\"connack2\")\n    mosq_test.do_ping(sock)\n\n    # Connect helper and publish\n    helper(port, packets)\n\n    # Does the client get the messages\n    mosq_test.expect_packet(sock, \"publish 0\", packets[\"publish0\"])\n    mosq_test.do_receive_send(sock, packets[\"publish1\"], packets[\"puback1\"], \"publish 1\")\n    mosq_test.do_ping(sock)\n\n    (broker_terminate_rc, stde) = mosq_test.terminate_broker(broker)\n    broker = None\n    persist_help.check_counts(port, clients=1, subscriptions=2)\n\n    rc = broker_terminate_rc\nfinally:\n    if broker is not None:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated (3)\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n    os.remove(conf_file)\n    rc += persist_help.cleanup(port)\n\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/16-cmd-args.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether command line args are handled\n\nfrom mosq_test_helper import *\n\nport = mosq_test.get_port()\n\ndo_test_broker_failure(\"\", [], port, cmd_args=[\"-h\"], rc_expected=0, stdout_entry=\"Usage: mosquitto [-c config_file] [-d] [-h] [-p port] [-v]\")\ndo_test_broker_failure(\"\", [], port, cmd_args=[\"-p\", \"0\"], rc_expected=3, error_log_entry=\"Error: Invalid port specified (0).\") # Port invalid\ndo_test_broker_failure(\"\", [], port, cmd_args=[\"-p\", \"65536\"], rc_expected=3, error_log_entry=\"Error: Invalid port specified (65536).\") # Port invalid\ndo_test_broker_failure(\"\", [], port, cmd_args=[\"-p\"], rc_expected=3, error_log_entry=\"Error: -p argument given, but no port specified.\") # Missing port\ndo_test_broker_failure(\"\", [], port, cmd_args=[\"-c\"], rc_expected=3, error_log_entry=\"Error: -c argument given, but no config file specified.\") # Missing config\ndo_test_broker_failure(\"\", [], port, cmd_args=[\"--tls-keylog\"], rc_expected=3, error_log_entry=\"Error: --tls-keylog argument given, but no file specified.\") # Missing filename\ndo_test_broker_failure(\"\", [], port, cmd_args=[\"--unknown\"], rc_expected=3, error_log_entry=\"Error: Unknown option '--unknown'.\") # Unknown option\n\nexit(0)\n"
  },
  {
    "path": "test/broker/16-config-huge.py",
    "content": "#!/usr/bin/env python3\n\n# Test many different config options at once, and also reloading. This is\n# primarily intended as a test of the config code, not the functionality of the\n# options being set.\n\nfrom mosq_test_helper import *\nimport os\nimport platform\nimport signal\n\ndef write_acl(filename):\n    with open(filename, 'w') as f:\n        f.write('user new_username\\n')\n        f.write('topic readwrite topic/one\\n')\n\ndef write_config(filename, ports, per_listener_settings, plugver, acl_file):\n    with open(filename, 'w') as f:\n        f.write(\"per_listener_settings %s\\n\" % (per_listener_settings))\n\n        # Global options\n        f.write(\"allow_duplicate_messages true\\n\")\n        f.write(\"autosave_interval 1\\n\")\n        f.write(\"autosave_on_changes true\\n\")\n        f.write(\"check_retain_source true\\n\")\n        f.write(\"enable_control_api true\\n\")\n        f.write(\"global_max_clients 10\\n\")\n        f.write(\"global_max_connections 10\\n\")\n        #f.write(\"include_dir path\\n\")\n        f.write(\"global_max_connections 10\\n\")\n        f.write(\"log_dest stderr\\n\")\n        f.write(\"log_timestamp true\\n\")\n        f.write(\"log_timestamp_format %Y-%m-%dT%H:%M:%S\\n\")\n        f.write(\"log_type all\\n\")\n        f.write(\"max_inflight_bytes 10000\\n\")\n        f.write(\"max_inflight_messages 100\\n\")\n        f.write(\"max_keepalive 60\\n\")\n        f.write(\"max_packet_size 1000\\n\")\n        f.write(\"max_queued_bytes 10000\\n\")\n        f.write(\"max_queued_messages 100\\n\")\n        f.write(\"message_size_limit 1000\\n\")\n        f.write(\"memory_limit 100000000\\n\")\n        f.write(\"persistence true\\n\")\n        f.write(f\"persistence_file {ports[0]}.db\\n\")\n        f.write(\"persistence_location .\\n\")\n        f.write(\"persistent_client_expiration 1h\\n\")\n        f.write(f\"pid_file {ports[0]}.pid\\n\")\n        f.write(\"queue_qos0_messages true\\n\")\n        f.write(\"retain_available true\\n\")\n        f.write(\"retry_interval not-used\\n\")\n        f.write(\"set_tcp_nodelay true\\n\")\n        f.write(\"sys_interval 60\\n\")\n        f.write(\"upgrade_outgoing_qos true\\n\")\n        if os.environ.get('WITH_WEBSOCKETS') != \"no\":\n            f.write(\"websockets_log_level 255\\n\")\n            f.write(\"websockets_headers_size 4096\\n\")\n\n        # Listener and global\n        if not per_listener_settings:\n            f.write(\"allow_zero_length_clientid false\\n\")\n            f.write(\"auto_id_prefix pre\\n\")\n            f.write(f\"acl_file {acl_file}\\n\")\n\n        # Bridge options\n        f.write(\"connection bridge\\n\")\n        f.write(f\"address 127.0.0.1:{ports[2]} 127.0.0.1:{ports[3]}\\n\")\n        f.write(\"bridge_alpn alpn\\n\")\n        f.write(\"bridge_attempt_unsubscribe false\\n\")\n        f.write(\"bridge_bind_address 127.0.0.1\\n\")\n        f.write(f\"bridge_cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(\"bridge_capath asasdf\\n\")\n        f.write(f\"bridge_certfile {ssl_dir}/client.crt\\n\")\n        f.write(f\"bridge_keyfile {ssl_dir}/client.key\\n\")\n        f.write(\"bridge_ciphers ECDHE-ECDSA-AES256-GCM-SHA384\\n\")\n        f.write(\"bridge_ciphers_tls1.3 TLS_AES_256_GCM_SHA384\\n\")\n        #f.write(\"bridge_identity identity\\n\")\n        f.write(\"bridge_insecure true\\n\")\n        f.write(\"bridge_max_packet_size 10000\\n\")\n        f.write(\"bridge_max_topic_alias 1000\\n\")\n        f.write(\"bridge_outgoing_retain false\\n\")\n        f.write(\"bridge_protocol_version mqttv50\\n\")\n        #f.write(\"bridge_psk\\n\")\n        f.write(\"bridge_receive_maximum 100\\n\")\n        #f.write(\"bridge_reload_type immediate\\n\")\n        f.write(\"bridge_reload_type lazy\\n\")\n        f.write(\"bridge_require_ocsp false\\n\")\n        f.write(\"bridge_session_expiry_interval 63\\n\")\n        f.write(\"bridge_tcp_keepalive 100 100 100\\n\")\n        f.write(\"bridge_tcp_user_timeout 60\\n\")\n        f.write(\"bridge_tls_use_os_certs true\\n\")\n        f.write(\"bridge_tls_version tlsv1.2\\n\")\n        f.write(\"local_cleansession true\\n\")\n        f.write(\"local_clientid blci\\n\")\n        #f.write(\"local_username username\\n\")\n        #f.write(\"local_password password\\n\")\n        f.write(\"connection_messages true\\n\")\n        f.write(\"idle_timeout 60\\n\")\n        f.write(\"keepalive_interval 40\\n\")\n        f.write(\"notification_topic notifications\\n\")\n        f.write(\"notifications false\\n\")\n        f.write(\"notifications_local_only true\\n\")\n        f.write(\"remote_clientid brci\\n\")\n        f.write(\"remote_username username\\n\")\n        f.write(\"remote_password password\\n\")\n        f.write(\"restart_timeout 60\\n\")\n        f.write(\"round_robin false\\n\")\n        f.write(\"start_type lazy\\n\")\n        f.write(\"threshold 100\\n\")\n        f.write(\"try_private false\\n\")\n        f.write(\"topic bridge both 1\\n\")\n\n        # Default listener\n\n        # Listeners\n        f.write(\"plugin_load auth c/auth_plugin_v%d.so\\n\" % (plugver))\n        f.write(\"plugin_opt_test true\\n\")\n        f.write(\"auth_plugin_deny_special_chars false\\n\")\n\n        f.write(\"listener %d\\n\" % (ports[0]))\n        f.write(\"plugin_use auth\\n\")\n\n        f.write(\"listener %d\\n\" % (ports[1]))\n        f.write(\"allow_anonymous false\\n\")\n        #f.write(\"psk_hint hint\\n\")\n\n        f.write(\"listener %d\\n\" % (ports[2]))\n        f.write(\"plugin_use auth\\n\")\n        f.write(\"accept_protocol_versions 3,4,5\\n\")\n        if platform.system() == \"Darwin\":\n            f.write(\"bind_interface lo0\\n\")\n        else:\n            f.write(\"bind_interface lo\\n\")\n        f.write(f\"cafile {ssl_dir}/all-ca.crt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        #f.write(\"capath path\\n\")\n        f.write(\"ciphers ECDHE-ECDSA-AES256-GCM-SHA384\\n\")\n        f.write(\"ciphers_tls1.3 TLS_AES_256_GCM_SHA384\\n\")\n        f.write(\"clientid_prefixes client\\n\")\n        f.write(f\"crlfile {ssl_dir}/crl.pem\\n\")\n        #f.write(\"dhparamfile file\\n\")\n        f.write(\"disable_client_cert_date_checks true\\n\")\n        f.write(\"http_dir .\\n\")\n        f.write(\"max_connections 10\\n\")\n        f.write(\"max_qos 1\\n\")\n        f.write(\"mount_point mount/\\n\")\n        if os.environ.get('WITH_WEBSOCKETS') != \"no\":\n            f.write(\"protocol websockets\\n\")\n        f.write(\"require_certificate true\\n\")\n        f.write(\"socket_domain ipv4\\n\")\n        f.write(\"tls_version tlsv1.2\\n\")\n        f.write(\"max_topic_alias 15\\n\")\n        f.write(\"max_topic_alias_broker 15\\n\")\n        f.write(\"use_identity_as_username true\\n\")\n        f.write(\"use_subject_as_username true\\n\")\n        f.write(\"use_username_as_clientid true\\n\")\n        if os.environ.get('WITH_WEBSOCKETS') != \"no\":\n            f.write(\"websockets_origin localhost\\n\")\n        if per_listener_settings:\n            f.write(\"allow_zero_length_clientid false\\n\")\n            f.write(\"auto_id_prefix pre\\n\")\n            f.write(f\"acl_file {acl_file}\\n\")\n\n        f.write(\"port %d\\n\" % (ports[3]))\n\ndef client_check(username, password, rc, port):\n    connect_packet = mosq_test.gen_connect(client_id=\"client-id\", username=username, password=password)\n    connack_packet = mosq_test.gen_connack(rc=rc)\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    sock.close()\n\ndef do_test(per_listener_settings):\n    proto_ver = 5\n    ports = mosq_test.get_port(4)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    acl_file = os.path.basename(__file__).replace('.py', '.acl')\n    write_acl(acl_file)\n    write_config(conf_file, ports, per_listener_settings, 2, acl_file)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=ports[0])\n\n    rc = 1\n    try:\n        client_check(\"test-username\", \"cnwTICONIURW\", 0, ports[0]) # Should succeed\n        client_check(\"test-username\", \"cnwTICONIURW\", 5, ports[1]) # Should fail\n\n        client_check(\"bad-actor\", \"nope\", 5, ports[0]) # Should fail\n        client_check(\"bad-actor\", \"nope\", 5, ports[1]) # Should fail\n\n        client_check(None, None, 5, ports[0]) # Should fail\n        client_check(None, None, 5, ports[1]) # Should fail\n\n        broker.send_signal(signal.SIGHUP)\n        client_check(\"test-username\", \"cnwTICONIURW\", 0, ports[0]) # Should succeed\n\n        broker.send_signal(signal.SIGHUP)\n        client_check(\"test-username\", \"cnwTICONIURW\", 0, ports[0]) # Should succeed\n\n        rc = 0\n    except Exception as err:\n        print(err)\n    finally:\n        broker.terminate()\n        broker.wait()\n        os.remove(conf_file)\n        os.remove(acl_file)\n        try:\n            os.remove(f\"{ports[0]}.db\")\n        except FileNotFoundError:\n            pass\n        if broker.returncode == 139:\n            # Crash\n            print(\"Broker crashed!\")\n            rc = 1\n        if rc:\n            print(f\"per_listener_settings:{per_listener_settings}\")\n            (_, stde) = broker.communicate()\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(\"false\")\ndo_test(\"true\")\n"
  },
  {
    "path": "test/broker/16-config-includedir.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether include_dir works properly\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(f\"include_dir {port}\\n\")\n    os.mkdir(str(port))\n    with open(f\"{port}/1.conf\", 'w') as f:\n        f.write(f\"listener {port}\\n\")\n    with open(f\"{port}/2.conf\", 'w') as f:\n        f.write(f\"allow_anonymous true\\n\")\n\n\ndef do_test():\n    port = mosq_test.get_port()\n\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        rc = 1\n        connect_packet = mosq_test.gen_connect(\"connect-include-dir\")\n        connack_packet = mosq_test.gen_connack(rc=0)\n\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        os.remove(conf_file)\n        os.remove(f\"{port}/1.conf\")\n        os.remove(f\"{port}/2.conf\")\n        os.rmdir(f\"{port}\")\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test()\n\nexit(0)\n"
  },
  {
    "path": "test/broker/16-config-missing.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether config parse errors are handled\n\nfrom mosq_test_helper import *\nport = mosq_test.get_port()\n\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\n\ndo_test_broker_failure(conf_file, [], port, cmd_args=['-c', conf_file], rc_expected=3, error_log_entry=f\"Error: Unable to open config file '{conf_file}'.\\n\")\n\nexit(0)\n"
  },
  {
    "path": "test/broker/16-config-parse-errors-tls-psk.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether config parse errors are handled\n\nfrom mosq_test_helper import *\nport = mosq_test.get_port()\n\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\n\ndo_test_broker_failure(conf_file, [\"bridge_psk string\"], port, 3, \"Error: The 'bridge_psk' option requires a bridge to be defined first.\")\ndo_test_broker_failure(conf_file, [\"bridge_identity string\"], port, 3, \"Error: The 'bridge_identity' option requires a bridge to be defined first.\")\n\nbridge_config=[\"connection invalid-psk\", \"address localhost\", \"topic dummy-topic\"]\ndo_test_broker_failure(conf_file, bridge_config+ [\"bridge_psk\"], port, 3, \"Error: Empty 'bridge_psk' value in configuration.\") # Empty bridge_psk in bridge config\ndo_test_broker_failure(conf_file, bridge_config+ [\"bridge_identity\"], port, 3, \"Error: Empty 'bridge_identity' value in configuration.\") # Empty bridge_identity in bridge config\ndo_test_broker_failure(conf_file, bridge_config+ [\"bridge_psk my_psk\"], port, 3, \"Error: Invalid bridge configuration: missing bridge_identity.\") # Missing bridge_identity in bridge config\ndo_test_broker_failure(conf_file, bridge_config+ [\"bridge_identity my_identity\"], port, 3, \"Error: Invalid bridge configuration: missing bridge_psk.\") # Missing bridge_psk in bridge config\n\n\nexit(0)\n"
  },
  {
    "path": "test/broker/16-config-parse-errors-tls.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether config parse errors are handled\n\nfrom mosq_test_helper import *\n\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nport = mosq_test.get_port()\n\ndo_test_broker_failure(conf_file, [\"bridge_cafile string\"], port, 3) # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_alpn string\"], port, 3) # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_ciphers string\"], port, 3) # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_ciphers_tls1.3 string\"], port, 3) # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_capath string\"], port, 3) # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_certfile string\"], port, 3) # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_keyfile string\"], port, 3) # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_tls_version string\"], port, 3) # Missing bridge config\n\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"certfile\"], port, 3) # empty certfile\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"keyfile\"], port, 3) # empty keyfile\n\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"certfile ./16-config-parse-errors.py\",\"keyfile ../ssl/server.key\"], port, 1, with_test_config=False) # invalid certfile\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"certfile ../ssl/server.crt\",\"keyfile ./16-config-parse-errors.py\"], port, 1, with_test_config=False) # invalid keyfile\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"certfile ../ssl/server.crt\",\"keyfile ../ssl/client.key\"], port, 1, with_test_config=False) # mismatched certfile / keyfile\n\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"certfile ../ssl/server.crt\",\"keyfile ../ssl/server.key\",\"tls_version invalid\"], port, 1, with_test_config=False) # invalid tls_version\n\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"certfile ../ssl/server.crt\",\"keyfile ../ssl/server.key\",\"crlfile invalid\"], port, 1, with_test_config=False) # missing crl file\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"certfile ../ssl/server.crt\",\"keyfile ../ssl/server.key\",\"ciphers invalid\"], port, 1, with_test_config=False) # invalid ciphers\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"certfile ../ssl/server.crt\",\"keyfile ../ssl/server.key\",\"ciphers_tls1.3 invalid\"], port, 1, with_test_config=False) # invalid ciphers_tls1.3\n\nexit(0)\n"
  },
  {
    "path": "test/broker/16-config-parse-errors-without-tls.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether config parse errors are handled\n\nfrom mosq_test_helper import *\nport = mosq_test.get_port()\n\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\n\ndo_test_broker_failure(conf_file, [\"unknown_option unknown\"], port, 3, \"Error: Unknown configuration variable 'unknown_option'\")\ndo_test_broker_failure(conf_file, [\"user\"], port, 3, \"Error: Empty 'user' value in configuration.\") # Empty string, no space\ndo_test_broker_failure(conf_file, [\"user \"], port, 3, \"Error: Empty 'user' value in configuration.\") # Empty string, single space\ndo_test_broker_failure(conf_file, [\"user  \"], port, 3, \"Error: Empty 'user' value in configuration.\") # Empty string, double space\ndo_test_broker_failure(conf_file, [\"pid_file /tmp/pid\",\"pid_file /tmp/pid\"], port, 3, \"Error: Duplicate 'pid_file' value in configuration.\") # Duplicate string\ndo_test_broker_failure(conf_file, [\"memory_limit\"], port, 3, \"Empty 'memory_limit' value in configuration.\") # Empty ssize_t\ndo_test_broker_failure(conf_file, [\"accept_protocol_versions 3\"], port, 3, \"Error: The 'accept_protocol_versions' option requires a listener to be defined first.\") # Missing listener\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"accept_protocol_versions\"], port, 3, \"Error: Empty 'accept_protocol_versions' value in configuration.\") # Empty value\ndo_test_broker_failure(conf_file, [\"allow_anonymous\"], port, 3, \"Error: Empty 'allow_anonymous' value in configuration.\") # Empty bool\ndo_test_broker_failure(conf_file, [\"allow_anonymous falst\"], port, 3, \"Error: Invalid 'allow_anonymous' value (falst).\") # Invalid bool\n\ndo_test_broker_failure(conf_file, [\"autosave_interval\"], port, 3, \"Error: Empty 'autosave_interval' value in configuration.\") # Empty int\ndo_test_broker_failure(conf_file, [\"autosave_interval string\"], port, 3, \"Error: 'autosave_interval' value not a number.\") # Invalid int\ndo_test_broker_failure(conf_file, [\"listener\"], port, 3, \"Error: Empty 'listener port' value in configuration.\") # Empty listener\ndo_test_broker_failure(conf_file, [\"mount_point test/\"], port, 3, \"Error: The 'mount_point' option requires a listener to be defined first.\") # Missing listener config\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"mount_point test/+/\"], port, 3, \"Error: Invalid 'mount_point' value (test/+/). Does it contain a wildcard character?\") # Wildcard in mount point.\ndo_test_broker_failure(conf_file, [f\"listener 100000\"], port, 3, \"Error: Invalid 'port' value (100000).\") # Out of range\ndo_test_broker_failure(conf_file, [f\"listener 0\"], port, 3, \"Error: A listener with port 0 must provide a Unix socket path.\") # Missing unix socket\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"protocol\"], port, 3, \"Error: Empty 'protocol' value in configuration.\") # Empty proto\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"protocol test\"], port, 3, \"Error: Invalid 'protocol' value (test).\") # Invalid proto\ndo_test_broker_failure(conf_file, [f\"listener {port}\",\"accept_protocol_versions\"], port, 3, \"Error: Empty 'accept_protocol_versions' value in configuration.\")\n\ndo_test_broker_failure(conf_file, [\"plugin_opt_inval string\"], port, 3, \"Error: The 'plugin_opt_inval' option requires plugin/global_plugin/plugin_load to be defined first.\") # plugin_opt_ without plugin\ndo_test_broker_failure(conf_file, [\"plugin c/auth_plugin.so\",\"plugin_opt_ string\"], port, 3, \"Error: Invalid 'plugin_opt_' config option.\") # Incomplete plugin_opt_\ndo_test_broker_failure(conf_file, [\"plugin c/auth_plugin.so\",\"plugin_opt_test\"], port, 3, \"Error: Empty 'test' value in configuration.\") # Empty plugin_opt_\n\ndo_test_broker_failure(conf_file, [\"bridge_attempt_unsubscribe true\"], port, 3, \"Error: The 'bridge_attempt_unsubscribe' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_bind_address string\"], port, 3, \"Error: The 'bridge_bind_address' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_insecure true\"], port, 3, \"Error: The 'bridge_insecure' option requires a bridge to be defined first.\") # Missing bridge config\n#do_test_broker_failure(conf_file, [\"bridge_require_oscp true\"], port, 3, \"Error: The 'bridge_require_oscp' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_max_packet_size 1000\"], port, 3, \"Error: The 'bridge_max_packet_size' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_max_topic_alias 1000\"], port, 3, \"Error: The 'bridge_max_topic_alias' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_outgoing_retain false\"], port, 3, \"Error: The 'bridge_outgoing_retain' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_protocol_version string\"], port, 3, \"Error: The 'bridge_protocol_version' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_receive_maximum 10\"], port, 3, \"Error: The 'bridge_receive_maximum' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_reload_type string\"], port, 3, \"Error: The 'bridge_reload_type' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_session_expiry_interval 10000\"], port, 3, \"Error: The 'bridge_session_expiry_interval' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_tcp_keepalive 10000\"], port, 3, \"Error: The 'bridge_tcp_keepalive' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"bridge_tcp_user_timeout 10000\"], port, 3, \"Error: The 'bridge_tcp_user_timeout' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"connection\"], port, 3, \"Error: Empty 'connection' value in configuration.\") # Missing bridge name\ndo_test_broker_failure(conf_file, [\"connection just-name\"], port, 3, \"Error: Invalid bridge configuration: no remote addresses defined.\") # Missing bridge topic and address\ndo_test_broker_failure(conf_file, [\"connection no-topic\", \"address localhost\"], port, 3, \"Error: Invalid bridge configuration: no topics defined.\") # Missing bridge topic\ndo_test_broker_failure(conf_file, [\"connection no-address\", \"topic dummy-topic\"], port, 3, \"Error: Invalid bridge configuration: no remote addresses defined.\") # Missing bridge address\ndo_test_broker_failure(conf_file, [\"connection no-address\", \"topic \\\"missing quote\"], port, 3, \"Error: Missing closing quote in topic value (quote).\") # Missing topic quote\n\ndo_test_broker_failure(conf_file, [\"local_clientid str\"], port, 3, \"Error: The 'local_clientid' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"local_password str\"], port, 3, \"Error: The 'local_password' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"local_username str\"], port, 3, \"Error: The 'local_username' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"notifications true\"], port, 3, \"Error: The 'notifications' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"notifications_local_only true\"], port, 3, \"Error: The 'notifications_local_only' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"notification_topic true\"], port, 3, \"Error: The 'notification_topic' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"password pw\"], port, 3, \"Error: The 'password' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"remote_password pw\"], port, 3, \"Error: The 'remote_password' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"restart_timeout 10\"], port, 3, \"Error: The 'restart_timeout' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"round_robin true\"], port, 3, \"Error: The 'round_robin' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"start_type lazy\"], port, 3, \"Error: The 'start_type' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"threshold 10\"], port, 3, \"Error: The 'threshold' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"topic topic/10\"], port, 3, \"Error: The 'topic' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"try_private true\"], port, 3, \"Error: The 'try_private' option requires a bridge to be defined first.\") # Missing bridge config\ndo_test_broker_failure(conf_file, [\"username un\"], port, 3, \"Error: The 'username' option requires a bridge to be defined first.\") # Missing bridge config\n\ndo_test_broker_failure(conf_file, [\"maximum_qos 3\"], port, 3, \"Error: 'max_qos' must be between 0 and 2 inclusive.\") # Invalid maximum qos\ndo_test_broker_failure(conf_file, [\"maximum_qos -1\"], port, 3, \"Error: 'max_qos' must be between 0 and 2 inclusive.\") # Invalid maximum qos\n\ndo_test_broker_failure(conf_file, [\"max_inflight_messages 65536\"], port, 3, \"Error: 'max_inflight_messages' must be <= 65535.\") # Invalid value\n\ndo_test_broker_failure(conf_file, [\"max_packet_size 19\"], port, 3, \"Error: 'max_packet_size' must be >= 20.\") # Invalid value\ndo_test_broker_failure(conf_file, [\"message_size_limit 556000000\"], port, 3, \"Error: Invalid 'message_size_limit' value (556000000).\") # Invalid value\n\ndo_test_broker_failure(conf_file, [\"max_keepalive 65536\"], port, 3, \"Error: Invalid 'max_keepalive' value (65536).\") # Invalid value\ndo_test_broker_failure(conf_file, [\"max_keepalive -1\"], port, 3, \"Error: Invalid 'max_keepalive' value (-1).\") # Invalid value\n\ndo_test_broker_failure(conf_file, [f\"listener {port}\", \"max_topic_alias 65536\"], port, 3, \"Error: Invalid 'max_topic_alias' value in configuration.\") # Invalid value\ndo_test_broker_failure(conf_file, [f\"listener {port}\", \"max_topic_alias -1\"], port, 3, \"Error: Invalid 'max_topic_alias' value in configuration.\") # Invalid value\ndo_test_broker_failure(conf_file, [f\"listener {port}\", \"max_topic_alias_broker 65536\"], port, 3, \"Error: Invalid 'max_topic_alias_broker' value in configuration.\") # Invalid value\ndo_test_broker_failure(conf_file, [f\"listener {port}\", \"max_topic_alias_broker -1\"], port, 3, \"Error: Invalid 'max_topic_alias_broker' value in configuration.\") # Invalid value\ndo_test_broker_failure(conf_file, [f\"listener {port}\", \"listener_auto_id_prefix\"], port, 3, \"Error: Empty 'listener_auto_id_prefix' value in configuration.\") # Empty string\ndo_test_broker_failure(conf_file, [f\"listener {port}\", f\"listener_auto_id_prefix {'a'*51}\"], port, 3, \"Error: 'listener_auto_id_prefix' length must be <= 50.\") # Invalid value\ndo_test_broker_failure(conf_file, [\"websockets_headers_size 65536\"], port, 3, \"Error: Packet buffer size must be between 0 and 65535 inclusive.\") # Invalid value\ndo_test_broker_failure(conf_file, [\"websockets_headers_size -1\"], port, 3, \"Error: Packet buffer size must be between 0 and 65535 inclusive.\") # Invalid value\ndo_test_broker_failure(conf_file, [\"memory_limit -1\"], port, 3, \"Error: Invalid 'memory_limit' value (-1).\") # Invalid value\n\ndo_test_broker_failure(conf_file, [\"sys_interval -1\"], port, 3, \"Error: Invalid 'sys_interval' value (-1).\") # Invalid value\ndo_test_broker_failure(conf_file, [\"sys_interval 65536\"], port, 3, \"Error: Invalid 'sys_interval' value (65536).\") # Invalid value\n\n\n\nexit(0)\n"
  },
  {
    "path": "test/broker/17-control-list-listeners.py",
    "content": "#!/usr/bin/env python3\n\n# Test $CONTROL/broker/v1 listListeners\n\nfrom mosq_test_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, ports):\n    with open(filename, 'w') as f:\n        f.write(\"enable_control_api true\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (ports[0]))\n        f.write(\"protocol mqtt\\n\")\n        f.write(\"listener %d\\n\" % (ports[1]))\n        f.write(\"protocol websockets\\n\")\n        f.write(\"listener %d\\n\" % (ports[2]))\n        f.write(\"protocol mqtt\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"listener %d\\n\" % (ports[3]))\n        f.write(\"protocol websockets\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n        f.write(\"listener 0 17-list-listeners-mqtt.sock\\n\")\n        f.write(\"protocol mqtt\\n\")\n        f.write(\"listener 0 17-list-listeners-websockets.sock\\n\")\n        f.write(\"protocol websockets\\n\")\n\ndef command_check(sock, command_payload, expected_response):\n    command_packet = mosq_test.gen_publish(topic=\"$CONTROL/broker/v1\", qos=0, payload=json.dumps(command_payload))\n    sock.send(command_packet)\n    response = json.loads(mosq_test.read_publish(sock))\n    if response != expected_response:\n        print(expected_response)\n        print(response)\n        raise ValueError(response)\n\ndef invalid_command_check(sock, command_payload, cmd_name, error_msg):\n    command_packet = mosq_test.gen_publish(topic=\"$CONTROL/broker/v1\", qos=0, payload=command_payload)\n    sock.send(command_packet)\n    response = json.loads(mosq_test.read_publish(sock))\n    expected_response = {'responses': [{'command': cmd_name, 'error': error_msg}]}\n    if response != expected_response:\n        print(expected_response)\n        print(response)\n        raise ValueError(response)\n\n\n\nports = mosq_test.get_port(4)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, ports)\n\ncmd_success = {\"commands\":[{\"command\": \"listListeners\", \"correlationData\": \"m3CtYVnySLCOwnHzITSeowvgla0InV4G\"}]}\n\nresponse_success = {'responses': [{'command': 'listListeners', \"correlationData\": \"m3CtYVnySLCOwnHzITSeowvgla0InV4G\", 'data':{\n    'listeners':[\n        {\n            'port': ports[0],\n            'protocol': 'mqtt',\n            'tls': False\n        },{\n            'port': ports[1],\n            'protocol': 'mqtt+websockets',\n            'tls': False\n        },{\n            'port': ports[2],\n            'protocol': 'mqtt',\n            'tls': True\n        },{\n            'port': ports[3],\n            'protocol': 'mqtt+websockets',\n            'tls': True\n        },{\n            'port': 0,\n            'protocol': 'mqtt',\n            'socket-path': '17-list-listeners-mqtt.sock',\n            'tls': False\n        },{\n            'port': 0,\n            'protocol': 'mqtt+websockets',\n            'socket-path': '17-list-listeners-websockets.sock',\n            'tls': False\n        }\n    ]}}]}\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"17-list-listeners\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/broker/#\", 0)\nsuback_packet = mosq_test.gen_suback(mid, 0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=ports[0])\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=ports[0])\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    invalid_command_check(sock, \"not valid json\", \"Unknown command\", \"Payload not valid JSON\")\n    invalid_command_check(sock, \"{}\", \"Unknown command\", \"Invalid/missing commands\")\n\n    cmd = {\"commands\":[\"command\"]}\n    invalid_command_check(sock, json.dumps(cmd), \"Unknown command\", \"Command not an object\")\n\n    cmd = {\"commands\":[{}]}\n    invalid_command_check(sock, json.dumps(cmd), \"Unknown command\", \"Missing command\")\n\n    cmd = {\"commands\":[{\"command\": \"unknown command\"}]}\n    invalid_command_check(sock, json.dumps(cmd), \"unknown command\", \"Unknown command\")\n\n    cmd = {\"commands\":[{\"command\": \"listListeners\", \"correlationData\": True}]}\n    invalid_command_check(sock, json.dumps(cmd), \"listListeners\", \"Invalid correlationData data type.\")\n\n    command_check(sock, cmd_success, response_success)\n    mosq_test.do_ping(sock)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    for f in [\"17-list-listeners-mqtt.sock\", \"17-list-listeners-websockets.sock\"]:\n        try:\n            os.remove(f)\n        except FileNotFoundError:\n            pass\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/17-control-list-plugins.py",
    "content": "#!/usr/bin/env python3\n\n# Test $CONTROL/broker/v1 listListeners\n\nfrom mosq_test_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"enable_control_api true\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"plugin c/auth_plugin_v5_control.so\\n\")\n\ndef command_check(sock, command_payload, expected_response):\n    command_packet = mosq_test.gen_publish(topic=\"$CONTROL/broker/v1\", qos=0, payload=json.dumps(command_payload))\n    sock.send(command_packet)\n    response = json.loads(mosq_test.read_publish(sock))\n    if response != expected_response:\n        print(expected_response)\n        print(response)\n        raise ValueError(response)\n\ndef invalid_command_check(sock, command_payload, cmd_name, error_msg):\n    command_packet = mosq_test.gen_publish(topic=\"$CONTROL/broker/v1\", qos=0, payload=command_payload)\n    sock.send(command_packet)\n    response = json.loads(mosq_test.read_publish(sock))\n    expected_response = {'responses': [{'command': cmd_name, 'error': error_msg}]}\n    if response != expected_response:\n        print(expected_response)\n        print(response)\n        raise ValueError(response)\n\n\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\ncmd_success = {\"commands\":[{\"command\": \"listPlugins\", \"correlationData\": \"m3CtYVnySLCOwnHzITSeowvgla0InV4G\"}]}\n\nresponse_success = {'responses': [{'command': 'listPlugins', \"correlationData\": \"m3CtYVnySLCOwnHzITSeowvgla0InV4G\", 'data':{\n    'plugins':[\n        {\n            'name': 'test-plugin',\n            'control-endpoints': ['$CONTROL/test/v1']\n        }\n    ]}}]}\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"17-list-listeners\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/broker/#\", 0)\nsuback_packet = mosq_test.gen_suback(mid, 0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    invalid_command_check(sock, \"not valid json\", \"Unknown command\", \"Payload not valid JSON\")\n    invalid_command_check(sock, \"{}\", \"Unknown command\", \"Invalid/missing commands\")\n\n    cmd = {\"commands\":[\"command\"]}\n    invalid_command_check(sock, json.dumps(cmd), \"Unknown command\", \"Command not an object\")\n\n    cmd = {\"commands\":[{}]}\n    invalid_command_check(sock, json.dumps(cmd), \"Unknown command\", \"Missing command\")\n\n    cmd = {\"commands\":[{\"command\": \"unknown command\"}]}\n    invalid_command_check(sock, json.dumps(cmd), \"unknown command\", \"Unknown command\")\n\n    cmd = {\"commands\":[{\"command\": \"listListeners\", \"correlationData\": True}]}\n    invalid_command_check(sock, json.dumps(cmd), \"listListeners\", \"Invalid correlationData data type.\")\n\n    command_check(sock, cmd_success, response_success)\n    mosq_test.do_ping(sock)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    for f in [\"17-list-listeners-mqtt.sock\", \"17-list-listeners-websockets.sock\"]:\n        try:\n            os.remove(f)\n        except FileNotFoundError:\n            pass\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/17-control-missing-endpoint.py",
    "content": "#!/usr/bin/env python3\n\n# Test $CONTROL/broker/v1 listListeners\n\nfrom mosq_test_helper import *\nimport json\nimport shutil\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"enable_control_api true\\n\")\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port}\\n\")\n\ndef command_check(sock, command_payload, expected_response):\n    command_packet = mosq_test.gen_publish(topic=\"$CONTROL/missing-endpoint/v1\", qos=0, payload=json.dumps(command_payload))\n    sock.send(command_packet)\n    response = json.loads(mosq_test.read_publish(sock))\n    if response != expected_response:\n        print(expected_response)\n        print(response)\n        raise ValueError(response)\n\n\nport = mosq_test.get_port(1)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nrc = 1\nconnect_packet = mosq_test.gen_connect(\"17-missing-endpoint\")\nconnack_packet = mosq_test.gen_connack(rc=0)\n\nmid = 2\nsubscribe_packet = mosq_test.gen_subscribe(mid, \"$CONTROL/missing-endpoint/#\", 0)\nsuback_packet = mosq_test.gen_suback(mid, 0)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\ntry:\n    sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n    mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n\n    cmd = {\"commands\":[{\"command\": \"listListeners\", \"correlationData\": \"m3CtYVnySLCOwnHzITSeowvgla0InV4G\"}]}\n    response = {\"error\": \"endpoint not available\"}\n    command_check(sock, cmd, response)\n    mosq_test.do_ping(sock)\n\n    rc = 0\n\n    sock.close()\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/20-sparkplug-aware.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport uuid\n\nnamespace = \"spBv1.0\"\nclient_id = \"test-client\"\nusername = None\npassword = None\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/sparkplug-aware/mosquitto_sparkplug_aware.so\\n\")\n\ndef proc(port, proto_ver):\n    group_id = str(uuid.uuid4())\n    edge_node_id = str(uuid.uuid4())\n    device_id = str(uuid.uuid4())\n\n    # ======================================================================\n    # Connect a client to monitor the spBv1.0/<group_id>/NDEATH/# and\n    # $sparkplug/certificates/spBv1.0/<group_id>/# topics\n    # ======================================================================\n\n    topic = f\"$sparkplug/certificates/{namespace}/{group_id}/#\"\n    qos = 1\n    monitor = mosq_test.sub_helper(port, topic, qos=qos, proto_ver=proto_ver)\n\n    topic = f\"{namespace}/{group_id}/NDEATH/#\"\n    qos = 1\n    subscribe_packet = mosq_test.gen_subscribe(topic=topic, mid=1, qos=qos, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid=1, qos=qos, proto_ver=proto_ver)\n    mosq_test.do_send_receive(monitor, subscribe_packet, suback_packet)\n\n    # ======================================================================\n    # Connect a test client that will act as a sparkplug device\n    # ======================================================================\n\n    # • [tck-id-message-flow-edge-node-birth-publish-will-message-topic]\n    #   The Edge Node’s MQTT Will Message’s topic MUST be of the form\n    #   spBv1.0/group_id/NDEATH/edge_node_id where group_id is the Sparkplug\n    #   Group ID and the edge_node_id is the Sparkplug Edge Node ID for this\n    #   Edge Node\n    NDEATH_topic = f\"{namespace}/{group_id}/NDEATH/{edge_node_id}\"\n\n    # • [tck-id-message-flow-edge-node-birth-publish-will-message-payload]\n    #   The Edge Node’s MQTT Will Message’s payload MUST be a Sparkplug Google\n    #   Protobuf encoded payload.\n    # • [tck-id-message-flow-edge-node-birth-publish-will-message-payload-bdSeq]\n    #   The Edge Node’s MQTT Will Message’s payload MUST include a metric with\n    #   the name of bdSeq, the datatype of INT64, and the value MUST be\n    #   incremented by one from the value in the previous MQTT CONNECT packet\n    #   unless the value would be greater than 255. If in the previous NBIRTH a\n    #   value of 255 was sent, the next MQTT Connect packet Will Message\n    #   payload bdSeq number value MUST have a value of 0.\n    NDEATH_payload = \"\"\n\n    # • [tck-id-message-flow-edge-node-birth-publish-will-message-qos]\n    #   The Edge Node’s MQTT Will Message’s MQTT QoS MUST be 1.\n    NDEATH_qos = 1\n\n    # • [tck-id-message-flow-edge-node-birth-publish-will-message-will-retained]\n    #   The Edge Node’s MQTT Will Message’s retained flag MUST be set to false.\n    NDEATH_retain = False\n\n    # • [tck-id-principles-persistence-clean-session-311]\n    #   If the MQTT client is using MQTT v3.1.1, the Edge Node’s MQTT CONNECT\n    #   packet MUST set the Clean Session flag to true.\n    # • [tck-id-principles-persistence-clean-session-50]\n    #   If the MQTT client is using MQTT v5.0, the Edge Node’s MQTT CONNECT\n    #   packet MUST set the Clean Start flag to true and the Session Expiry\n    #   Interval to 0.\n    clean_session = True\n    session_expiry_interval = 0\n\n    # • [tck-id-message-flow-edge-node-birth-publish-connect]\n    #   Any Edge Node in the MQTT infrastructure MUST establish an MQTT Session\n    #   prior to publishing NBIRTH and DBIRTH messages.\n    # • [tck-id-message-flow-edge-node-birth-publish-will-message]\n    #   When a Sparkplug Edge Node sends its MQTT CONNECT packet, it MUST\n    #   include a Will Message.\n    connect_packet = mosq_test.gen_connect(client_id, clean_session=clean_session, username=username, password=password,\n                                           will_topic=NDEATH_topic, will_qos=NDEATH_qos, will_retain=NDEATH_retain,\n                                           will_payload=NDEATH_payload, proto_ver=proto_ver, session_expiry=session_expiry_interval)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)\n    test_client = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, connack_error=f\"client connack\")\n\n    # • [tck-id-message-flow-edge-node-ncmd-subscribe]\n    #   The MQTT client associated with the Edge Node MUST subscribe to a topic\n    #   of the form spBv1.0/group_id/NCMD/edge_node_id where group_id is the\n    #   Sparkplug Group ID and the edge_node_id is the Sparkplug Edge Node ID\n    #   for this Edge Node. It MUST subscribe on this topic with a QoS of 1.\n    NCMD_topic = f\"{namespace}/{group_id}/NCMD/{edge_node_id}\"\n    qos = 1\n\n    subscribe_packet = mosq_test.gen_subscribe(topic=NCMD_topic, mid=1, qos=qos, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid=1, qos=qos, proto_ver=proto_ver)\n    mosq_test.do_send_receive(test_client, subscribe_packet, suback_packet)\n\n    # ======================================================================\n    # Test client publishes its NBIRTH certificate\n    # ======================================================================\n\n    # • [tck-id-message-flow-edge-node-birth-publish-nbirth-topic]\n    #   The Edge Node’s NBIRTH MQTT topic MUST be of the form\n    #   spBv1.0/group_id/NBIRTH/edge_node_id where group_id is the Sparkplug\n    #   Group ID and the edge_node_id is the Sparkplug Edge Node ID for this\n    #   Edge Node\n    NBIRTH_topic = f\"{namespace}/{group_id}/NBIRTH/{edge_node_id}\"\n\n    # • [tck-id-message-flow-edge-node-birth-publish-nbirth-payload]\n    #   The Edge Node’s NBIRTH payload MUST be a Sparkplug Google Protobuf\n    #   encoded payload.\n    # • [tck-id-message-flow-edge-node-birth-publish-nbirth-payload-bdSeq]\n    #   The Edge Node’s NBIRTH payload MUST include a metric with the name of\n    #   bdSeq the datatype of INT64 and the value MUST be the same as the\n    #   previous MQTT CONNECT packet.\n    # • [tck-id-message-flow-edge-node-birth-publish-nbirth-payload-seq]\n    #   The Edge Node’s NBIRTH payload MUST include a seq number that is\n    #   between 0 and 255 (inclusive).\n    #   ◦ This will become the starting sequence number which all following\n    #     messages will include a sequence number that is one more than the\n    #     previous up to 255 where it wraps back to zero.\n    # • [tck-id-principles-birth-certificates-order]\n    #   Birth Certificates MUST be the first MQTT messages published by any\n    #   Edge Node or any Host Application.\n    NBIRTH_payload = \"FIXME\"\n\n    # • [tck-id-message-flow-edge-node-birth-publish-nbirth-qos]\n    #   The Edge Node’s NBIRTH MQTT QoS MUST be 0.\n    NBIRTH_qos = 0\n\n    # • [tck-id-message-flow-edge-node-birth-publish-nbirth-retained]\n    #   The Edge Node’s NBIRTH retained flag MUST be set to false.\n    NBIRTH_retain = False\n\n    publish_packet = mosq_test.gen_publish(topic=NBIRTH_topic, qos=NBIRTH_qos, payload=NBIRTH_payload, retain=NBIRTH_retain, proto_ver=proto_ver)\n    test_client.send(publish_packet)\n\n    # **********************************************************************\n    # Monitor client should receive the NBIRTH certificate republished by the\n    # broker\n    # **********************************************************************\n\n    NBIRTH_topic_aware = f\"$sparkplug/certificates/{namespace}/{group_id}/NBIRTH/{edge_node_id}\"\n    publish_packet = mosq_test.gen_publish(NBIRTH_topic_aware, mid=1, qos=0, proto_ver=proto_ver, payload=NBIRTH_payload)\n    mosq_test.expect_packet(monitor, \"error1\", publish_packet)\n\n    # ======================================================================\n    # Test client publishes its DBIRTH certificate\n    # ======================================================================\n\n    # • [tck-id-message-flow-device-birth-publish-dbirth-match-edge-node-topic]\n    #   The Device’s DBIRTH MQTT topic group_id and edge_node_id MUST match the\n    #   group_id and edge_node_id that were sent in the prior NBIRTH message\n    #   for the Edge Node this Device is associated with.\n    # • [tck-id-message-flow-device-birth-publish-dbirth-topic]\n    #   The Device’s DBIRTH MQTT topic MUST be of the form\n    #   spBv1.0/group_id/DBIRTH/edge_node_id/device_id where group_id is the\n    #   Sparkplug Group ID the edge_node_id is the Sparkplug Edge Node ID and\n    #   the device_id is the Sparkplug Device ID for this Device.\n    DBIRTH_topic = f\"{namespace}/{group_id}/DBIRTH/{edge_node_id}/{device_id}\"\n\n    # • [tck-id-message-flow-device-birth-publish-dbirth-payload]\n    #   The Device’s DBIRTH payload MUST be a Sparkplug Google Protobuf encoded\n    #   payload.\n    # • [tck-id-message-flow-device-birth-publish-dbirth-payload-seq]\n    #   The Device’s DBIRTH payload MUST include a seq number that is between 0\n    #   and 255 (inclusive) and be one more than was included in the prior\n    #   Sparkplug message sent from the Edge Node associated with this Device.\n    DBIRTH_payload = \"FIXME\"\n\n    # • [tck-id-message-flow-device-birth-publish-dbirth-qos]\n    #   The Device’s DBIRTH MQTT QoS MUST be 0.\n    DBIRTH_qos = 0\n\n    # • [tck-id-message-flow-device-birth-publish-dbirth-retained]\n    #   The Device’s DBIRTH retained flag MUST be set to false.\n    DBIRTH_retain = False\n\n    # • [tck-id-message-flow-device-birth-publish-nbirth-wait]\n    #   The NBIRTH message MUST have been sent within the current MQTT session\n    #   prior to a DBIRTH being published.\n    publish_packet = mosq_test.gen_publish(topic=DBIRTH_topic, qos=DBIRTH_qos, payload=DBIRTH_payload, retain=DBIRTH_retain, proto_ver=proto_ver)\n    test_client.send(publish_packet)\n\n    # **********************************************************************\n    # Monitor client should receive the DBIRTH certificate republished by the\n    # broker\n    # **********************************************************************\n\n    DBIRTH_topic_aware = f\"$sparkplug/certificates/{namespace}/{group_id}/DBIRTH/{edge_node_id}/{device_id}\"\n    publish_packet = mosq_test.gen_publish(DBIRTH_topic_aware, mid=1, qos=0, proto_ver=proto_ver, payload=DBIRTH_payload)\n    mosq_test.expect_packet(monitor, \"error0\", publish_packet)\n\n    # ======================================================================\n    # The test client disconnects \"unintentionally\"\n    # ======================================================================\n\n    # • [tck-id-operational-behavior-edge-node-intentional-disconnect-ndeath]\n    #   When an Edge Node disconnects intentionally, it MUST publish an NDEATH\n    #   before terminating the connection.\n    # • [tck-id-operational-behavior-edge-node-intentional-disconnect-packet]\n    #   Immediately the NDEATH publish, a DISCONNECT packet MAY be sent to the\n    #   MQTT Server.\n\n    test_client.close()\n\n    # **********************************************************************\n    # Monitor client should receive the NDEATH certificate\n    # **********************************************************************\n\n    publish_packet = mosq_test.gen_publish(NDEATH_topic, mid=1, qos=NDEATH_qos, proto_ver=proto_ver, payload=NDEATH_payload)\n    mosq_test.expect_packet(monitor, \"error 2\", publish_packet)\n\n    # **********************************************************************\n    # Clear the republished certificates ready for the next test\n    # **********************************************************************\n    publish_packet = mosq_test.gen_publish(NBIRTH_topic_aware, qos=0, proto_ver=proto_ver, payload=None, retain=True)\n    monitor.send(publish_packet)\n    publish_packet = mosq_test.gen_publish(DBIRTH_topic_aware, qos=0, proto_ver=proto_ver, payload=None, retain=True)\n    monitor.send(publish_packet)\n    monitor.close()\n\n\ndef do_tests():\n    rc = 1\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        proc(port, 4)\n        proc(port, 5)\n\n        rc = 0\n    except Exception as e:\n        print(e)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\nif __name__ == '__main__':\n    do_tests()\n"
  },
  {
    "path": "test/broker/20-sparkplug-compliance.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport uuid\n\ndef tck_id_conformance_mqtt_qos0(port, proto_ver):\n    # A Sparkplug conformant MQTT Server MUST support publish and subscribe on\n    # QoS 0\n\n    topic = str(uuid.uuid4())\n    payload = str(uuid.uuid4())\n    client = mosq_test.sub_helper(port, topic, qos=0, proto_ver=proto_ver)\n    publish_packet = mosq_test.gen_publish(topic, qos=0, payload=payload, retain=False, proto_ver=proto_ver)\n    mosq_test.do_send_receive(client, publish_packet, publish_packet, error_string=\"tck-id-conformance-mqtt-qos0\")\n    client.close()\n\n\ndef tck_id_conformance_mqtt_qos1(port, proto_ver):\n    # A Sparkplug conformant MQTT Server MUST support publish and subscribe on\n    # QoS 1\n\n    topic = str(uuid.uuid4())\n    payload = str(uuid.uuid4())\n    client = mosq_test.sub_helper(port, topic, qos=1, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(topic, qos=1, mid=1, payload=payload, retain=False, proto_ver=proto_ver)\n    puback_packet = mosq_test.gen_puback(mid=1, proto_ver=proto_ver)\n\n    client.send(publish_packet)\n    mosq_test.receive_unordered(client, publish_packet, puback_packet, \"tck-id-conformance-mqtt-qos1\")\n    client.close()\n\n\ndef tck_id_conformance_mqtt_will_messages(port, proto_ver):\n    # A Sparkplug conformant MQTT Server MUST support all aspects of Will\n    # Messages including use of the retain flag and QoS 1\n    pass\n\n\ndef tck_id_conformance_mqtt_retained(port, proto_ver):\n    # A Sparkplug conformant MQTT Server MUST support all aspects of the retain\n    # flag\n\n    topic = str(uuid.uuid4())\n    payload = str(uuid.uuid4())\n    client = mosq_test.pub_helper(port, proto_ver=proto_ver)\n\n    publish_packet = mosq_test.gen_publish(topic, qos=0, payload=payload, retain=True, proto_ver=proto_ver)\n    subscribe_packet = mosq_test.gen_subscribe(mid=1, topic=topic, qos=0, proto_ver=proto_ver)\n    suback_packet = mosq_test.gen_suback(mid=1, qos=0, proto_ver=proto_ver)\n\n    client.send(publish_packet)\n    client.send(subscribe_packet)\n    mosq_test.receive_unordered(client, publish_packet, suback_packet, \"tck-id-conformance-mqtt-retained\")\n    client.close()\n\n\ndef do_tests():\n    rc = 1\n    port = mosq_test.get_port()\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        tck_id_conformance_mqtt_qos0(port, 4)\n        tck_id_conformance_mqtt_qos0(port, 5)\n\n        tck_id_conformance_mqtt_qos1(port, 4)\n        tck_id_conformance_mqtt_qos1(port, 5)\n\n        tck_id_conformance_mqtt_will_messages(port, 4)\n        tck_id_conformance_mqtt_will_messages(port, 5)\n\n        tck_id_conformance_mqtt_retained(port, 4)\n        tck_id_conformance_mqtt_retained(port, 5)\n\n        rc = 0\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\nif __name__ == '__main__':\n    do_tests()\n"
  },
  {
    "path": "test/broker/21-proxy-bad-version.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port, ver):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"enable_proxy_protocol {ver}\\n\")\n\ndef do_test(ver, expect_fail_log):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    rc = 1\n    write_config(conf_file, port, ver)\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port, expect_fail=True, expect_fail_log=expect_fail_log)\n        rc = 0\n    except subprocess.TimeoutExpired:\n        pass\n\n    os.remove(conf_file)\n    if rc != 0:\n        raise ValueError(rc)\n\ndo_test(0, \"Error: enable_proxy_protocol must be 1 or 2.\")\ndo_test(3, \"Error: enable_proxy_protocol must be 1 or 2.\")\n"
  },
  {
    "path": "test/broker/21-proxy-v1-bad.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 1\\n\")\n\ndef do_test(data, expect_log):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    connect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = 1\n\n    try:\n        sock = do_proxy_v1_connect(port, data)\n        try:\n            d = sock.recv(1)\n            if len(d) == 0:\n                rc = 0\n        except ConnectionResetError:\n            rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0 or expect_log not in stde.decode('utf-8'):\n            print(stde.decode('utf-8'))\n            print(data)\n            exit(1)\n\n\n# Basic\ndo_test(b\"PROXY TCP3 192.0.2.5 127.0.0.1 6275 1234\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # Bad transport\ndo_test(b\"PROXY TCP4 192.0.2.5 127.0.0.1 6275 1234                                                                   \\r\\n\", \"Connection rejected, corrupt PROXY header.\") # Too long\n\n# TCP4\ndo_test(b\"PROXY TCP4 192.0.2.5 127.0.0.1 6275\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # Missing dport\ndo_test(b\"PROXY TCP4 192.0.2.5 127.0.0.1\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # Missing sport\ndo_test(b\"PROXY TCP4 192.0.2.5\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # Missing daddr\ndo_test(b\"PROXY TCP4 \\r\\n\", \"Connection rejected, corrupt PROXY header.\") # Missing saddr\ndo_test(b\"PROXY TCP4 192.0.2.5 127.0.0.1 6275 0\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # dport == 0\ndo_test(b\"PROXY TCP4 192.0.2.5 127.0.0.1 6275 65536\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # dport == 65536\ndo_test(b\"PROXY TCP4 192.0.2.5 127.0.0.1 0 1234\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # sport == 0\ndo_test(b\"PROXY TCP4 192.0.2.5 127.0.0.1 65536 1234\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # sport == 65536\ndo_test(b\"PROXY TCP4 192.0.2.5 127.0.0.256 6275 1234\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # daddr invalid\ndo_test(b\"PROXY TCP4 192.0.2.256 127.0.0.1 6275 1234\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # saddr invalid\n\n# TCP6\ndo_test(b\"PROXY TCP6 192.0.2.5 127.0.0.1 6275\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # Missing dport\ndo_test(b\"PROXY TCP6 192.0.2.5 127.0.0.1\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # Missing sport\ndo_test(b\"PROXY TCP6 192.0.2.5\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # Missing daddr\ndo_test(b\"PROXY TCP6 \\r\\n\", \"Connection rejected, corrupt PROXY header.\") # Missing saddr\ndo_test(b\"PROXY TCP6 192.0.2.5 127.0.0.1 6275 0\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # dport == 0\ndo_test(b\"PROXY TCP6 192.0.2.5 127.0.0.1 6275 65536\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # dport == 65536\ndo_test(b\"PROXY TCP6 192.0.2.5 127.0.0.1 0 1234\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # sport == 0\ndo_test(b\"PROXY TCP6 192.0.2.5 127.0.0.1 65536 1234\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # sport == 65536\ndo_test(b\"PROXY TCP6 192.0.2.5 127.0.0.256 6275 1234\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # daddr invalid\ndo_test(b\"PROXY TCP6 192.0.2.256 127.0.0.1 6275 1234\\r\\n\", \"Connection rejected, corrupt PROXY header.\") # saddr invalid\n"
  },
  {
    "path": "test/broker/21-proxy-v1-success.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 1\\n\")\n\ndef do_test(data, expect_log):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    connect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = 1\n\n    try:\n        sock = do_proxy_v1_connect(port, data)\n        mosq_test.do_send_receive(sock, connect_packet, connack_packet, \"connack\")\n        mosq_test.do_ping(sock)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0 or expect_log not in stde.decode('utf-8'):\n            print(stde.decode('utf-8'))\n            exit(1)\n\ndo_test(b\"PROXY TCP4 192.0.2.5 127.0.0.1 6275 1234\\r\\n\", \"New client connected from 192.0.2.5:6275\")\ndo_test(b\"PROXY TCP6 2001:db8:506:708:900::1 ::1 6275 1234\\r\\n\", \"New client connected from 2001:db8:506:708:900::1:6275 as proxy-test (p5, c0, k42)\")\ndo_test(b\"PROXY UNKNOWN \\r\\n\", \"New client connected from 127.0.0.1:\")\n"
  },
  {
    "path": "test/broker/21-proxy-v2-bad-config.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port, extra_options):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n        f.write(extra_options)\n\ndef do_test(extra_options, expect_fail_log):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    rc = 1\n    write_config(conf_file, port, extra_options)\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port, expect_fail=True, expect_fail_log=expect_fail_log)\n        rc = 0\n    except subprocess.TimeoutExpired:\n        pass\n\n    os.remove(conf_file)\n    if rc != 0:\n        raise ValueError(rc)\n\ndo_test(\"use_subject_as_username true\\n\", \"Error: use_subject_as_username cannot be used with `enable_proxy_protocol 2`.\")\ndo_test(f\"certfile {ssl_dir}/server.crt\\nkeyfile {ssl_dir}/server.key\\n\", \"Error: certfile and keyfile cannot be used with `enable_proxy_protocol 2`.\")\n"
  },
  {
    "path": "test/broker/21-proxy-v2-bad-header.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n\nconnect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\ndef do_test(headers):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = len(headers)\n\n    try:\n        for header in headers:\n            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n            sock.settimeout(5)\n            sock.connect((\"localhost\", port))\n            sock.send(header)\n            try:\n                data = sock.recv(10)\n                if len(data) == 0:\n                    rc -= 1\n            except ConnectionResetError:\n                rc -= 1\n            sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0:\n            print(stde.decode('utf-8'))\n            rc = 1\n            raise ValueError(rc)\n\n\nmagic = b\"\\x0d\\x0a\\x0d\\x0a\\x00\\x0d\\x0a\\x51\\x55\\x49\\x54\\x0a\"\n\nproxy_headers = []\n# Bad magic\nproxy_headers.append(b\"\\x0d\\x0a\\x0d\\x0a\\x00\\x0d\\x0a\\x51\\x55\\x49\\x54\\x0b\" + b\"\\x21\\x01\\x00\\x0c\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x00\\x00\")\n\n# Bad version\nproxy_headers.append(magic + b\"\\x31\\x01\\x00\\x0c\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x00\\x00\")\n\n# Bad command\nproxy_headers.append(magic + b\"\\x23\\x01\\x00\\x0c\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x00\\x00\")\n\n# Bad family (proxy command with unspecified family)\nproxy_headers.append(magic + b\"\\x21\\x00\\x00\\x0c\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x00\\x00\")\n\n# Short length IPv4\nproxy_headers.append(magic + b\"\\x21\\x11\\x00\\x0b\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x00\\x00\")\n\n# IPv4 with header zero length\nproxy_headers.append(magic + b\"\\x21\\x11\\x00\\x00\" + b\"\\xc0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\")\n\n# Short length IPv6\nproxy_headers.append(magic + b\"\\x21\\x21\\x00\\x0b\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x00\\x00\")\n\n# IPv6 with header zero length\nproxy_headers.append(magic + b\"\\x21\\x21\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x00\\x00\")\n\n# Unix sock with header zero length\nproxy_headers.append(magic + b\"\\x21\\x31\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x00\\x00\")\n\n# Short SSL TLV\nproxy_headers.append(magic + b\"\\x21\\x11\\x00\\x10\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x20\" + b\"\\x00\\x01\" + b\"\\x21\\x00\")\n\n# Too long SSL TLV for overall length\nproxy_headers.append(magic + b\"\\x21\\x11\\x00\\x10\" + b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x19\" \\\n    + b\"\\x05\" \\\n    + b\"\\x00\\x00\\x00\\x00\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\" \\\n    + b\"\\x23\" \\\n    + b\"\\x00\\x08\" \\\n    + b\"\\x70\\x71\\x72\\x73\\x74\\x75\\x76\")\n\n# Too long SSL sub TLV for overall length\nproxy_headers.append(magic + b\"\\x21\\x11\\x00\\x28\" + b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x19\" \\\n    + b\"\\x05\" \\\n    + b\"\\x00\\x00\\x00\\x00\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\" \\\n    + b\"\\x23\" \\\n    + b\"\\x00\\x0A\" \\\n    + b\"\\x70\\x71\\x72\\x73\\x74\\x75\\x76\")\n\ndo_test(proxy_headers)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-ipv4.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nconnect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\nrc = 1\n\nexpect_log = \"New client connected from 192.0.2.5:6275\"\n\ntry:\n    data = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\"\n    sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)\n    mosq_test.do_send_receive(sock, connect_packet, connack_packet, \"connack\")\n    mosq_test.do_ping(sock)\n    sock.close()\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc != 0 or expect_log not in stde.decode('utf-8'):\n        print(stde.decode('utf-8'))\n        rc = 1\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-ipv6.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nconnect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\nrc = 1\n\nexpected_log = \"New client connected from 2001:db8:506:708:900::1:6275 as proxy-test (p5, c0, k42)\"\n\ntry:\n    data = b\"\\x20\\x01\\x0d\\xb8\\x05\\x06\\x07\\x08\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x01\" \\\n        +  b\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\" \\\n        +  b\"\\x18\\x83\" + b\"\\x00\\x00\"\n    sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV6 | PROXY_PROTO_TCP, data)\n    mosq_test.do_send_receive(sock, connect_packet, connack_packet, \"connack\")\n    mosq_test.do_ping(sock)\n    sock.close()\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc != 0 or expected_log not in stde.decode('utf-8'):\n        print(stde.decode('utf-8'))\n        rc = 1\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-local.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n\ndef do_test(header):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = 1\n\n    try:\n        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        sock.settimeout(5)\n        sock.connect((\"localhost\", port))\n        sock.send(header)\n        try:\n            data = sock.recv(10)\n            if len(data) == 0:\n                rc = 0\n        except ConnectionResetError:\n            rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0:\n            print(stde.decode('utf-8'))\n            rc = 1\n            raise ValueError(rc)\n\nproxy_header = b\"\\x0d\\x0a\\x0d\\x0a\\x00\\x0d\\x0a\\x51\\x55\\x49\\x54\\x0a\" + b\"\\x20\\x00\\x00\\x00\"\ndo_test(proxy_header)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-long-tlv.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n\ndef do_test(fam):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    connect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = 1\n\n    expect_log = \"Bad socket read/write on client <unknown>: Invalid input\"\n\n    try:\n        data = b\"a\"*501\n        sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, fam | PROXY_PROTO_TCP, data)\n        try:\n            data = sock.recv(10)\n            if len(data) == 0:\n                rc = 0\n        except ConnectionResetError:\n            rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0 or expect_log not in stde.decode('utf-8'):\n            print(stde.decode('utf-8'))\n            rc = 1\n\ndo_test(PROXY_FAM_IPV4)\ndo_test(PROXY_FAM_IPV6)\ndo_test(PROXY_FAM_UNIX)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-lost-connection.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n\nconnect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\ndef do_test(header):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = 1\n\n    try:\n        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        sock.settimeout(5)\n        sock.connect((\"localhost\", port))\n        sock.send(header)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0:\n            print(stde.decode('utf-8'))\n            rc = 1\n            raise ValueError(rc)\n\n# This test essentially should never fail, but it covers a code path that is\n# not covered by the other tests and should be used with valgrind/sanitisers\n\n# Not enough data, IPv4\nproxy_header = b\"\\x0d\\x0a\\x0d\\x0a\\x00\\x0d\\x0a\\x51\\x55\\x49\\x54\\x0a\" + b\"\\x21\\x11\\x00\\x0f\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x00\\x00\" + b\"\\x00\\x00\"\ndo_test(proxy_header)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-ssl-cipher.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n\ndef do_test(data):\n    expect_log = \"Connection from 192.0.2.5:6275 negotiated TLSv1.3 cipher pqrstuv\"\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    connect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5, username=\"none\", password=\"pw\")\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    rc = 1\n\n    try:\n        sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)\n        mosq_test.do_send_receive(sock, connect_packet, connack_packet, \"connack\")\n        mosq_test.do_ping(sock)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0 or expect_log not in stde.decode('utf-8'):\n            print(stde.decode('utf-8'))\n            rc = 1\n            raise ValueError(rc)\n\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x19\" \\\n    + b\"\\x05\" \\\n    + b\"\\x00\\x00\\x00\\x00\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\" \\\n    + b\"\\x23\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x70\\x71\\x72\\x73\\x74\\x75\\x76\"\ndo_test(data)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-ssl-common-name-failure.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n        f.write(\"require_certificate true\\n\")\n        f.write(\"use_identity_as_username true\\n\")\n\ndef do_test(data, expect_log):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    connect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=134, proto_ver=5)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = 1\n\n    try:\n        sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)\n        sock.send(connect_packet)\n        try:\n            mosq_test.expect_packet(sock, \"connack\", connack_packet)\n            data = sock.recv(10)\n            if len(data) == 0:\n                rc = 0\n        except (BrokenPipeError, ConnectionResetError):\n            rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0 or expect_log not in stde.decode('utf-8'):\n            print(stde.decode('utf-8'))\n            print(expect_log)\n            rc = 1\n            raise ValueError(rc)\n\n# No SSL at all\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\"\nexpect_log = \"rejected, client did not provide a certificate.\"\ndo_test(data, expect_log)\n\n# SSL but no certificate at all - this should fail\n# IP, IP, port, port, SSL-tlv, length, client, verify, SSL-version sub-tlv, length, value\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x0F\" \\\n    + b\"\\x01\" \\\n    + b\"\\x00\\x00\\x00\\x01\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\"\nexpect_log = \"rejected, client did not provide a certificate.\"\ndo_test(data, expect_log)\n\n# SSL, verify 0 but no certificate presented this session\n# IP, IP, port, port, SSL-tlv, length, client, verify, SSL-version sub-tlv, length, value\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x0F\" \\\n    + b\"\\x01\" \\\n    + b\"\\x00\\x00\\x00\\x00\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\"\nexpect_log = \"rejected, client did not provide a certificate.\"\ndo_test(data, expect_log)\n\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x0F\" \\\n    + b\"\\x05\" \\\n    + b\"\\x00\\x00\\x00\\x00\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\"\nexpect_log = \"Client proxy-test [192.0.2.5:6275] disconnected: not authorised.\"\ndo_test(data, expect_log)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-ssl-common-name-success.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n        f.write(\"require_certificate true\\n\")\n        f.write(\"use_identity_as_username true\\n\")\n\ndef do_test(data):\n    expect_log = \"New client connected from 192.0.2.5:6275 as proxy-test (p5, c0, k42, u'ppppppp').\"\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    connect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5, username=\"none\", password=\"pw\")\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    rc = 1\n\n    try:\n        sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)\n        mosq_test.do_send_receive(sock, connect_packet, connack_packet, \"connack\")\n        mosq_test.do_ping(sock)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0 or expect_log not in stde.decode('utf-8'):\n            print(stde.decode('utf-8'))\n            rc = 1\n            raise ValueError(rc)\n\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x19\" \\\n    + b\"\\x05\" \\\n    + b\"\\x00\\x00\\x00\\x00\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\" \\\n    + b\"\\x22\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x70\\x70\\x70\\x70\\x70\\x70\\x70\"\ndo_test(data)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-ssl-require-cert-failure.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n        f.write(\"require_certificate true\\n\")\n\ndef do_test(data):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = 1\n\n    expect_log = \"Connection from 192.0.2.5:6275 rejected, client did not provide a certificate.\"\n    try:\n        sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)\n        try:\n            data = sock.recv(10)\n            if len(data) == 0:\n                rc = 0\n        except ConnectionResetError:\n            rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0 or expect_log not in stde.decode('utf-8'):\n            print(stde.decode('utf-8'))\n            rc = 1\n            raise ValueError(rc)\n\n# No SSL at all\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\"\ndo_test(data)\n\n# SSL but no certificate at all - this should fail\n# IP, IP, port, port, SSL-tlv, length, client, verify, SSL-version sub-tlv, length, value\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x0F\" \\\n    + b\"\\x01\" \\\n    + b\"\\x00\\x00\\x00\\x01\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\"\ndo_test(data)\n\n# SSL, verify 0 but no certificate presented this session\n# IP, IP, port, port, SSL-tlv, length, client, verify, SSL-version sub-tlv, length, value\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x0F\" \\\n    + b\"\\x01\" \\\n    + b\"\\x00\\x00\\x00\\x00\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\"\ndo_test(data)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-ssl-require-cert-success.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n        f.write(\"require_certificate true\\n\")\n\ndef do_test(data):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    connect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    rc = 1\n\n    expect_log = \"New client connected from 192.0.2.5:6275 as proxy-test (p5, c0, k42).\"\n    try:\n        sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)\n        mosq_test.do_send_receive(sock, connect_packet, connack_packet, \"connack\")\n        mosq_test.do_ping(sock)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0 or expect_log not in stde.decode('utf-8'):\n            print(stde.decode('utf-8'))\n            rc = 1\n            raise ValueError(rc)\n\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x02\" \\\n    + b\"\\x00\\x05\" \\\n    + b\"\\x70\\x71\\x72\\x73\\x74\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x0F\" \\\n    + b\"\\x05\" \\\n    + b\"\\x00\\x00\\x00\\x00\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\"\ndo_test(data)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-ssl-require-tls-failure.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n        f.write(\"proxy_protocol_v2_require_tls true\\n\")\n\ndef do_test(data):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    rc = 1\n\n    expect_log = \"Connection from 192.0.2.5:6275 rejected, client did not connect using TLS.\"\n    try:\n        sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)\n        try:\n            data = sock.recv(10)\n            if len(data) == 0:\n                rc = 0\n        except ConnectionResetError:\n            rc = 0\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0 or expect_log not in stde.decode('utf-8'):\n            print(stde.decode('utf-8'))\n            rc = 1\n            raise ValueError(rc)\n\n# No SSL at all\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\"\ndo_test(data)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-ssl-require-tls-success.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n        f.write(\"proxy_protocol_v2_require_tls true\\n\")\n\ndef do_test(data):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config(conf_file, port)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    connect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    rc = 1\n\n    expect_log = \"New client connected from 192.0.2.5:6275 as proxy-test (p5, c0, k42).\"\n    try:\n        sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)\n        mosq_test.do_send_receive(sock, connect_packet, connack_packet, \"connack\")\n        mosq_test.do_ping(sock)\n        sock.close()\n        rc = 0\n    except mosq_test.TestError:\n        pass\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc != 0 or expect_log not in stde.decode('utf-8'):\n            print(stde.decode('utf-8'))\n            rc = 1\n            raise ValueError(rc)\n\ndata = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\" \\\n    + b\"\\x02\" \\\n    + b\"\\x00\\x05\" \\\n    + b\"\\x70\\x71\\x72\\x73\\x74\" \\\n    + b\"\\x20\" \\\n    + b\"\\x00\\x0F\" \\\n    + b\"\\x05\" \\\n    + b\"\\x00\\x00\\x00\\x00\" \\\n    + b\"\\x21\" \\\n    + b\"\\x00\\x07\" \\\n    + b\"\\x54\\x4C\\x53\\x76\\x31\\x2E\\x33\"\ndo_test(data)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-unix.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nconnect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\nrc = 1\n\nexpected_log = \"New client connected from /path:0 as proxy-test (p5, c0, k42).\"\n\ntry:\n    data = b\"/path\"\n    sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_UNIX | PROXY_PROTO_TCP, data)\n    mosq_test.do_send_receive(sock, connect_packet, connack_packet, \"connack\")\n    mosq_test.do_ping(sock)\n    sock.close()\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc != 0 or expected_log not in stde.decode('utf-8'):\n        print(stde.decode('utf-8'))\n        rc = 1\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/21-proxy-v2-websockets.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nfrom proxy_helper import *\nimport json\nimport shutil\nimport socket\n\ndef write_config(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"log_type all\\n\")\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"enable_proxy_protocol 2\\n\")\n        f.write(\"protocol websockets\\n\")\n\nport = mosq_test.get_port()\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, port)\n\nconnect_packet = mosq_test.gen_connect(\"proxy-test\", keepalive=42, clean_session=False, proto_ver=5)\nconnack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\nrc = 1\n\nexpect_log = \"New client connected from 192.0.2.5:6275\"\n\ntry:\n    data = b\"\\xC0\\x00\\x02\\x05\" + b\"\\x00\\x00\\x00\\x00\" + b\"\\x18\\x83\" + b\"\\x00\\x00\"\n    sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)\n    websocket_req_good = b\"GET /mqtt HTTP/1.1\\r\\n\" \\\n        + b\"Host: localhost\\r\\n\" \\\n        + b\"Upgrade: websocket\\r\\n\" \\\n        + b\"Connection: Upgrade\\r\\n\" \\\n        + B\"Sec-WebSocket-Key: 1JaITHdgDZVd/4OE2AzTTA==\\r\\n\" \\\n        + b\"Sec-WebSocket-Protocol: mqtt\\r\\n\" \\\n        + b\"Sec-WebSocket-Version: 13\\r\\n\" \\\n        + b\"Origin: example.org\\r\\n\" \\\n        + b\"\\r\\n\"\n\n    websocket_resp_good = b\"HTTP/1.1 101 Switching Protocols\\r\\n\" \\\n        + b\"Upgrade: WebSocket\\r\\n\" \\\n        + b\"Connection: Upgrade\\r\\n\" \\\n        + b\"Sec-WebSocket-Accept: Ako91O0lxiq8gN0+b9YCijMx8lk=\\r\\n\" \\\n        + b\"Sec-WebSocket-Protocol: mqtt\\r\\n\" \\\n        + b\"\\r\\n\"\n\n    connect_frame = bytearray()\n    length = len(connect_packet)\n    mask_key = bytearray(os.urandom(4))\n    connect_frame.append(0x82) # FIN + opcode\n    connect_frame.append(0x80 | length)\n    connect_frame.extend(mask_key)\n    for i in range(len(connect_packet)):\n        connect_frame.append(connect_packet[i] ^ mask_key[i % 4])\n\n    connack_frame = bytearray()\n    length = len(connack_packet)\n    connack_frame.append(0x82) # FIN + opcode\n    connack_frame.append(length)\n    for i in range(len(connack_packet)):\n        connack_frame.append(connack_packet[i])\n    connack_frame = bytes(connack_frame)\n\n    mosq_test.do_send_receive(sock, websocket_req_good, websocket_resp_good, \"websocket handshake\")\n    mosq_test.do_send_receive(sock, connect_frame, connack_frame, \"connack\")\n    sock.close()\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc != 0 or expect_log not in stde.decode('utf-8'):\n        print(stde.decode('utf-8'))\n        rc = 1\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/22-http-api-acl.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport http.client\nimport json\nimport re\n\ndef write_config(filename, mqtt_port, http_port):\n    with open(filename, 'w') as f:\n        f.write(f\"allow_anonymous true\\n\")\n        f.write(f\"listener {mqtt_port}\\n\")\n\n        f.write(f\"listener {http_port}\\n\")\n        f.write(\"protocol http_api\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/acl-file/mosquitto_acl_file.so\\n\")\n        f.write(f\"plugin_opt_acl_file {http_port}.acl\\n\")\n\nmqtt_port, http_port = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, mqtt_port, http_port)\n\nwith open(f\"{http_port}.acl\", \"wt\") as f:\n    f.write(\"topic read /api/v1/version\")\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=mqtt_port)\n\nrc = 1\n\ntry:\n    http_conn = http.client.HTTPConnection(f\"localhost:{http_port}\")\n\n    # systree API\n    http_conn.request(\"GET\", \"/api/v1/systree\")\n    response = http_conn.getresponse()\n    if response.status != 401:\n        raise ValueError(f\"/api/v1/systree {response.status}\")\n    payload = response.read().decode('utf-8')\n    if payload != \"Not authorised\\n\":\n        raise ValueError(f\"Error: {payload}\")\n\n    # Version API\n    http_conn.request(\"GET\", \"/api/v1/version\")\n    response = http_conn.getresponse()\n    if response.status != 200:\n        raise ValueError(f\"Error: /api/v1/version {response.status}\")\n    payload = response.read().decode('utf-8')\n    if not re.match(r'^\\d+\\.\\d+\\.\\d+.*$', payload):\n        raise ValueError(f\"Error: /api/v1/version\\n{payload}\")\n\n\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nexcept Exception as e:\n    print(e)\nfinally:\n    os.remove(conf_file)\n    os.remove(f\"{http_port}.acl\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc != 0:\n        print(stde.decode('utf-8'))\n        rc = 1\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/22-http-api-api.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport http.client\nimport json\nimport re\n\ndef write_config(filename, mqtt_port, ws_port, http_port):\n    with open(filename, 'w') as f:\n        f.write(f\"allow_anonymous true\\n\")\n        f.write(f\"listener {mqtt_port}\\n\")\n\n        f.write(f\"listener 0 {mqtt_port}.sock\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n\n        f.write(f\"listener {ws_port}\\n\")\n        f.write(\"protocol websockets\\n\")\n\n        f.write(f\"listener {http_port}\\n\")\n        f.write(\"protocol http_api\\n\")\n\nmqtt_port, ws_port, http_port = mosq_test.get_port(3)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, mqtt_port, ws_port, http_port)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=mqtt_port)\n\nrc = 1\n\ntry:\n    http_conn = http.client.HTTPConnection(f\"localhost:{http_port}\")\n\n    # Bad request type\n    http_conn.request(\"POST\", \"/api/badrequest\")\n    response = http_conn.getresponse()\n    if response.status != 405:\n        raise ValueError(f\"/api/badrequest {response.status}\")\n\n    # Missing API\n    http_conn.request(\"GET\", \"/api/missing\")\n    response = http_conn.getresponse()\n    if response.status != 404:\n        raise ValueError(f\"/api/missing {response.status}\")\n\n    # Listeners API\n    http_conn.request(\"GET\", \"/api/v1/listeners\")\n    response = http_conn.getresponse()\n    if response.status != 200:\n        raise ValueError(f\"/api/v1/listeners {response.status}\")\n    payload = json.loads(response.read().decode('utf-8'))\n    expected_payload = {\n        \"listeners\": [{\n            \"port\": mqtt_port,\n            \"protocol\": \"mqtt\",\n            \"tls\": False,\n            \"mtls\": False,\n            \"allow_anonymous\": True\n        }, {\n            \"path\": f\"{mqtt_port}.sock\",\n            \"protocol\": \"mqtt\",\n            \"tls\": True,\n            \"mtls\": False,\n            \"allow_anonymous\": True\n        }, {\n            \"port\": ws_port,\n            \"protocol\": \"websockets\",\n            \"tls\": False,\n            \"mtls\": False,\n            \"allow_anonymous\": True\n        }, {\n            \"port\": http_port,\n            \"protocol\": \"httpapi\",\n            \"tls\": False,\n            \"mtls\": False,\n            \"allow_anonymous\": True\n       }]\n    }\n    if payload != expected_payload:\n        raise ValueError(f\"/api/v1/listeners payload\\n{payload}\\n{expected_payload}\")\n\n    # systree API\n    http_conn.request(\"GET\", \"/api/v1/systree\")\n    response = http_conn.getresponse()\n    if response.status != 200:\n        raise ValueError(f\"/api/v1/systree {response.status}\")\n    payload = json.loads(response.read().decode('utf-8'))\n\n    for topic in [\n            '$SYS/broker/clients/connected',\n            '$SYS/broker/clients/disconnected',\n            '$SYS/broker/clients/maximum',\n            '$SYS/broker/connections/socket/count',\n            '$SYS/broker/heap/current',\n            '$SYS/broker/heap/maximum',\n            '$SYS/broker/messages/stored',\n            '$SYS/broker/retained messages/count',\n            '$SYS/broker/store/messages/bytes',\n            '$SYS/broker/uptime']:\n\n        # Protect against values being slightly different by\n        # setting to a known value\n        # This read will fail if the key doesn't already exist\n        if payload[topic] >= 0:\n            payload[topic] = -1\n\n\n    expected_payload = {\n        '$SYS/broker/clients/total': 0,\n        '$SYS/broker/clients/maximum': -1,\n        '$SYS/broker/clients/disconnected': -1,\n        '$SYS/broker/clients/connected': -1,\n        '$SYS/broker/clients/expired': 0,\n        '$SYS/broker/messages/stored': -1,\n        '$SYS/broker/store/messages/bytes': -1,\n        '$SYS/broker/subscriptions/count': 0,\n        '$SYS/broker/shared_subscriptions/count': 0,\n        '$SYS/broker/retained messages/count': -1,\n        '$SYS/broker/heap/current': -1,\n        '$SYS/broker/heap/maximum': -1,\n        '$SYS/broker/messages/received': 0,\n        '$SYS/broker/messages/sent': 0,\n        '$SYS/broker/bytes/received': 0,\n        '$SYS/broker/bytes/sent': 0,\n        '$SYS/broker/publish/bytes/received': 0,\n        '$SYS/broker/publish/bytes/sent': 0,\n        '$SYS/broker/packet/out/count': 0,\n        '$SYS/broker/packet/out/bytes': 0,\n        '$SYS/broker/connections/socket/count': -1,\n        '$SYS/broker/publish/messages/dropped': 0,\n        '$SYS/broker/publish/messages/received': 0,\n        '$SYS/broker/publish/messages/sent': 0,\n        '$SYS/broker/uptime': -1\n    }\n    if payload != expected_payload:\n        raise ValueError(f\"/api/v1/systree payload\\n{payload}\\n{expected_payload}\")\n\n    # Version API\n    http_conn.request(\"GET\", \"/api/v1/version\")\n    response = http_conn.getresponse()\n    if response.status != 200:\n        raise ValueError(f\"Error: /api/v1/version {response.status}\")\n    payload = response.read().decode('utf-8')\n    if not re.match(r'^\\d+\\.\\d+\\.\\d+.*$', payload):\n        raise ValueError(f\"Error: /api/v1/version\\n{payload}\")\n\n\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nexcept Exception as e:\n    print(e)\nfinally:\n    os.remove(conf_file)\n    os.remove(f\"{mqtt_port}.sock\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc != 0:\n        print(stde.decode('utf-8'))\n        rc = 1\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/22-http-api-auth.pwfile",
    "content": "user:$6$Ut1cUS9PG8+gC3vn$tOjCfSJJDe1Alu9HktxxyyzwN4+6mAMSWGRAF9gmMN8pzcGTPVEYYMAZpCEp96Oz2ZRRz5YKM6lPMf1tUbb6zA==\n"
  },
  {
    "path": "test/broker/22-http-api-auth.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport base64\nimport http.client\nimport json\nimport re\n\ndef write_config(filename, mqtt_port, http_port):\n    with open(filename, 'w') as f:\n        f.write(f\"listener {mqtt_port}\\n\")\n\n        f.write(f\"listener {http_port}\\n\")\n        f.write(\"protocol http_api\\n\")\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/password-file/mosquitto_password_file.so\\n\")\n        f.write(\"plugin_opt_password_file %s/%s\\n\" % (Path(__file__).resolve().parent, filename.replace('.conf', '.pwfile')))\n\nmqtt_port, http_port = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, mqtt_port, http_port)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=mqtt_port)\n\nrc = 1\n\ntry:\n    http_conn = http.client.HTTPConnection(f\"localhost:{http_port}\")\n\n    # No auth\n    http_conn.request(\"GET\", \"/api/v1/version\")\n    response = http_conn.getresponse()\n    if response.status != 401:\n        raise ValueError(f\"Error: /api/v1/version {response.status}\")\n    payload = response.read().decode('utf-8')\n    if payload != \"Not authorised\\n\":\n        raise ValueError(f\"Error: {payload}\")\n\n    # Bad auth\n    credentials = \"user:invalid\"\n    encoded_credentials = base64.b64encode(credentials.encode()).decode()\n    headers = {\n        \"Authorization\": f\"Basic {encoded_credentials}\"\n    }\n    http_conn.request(\"GET\", \"/api/v1/version\", headers=headers)\n    response = http_conn.getresponse()\n    if response.status != 401:\n        raise ValueError(f\"Error: /api/v1/version {response.status}\")\n    payload = response.read().decode('utf-8')\n    if payload != \"Not authorised\\n\":\n        raise ValueError(f\"Error: {payload}\")\n\n    # Good auth\n    credentials = \"user:password\"\n    encoded_credentials = base64.b64encode(credentials.encode()).decode()\n    headers = {\n        \"Authorization\": f\"Basic {encoded_credentials}\"\n    }\n    http_conn.request(\"GET\", \"/api/v1/version\", headers=headers)\n    response = http_conn.getresponse()\n    if response.status != 200:\n        raise ValueError(f\"Error: /api/v1/version {response.status}\")\n\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nexcept Exception as e:\n    print(e)\nfinally:\n    os.remove(conf_file)\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc != 0:\n        print(stde.decode('utf-8'))\n        rc = 1\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/22-http-api-file.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport http.client\nimport json\n\ndef write_config(filename, mqtt_port, http_port):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {mqtt_port}\\n\")\n        f.write(f\"listener {http_port} 127.0.0.1\\n\")\n        f.write(\"protocol http_api\\n\")\n        f.write(f\"http_dir ./\\n\")\n\ndef write_index():\n    with open(\"index.html\", 'w') as f:\n        f.write(\"<html></html>\")\n\nmqtt_port, http_port = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, mqtt_port, http_port)\nwrite_index()\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=mqtt_port)\n\nrc = 1\n\ntry:\n    http_conn = http.client.HTTPConnection(f\"localhost:{http_port}\")\n\n    # Bad request\n    http_conn.request(\"POST\", \"/post\")\n    response = http_conn.getresponse()\n    if response.status != 405:\n        raise ValueError(f\"Error: /post {response.status}\")\n\n    # Bad request\n    http_conn.request(\"PUT\", \"/put\")\n    response = http_conn.getresponse()\n    if response.status != 405:\n        raise ValueError(f\"Error: /put {response.status}\")\n\n    # Missing file\n    http_conn.request(\"GET\", \"/missing\")\n    response = http_conn.getresponse()\n    if response.status != 404:\n        raise ValueError(f\"Error: /api/missing {response.status}\")\n\n    # File not in dir\n    http_conn.request(\"GET\", \"../../../../../../../../etc/passwd\")\n    response = http_conn.getresponse()\n    if response.status != 404:\n        raise ValueError(f\"Error: ../../../../../../../../etc/passwd {response.status}\")\n\n    # Present file\n    http_conn.request(\"GET\", \"/index.html\")\n    response = http_conn.getresponse()\n    if response.status != 200:\n        raise ValueError(f\"Error: /index.html {response.status}\")\n\n    # Root\n    http_conn.request(\"GET\", \"/\")\n    response = http_conn.getresponse()\n    if response.status != 200:\n        raise ValueError(f\"Error: / {response.status}\")\n    payload = response.read().decode('utf-8')\n    if payload != \"<html></html>\":\n        raise ValueError(f\"Error: / {payload}\")\n\n\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nexcept Exception as e:\n    print(e)\nfinally:\n    os.remove(conf_file)\n    os.remove(\"index.html\")\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc != 0:\n        print(stde.decode('utf-8'))\n        rc = 1\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/22-http-api-tls.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport http.client\nimport json\nimport ssl\n\ndef write_config(filename, mqtt_port, http_port):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {mqtt_port}\\n\")\n        f.write(f\"listener 0 {mqtt_port}.sock\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n\n        f.write(f\"listener {http_port}\\n\")\n        f.write(\"protocol http_api\\n\")\n        f.write(f\"certfile {ssl_dir}/server.crt\\n\")\n        f.write(f\"keyfile {ssl_dir}/server.key\\n\")\n\nmqtt_port, http_port = mosq_test.get_port(2)\nconf_file = os.path.basename(__file__).replace('.py', '.conf')\nwrite_config(conf_file, mqtt_port, http_port)\n\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=mqtt_port)\n\nrc = 1\n\ntry:\n    context = ssl.create_default_context()\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    context.load_verify_locations(cafile=f\"{ssl_dir}/all-ca.crt\")\n    http_conn = http.client.HTTPSConnection(f\"localhost:{http_port}\", context=context)\n\n    # Bad request type\n    http_conn.request(\"POST\", \"/api/badrequest\")\n    response = http_conn.getresponse()\n    if response.status != 405:\n        raise ValueError(f\"Error: /api/badrequest {response.status}\")\n\n    # Missing API\n    http_conn.request(\"GET\", \"/api/missing\")\n    response = http_conn.getresponse()\n    if response.status != 404:\n        raise ValueError(f\"Error: /api/missing {response.status}\")\n\n    # Listeners API\n    http_conn.request(\"GET\", \"/api/v1/listeners\")\n    response = http_conn.getresponse()\n    if response.status != 200:\n        raise ValueError(f\"Error: /api/v1/listeners {response.status}\")\n    payload = json.loads(response.read().decode('utf-8'))\n    expected_payload = {\n        \"listeners\": [{\n            \"port\": mqtt_port,\n            \"protocol\": \"mqtt\",\n            \"tls\": False,\n            \"mtls\": False,\n            \"allow_anonymous\": True\n        }, {\n            \"path\": f\"{mqtt_port}.sock\",\n            \"protocol\": \"mqtt\",\n            \"tls\": True,\n            \"mtls\": False,\n            \"allow_anonymous\": True\n        }, {\n            \"port\": http_port,\n            \"protocol\": \"httpapi\",\n            \"tls\": True,\n            \"mtls\": False,\n            \"allow_anonymous\": True\n       }]\n    }\n    if payload != expected_payload:\n        raise ValueError(f\"Error: /api/v1/listeners payload {payload}\")\n\n    # systree API\n    http_conn.request(\"GET\", \"/api/v1/systree\")\n    response = http_conn.getresponse()\n    if response.status != 200:\n        raise ValueError(f\"Error: /api/v1/systree {response.status}\")\n    payload = json.loads(response.read().decode('utf-8'))\n\n    for topic in [\n            '$SYS/broker/clients/maximum',\n            '$SYS/broker/connections/socket/count',\n            '$SYS/broker/heap/current',\n            '$SYS/broker/heap/maximum',\n            '$SYS/broker/messages/received',\n            '$SYS/broker/bytes/received',\n            '$SYS/broker/messages/stored',\n            '$SYS/broker/retained messages/count',\n            '$SYS/broker/store/messages/bytes',\n            '$SYS/broker/uptime']:\n\n        # Protect against values being slightly different by\n        # setting to a known value\n        # This read will fail if the key doesn't already exist\n        if payload[topic] >= 0:\n            payload[topic] = -1\n\n\n    expected_payload = {\n        '$SYS/broker/clients/total': 0,\n        '$SYS/broker/clients/maximum': -1,\n        '$SYS/broker/clients/disconnected': 0,\n        '$SYS/broker/clients/connected': 0,\n        '$SYS/broker/clients/expired': 0,\n        '$SYS/broker/messages/stored': -1,\n        '$SYS/broker/store/messages/bytes': -1,\n        '$SYS/broker/subscriptions/count': 0,\n        '$SYS/broker/shared_subscriptions/count': 0,\n        '$SYS/broker/retained messages/count': -1,\n        '$SYS/broker/heap/current': -1,\n        '$SYS/broker/heap/maximum': -1,\n        '$SYS/broker/messages/received': -1,\n        '$SYS/broker/messages/sent': 0,\n        '$SYS/broker/bytes/received': -1,\n        '$SYS/broker/bytes/sent': 0,\n        '$SYS/broker/publish/bytes/received': 0,\n        '$SYS/broker/publish/bytes/sent': 0,\n        '$SYS/broker/packet/out/count': 0,\n        '$SYS/broker/packet/out/bytes': 0,\n        '$SYS/broker/connections/socket/count': -1,\n        '$SYS/broker/publish/messages/dropped': 0,\n        '$SYS/broker/publish/messages/received': 0,\n        '$SYS/broker/publish/messages/sent': 0,\n        '$SYS/broker/uptime': -1\n    }\n    if payload != expected_payload:\n        raise ValueError(f\"Error: /api/v1/systree payload\\n{payload}\\n{expected_payload}\")\n\n    rc = 0\nexcept mosq_test.TestError:\n    pass\nexcept Exception as e:\n    print(e)\nfinally:\n    os.remove(conf_file)\n    try:\n        os.remove(f\"{mqtt_port}.sock\")\n    except FileNotFoundError:\n        pass\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc != 0:\n        print(stde.decode('utf-8'))\n        rc = 1\n\n\nexit(rc)\n"
  },
  {
    "path": "test/broker/23-security-acl-file-reload.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config_default(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(f\"acl_file {port}.acl\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef write_config_plugin(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/acl-file/mosquitto_acl_file.so\\n\")\n        f.write(f\"plugin_opt_acl_file {port}.acl\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef do_test(write_config_func):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config_func(conf_file, port)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"acl-change-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    with open(f\"{port}.acl\", \"wt\") as f:\n        f.write(\"topic readwrite a/#\")\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n\n        with open(f\"{port}.acl\", \"wt\") as f:\n            f.write(\"topic readwrite a#\")\n\n        broker.send_signal(signal.SIGHUP)\n        # Broker should terminate\n        if mosq_test.wait_for_subprocess(broker) == 0 and broker.returncode == 3:\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    except Exception as err:\n        print(err)\n    finally:\n        os.remove(conf_file)\n        os.remove(f\"{port}.acl\")\n        broker.terminate()\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(write_config_default)\ndo_test(write_config_plugin)\n"
  },
  {
    "path": "test/broker/23-security-password-file-reload.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport signal\n\ndef write_config_default(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(f\"password_file {port}.password\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef write_config_plugin(filename, port):\n    with open(filename, 'w') as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(f\"plugin {mosq_test.get_build_root()}/plugins/password-file/mosquitto_password_file.so\\n\")\n        f.write(f\"plugin_opt_password_file {port}.password\\n\")\n        f.write(\"allow_anonymous true\\n\")\n\n\ndef do_test(write_config_func):\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n    write_config_func(conf_file, port)\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"password-change-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    with open(f\"{port}.password\", \"wt\") as f:\n        f.write(\"test:$7$1000$97ozvObcN5zP4MGzYUw4uRp8+mPQbThrHOX69vdHHNVwV4iZf2K2X23FS7weilZMKeV+9oLHdilybmpXcFApYg==$WlM0jUhsiQNQJe4IDt5K1rmtAdaenWGdntswJmDkp74W9pdrt/+RdIK3YaJ09o3pD1xbtokXq933bQh+CrjA4Q==\\n\")\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)\n        sock.close()\n\n        with open(f\"{port}.password\", \"wt\") as f:\n            f.write(\"test:bad\\n\")\n\n        broker.send_signal(signal.SIGHUP)\n        # Broker should terminate\n        if mosq_test.wait_for_subprocess(broker) == 0 and broker.returncode == 3:\n            rc = 0\n    except mosq_test.TestError:\n        pass\n    except Exception as err:\n        print(err)\n    finally:\n        os.remove(conf_file)\n        os.remove(f\"{port}.password\")\n        broker.terminate()\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\ndo_test(write_config_default)\ndo_test(write_config_plugin)\n"
  },
  {
    "path": "test/broker/CMakeLists.txt",
    "content": "add_subdirectory(c)\n\nfile(GLOB PY_TEST_FILES [0-9][0-9]-*.py)\nfile(GLOB PY_PERSIST_TEST_FILES 15-*.py)\nfile(GLOB PY_HTTP_API_TEST_FILES 22-*.py)\n\nlist(APPEND PY_TEST_FILES \"${CMAKE_CURRENT_SOURCE_DIR}/msg_sequence_test.py\")\n\nset(PERSIST_LIST\n    persist_sqlite\n)\nset(EXCLUDE_LIST\n    01-connect-uname-password-success-no-tls\n    03-publish-qos1-queued-bytes\n    03-publish-qos2-max-inflight-exceeded\n    09-extended-auth-single2\n    # Not a test\n    06-bridge-clean-session-core\n    08-ssl-bridge-helper\n)\n\nif(NOT MICROHTTPD_LIBRARY)\n\tforeach(PY_HTTP_API_TEST_FILE ${PY_HTTP_API_TEST_FILES})\n\t\tget_filename_component(PY_HTTP_API_TEST_NAME ${PY_HTTP_API_TEST_FILE} NAME_WE)\n\t\tlist(APPEND EXCLUDE_LIST ${PY_HTTP_API_TEST_NAME})\n\tendforeach()\nendif()\n\nforeach(PY_PERSIST_TEST_FILE ${PY_PERSIST_TEST_FILES})\n    get_filename_component(PY_PERSIST_TEST_NAME ${PY_PERSIST_TEST_FILE} NAME_WE)\n    list(APPEND EXCLUDE_LIST ${PY_PERSIST_TEST_NAME})\nendforeach()\n\nforeach(PY_TEST_FILE ${PY_TEST_FILES})\n    get_filename_component(PY_TEST_NAME ${PY_TEST_FILE} NAME_WE)\n    if(${PY_TEST_NAME} IN_LIST EXCLUDE_LIST OR ${PY_TEST_NAME} IN_LIST SQLITE_LIST)\n        continue()\n    endif()\n    add_test(NAME broker-${PY_TEST_NAME}\n        COMMAND ${PY_TEST_FILE}\n    )\n    set_tests_properties(broker-${PY_TEST_NAME}\n        PROPERTIES\n            ENVIRONMENT \"BUILD_ROOT=${CMAKE_BINARY_DIR}\"\n    )\nendforeach()\n\nforeach(PERSIST_TYPE ${PERSIST_LIST})\n    foreach(PY_TEST_FILE ${PY_PERSIST_TEST_FILES})\n        get_filename_component(PY_TEST_NAME ${PY_TEST_FILE} NAME_WE)\n        add_test(NAME broker-${PY_TEST_NAME}-${PERSIST_TYPE}\n            COMMAND ${PY_TEST_FILE} ${PERSIST_TYPE}\n        )\n        set_tests_properties(broker-${PY_TEST_NAME}-${PERSIST_TYPE}\n            PROPERTIES\n                ENVIRONMENT \"BUILD_ROOT=${CMAKE_BINARY_DIR}\"\n        )\n    endforeach()\nendforeach()\n"
  },
  {
    "path": "test/broker/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\n.PHONY: all check clean test ptest seqtest\n.NOTPARALLEL:\n\nall :\n\ncheck : test\n\nclean :\n\t-rm -f *.vglog *.db\n\t$(MAKE) -C c clean\n\ntest-compile :\n\t$(MAKE) -C c\n\nptest : test-compile\n\t./test.py\n\ntest : test-compile msg_sequence_test 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 20 21 22 23\n\nmsg_sequence_test:\n\t./msg_sequence_test.py\n\n01 :\n\t./01-bad-initial-packets.py\n\t./01-connect-575314.py\n\t./01-connect-accept-protocol.py\n\t./01-connect-allow-anonymous.py\n\t./01-connect-auto-id.py\n\t./01-connect-disconnect-v5.py\n\t./01-connect-global-max-clients.py\n\t./01-connect-global-max-connections.py\n\t./01-connect-listener-allow-anonymous.py\n\t./01-connect-max-connections.py\n\t./01-connect-max-keepalive.py\n\t./01-connect-take-over.py\n\t./01-connect-uname-no-password-denied.py\n\t./01-connect-uname-or-anon.py\n\t./01-connect-uname-password-denied-no-will.py\n\t./01-connect-uname-password-denied.py\n\t./01-connect-unix-socket.py\n\t./01-connect-windows-line-endings.py\n\t./01-connect-zero-length-id.py\n\t./01-plugin-connect-uname-password-denied.py\n\n\n02 :\n\t./02-shared-nolocal.py\n\t./02-shared-qos0-v5.py\n\t./02-subhier-crash.py\n\t./02-subpub-b2c-topic-alias.py\n\t./02-subpub-qos0-long-topic.py\n\t./02-subpub-qos0-oversize-payload.py\n\t./02-subpub-qos0-queued-bytes.py\n\t./02-subpub-qos0-retain-as-publish.py\n\t./02-subpub-qos0-send-retain.py\n\t./02-subpub-qos0-subscription-id.py\n\t./02-subpub-qos0-topic-alias-unknown.py\n\t./02-subpub-qos0-topic-alias.py\n\t./02-subpub-qos1-message-expiry-retain.py\n\t./02-subpub-qos1-message-expiry-will.py\n\t./02-subpub-qos1-message-expiry.py\n\t./02-subpub-qos1-nolocal.py\n\t./02-subpub-qos1-oversize-payload.py\n\t./02-subpub-qos1.py\n\t./02-subpub-qos2-1322.py\n\t./02-subpub-qos2-max-inflight-bytes.py\n\t./02-subpub-qos2-pubrec-error.py\n\t./02-subpub-qos2-receive-maximum-1.py\n\t./02-subpub-qos2-receive-maximum-2.py\n\t./02-subpub-qos2.py\n\t./02-subpub-recover-subscriptions.py\n\t./02-subscribe-dollar-v5.py\n\t./02-subscribe-invalid-utf8.py\n\t./02-subscribe-long-topic.py\n\t./02-subscribe-persistence-flipflop.py\n\n03 :\n\t#./03-publish-qos1-queued-bytes.py\n\t./03-pattern-matching.py\n\t./03-publish-bad-flags.py\n\t./03-publish-b2c-disconnect-qos1.py\n\t./03-publish-b2c-disconnect-qos2.py\n\t./03-publish-b2c-qos1-len.py\n\t./03-publish-b2c-qos2-len.py\n\t./03-publish-c2b-disconnect-qos2.py\n\t./03-publish-c2b-qos2-len.py\n\t./03-publish-dollar-v5.py\n\t./03-publish-dollar.py\n\t./03-publish-invalid-utf8.py\n\t./03-publish-long-topic.py\n\t./03-publish-qos1-max-inflight-expire.py\n\t./03-publish-qos1-no-subscribers-v5.py\n\t./03-publish-qos1-retain-disabled.py\n\t./03-publish-qos1.py\n\t./03-publish-qos2-dup.py\n\t#./03-publish-qos2-max-inflight-exceeded.py\n\t./03-publish-qos2-max-inflight.py\n\t./03-publish-qos2-reuse-mid.py\n\t./03-publish-qos2.py\n\n04 :\n\t./04-retain-check-source-persist-diff-port.py\n\t./04-retain-check-source-persist.py\n\t./04-retain-check-source.py\n\t./04-retain-clear-multiple.py\n\t./04-retain-qos0-clear.py\n\t./04-retain-qos0-fresh.py\n\t./04-retain-qos0-repeated.py\n\t./04-retain-qos0.py\n\t./04-retain-qos1-qos0.py\n\t./04-retain-upgrade-outgoing-qos.py\n\n05 :\n\t./05-clean-session-qos1.py\n\t./05-session-expiry-v5.py\n\t./05-session-expiry-kick.py\n\n06 :\n\t./06-bridge-b2br-disconnect-qos1.py\n\t./06-bridge-b2br-disconnect-qos2.py\n\t./06-bridge-b2br-late-connection-retain.py\n\t./06-bridge-b2br-late-connection.py\n\t./06-bridge-b2br-remapping.py\n\t./06-bridge-br2b-disconnect-qos1.py\n\t./06-bridge-br2b-disconnect-qos2.py\n\t./06-bridge-br2b-remapping.py\n\t./06-bridge-clean-session-csF-lcsF.py\n\t./06-bridge-clean-session-csF-lcsN.py\n\t./06-bridge-clean-session-csF-lcsT.py\n\t./06-bridge-clean-session-csT-lcsF.py\n\t./06-bridge-clean-session-csT-lcsN.py\n\t./06-bridge-clean-session-csT-lcsT.py\n\t./06-bridge-fail-persist-resend-qos1.py\n\t./06-bridge-fail-persist-resend-qos2.py\n\t./06-bridge-no-local.py\n\t./06-bridge-outgoing-retain.py\n\t./06-bridge-per-listener-settings.py\n\t./06-bridge-reconnect-local-out.py\n\t./06-bridge-remote-shutdown.py\n\t./06-bridge-config-reload.py\n\t./06-bridge-remap-receive-wildcard.py\n\n07 :\n\t./07-will-control.py\n\t./07-will-delay-invalid-573191.py\n\t./07-will-delay-reconnect.py\n\t./07-will-delay-recover.py\n\t./07-will-delay-session-expiry-0.py\n\t./07-will-delay-session-expiry.py\n\t./07-will-delay-session-expiry2.py\n\t./07-will-delay.py\n\t./07-will-disconnect-with-will.py\n\t./07-will-invalid-utf8.py\n\t./07-will-no-flag.py\n\t./07-will-null-topic.py\n\t./07-will-null.py\n\t./07-will-oversize-payload.py\n\t./07-will-per-listener.py\n\t./07-will-properties.py\n\t./07-will-qos0.py\n\t./07-will-reconnect-1273.py\n\t./07-will-takeover.py\n\n08 :\nifeq ($(WITH_TLS),yes)\n\t./08-ssl-bridge.py\n\t./08-ssl-connect-cert-auth-crl.py\n\t./08-ssl-connect-cert-auth-expired-allowed.py\n\t./08-ssl-connect-cert-auth-expired.py\n\t./08-ssl-connect-cert-auth-revoked.py\n\t./08-ssl-connect-cert-auth-without.py\n\t./08-ssl-connect-cert-auth.py\n\t./08-ssl-connect-dhparam.py\n\t./08-ssl-connect-identity.py\n\t./08-ssl-connect-no-auth-wrong-ca.py\n\t./08-ssl-connect-no-auth.py\n\t./08-ssl-connect-no-identity.py\n\t./08-ssl-hup-disconnect.py\nifeq ($(WITH_TLS_PSK),yes)\n\t./08-tls-psk-pub.py\n\t./08-tls-psk-bridge.py\nendif\nendif\n\n09 :\n\t./09-acl-access-variants.py\n\t./09-acl-change.py\n\t./09-acl-empty-file.py\n\t./09-auth-bad-method.py\n\t./09-extended-auth-change-username.py\n\t./09-extended-auth-multistep-reauth.py\n\t./09-extended-auth-multistep.py\n\t./09-extended-auth-reauth.py\n\t./09-extended-auth-single.py\n\t./09-plugin-acl-access-variants.py\n\t./09-plugin-acl-change.py\n\t./09-plugin-auth-acl-pub.py\n\t./09-plugin-auth-acl-pub-prop.py\n\t./09-plugin-auth-acl-sub-denied.py\n\t./09-plugin-auth-acl-sub.py\n\t./09-plugin-auth-context-params.py\n\t./09-plugin-auth-defer-unpwd-fail.py\n\t./09-plugin-auth-defer-unpwd-success.py\n\t./09-plugin-auth-msg-params.py\n\t./09-plugin-auth-unpwd-fail.py\n\t./09-plugin-auth-unpwd-success.py\n\t./09-plugin-auth-v2-unpwd-fail.py\n\t./09-plugin-auth-v2-unpwd-success.py\n\t./09-plugin-auth-v3-unpwd-fail.py\n\t./09-plugin-auth-v3-unpwd-success.py\n\t./09-plugin-auth-v4-unpwd-fail.py\n\t./09-plugin-auth-v4-unpwd-success.py\n\t./09-plugin-auth-v5-unpwd-fail.py\n\t./09-plugin-auth-v5-unpwd-success.py\n\t./09-plugin-bad.py\n\t./09-plugin-change-id.py\n\t./09-plugin-delayed-auth.py\n\t./09-plugin-evt-client-offline.py\n\t./09-plugin-evt-message-in.py\n\t./09-plugin-evt-message-out.py\n\t./09-plugin-evt-psk-key.py\n\t./09-plugin-evt-reload.py\n\t./09-plugin-evt-subscribe.py\n\t./09-plugin-evt-tick.py\n\t./09-plugin-evt-unsubscribe.py\n\t./09-plugin-load-acl.py\n\t./09-plugin-load-basic-auth.py\n\t./09-plugin-load-extended-auth.py\n\t./09-plugin-publish.py\n\t./09-plugin-unsupported.py\n\t./09-pwfile-parse-invalid.py\n\n10 :\n\t./10-listener-mount-point.py\n\n11 :\n\t./11-message-expiry.py\n\t./11-persistence-autosave-changes.py\n\t./11-persistent-subscription-no-local.py\n\t./11-persistent-subscription.py\n\t./11-pub-props.py\n\t./11-subscription-id.py\n\n12 :\n\t./12-prop-assigned-client-identifier.py\n\t./12-prop-maximum-packet-size-broker.py\n\t./12-prop-maximum-packet-size-publish-qos1.py\n\t./12-prop-maximum-packet-size-publish-qos2.py\n\t./12-prop-response-topic-correlation-data.py\n\t./12-prop-response-topic.py\n\t./12-prop-server-keepalive.py\n\t./12-prop-subpub-content-type.py\n\t./12-prop-subpub-payload-format.py\n\n13 :\nifneq ($(WITH_WEBSOCKETS),no)\n\t./13-websocket-bad-origin.py\nendif\n\n14 :\nifeq ($(WITH_TLS),yes)\n\t./14-dynsec-acl.py\n\t./14-dynsec-allow-wildcard.py\n\t./14-dynsec-anon-group.py\n\t./14-dynsec-auth.py\n\t./14-dynsec-client-invalid.py\n\t./14-dynsec-client.py\n\t./14-dynsec-config-init-env.py\n\t./14-dynsec-config-init-file.py\n\t./14-dynsec-config-init-random.py\n\t./14-dynsec-default-access.py\n\t./14-dynsec-disable-client.py\n\t./14-dynsec-group-invalid.py\n\t./14-dynsec-group.py\n\t./14-dynsec-modify-client.py\n\t./14-dynsec-modify-group.py\n\t./14-dynsec-modify-role.py\n\t./14-dynsec-plugin-invalid.py\n\t./14-dynsec-role-invalid.py\n\t./14-dynsec-role.py\nendif\n\nPERSIST_TESTS = \\\n\t./15-persist-bridge-queue.py \\\n\t./15-persist-client-drop-expired-messages.py \\\n\t./15-persist-client-expired-session.py \\\n\t./15-persist-client-msg-in-v3-1-1.py \\\n\t./15-persist-client-msg-in-v5-0.py \\\n\t./15-persist-client-msg-modify-acl.py \\\n\t./15-persist-client-msg-out-clear-v3-1-1.py \\\n\t./15-persist-client-msg-out-dup-v3-1-1.py \\\n\t./15-persist-client-msg-out-queue-v3-1-1.py \\\n\t./15-persist-client-msg-out-v3-1-1-db.py \\\n\t./15-persist-client-msg-out-v3-1-1.py \\\n\t./15-persist-client-msg-out-v5-0.py \\\n\t./15-persist-client-v3-1-1.py \\\n\t./15-persist-client-v5-0.py \\\n\t./15-persist-client-will.py \\\n\t./15-persist-publish-properties-v5-0.py \\\n\t./15-persist-retain-clear.py \\\n\t./15-persist-retain-v3-1-1.py \\\n\t./15-persist-retain-v5-0.py \\\n\t./15-persist-subscription-v3-1-1.py \\\n\t./15-persist-subscription-v5-0.py\n\nPERSIST_PLUGINS =\nifeq ($(WITH_SQLITE),yes)\nPERSIST_PLUGINS+=persist_sqlite\nendif\n\n15 :\t$(PERSISTENCE_TESTS)\n\tset -e; for  p in $(PERSIST_PLUGINS); do for t in $(PERSIST_TESTS); do echo $$t $$p; $$t $$p; done; done\nifeq ($(WITH_SQLITE),yes)\n\t./15-persist-migrate-db.py persist_sqlite\nendif\n\n16 :\n\t./16-cmd-args.py\n\t./16-config-huge.py\n\t./16-config-includedir.py\n\t./16-config-missing.py\n\t./16-config-parse-errors-without-tls.py\nifeq ($(WITH_TLS),yes)\n\t./16-config-parse-errors-tls.py\nifeq ($(WITH_TLS_PSK),yes)\n\t./16-config-parse-errors-tls-psk.py\nendif\nendif\n\n17 :\nifneq ($(WITH_WEBSOCKETS),no)\n\t./17-control-list-listeners.py\nendif\n\t./17-control-list-plugins.py\n\t./17-control-missing-endpoint.py\n\n20 :\n\t./20-sparkplug-compliance.py\n\t./20-sparkplug-aware.py\n\n21:\n\t./21-proxy-bad-version.py\n\t./21-proxy-v1-bad.py\n\t./21-proxy-v1-success.py\n\t./21-proxy-v2-bad-config.py\n\t./21-proxy-v2-bad-header.py\n\t./21-proxy-v2-local.py\n\t./21-proxy-v2-ipv4.py\n\t./21-proxy-v2-ipv6.py\n\t./21-proxy-v2-unix.py\nifneq ($(WITH_WEBSOCKETS),no)\n\t./21-proxy-v2-websockets.py\nendif\n\t./21-proxy-v2-long-tlv.py\n\t./21-proxy-v2-lost-connection.py\n\t./21-proxy-v2-ssl-require-cert-failure.py\n\t./21-proxy-v2-ssl-require-cert-success.py\n\t./21-proxy-v2-ssl-common-name-failure.py\n\t./21-proxy-v2-ssl-common-name-success.py\n\t./21-proxy-v2-ssl-cipher.py\n\t./21-proxy-v2-ssl-require-tls-failure.py\n\t./21-proxy-v2-ssl-require-tls-success.py\n\n22:\nifeq ($(WITH_HTTP_API),yes)\n\t./22-http-api-acl.py\n\t./22-http-api-api.py\n\t./22-http-api-auth.py\n\t./22-http-api-file.py\n\t./22-http-api-tls.py\nendif\n\n23:\n\t./23-security-acl-file-reload.py\n"
  },
  {
    "path": "test/broker/c/08-tls-psk-bridge.c",
    "content": "#include <errno.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\nstatic int sent_mid;\n\n\nstatic void on_log(struct mosquitto *mosq, void *obj, int level, const char *str)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)level;\n\n\tprintf(\"%s\\n\", str);\n}\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_publish(mosq, &sent_mid, \"psk/test\", strlen(\"message\"), \"message\", 1, false);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\n\tif(mid == sent_mid){\n\t\tmosquitto_disconnect(mosq);\n\t\trun = 0;\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"08-tls-psk-bridge\", true, NULL);\n\tmosquitto_tls_opts_set(mosq, 1, \"tlsv1\", NULL);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\tmosquitto_log_callback_set(mosq, on_log);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/broker/c/08-tls-psk-pub.c",
    "content": "#include <errno.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#ifndef WIN32\n#  include <signal.h>\n#endif\n\nstatic int run = -1;\nstatic int sent_mid;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_publish(mosq, &sent_mid, \"psk/test\", strlen(\"message\"), \"message\", 0, false);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\n\tif(mid == sent_mid){\n\t\tmosquitto_disconnect(mosq);\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n#ifndef WIN32\n\tsignal(SIGPIPE, SIG_IGN);\n#endif\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"08-tls-psk-pub\", true, NULL);\n\tmosquitto_tls_opts_set(mosq, 1, \"tlsv1\", NULL);\n\trc = mosquitto_tls_psk_set(mosq, \"deadbeef\", \"psk-id\", NULL);\n\tif(rc){\n\t\tmosquitto_destroy(mosq);\n\t\treturn rc;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc){\n\t\tmosquitto_destroy(mosq);\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/broker/c/CMakeLists.txt",
    "content": "set(PLUGINS\n    auth_plugin_acl\n    auth_plugin_acl_change\n    auth_plugin_acl_sub_denied\n    auth_plugin_context_params\n    auth_plugin_delayed\n    auth_plugin_extended_multiple\n    auth_plugin_extended_reauth\n    auth_plugin_extended_single\n    auth_plugin_extended_single2\n    auth_plugin_id_change\n    auth_plugin_msg_params\n    auth_plugin_publish\n    auth_plugin_pwd\n    auth_plugin_v2\n    auth_plugin_v3\n    auth_plugin_v4\n    auth_plugin_v5\n    auth_plugin_v5_control\n    bad_v1\n    bad_v2_1\n    bad_v2_2\n    bad_v2_3\n    bad_v2_4\n    bad_v2_5\n    bad_v2_6\n    bad_v2_7\n    bad_v3_1\n    bad_v3_2\n    bad_v3_3\n    bad_v3_4\n    bad_v3_5\n    bad_v3_6\n    bad_v3_7\n    bad_v4_1\n    bad_v4_2\n    bad_v4_3\n    bad_v4_4\n    bad_v5_1\n    bad_v6\n    bad_vnone_1\n    kick_last_client\n    plugin_control\n    plugin_evt_client_offline\n    plugin_evt_message_in\n    plugin_evt_message_out\n    plugin_evt_persist_client_update\n    plugin_evt_psk_key\n    plugin_evt_reload\n    plugin_evt_subscribe\n    plugin_evt_tick\n    plugin_evt_unsubscribe\n    plugin_load_acl\n    plugin_load_extended_auth\n)\n\nforeach(PLUGIN ${PLUGINS})\n    add_library(${PLUGIN} MODULE\n        ${PLUGIN}.c\n    )\n    set_property(TARGET ${PLUGIN}\n        PROPERTY PREFIX \"\"\n    )\n    target_link_libraries(${PLUGIN} PRIVATE mosquitto)\nendforeach()\n\nset(BINARIES\n    08-tls-psk-pub\n    08-tls-psk-bridge\n)\n\nforeach(BINARY ${BINARIES})\n    add_executable(${BINARY}\n        ${BINARY}.c\n    )\n    set_property(TARGET ${BINARY}\n        PROPERTY SUFFIX .test\n    )\n    target_link_libraries(${BINARY} PRIVATE common-options libmosquitto)\nendforeach()\n"
  },
  {
    "path": "test/broker/c/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all test clean reallyclean\n\nLOCAL_CPPFLAGS+=-I${R}/include\nLOCAL_CFLAGS+=-Wall -Werror\nLOCAL_LDFLAGS+=-fPIC -shared\n\nPLUGIN_SRC = \\\n\tauth_plugin_acl.c \\\n\tauth_plugin_acl_change.c \\\n\tauth_plugin_acl_sub_denied.c \\\n\tauth_plugin_context_params.c \\\n\tauth_plugin_delayed.c \\\n\tauth_plugin_extended_multiple.c \\\n\tauth_plugin_extended_reauth.c \\\n\tauth_plugin_extended_single.c \\\n\tauth_plugin_extended_single2.c \\\n\tauth_plugin_id_change.c \\\n\tauth_plugin_msg_params.c \\\n\tauth_plugin_publish.c \\\n\tauth_plugin_pwd.c \\\n\tauth_plugin_v2.c \\\n\tauth_plugin_v3.c \\\n\tauth_plugin_v4.c \\\n\tauth_plugin_v5.c \\\n\tauth_plugin_v5_control.c \\\n\tbad_vnone_1.c \\\n\tbad_v1.c \\\n\tbad_v2_1.c \\\n\tbad_v2_2.c \\\n\tbad_v2_3.c \\\n\tbad_v2_4.c \\\n\tbad_v2_5.c \\\n\tbad_v2_6.c \\\n\tbad_v2_7.c \\\n\tbad_v3_1.c \\\n\tbad_v3_2.c \\\n\tbad_v3_3.c \\\n\tbad_v3_4.c \\\n\tbad_v3_5.c \\\n\tbad_v3_6.c \\\n\tbad_v3_7.c \\\n\tbad_v4_1.c \\\n\tbad_v4_2.c \\\n\tbad_v4_3.c \\\n\tbad_v4_4.c \\\n\tbad_v5_1.c \\\n\tbad_v6.c \\\n\tkick_last_client.c \\\n\tplugin_control.c \\\n\tplugin_evt_client_offline.c \\\n\tplugin_evt_message_in.c \\\n\tplugin_evt_message_out.c \\\n\tplugin_evt_psk_key.c \\\n\tplugin_evt_reload.c \\\n\tplugin_evt_subscribe.c \\\n\tplugin_evt_tick.c \\\n\tplugin_evt_unsubscribe.c \\\n\tplugin_evt_persist_client_update.c \\\n\tplugin_load_acl.c \\\n\tplugin_load_extended_auth.c\n\nPLUGINS = ${PLUGIN_SRC:.c=.so}\n\nSRC = \\\n\t08-tls-psk-pub.c \\\n\t08-tls-psk-bridge.c\n\nTESTS = ${SRC:.c=.test}\n\n\nall : ${PLUGINS} ${TESTS}\n\n${PLUGINS} : %.so: %.c\n\t$(CC) $(LOCAL_CPPFLAGS) ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} $< -o $@\n\n\n${TESTS} : %.test: %.c\n\t$(CC) $(LOCAL_CPPFLAGS) ${LOCAL_CFLAGS} $< -o $@ ${LIBMOSQ}\n\n\nreallyclean : clean\n\t-rm -f *.orig\n\nclean :\n\trm -f *.so *.test\n"
  },
  {
    "path": "test/broker/c/auth_plugin_acl.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\tconst char *username = mosquitto_client_username(client);\n\n\t(void)user_data;\n\n\tif(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_READ){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_SUBSCRIBE &&!strchr(msg->topic, '#') && !strchr(msg->topic, '+')){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)username;\n\t(void)password;\n\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n\n\nint mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)hint;\n\t(void)identity;\n\t(void)key;\n\t(void)max_key_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n"
  },
  {
    "path": "test/broker/c/auth_plugin_acl_change.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nint mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data);\nint mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\nstatic int login_count = 0;\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL, NULL);\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL);\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_ACL_CHECK){\n\t\tabort();\n\t}\n\n\tif(login_count == 2 && ed->access == MOSQ_ACL_WRITE){\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data)\n{\n\t(void)user_data;\n\t(void)event_data;\n\n\tif(event != MOSQ_EVT_BASIC_AUTH){\n\t\tabort();\n\t}\n\n\tlogin_count++;\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/auth_plugin_acl_sub_denied.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)msg;\n\n\tif(access == MOSQ_ACL_SUBSCRIBE){\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}else{\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)username;\n\t(void)password;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)hint;\n\t(void)identity;\n\t(void)key;\n\t(void)max_key_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n"
  },
  {
    "path": "test/broker/c/auth_plugin_context_params.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\t(void)user_data;\n\t(void)access;\n\t(void)client;\n\t(void)msg;\n\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password)\n{\n\tconst char *tmp;\n\n\t(void)user_data;\n\t(void)username;\n\t(void)password;\n\n\ttmp = mosquitto_client_address(client);\n\tif(!tmp || strcmp(tmp, \"127.0.0.1\")){\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\tif(!mosquitto_client_clean_session(client)){\n\t\tfprintf(stderr, \"mosquitto_auth_unpwd_check clean_session error: %d\\n\", mosquitto_client_clean_session(client));\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\ttmp = mosquitto_client_id(client);\n\tif(!tmp || strcmp(tmp, \"client-params-test\")){\n\t\tfprintf(stderr, \"mosquitto_auth_unpwd_check clientid error: %s\\n\", tmp);\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\tif(mosquitto_client_keepalive(client) != 42){\n\t\tfprintf(stderr, \"mosquitto_auth_unpwd_check keepalive error: %d\\n\", mosquitto_client_keepalive(client));\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\tif(!mosquitto_client_certificate(client)){\n\t\t// FIXME\n\t\t//return MOSQ_ERR_AUTH;\n\t}\n\n\tif(mosquitto_client_protocol(client) != mp_mqtt){\n\t\tfprintf(stderr, \"mosquitto_auth_unpwd_check protocol error: %d\\n\", mosquitto_client_protocol(client));\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\tif(mosquitto_client_sub_count(client)){\n\t\tfprintf(stderr, \"mosquitto_auth_unpwd_check sub_count error: %d\\n\", mosquitto_client_sub_count(client));\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\ttmp = mosquitto_client_username(client);\n\tif(!tmp || strcmp(tmp, \"client-username\")){\n\t\tfprintf(stderr, \"mosquitto_auth_unpwd_check username error: %s\\n\", tmp);\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)hint;\n\t(void)identity;\n\t(void)key;\n\t(void)max_key_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n"
  },
  {
    "path": "test/broker/c/auth_plugin_delayed.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nstatic int tick_callback(int event, void *event_data, void *user_data);\nstatic int unpwd_check_callback(int event, void *event_data, void *user_data);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\nstatic char *username = NULL;\nstatic char *password = NULL;\nstatic char *clientid = NULL;\nstatic int auth_delay = -1;\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_TICK, tick_callback, NULL, NULL);\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, unpwd_check_callback, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tfree(username);\n\tfree(password);\n\tfree(clientid);\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_BASIC_AUTH, unpwd_check_callback, NULL);\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_TICK, tick_callback, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int tick_callback(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_tick *ed = event_data;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_TICK){\n\t\tabort();\n\t}\n\n\tif(auth_delay == 0){\n\t\tif(clientid && username && password\n\t\t\t\t&& !strcmp(username, \"delayed-username\") && !strcmp(password, \"good\")){\n\n\t\t\tmosquitto_complete_basic_auth(clientid, MOSQ_ERR_SUCCESS);\n\t\t}else{\n\t\t\tmosquitto_complete_basic_auth(clientid, MOSQ_ERR_AUTH);\n\t\t}\n\t\tfree(username);\n\t\tfree(password);\n\t\tfree(clientid);\n\t\tusername = NULL;\n\t\tpassword = NULL;\n\t\tclientid = NULL;\n\t}else if(auth_delay > 0){\n\t\tauth_delay--;\n\t}\n\n\t/* fast turn around for quick testing */\n\ted->next_ms = 10;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nstatic int unpwd_check_callback(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\n\t(void)event;\n\t(void)user_data;\n\n\tfree(username);\n\tfree(password);\n\tfree(clientid);\n\n\tif(ed->username){\n\t\tusername = strdup(ed->username);\n\t}\n\tif(ed->password){\n\t\tpassword = strdup(ed->password);\n\t}\n\tclientid = strdup(mosquitto_client_id(ed->client));\n\t/* Delay for arbitrary 10 ticks */\n\tauth_delay = 10;\n\n\treturn MOSQ_ERR_AUTH_DELAYED;\n}\n"
  },
  {
    "path": "test/broker/c/auth_plugin_extended_multiple.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\t(void)user_data;\n\t(void)access;\n\t(void)client;\n\t(void)msg;\n\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n\n\nint mosquitto_auth_start(void *user_data, struct mosquitto *client, const char *method, bool reauth, const void *data, uint16_t data_len, void **data_out, uint16_t *data_out_len)\n{\n\tint i;\n\n\t(void)user_data;\n\t(void)client;\n\t(void)reauth;\n\n\tif(!strcmp(method, \"mirror\")){\n\t\tif(data_len > 0){\n\t\t\t*data_out = malloc(data_len);\n\t\t\tif(!(*data_out)){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tfor(i=0; i<data_len; i++){\n\t\t\t\t((uint8_t *)(*data_out))[i] = ((uint8_t *)data)[data_len-i-1];\n\t\t\t}\n\t\t\t*data_out_len = data_len;\n\n\t\t\treturn MOSQ_ERR_AUTH_CONTINUE;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n}\n\n\nint mosquitto_auth_continue(void *user_data, struct mosquitto *client, const char *method, const void *data, uint16_t data_len, void **data_out, uint16_t *data_out_len)\n{\n\tsize_t len;\n\n\t(void)user_data;\n\t(void)client;\n\t(void)data_out;\n\t(void)data_out_len;\n\n\tif(!strcmp(method, \"mirror\")){\n\t\tif(data_len > 0){\n\t\t\tlen = strlen(\"supercalifragilisticexpialidocious\")>data_len?data_len:strlen(\"supercalifragilisticexpialidocious\");\n\t\t\tif(!memcmp(data, \"supercalifragilisticexpialidocious\", len)){\n\t\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t\t}else{\n\t\t\t\treturn MOSQ_ERR_AUTH;\n\t\t\t}\n\t\t}else{\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n}\n"
  },
  {
    "path": "test/broker/c/auth_plugin_extended_reauth.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n#define UNUSED(A) (void)(A)\n\nstatic int auth_count = 0;\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\tUNUSED(reload);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\tUNUSED(reload);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\tUNUSED(user_data);\n\tUNUSED(access);\n\tUNUSED(client);\n\tUNUSED(msg);\n\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n\n\nint mosquitto_auth_start(void *user_data, struct mosquitto *client, const char *method, bool reauth, const void *data, uint16_t data_len, void **data_out, uint16_t *data_out_len)\n{\n\tUNUSED(user_data);\n\tUNUSED(client);\n\tUNUSED(method);\n\tUNUSED(reauth);\n\tUNUSED(data);\n\tUNUSED(data_len);\n\tUNUSED(data_out);\n\tUNUSED(data_out_len);\n\n\tif(auth_count == 0){\n\t\tauth_count++;\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nint mosquitto_auth_continue(void *user_data, struct mosquitto *client, const char *method, const void *data, uint16_t data_len, void **data_out, uint16_t *data_out_len)\n{\n\tUNUSED(user_data);\n\tUNUSED(client);\n\tUNUSED(method);\n\tUNUSED(data);\n\tUNUSED(data_len);\n\tUNUSED(data_out);\n\tUNUSED(data_out_len);\n\n\treturn MOSQ_ERR_AUTH;\n}\n"
  },
  {
    "path": "test/broker/c/auth_plugin_extended_single.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\t(void)user_data;\n\t(void)access;\n\t(void)client;\n\t(void)msg;\n\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n\n\nint mosquitto_auth_start(void *user_data, struct mosquitto *client, const char *method, bool reauth, const void *data, uint16_t data_len, void **data_out, uint16_t *data_out_len)\n{\n\tint i;\n\n\t(void)user_data;\n\t(void)reauth;\n\n\tif(!strcmp(method, \"error\")){\n\t\treturn MOSQ_ERR_INVAL;\n\t}else if(!strcmp(method, \"non-matching\")){\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}else if(!strcmp(method, \"single\")){\n\t\tdata_len = data_len>strlen(\"data\")?strlen(\"data\"):data_len;\n\t\tif(!memcmp(data, \"data\", data_len)){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}else if(!strcmp(method, \"change\")){\n\t\treturn mosquitto_set_username(client, \"new_username\");\n\t}else if(!strcmp(method, \"mirror\")){\n\t\tif(data_len > 0){\n\t\t\t*data_out = malloc(data_len);\n\t\t\tif(!(*data_out)){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tfor(i=0; i<data_len; i++){\n\t\t\t\t((uint8_t *)(*data_out))[i] = ((uint8_t *)data)[data_len-i-1];\n\t\t\t}\n\t\t\t*data_out_len = data_len;\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n}\n\n\nint mosquitto_auth_continue(void *user_data, struct mosquitto *client, const char *method, const void *data, uint16_t data_len, void **data_out, uint16_t *data_out_len)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)method;\n\t(void)data;\n\t(void)data_len;\n\t(void)data_out;\n\t(void)data_out_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n"
  },
  {
    "path": "test/broker/c/auth_plugin_extended_single2.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\t(void)user_data;\n\t(void)access;\n\t(void)client;\n\t(void)msg;\n\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n\n\nint mosquitto_auth_start(void *user_data, struct mosquitto *client, const char *method, bool reauth, const void *data, uint16_t data_len, void **data_out, uint16_t *data_out_len)\n{\n\tint i;\n\n\t(void)user_data;\n\t(void)reauth;\n\n\tif(!strcmp(method, \"error2\")){\n\t\treturn MOSQ_ERR_INVAL;\n\t}else if(!strcmp(method, \"non-matching2\")){\n\t\treturn MOSQ_ERR_NOT_SUPPORTED;\n\t}else if(!strcmp(method, \"single2\")){\n\t\tdata_len = data_len>strlen(\"data\")?strlen(\"data\"):data_len;\n\t\tif(!memcmp(data, \"data\", data_len)){\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_AUTH;\n\t\t}\n\t}else if(!strcmp(method, \"change2\")){\n\t\treturn mosquitto_set_username(client, \"new_username\");\n\t}else if(!strcmp(method, \"mirror2\")){\n\t\tif(data_len > 0){\n\t\t\t*data_out = malloc(data_len);\n\t\t\tif(!(*data_out)){\n\t\t\t\treturn MOSQ_ERR_NOMEM;\n\t\t\t}\n\t\t\tfor(i=0; i<data_len; i++){\n\t\t\t\t((uint8_t *)(*data_out))[i] = ((uint8_t *)data)[data_len-i-1];\n\t\t\t}\n\t\t\t*data_out_len = data_len;\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_INVAL;\n\t\t}\n\t}\n\treturn MOSQ_ERR_NOT_SUPPORTED;\n}\n\n\nint mosquitto_auth_continue(void *user_data, struct mosquitto *client, const char *method, const void *data, uint16_t data_len, void **data_out, uint16_t *data_out_len)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)method;\n\t(void)data;\n\t(void)data_len;\n\t(void)data_out;\n\t(void)data_out_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n"
  },
  {
    "path": "test/broker/c/auth_plugin_id_change.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nint mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data);\nint mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL, NULL);\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL);\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\tint rc;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_ACL_CHECK){\n\t\tabort();\n\t}\n\n\tif(!strcmp(mosquitto_client_id(ed->client), \"allowed\")){\n\t\t/* Attempt to change to a different existing ID after we have\n\t\t * authenticated - should fail */\n\t\trc = mosquitto_set_clientid(ed->client, \"already-exists\");\n\t\tif(rc != MOSQ_ERR_ALREADY_EXISTS){\n\t\t\texit(1);\n\t\t}\n\t}\n\n\n\tif(!strcmp(mosquitto_client_id(ed->client), \"allowed\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tconst char *clientid;\n\tint rc;\n\n\t(void)user_data;\n\n\tclientid = mosquitto_client_id(ed->client);\n\n\tif(event != MOSQ_EVT_BASIC_AUTH){\n\t\tabort();\n\t}\n\n\tif(!strcmp(clientid, \"id-change-test\")){\n\t\trc = mosquitto_set_clientid(ed->client, \"allowed\");\n\t\tif(strcmp(mosquitto_client_id(ed->client), \"allowed\")){\n\t\t\trc = MOSQ_ERR_INVAL;\n\t\t}\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\texit(1);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/auth_plugin_msg_params.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\t(void)user_data;\n\t(void)client;\n\n\tif(access == MOSQ_ACL_SUBSCRIBE){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}\n\n\tif(!msg->topic || strcmp(msg->topic, \"param/topic\")){\n\t\tabort();\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n\n\tif(!msg->payload || strncmp(msg->payload, \"payload contents\", strlen(\"payload contents\"))){\n\t\tabort();\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n\n\tif(msg->payloadlen != strlen(\"payload contents\")){\n\t\tabort();\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n\n\tif(msg->qos != 1){\n\t\tabort();\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n\n\tif(!msg->retain){\n\t\tabort();\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)username;\n\t(void)password;\n\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n\n\nint mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)hint;\n\t(void)identity;\n\t(void)key;\n\t(void)max_key_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n"
  },
  {
    "path": "test/broker/c/auth_plugin_publish.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto/mqtt_protocol.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\tstatic int count = 0;\n\tmosquitto_property *props = NULL;\n\n\t(void)user_data;\n\t(void)client;\n\t(void)msg;\n\n\tif(access == MOSQ_ACL_WRITE){\n\t\tif(count == 0){\n\t\t\t/* \"missing-client\" isn't connected, so we can check memory usage properly. */\n\t\t\tmosquitto_broker_publish_copy(\"missing-client\", \"topic/2\", strlen(\"test-message-2\"), \"test-message-2\", 2, true, NULL);\n\t\t\tmosquitto_broker_publish_copy(\"test-client\", \"topic/0\", strlen(\"test-message-0\"), \"test-message-0\", 0, true, NULL);\n\t\t\tmosquitto_broker_publish_copy(\"missing-client\", \"topic/2\", strlen(\"test-message-2\"), \"test-message-2\", 2, true, NULL);\n\t\t\tmosquitto_broker_publish_copy(\"test-client\", \"topic/1\", strlen(\"test-message-1\"), \"test-message-1\", 1, true, NULL);\n\t\t\tmosquitto_broker_publish_copy(\"missing-client\", \"topic/2\", strlen(\"test-message-2\"), \"test-message-2\", 2, true, NULL);\n\t\t\tmosquitto_broker_publish_copy(\"test-client\", \"topic/2\", strlen(\"test-message-2\"), \"test-message-2\", 2, true, NULL);\n\t\t\tcount = 1;\n\t\t}else{\n\t\t\tif(mosquitto_property_add_byte(&props, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1)){\n\t\t\t\tabort();\n\t\t\t}\n\t\t\tmosquitto_broker_publish_copy(\"test-client\", \"topic/0\", strlen(\"test-message-0\"), \"test-message-0\", 0, true, props);\n\t\t\tprops = NULL;\n\t\t\tif(mosquitto_property_add_byte(&props, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1)){\n\t\t\t\tabort();\n\t\t\t}\n\t\t\tmosquitto_broker_publish_copy(\"test-client\", \"topic/1\", strlen(\"test-message-1\"), \"test-message-1\", 1, true, props);\n\t\t\tprops = NULL;\n\t\t\tif(mosquitto_property_add_byte(&props, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1)){\n\t\t\t\tabort();\n\t\t\t}\n\t\t\tmosquitto_broker_publish_copy(\"test-client\", \"topic/2\", strlen(\"test-message-2\"), \"test-message-2\", 2, true, props);\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)username;\n\t(void)password;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)hint;\n\t(void)identity;\n\t(void)key;\n\t(void)max_key_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n"
  },
  {
    "path": "test/broker/c/auth_plugin_pwd.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\t(void)user_data;\n\t(void)access;\n\t(void)client;\n\t(void)msg;\n\n\treturn MOSQ_ERR_PLUGIN_DEFER;\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password)\n{\n\t(void)user_data;\n\t(void)client;\n\n\tif(!strcmp(username, \"test-username\") && password && !strcmp(password, \"cnwTICONIURW\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(!strcmp(username, \"readonly\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(!strcmp(username, \"test-username@v2\")){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nint mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)hint;\n\t(void)identity;\n\t(void)key;\n\t(void)max_key_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n"
  },
  {
    "path": "test/broker/c/auth_plugin_v2.c",
    "content": "#include <string.h>\n#include <stdbool.h>\n#include \"mosquitto_plugin_v2.h\"\n\n/*\n * Following constant come from mosquitto.h\n *\n * They are copied here to fix value of those constant at the time of MOSQ_AUTH_PLUGIN_VERSION == 2\n */\nenum mosq_err_t {\n\tMOSQ_ERR_SUCCESS = 0,\n\tMOSQ_ERR_AUTH = 11,\n\tMOSQ_ERR_ACL_DENIED = 12,\n};\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 2;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, const char *clientid, const char *username, const char *topic, int access)\n{\n\t(void)user_data;\n\t(void)clientid;\n\t(void)topic;\n\n\tif(access != MOSQ_ACL_READ && access != MOSQ_ACL_WRITE){\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}else if(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_READ){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readwrite\")){\n\t\tif((!strcmp(topic, \"readonly\") && access == MOSQ_ACL_READ)\n\t\t\t\t|| !strcmp(topic, \"writeable\")){\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, const char *username, const char *password)\n{\n\t(void)user_data;\n\n\tif(username && !strcmp(username, \"test-username\") && password && !strcmp(password, \"cnwTICONIURW\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && (!strcmp(username, \"readonly\") || !strcmp(username, \"readwrite\"))){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"test-username@v2\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nint mosquitto_auth_psk_key_get(void *user_data, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\t(void)user_data;\n\t(void)hint;\n\t(void)identity;\n\t(void)key;\n\t(void)max_key_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n"
  },
  {
    "path": "test/broker/c/auth_plugin_v3.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 3;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\tconst char *username = mosquitto_client_username(client);\n\n\t(void)user_data;\n\n\tif(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_READ){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_SUBSCRIBE &&!strchr(msg->topic, '#') && !strchr(msg->topic, '+')){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readwrite\")){\n\t\tif((!strcmp(msg->topic, \"readonly\") && access == MOSQ_ACL_READ)\n\t\t\t\t|| !strcmp(msg->topic, \"writeable\")){\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password)\n{\n\t(void)user_data;\n\t(void)client;\n\n\tif(username && !strcmp(username, \"test-username\") && password && !strcmp(password, \"cnwTICONIURW\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && (!strcmp(username, \"readonly\") || !strcmp(username, \"readwrite\"))){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"test-username@v2\")){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nint mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)hint;\n\t(void)identity;\n\t(void)key;\n\t(void)max_key_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n"
  },
  {
    "path": "test/broker/c/auth_plugin_v4.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\tconst char *username = mosquitto_client_username(client);\n\n\t(void)user_data;\n\n\tif(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_READ){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_SUBSCRIBE &&!strchr(msg->topic, '#') && !strchr(msg->topic, '+')){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readwrite\")){\n\t\tif((!strcmp(msg->topic, \"readonly\") && access == MOSQ_ACL_READ)\n\t\t\t\t|| !strcmp(msg->topic, \"writeable\")){\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password)\n{\n\t(void)user_data;\n\t(void)client;\n\n\tif(username && !strcmp(username, \"test-username\") && password && !strcmp(password, \"cnwTICONIURW\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && (!strcmp(username, \"readonly\") || !strcmp(username, \"readwrite\"))){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"test-username@v2\")){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nint mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\t(void)user_data;\n\t(void)client;\n\t(void)hint;\n\t(void)identity;\n\t(void)key;\n\t(void)max_key_len;\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n"
  },
  {
    "path": "test/broker/c/auth_plugin_v5.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nint mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data);\nint mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL, NULL);\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL);\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\tconst char *username = mosquitto_client_username(ed->client);\n\tchar *prop_name = NULL, *prop_value = NULL;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_ACL_CHECK){\n\t\tabort();\n\t}\n\n\tif(username && !strcmp(username, \"readonly\") && ed->access == MOSQ_ACL_READ){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readonly\") && ed->access == MOSQ_ACL_SUBSCRIBE &&!strchr(ed->topic, '#') && !strchr(ed->topic, '+')){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readwrite\")){\n\t\tif((!strcmp(ed->topic, \"readonly\") && ed->access == MOSQ_ACL_READ)\n\t\t\t\t|| !strcmp(ed->topic, \"writeable\")){\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\t}else if(mosquitto_property_read_string_pair(ed->properties, MQTT_PROP_USER_PROPERTY, &prop_name, &prop_value, false)\n\t\t\t&& !strcmp(prop_name, \"custom-name\") && !strcmp(prop_value, \"custom-value\")){\n\n\t\tmosquitto_FREE(prop_name);\n\t\tmosquitto_FREE(prop_value);\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\tmosquitto_FREE(prop_name);\n\t\tmosquitto_FREE(prop_value);\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tconst char binary_pw[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_BASIC_AUTH){\n\t\tabort();\n\t}\n\n\tif(ed->username\n\t\t\t&& !strcmp(ed->username, \"test-username\")\n\t\t\t&& ed->password\n\t\t\t&& !strcmp(ed->password, \"cnwTICONIURW\")\n\t\t\t&& strlen(\"cnwTICONIURW\") == ed->password_len\n\t\t\t){\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(ed->username\n\t\t\t&& !strcmp(ed->username, \"binary-password\")\n\t\t\t&& ed->password\n\t\t\t&& ed->password_len == sizeof(binary_pw)\n\t\t\t&& !memcmp(ed->password, binary_pw, ed->password_len)\n\t\t\t){\n\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(ed->username && (!strcmp(ed->username, \"readonly\") || !strcmp(ed->username, \"readwrite\"))){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(ed->username && !strcmp(ed->username, \"test-username@v2\")){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n"
  },
  {
    "path": "test/broker/c/auth_plugin_v5_control.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nint mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data);\nint mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_plugin_set_info(identifier, \"test-plugin\", NULL);\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL, NULL);\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL, NULL);\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_CONTROL, mosquitto_auth_unpwd_check_v5, \"$CONTROL/test/v1\", NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL);\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data)\n{\n\t(void)event;\n\t(void)event_data;\n\t(void)user_data;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data)\n{\n\t(void)event;\n\t(void)event_data;\n\t(void)user_data;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v1.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(1);\n"
  },
  {
    "path": "test/broker/c/bad_v2_1.c",
    "content": "#include <string.h>\n#include <stdbool.h>\n#include \"mosquitto_plugin_v2.h\"\n\n/*\n * Following constant come from mosquitto.h\n *\n * They are copied here to fix value of those constant at the time of MOSQ_AUTH_PLUGIN_VERSION == 2\n */\nenum mosq_err_t {\n\tMOSQ_ERR_SUCCESS = 0,\n\tMOSQ_ERR_AUTH = 11,\n\tMOSQ_ERR_ACL_DENIED = 12,\n};\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 2;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, const char *clientid, const char *username, const char *topic, int access)\n{\n\t(void)user_data;\n\t(void)clientid;\n\t(void)topic;\n\n\tif(access != MOSQ_ACL_READ && access != MOSQ_ACL_WRITE){\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}else if(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_READ){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readwrite\")){\n\t\tif((!strcmp(topic, \"readonly\") && access == MOSQ_ACL_READ)\n\t\t\t\t|| !strcmp(topic, \"writeable\")){\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, const char *username, const char *password)\n{\n\t(void)user_data;\n\n\tif(!strcmp(username, \"test-username\") && password && !strcmp(password, \"cnwTICONIURW\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(!strcmp(username, \"readonly\") || !strcmp(username, \"readwrite\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(!strcmp(username, \"test-username@v2\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n"
  },
  {
    "path": "test/broker/c/bad_v2_2.c",
    "content": "#include <string.h>\n#include <stdbool.h>\n#include \"mosquitto_plugin_v2.h\"\n\n/*\n * Following constant come from mosquitto.h\n *\n * They are copied here to fix value of those constant at the time of MOSQ_AUTH_PLUGIN_VERSION == 2\n */\nenum mosq_err_t {\n\tMOSQ_ERR_SUCCESS = 0,\n\tMOSQ_ERR_AUTH = 11,\n\tMOSQ_ERR_ACL_DENIED = 12,\n};\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 2;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, const char *clientid, const char *username, const char *topic, int access)\n{\n\t(void)user_data;\n\t(void)clientid;\n\t(void)topic;\n\n\tif(access != MOSQ_ACL_READ && access != MOSQ_ACL_WRITE){\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}else if(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_READ){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readwrite\")){\n\t\tif((!strcmp(topic, \"readonly\") && access == MOSQ_ACL_READ)\n\t\t\t\t|| !strcmp(topic, \"writeable\")){\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n"
  },
  {
    "path": "test/broker/c/bad_v2_3.c",
    "content": "#include <string.h>\n#include <stdbool.h>\n#include \"mosquitto_plugin_v2.h\"\n\n/*\n * Following constant come from mosquitto.h\n *\n * They are copied here to fix value of those constant at the time of MOSQ_AUTH_PLUGIN_VERSION == 2\n */\nenum mosq_err_t {\n\tMOSQ_ERR_SUCCESS = 0,\n\tMOSQ_ERR_AUTH = 11,\n\tMOSQ_ERR_ACL_DENIED = 12,\n};\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 2;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v2_4.c",
    "content": "#include <string.h>\n#include <stdbool.h>\n#include \"mosquitto_plugin_v2.h\"\n\n/*\n * Following constant come from mosquitto.h\n *\n * They are copied here to fix value of those constant at the time of MOSQ_AUTH_PLUGIN_VERSION == 2\n */\nenum mosq_err_t {\n\tMOSQ_ERR_SUCCESS = 0,\n\tMOSQ_ERR_AUTH = 11,\n\tMOSQ_ERR_ACL_DENIED = 12,\n};\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 2;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v2_5.c",
    "content": "#include <string.h>\n#include <stdbool.h>\n#include \"mosquitto_plugin_v2.h\"\n\n/*\n * Following constant come from mosquitto.h\n *\n * They are copied here to fix value of those constant at the time of MOSQ_AUTH_PLUGIN_VERSION == 2\n */\nenum mosq_err_t {\n\tMOSQ_ERR_SUCCESS = 0,\n\tMOSQ_ERR_AUTH = 11,\n\tMOSQ_ERR_ACL_DENIED = 12,\n};\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 2;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v2_6.c",
    "content": "#include <string.h>\n#include <stdbool.h>\n#include \"mosquitto_plugin_v2.h\"\n\n/*\n * Following constant come from mosquitto.h\n *\n * They are copied here to fix value of those constant at the time of MOSQ_AUTH_PLUGIN_VERSION == 2\n */\nenum mosq_err_t {\n\tMOSQ_ERR_SUCCESS = 0,\n\tMOSQ_ERR_AUTH = 11,\n\tMOSQ_ERR_ACL_DENIED = 12,\n};\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 2;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v2_7.c",
    "content": "#include <string.h>\n#include <stdbool.h>\n#include \"mosquitto_plugin_v2.h\"\n\n/*\n * Following constant come from mosquitto.h\n *\n * They are copied here to fix value of those constant at the time of MOSQ_AUTH_PLUGIN_VERSION == 2\n */\nenum mosq_err_t {\n\tMOSQ_ERR_SUCCESS = 0,\n\tMOSQ_ERR_AUTH = 11,\n\tMOSQ_ERR_ACL_DENIED = 12,\n};\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 2;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v3_1.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 3;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\tconst char *username = mosquitto_client_username(client);\n\n\t(void)user_data;\n\n\tif(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_READ){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_SUBSCRIBE &&!strchr(msg->topic, '#') && !strchr(msg->topic, '+')){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readwrite\")){\n\t\tif((!strcmp(msg->topic, \"readonly\") && access == MOSQ_ACL_READ)\n\t\t\t\t|| !strcmp(msg->topic, \"writeable\")){\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password)\n{\n\t(void)user_data;\n\t(void)client;\n\n\tif(!strcmp(username, \"test-username\") && password && !strcmp(password, \"cnwTICONIURW\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(!strcmp(username, \"readonly\") || !strcmp(username, \"readwrite\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(!strcmp(username, \"test-username@v2\")){\n\t\treturn MOSQ_ERR_PLUGIN_DEFER;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n"
  },
  {
    "path": "test/broker/c/bad_v3_2.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 3;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\tconst char *username = mosquitto_client_username(client);\n\n\t(void)user_data;\n\n\tif(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_READ){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readonly\") && access == MOSQ_ACL_SUBSCRIBE &&!strchr(msg->topic, '#') && !strchr(msg->topic, '+')){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else if(username && !strcmp(username, \"readwrite\")){\n\t\tif((!strcmp(msg->topic, \"readonly\") && access == MOSQ_ACL_READ)\n\t\t\t\t|| !strcmp(msg->topic, \"writeable\")){\n\n\t\t\treturn MOSQ_ERR_SUCCESS;\n\t\t}else{\n\t\t\treturn MOSQ_ERR_ACL_DENIED;\n\t\t}\n\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n"
  },
  {
    "path": "test/broker/c/bad_v3_3.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 3;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v3_4.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 3;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v3_5.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 3;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v3_6.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 3;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v3_7.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 3;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v4_1.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\t(void)reload;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v4_2.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v4_3.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v4_4.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn 4;\n}\n"
  },
  {
    "path": "test/broker/c/bad_v5_1.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n"
  },
  {
    "path": "test/broker/c/bad_v6.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(6);\n"
  },
  {
    "path": "test/broker/c/bad_vnone_1.c",
    "content": "int dummy(void)\n{\n\treturn 4;\n}\n"
  },
  {
    "path": "test/broker/c/kick_last_client.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n#define UNUSED(A) (void)(A)\n\nstatic int handle_tick(int event, void *event_data, void *user_data);\nstatic int handle_connect(int event, void *event_data, void *user_data);\nstatic int handle_disconnect(int event, void *event_data, void *user_data);\n\nstatic mosquitto_plugin_id_t *plg_id;\nstatic char *last_clientid = NULL;\nstatic int can_kick = 0;\n\n\nint mosquitto_plugin_version(int supported_version_count, const int *supported_versions)\n{\n\tUNUSED(supported_version_count);\n\tUNUSED(supported_versions);\n\n\treturn 5;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, handle_connect, NULL, NULL);\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_DISCONNECT, handle_disconnect, NULL, NULL);\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_CLIENT_OFFLINE, handle_disconnect, NULL, NULL);\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_TICK, handle_tick, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_CONNECT, handle_connect, NULL);\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_DISCONNECT, handle_disconnect, NULL);\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_CLIENT_OFFLINE, handle_disconnect, NULL);\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_TICK, handle_tick, NULL);\n\tmosquitto_FREE(last_clientid);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint handle_tick(int event, void *event_data, void *user_data)\n{\n\tUNUSED(event);\n\tUNUSED(event_data);\n\tUNUSED(user_data);\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"plugin tick %p %d\", last_clientid, can_kick);\n\tif(last_clientid && can_kick){\n\t\tif(can_kick == 1){\n\t\t\tmosquitto_log_printf(MOSQ_LOG_INFO, \"plugin kick %s\", last_clientid);\n\t\t\tmosquitto_kick_client_by_clientid(last_clientid, false);\n\t\t\tmosquitto_FREE(last_clientid);\n\t\t\tcan_kick--;\n\t\t}else{\n\t\t\tcan_kick--;\n\t\t}\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint handle_connect(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_basic_auth *ed = event_data;\n\tUNUSED(event);\n\tUNUSED(user_data);\n\n\tconst char *id = mosquitto_client_id(ed->client);\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"plugin connect %s\", id);\n\tmosquitto_FREE(last_clientid);\n\tlast_clientid = mosquitto_strdup(id);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint handle_disconnect(int event, void *event_data, void *user_data)\n{\n\tUNUSED(event);\n\tUNUSED(event_data);\n\tUNUSED(user_data);\n\n\tmosquitto_log_printf(MOSQ_LOG_INFO, \"plugin disconnect %d\", event);\n\tcan_kick = 5;\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/mosquitto_plugin_v2.h",
    "content": "/*\nCopyright (c) 2012-2014 Roger Light <roger@atchoo.org>\n\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License 2.0\nand Eclipse Distribution License v1.0 which accompany this distribution.\n\nThe Eclipse Public License is available at\n   https://www.eclipse.org/legal/epl-2.0/\nand the Eclipse Distribution License is available at\n  http://www.eclipse.org/org/documents/edl-v10.php.\n\nContributors:\n   Roger Light - initial implementation and documentation.\n*/\n\n#ifndef MOSQUITTO_PLUGIN_V2_H\n#define MOSQUITTO_PLUGIN_V2_H\n\n#define MOSQ_AUTH_PLUGIN_VERSION 2\n\n#define MOSQ_ACL_NONE 0x00\n#define MOSQ_ACL_READ 0x01\n#define MOSQ_ACL_WRITE 0x02\n\nstruct mosquitto_auth_opt {\n\tchar *key;\n\tchar *value;\n};\n\n/*\n * To create an authentication plugin you must include this file then implement\n * the functions listed below. The resulting code should then be compiled as a\n * shared library. Using gcc this can be achieved as follows:\n *\n * gcc -I<path to mosquitto_plugin.h> -fPIC -shared plugin.c -o plugin.so\n *\n * On Mac OS X:\n *\n * gcc -I<path to mosquitto_plugin.h> -fPIC -shared plugin.c -undefined dynamic_lookup -o plugin.so\n *\n */\n\n/* =========================================================================\n *\n * Utility Functions\n *\n * Use these functions from within your plugin.\n *\n * There are also very useful functions in libmosquitto.\n *\n * ========================================================================= */\n\n/*\n * Function: mosquitto_log_printf\n *\n * Write a log message using the broker configured logging.\n *\n * Parameters:\n * \tlevel -    Log message priority. Can currently be one of:\n *\n *             MOSQ_LOG_INFO\n *             MOSQ_LOG_NOTICE\n *             MOSQ_LOG_WARNING\n *             MOSQ_LOG_ERR\n *             MOSQ_LOG_DEBUG\n *             MOSQ_LOG_SUBSCRIBE (not recommended for use by plugins)\n *             MOSQ_LOG_UNSUBSCRIBE (not recommended for use by plugins)\n *\n *             These values are defined in mosquitto.h.\n *\n *\tfmt, ... - printf style format and arguments.\n */\nvoid mosquitto_log_printf(int level, const char *fmt, ...);\n\n\n\n/* =========================================================================\n *\n * Plugin Functions\n *\n * You must implement these functions in your plugin.\n *\n * ========================================================================= */\n\n/*\n * Function: mosquitto_auth_plugin_version\n *\n * The broker will call this function immediately after loading the plugin to\n * check it is a supported plugin version. Your code must simply return\n * MOSQ_AUTH_PLUGIN_VERSION.\n */\nint mosquitto_auth_plugin_version(void);\n\n/*\n * Function: mosquitto_auth_plugin_init\n *\n * Called after the plugin has been loaded and <mosquitto_auth_plugin_version>\n * has been called. This will only ever be called once and can be used to\n * initialise the plugin.\n *\n * Parameters:\n *\n *\tuser_data :      The pointer set here will be passed to the other plugin\n *\t                 functions. Use to hold connection information for example.\n *\tauth_opts :      Pointer to an array of struct mosquitto_auth_opt, which\n *\t                 provides the plugin options defined in the configuration file.\n *\tauth_opt_count : The number of elements in the auth_opts array.\n *\n * Return value:\n *\tReturn 0 on success\n *\tReturn >0 on failure.\n */\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count);\n\n/*\n * Function: mosquitto_auth_plugin_cleanup\n *\n * Called when the broker is shutting down. This will only ever be called once.\n * Note that <mosquitto_auth_security_cleanup> will be called directly before\n * this function.\n *\n * Parameters:\n *\n *\tuser_data :      The pointer provided in <mosquitto_auth_plugin_init>.\n *\tauth_opts :      Pointer to an array of struct mosquitto_auth_opt, which\n *\t                 provides the plugin options defined in the configuration file.\n *\tauth_opt_count : The number of elements in the auth_opts array.\n *\n * Return value:\n *\tReturn 0 on success\n *\tReturn >0 on failure.\n */\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count);\n\n/*\n * Function: mosquitto_auth_security_init\n *\n * Called when the broker initialises the security functions when it starts up.\n * If the broker is requested to reload its configuration whilst running,\n * <mosquitto_auth_security_cleanup> will be called, followed by this function.\n * In this situation, the reload parameter will be true.\n *\n * Parameters:\n *\n *\tuser_data :      The pointer provided in <mosquitto_auth_plugin_init>.\n *\tauth_opts :      Pointer to an array of struct mosquitto_auth_opt, which\n *\t                 provides the plugin options defined in the configuration file.\n *\tauth_opt_count : The number of elements in the auth_opts array.\n *\treload :         If set to false, this is the first time the function has\n *\t                 been called. If true, the broker has received a signal\n *\t                 asking to reload its configuration.\n *\n * Return value:\n *\tReturn 0 on success\n *\tReturn >0 on failure.\n */\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload);\n\n/*\n * Function: mosquitto_auth_security_cleanup\n *\n * Called when the broker cleans up the security functions when it shuts down.\n * If the broker is requested to reload its configuration whilst running,\n * this function will be called, followed by <mosquitto_auth_security_init>.\n * In this situation, the reload parameter will be true.\n *\n * Parameters:\n *\n *\tuser_data :      The pointer provided in <mosquitto_auth_plugin_init>.\n *\tauth_opts :      Pointer to an array of struct mosquitto_auth_opt, which\n *\t                 provides the plugin options defined in the configuration file.\n *\tauth_opt_count : The number of elements in the auth_opts array.\n *\treload :         If set to false, this is the first time the function has\n *\t                 been called. If true, the broker has received a signal\n *\t                 asking to reload its configuration.\n *\n * Return value:\n *\tReturn 0 on success\n *\tReturn >0 on failure.\n */\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload);\n\n/*\n * Function: mosquitto_auth_acl_check\n *\n * Called by the broker when topic access must be checked. access will be one\n * of MOSQ_ACL_READ (for subscriptions) or MOSQ_ACL_WRITE (for publish). Return\n * MOSQ_ERR_SUCCESS if access was granted, MOSQ_ERR_ACL_DENIED if access was\n * not granted, or MOSQ_ERR_UNKNOWN for an application specific error.\n */\nint mosquitto_auth_acl_check(void *user_data, const char *clientid, const char *username, const char *topic, int access);\n\n/*\n * Function: mosquitto_auth_unpwd_check\n *\n * Called by the broker when a username/password must be checked. Return\n * MOSQ_ERR_SUCCESS if the user is authenticated, MOSQ_ERR_AUTH if\n * authentication failed, or MOSQ_ERR_UNKNOWN for an application specific\n * error.\n */\nint mosquitto_auth_unpwd_check(void *user_data, const char *username, const char *password);\n\n/*\n * Function: mosquitto_psk_key_get\n *\n * Called by the broker when a client connects to a listener using TLS/PSK.\n * This is used to retrieve the pre-shared-key associated with a client\n * identity.\n *\n * Examine hint and identity to determine the required PSK (which must be a\n * hexadecimal string with no leading \"0x\") and copy this string into key.\n *\n * Parameters:\n *\tuser_data :   the pointer provided in <mosquitto_auth_plugin_init>.\n *\thint :        the psk_hint for the listener the client is connecting to.\n *\tidentity :    the identity string provided by the client\n *\tkey :         a string where the hex PSK should be copied\n *\tmax_key_len : the size of key\n *\n * Return value:\n *\tReturn 0 on success.\n *\tReturn >0 on failure.\n *\tReturn >0 if this function is not required.\n */\nint mosquitto_auth_psk_key_get(void *user_data, const char *hint, const char *identity, char *key, int max_key_len);\n\n#endif\n"
  },
  {
    "path": "test/broker/c/plugin_control.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto/mqtt_protocol.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nstatic mosquitto_plugin_id_t *plg_id = NULL;\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\n\nstatic int control_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_control *ed = event_data;\n\n\t(void)userdata;\n\n\tif(event != MOSQ_EVT_CONTROL){\n\t\tabort();\n\t}\n\n\tmosquitto_broker_publish_copy(NULL, ed->topic, (int)ed->payloadlen, ed->payload, 0, 0, NULL);\n\n\treturn 0;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tint i;\n\tchar buf[100];\n\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tplg_id = identifier;\n\n\tfor(i=0; i<100; i++){\n\t\tsnprintf(buf, sizeof(buf), \"$CONTROL/user-management/v%d\", i);\n\t\tmosquitto_callback_register(plg_id, MOSQ_EVT_CONTROL, control_callback, \"$CONTROL/user-management/v1\", NULL);\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tint i;\n\tchar buf[100];\n\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tfor(i=0; i<100; i++){\n\t\tsnprintf(buf, sizeof(buf), \"$CONTROL/user-management/v%d\", i);\n\t\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_CONTROL, control_callback, \"$CONTROL/user-management/v1\");\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/plugin_evt_client_offline.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\n\nint callback_client_offline(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_client_offline *ed = event_data;\n\tconst char *clientid;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_CLIENT_OFFLINE){\n\t\tabort();\n\t}\n\tclientid = mosquitto_client_id(ed->client);\n\tmosquitto_broker_publish_copy(NULL, \"evt/client/offline\", (int)strlen(clientid), clientid, 0, false, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_CLIENT_OFFLINE, callback_client_offline, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_CLIENT_OFFLINE, callback_client_offline, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/plugin_evt_message_in.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\n\nint callback_message_in(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_MESSAGE_IN){\n\t\tabort();\n\t}\n\n\ted->topic = mosquitto_strdup(\"fixed-topic\");\n\ted->payload = mosquitto_strdup(\"new-message\");\n\ted->payloadlen = (uint32_t)strlen(ed->payload);\n\n\ted->properties = NULL;\n\tif(mosquitto_property_add_string_pair(&ed->properties, MQTT_PROP_USER_PROPERTY, \"key\", \"value\")){\n\t\tabort();\n\t}\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_MESSAGE_IN, callback_message_in, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/plugin_evt_message_out.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\n\nint callback_message_out(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_message *ed = event_data;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_MESSAGE_OUT){\n\t\tabort();\n\t}\n\tif(!strcmp(ed->topic, \"deny\")){\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n\ted->topic = mosquitto_strdup(\"new-topic\");\n\ted->payload = mosquitto_strdup(\"new-message\");\n\ted->payloadlen = (uint32_t)strlen(ed->payload);\n\ted->properties = NULL;\n\tif(mosquitto_property_add_string_pair(&ed->properties, MQTT_PROP_USER_PROPERTY, \"key\", \"value\")){\n\t\tabort();\n\t}\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_MESSAGE_OUT, callback_message_out, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_MESSAGE_OUT, callback_message_out, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/plugin_evt_persist_client_update.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\n\nint callback_persist_client_update(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_persist_client *ed = event_data;\n\tconst char *clientid;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_PERSIST_CLIENT_UPDATE){\n\t\tabort();\n\t}\n\tclientid = ed->data.clientid;\n\tmosquitto_broker_publish_copy(NULL, \"evt/persist/client/update\", (int)strlen(clientid), clientid, 0, false, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_PERSIST_CLIENT_UPDATE, callback_persist_client_update, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_PERSIST_CLIENT_UPDATE, callback_persist_client_update, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/plugin_evt_psk_key.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto/mqtt_protocol.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n#define UNUSED(A) (void)(A)\n\nstatic mosquitto_plugin_id_t *plg_id = NULL;\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\n\nstatic int psk_callback(int event, void *event_data, void *userdata)\n{\n\tstruct mosquitto_evt_psk_key *ed = event_data;\n\n\tUNUSED(event);\n\tUNUSED(userdata);\n\n\tif(!strcmp(ed->hint, \"myhint\") && !strcmp(ed->identity, \"subidentity\")){\n\t\tsnprintf(ed->key, (size_t)ed->max_key_len, \"159445\");\n\t}else if(!strcmp(ed->hint, \"myhint\") && !strcmp(ed->identity, \"pubidentity\")){\n\t\tsnprintf(ed->key, (size_t)ed->max_key_len, \"297A49\");\n\t}else{\n\t\treturn MOSQ_ERR_INVAL;\n\t}\n\n\treturn 0;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_PSK_KEY, psk_callback, NULL, NULL);\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/plugin_evt_reload.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n#define UNUSED(A) (void)(A)\n\nstatic int handle_reload(int event, void *event_data, void *user_data);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\n\nint mosquitto_plugin_version(int supported_version_count, const int *supported_versions)\n{\n\tUNUSED(supported_version_count);\n\tUNUSED(supported_versions);\n\n\treturn 5;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_RELOAD, handle_reload, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_RELOAD, handle_reload, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint handle_reload(int event, void *event_data, void *user_data)\n{\n\tUNUSED(event);\n\tUNUSED(event_data);\n\tUNUSED(user_data);\n\n\tmosquitto_broker_publish_copy(\"plugin-reload-test\", \"topic/reload\", strlen(\"test-message\"), \"test-message\", 0, false, NULL);\n\tprintf(\"reload\\n\");\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/plugin_evt_subscribe.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\n\nint callback_subscribe(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_subscribe *ed = event_data;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_SUBSCRIBE){\n\t\tabort();\n\t}\n\ted->data.topic_filter = mosquitto_strdup(\"new-topic\");\n\tMQTT_SUB_OPT_SET_QOS(ed->data.options, 0);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_SUBSCRIBE, callback_subscribe, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_SUBSCRIBE, callback_subscribe, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/plugin_evt_tick.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\n#define UNUSED(A) (void)(A)\n\nstatic int handle_tick(int event, void *event_data, void *user_data);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\n\nint mosquitto_plugin_version(int supported_version_count, const int *supported_versions)\n{\n\tUNUSED(supported_version_count);\n\tUNUSED(supported_versions);\n\n\treturn 5;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_TICK, handle_tick, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tUNUSED(user_data);\n\tUNUSED(auth_opts);\n\tUNUSED(auth_opt_count);\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_TICK, handle_tick, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint handle_tick(int event, void *event_data, void *user_data)\n{\n\tUNUSED(event);\n\tUNUSED(event_data);\n\tUNUSED(user_data);\n\n\tmosquitto_broker_publish_copy(\"plugin-tick-test\", \"topic/tick\", strlen(\"test-message\"), \"test-message\", 0, false, NULL);\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/plugin_evt_unsubscribe.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\nstatic mosquitto_plugin_id_t *plg_id;\n\n\nint callback_unsubscribe(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_unsubscribe *ed = event_data;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_UNSUBSCRIBE){\n\t\tabort();\n\t}\n\ted->data.topic_filter = mosquitto_strdup(\"missing-topic\");\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tplg_id = identifier;\n\n\tmosquitto_callback_register(plg_id, MOSQ_EVT_UNSUBSCRIBE, callback_unsubscribe, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)\n{\n\t(void)user_data;\n\t(void)opts;\n\t(void)opt_count;\n\n\tmosquitto_callback_unregister(plg_id, MOSQ_EVT_UNSUBSCRIBE, callback_unsubscribe, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/c/plugin_load_acl.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nint mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data);\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tmosquitto_callback_register(identifier, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_acl_check *ed = event_data;\n\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_ACL_CHECK){\n\t\tabort();\n\t}\n\n\tif(!strcmp(ed->topic, \"allowed-topic\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n"
  },
  {
    "path": "test/broker/c/plugin_load_extended_auth.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/broker.h>\n#include <mosquitto/broker_plugin.h>\n\nMOSQUITTO_PLUGIN_DECLARE_VERSION(5);\n\n\nstatic int on_ext_auth_start(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_extended_auth *ed = event_data;\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_EXT_AUTH_START){\n\t\tabort();\n\t}\n\n\tif(!strcmp((char *)ed->data_in, \"allowed-start\")){\n\t\ted->data_out = mosquitto_strdup(\"start-ok\");\n\t\ted->data_out_len = (uint16_t)strlen(ed->data_out);\n\t\treturn MOSQ_ERR_AUTH_CONTINUE;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nstatic int on_ext_auth_continue(int event, void *event_data, void *user_data)\n{\n\tstruct mosquitto_evt_extended_auth *ed = event_data;\n\t(void)user_data;\n\n\tif(event != MOSQ_EVT_EXT_AUTH_CONTINUE){\n\t\tabort();\n\t}\n\n\tif(!strcmp((char *)ed->data_in, \"allowed-continue\")){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nint mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\t(void)user_data;\n\t(void)auth_opts;\n\t(void)auth_opt_count;\n\n\tmosquitto_callback_register(identifier, MOSQ_EVT_EXT_AUTH_START, on_ext_auth_start, NULL, NULL);\n\tmosquitto_callback_register(identifier, MOSQ_EVT_EXT_AUTH_CONTINUE, on_ext_auth_continue, NULL, NULL);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/broker/data/AUTH.json",
    "content": "[\n\t{\n\t\t\"comment\": \"AUTH TESTS ARE INCOMPLETE\",\n\t\t\"group\": \"v3.1.1 AUTH\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"F0 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"F0 r0\"}]},\n\t\t\t{ \"name\": \"F0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F0 r1 00\"}]},\n\t\t\t{ \"name\": \"F1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F1 r0\"}]},\n\t\t\t{ \"name\": \"F2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F2 r0\"}]},\n\t\t\t{ \"name\": \"F4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F4 r0\"}]},\n\t\t\t{ \"name\": \"F8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F8 r0\"}]},\n\t\t\t{ \"name\": \"F0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"F0 r268435456\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   AUTH\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"F0 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"F0 00\"}]},\n\t\t\t{ \"name\": \"F0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"F0 r268435456\"}]},\n\t\t\t{ \"name\": \"F0 long\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"F0 r1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"F1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"F1 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"F2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"F2 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"F4\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"F4 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"F8\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"F8 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/CONNACK.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 CONNACK\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"20 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 00 00\"}]},\n\t\t\t{ \"name\": \"20 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"20 r268435456\"}]},\n\t\t\t{ \"name\": \"20 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 00 00\"}]},\n\t\t\t{ \"name\": \"20 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r1 00\"}]},\n\t\t\t{ \"name\": \"20 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r0\"}]},\n\t\t\t{ \"name\": \"20\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 00 00\"}]},\n\t\t\t{ \"name\": \"21\", \"msgs\": [{\"type\":\"send\", \"payload\":\"21 r2 00 00\"}]},\n\t\t\t{ \"name\": \"22\", \"msgs\": [{\"type\":\"send\", \"payload\":\"22 r2 00 00\"}]},\n\t\t\t{ \"name\": \"24\", \"msgs\": [{\"type\":\"send\", \"payload\":\"24 r2 00 00\"}]},\n\t\t\t{ \"name\": \"28\", \"msgs\": [{\"type\":\"send\", \"payload\":\"28 r2 00 00\"}]},\n\t\t\t{ \"name\": \"issue 2163 v3\", \"ver\":3, \"msgs\": [{\"type\":\"send\", \"payload\":\"29 r2 00 01\"}]},\n\t\t\t{ \"name\": \"issue 2163 v4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"29 r2 00 01\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x01\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 01 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x02\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 02 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x04\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 04 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x08\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 08 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x10\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 10 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x20\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 20 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x40\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 40 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x80\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 80 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNACK\",\n\t\t\"comment\": \"CMD RL FLAG RC PROPLEN PROPS\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"20 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 00 00\"}]},\n\t\t\t{ \"name\": \"20 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"20 r268435456\"}]},\n\t\t\t{ \"name\": \"20 with properties\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 03 21 000A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 long\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 short 2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r2 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 short 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 short 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"21\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"21 r3 00 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"22\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"22 r3 00 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"24\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"24 r3 00 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"28\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"28 r3 00 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"issue 2163 v5\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"29 r2 00 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 CAF=0x01\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 01 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 CAF=0x02\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 02 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 CAF=0x04\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 04 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 CAF=0x08\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 08 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 CAF=0x10\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 10 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 CAF=0x20\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 20 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 CAF=0x40\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 40 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 CAF=0x80\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 80 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x01 (invalid)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 01 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x80 (unspecified error)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 80 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x81 (malformed packet)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 81 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x82 (protocol error)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 82 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x83 (implementation specific error)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 83 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x84 (unsupported protocol version)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 84 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x85 (client identifier not valid)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 85 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x86 (bad user name or password)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 86 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x87 (not authorised)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 87 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x88 (server unavailable)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 88 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x89 (server busy)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 89 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x8A (banned)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 8A 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x8C (bad authentication method)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 8C 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x90 (topic name invalid)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 90 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x95 (packet too large)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 95 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x97 (quota exceeded)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 97 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x99 (payload format invalid)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 99 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x9A (retain not supported)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 9A 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x9B (qos not supported)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 9B 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x9C (use another server)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 9C 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x9D (server moved)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 9D 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0x9F (connection rate exceeded)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 9F 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 RC=0xFF (invalid)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r3 00 FF 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNACK ALLOWED PROPERTIES\",\n\t\t\"comment\": \"CMD RL FLAG RC PROPLEN PROPS\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"20 with session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 11 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 11\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 21 0101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with receive-maximum (two byte integer) 0 value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 21 H0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 21\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 24 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with maximum-qos (byte) 2 value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 24 i2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 24\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 25 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 25\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 27 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 27 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 27\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 12 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 12\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with assigned-client-identifier (UTF-8 string) empty\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 12 0000\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 220101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 22\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 1F s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with reason-string property missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 1F\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with reason-string property empty\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 1F s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 28 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 28\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 29 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 29\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 13 0101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 13\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 2A i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 2A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 1A s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 1A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 1C s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 1C\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 15 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 15\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 16 H1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 16\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with user-property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r10 00 00 v7 26 s1 'p' s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 26 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 26\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNACK DISALLOWED PROPERTIES\",\n\t\t\"comment\": \"CMD RL FLAG RC PROPLEN PROPS\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"20 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 01 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 17 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 17\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"20 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 02 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 18 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 02\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 18\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 03 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 08 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"20 with content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 03\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 08\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"20 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 09 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 09\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"20 with subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0B v1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"20 with subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 0B\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"20 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 23 0101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"20 with topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 23\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"20 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 00 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 04 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 05 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 06 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 07 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0A i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0C i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0D i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0E i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 10 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 14 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 1B i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 1D i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 1E i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 20 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 8000 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 8001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 FF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 808001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 FFFF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 80808001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"20 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 FFFFFF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/CONNECT.json",
    "content": "[\n\t{\n\t\t\"comment\": \"CONNECT TESTS ARE INCOMPLETE\",\n\t\t\"group\": \"v3.1   CONNECT\",\n\t\t\"ver\":3,\n\t\t\"connect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"10 ok \", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 03 01 k10 s1 'p'\", \"comment\":\"minimal valid CONNECT\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 00\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"14 ok \", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"14 r15 s6 'MQIsdp' 03 01 k10 s1 'p'\", \"comment\":\"CONNECT with QoS=1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 00\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 proto ver 2\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 02 00 k10 s1 'p'\", \"comment\":\"CONNECT\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 84 00\", \"comment\": \"CONNACK identifier rejected\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 proto ver 6\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 06 00 k10 s1 'p'\", \"comment\":\"CONNECT\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 84 00\", \"comment\": \"CONNACK identifier rejected\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 empty client ID\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r14 s6 'MQIsdp' 03 02 k10 0000\", \"comment\":\"CONNECT clean session true, no client id\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 02\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 ok\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 03 02 k10 s1 'p'\", \"comment\":\"CONNECT clean session true, no client id\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 00\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 remaining length 5 bytes\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r268435456 s6 'MQIsdp' 03 02 k10 s1 'p'\", \"comment\":\"minimal valid CONNECT\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v3.1.1 CONNECT\",\n\t\t\"ver\":4,\n\t\t\"connect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"10 ok \", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 02 k10 s1 'p'\", \"comment\":\"minimal valid CONNECT\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 00\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.0-2]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 02 k10 s1 'p'\", \"comment\":\"minimal valid CONNECT\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 00\", \"comment\": \"CONNACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 02 k10 s1 'p'\", \"comment\":\"minimal valid CONNECT\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 missing client ID\", \"connect\":false, \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r10 s4 'MQTT' 04 02 k10\"}]},\n\t\t\t{ \"name\": \"10 empty client ID\", \"connect\":false, \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r12 s4 'MQTT' 04 02 k10 0000\", \"comment\":\"CONNECT clean session true, no client id\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 00\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 empty client ID clean false [MQTT-3.1.3-7]\", \"expect_disconnect\":true, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r12 s4 'MQTT' 04 00 k10 0000\", \"comment\":\"CONNECT clean session false, no client id\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 02\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 proto ver 2 [MQTT-3.1.2-2]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 02 00 k10 s1 'p'\", \"comment\":\"CONNECT\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 84 00\", \"comment\": \"v3.1.1 CONNACK identifier rejected\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 proto ver 6 [MQTT-3.1.2-2]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 06 00 k10 s1 'p'\", \"comment\":\"CONNECT\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 84 00\", \"comment\": \"v3.1.1 CONNACK identifier rejected\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 remaining length 5 bytes\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r268435456 s4 'MQTT' 05 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"11\", \"msgs\":[{\"type\":\"send\", \"payload\":\"11 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"12\", \"msgs\":[{\"type\":\"send\", \"payload\":\"12 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"14\", \"msgs\":[{\"type\":\"send\", \"payload\":\"14 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"18\", \"msgs\":[{\"type\":\"send\", \"payload\":\"18 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 short proto\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r12 s3 'MQT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 zero proto\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r9 s0 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 long proto\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r14 s5 'MQTTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-1]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTU' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-3] \", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 01 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 0 Will QoS 1 [MQTT-3.1.2-11]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 0A k10 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Will flag 0 Will retain 1 [MQTT-3.1.2-11]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 12 k10 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Will flag 1 no Will topic no Will message [MQTT-3.1.2-9]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 06 k10 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Will flag 1 no Will topic [MQTT-3.1.2-9]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 06 k10 s1 'p' s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Will flag 1 ok\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 06 k10 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 00\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Will flag 1 Will Qos 3 [MQTT-3.1.2-14]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 1E k10 s1 'p' s1 'p' s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Will topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F700000 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746FEDA080 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F700170 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F701F70 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F707F70 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746FC29F70 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746FEDBFBF s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Client ID with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F700000\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F700170\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-18]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 02 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-19]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 82 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x0000\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F700000\"}]},\n\t\t\t{ \"name\": \"10 Username with 0xD800\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x0001\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F700170\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x001F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x007F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x009F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0xFFFF\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"10 Username zero length ok\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 04 82 k10 s1 'p' s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 00\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Username flag 1 Password flag 1 ok\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 C2 k10 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 00\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-20]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 82 k10 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-21]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 C2 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-22]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 42 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Password with 0x0000\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r23 00 04 'MQTT' 04 C2 k10 s1 'p' s1 'p' s5 746F700000\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r2 00 00\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"duplicate CONNECT\", \"connect\":true, \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 02 k10 s1 'p'\", \"comment\":\"minimal valid duplicate CONNECT\"}]},\n\t\t\t{ \"name\": \"NanoMQ CWE-119\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r7 s4 'MQTT' 04 C2 k60 s11 'test-python' s5 'admin' s8 'password'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNECT\",\n\t\t\"ver\":5,\n\t\t\"connect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"10 ok \", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r14 s4 'MQTT' 05 02 k10 v0 s1 'p'\", \"comment\":\"minimal valid CONNECT\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 remaining length 5 bytes\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r268435456 s4 'MQTT' 05 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Username flag 1 ok\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 82 k10 v0 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Client ID with 0x0000\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 v0 s5 746F700000\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 85 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Client ID with U+D800\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 v0 s5 746FEDA080\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 85 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Client ID with U+0001\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 v0 s5 746F700170\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 85 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Client ID with U+001F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 v0 s5 746F701F70\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 85 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Client ID with U+007F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 v0 s5 746F707F70\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 85 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Client ID with U+009F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 v0 s5 746FC29F70\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 85 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Client ID with U+FFFF\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 v0 s5 746FEDBFBF\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 85 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-16]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 v0 s1 'q' s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-17]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r14 s4 'MQTT' 05 82 k10 v0 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Username with 0x0000\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 v0 s1 'p' s5 746F700000\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Username with 0xD800\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 v0 s1 'p' s5 746FEDA080\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Username with 0x0001\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 v0 s1 'p' s5 746F700170\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Username with 0x001F\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 v0 s1 'p' s5 746F701F70\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Username with 0x007F\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 v0 s1 'p' s5 746F707F70\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Username with 0x009F\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 v0 s1 'p' s5 746FC29F70\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Username with 0xFFFF\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 v0 s1 'p' s5 746FEDBFBF\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-18]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 82 k10 v0 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-19]\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 C2 k10 v0 s1 'p' s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"10 Will flag 1 ok\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 06 k10 v0 s1 'p' 00 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"tiny max packet\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 27 L2 s1 'p'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNECT EXTENDED AUTH\",\n\t\t\"ver\":5,\n\t\t\"connect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"unsupported authentication method\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r35 s4 'MQTT' 05 02 k10 15 15000B756E737570706F7274656416000474657374 s1 'p'\", \"comment\":\"auth-method:unsupported, auth-data:test\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 8C 00\", \"comment\": \"CONNACK Bad authentication method\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNECT ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"connect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer)\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 11 L1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*session-expiry-interval (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 02 k10 0A 11 L1 11 L1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 11 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"receive-maximum (two byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 21 0101 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer) 0 value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 21 0000 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 21 0101 21 0101 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 21 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer)\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 27 10000001 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*maximum-packet-size (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 02 k10 0A 27 10000001 27 10000001 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 27 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer) 0 value\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 27 L0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer) FFFFFFFF value\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 27 FFFFFFFF s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 22 0101 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer) 0 value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 22 0000 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 22 0101 22 0101 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 22 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"request-response-information (byte)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 19 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*request-response-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 19 01 19 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"request-response-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 19 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"request-response-information (byte) 2 value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 19 02 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"request-problem-information (byte)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 17 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 17 01 17 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 17 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"request-problem-information (byte) 2 value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 17 02 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 02 k10 07 26 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r28 s4 'MQTT' 05 02 k10 0E 26 s1 'p' s1 'p' 26 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 26 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 26 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty key\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 26 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 26 s1 'p' 0000 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 26 0000 0000 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 15 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 02 k10 08 15 s1 'p' 15 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"authentication-data (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 15 s1 'p' 16 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"authentication-data (UTF-8 string) no authentication-method\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 16 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*authentication-data (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 02 k10 0C 15 s1 'p' 16 s1 'p' 16 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNECT DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"connect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 01 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 24 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 24 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 25 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 25 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 28 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 28 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 29 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 29 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 2A 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 2A 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 00 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 04 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 05 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 06 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 07 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0A 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0C 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0D 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0E 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0F 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 10 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 14 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 1B 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 1D 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 1E 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 20 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 7F 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 8000 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 8001 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 FF7F 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 808001 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 FFFF7F 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 80808001 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 FFFFFF7F 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x8080808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 8080808001 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 02 10000001 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*message-expiry-interval (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 02 k10 0A 02 10000001 02 10000001 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 02 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer) 0 value\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 02 L0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer) FFFFFFFF value\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 02 FFFFFFFF s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 18 10000001 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*will-delay-interval (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 02 k10 0A 18 10000001 18 10000001 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 18 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer) 0 value\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 18 L0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer) FFFFFFFF value\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 18 FFFFFFFF s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 13 0001 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*server-keep-alive (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 13 0001 13 0001 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 13 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"topic-alias (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 23 0001 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*topic-alias (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 23 0001 23 0001 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias (two byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 23 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"content-type (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 03 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 03 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 03 0000 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"response-topic (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 08 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 08 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 08 0000 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 12 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 12 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 12 0000 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"response-information (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 1A s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 1A s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 1A 0000 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"correlation-data (binary)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 09 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"correlation-data (binary) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 09 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"correlation-data (binary) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 09 0000 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{\"name\": \"subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0B 01 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{\"name\": \"subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 0B s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   WILL ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"connect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"payload-format-indicator (byte)\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 v0 s1 'p' 02 01 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"payload-format-indicator (byte) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*payload-format-indicator (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 01 01 01 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer)\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 06 k10 v0 s1 'p' 05 02 L1 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*message-expiry-interval (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r31 s4 'MQTT' 05 06 k10 v0 s1 'p' 0A 02 L1 02 L1 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 02 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer)\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 06 k10 v0 s1 'p' 05 18 L1 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r31 s4 'MQTT' 05 06 k10 v0 s1 'p' 0A 18 L1 18 L1 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 18 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"content-type (UTF-8 string)\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 03 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*content-type (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 v0 s1 'p' 08 03 s1 'p' 03 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 03 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) empty\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 03 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"response-topic (UTF-8 string)\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 08 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*response-topic (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 v0 s1 'p' 08 08 s1 'p' 08 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 08 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 08 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"correlation-data (binary)\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 09 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*correlation-data (binary)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 v0 s1 'p' 08 09 s1 'p' 09 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"correlation-data (binary) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 09 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"correlation-data (binary) empty\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 09 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"user-property\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r28 s4 'MQTT' 05 06 k10 v0 s1 'p' 07 26 s1 'p' s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*user-property\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r35 s4 'MQTT' 05 06 k10 v0 s1 'p' 0E 26 s1 'p' s1 'p' 26 s1 'p' s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property missing value\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 26 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property missing key,value\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 26 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty key\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 v0 s1 'p' 06 26 0000 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty value\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 v0 s1 'p' 06 26 s1 'p' 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty key,value\", \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 06 k10 v0 s1 'p' 05 26 0000 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   WILL DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"connect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"request-problem-information (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 v0 s1 'p' 02 17 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"request-problem-information (byte) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 17 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*request-problem-information (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 17 01 17 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"request-response-information (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 v0 s1 'p' 02 19 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"request-response-information (byte) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 19 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*request-response-information (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 19 01 19 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"maximum-qos (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 v0 s1 'p' 02 24 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-qos (byte) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 24 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*maximum-qos (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 24 01 24 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"retain-available (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 v0 s1 'p' 02 25 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"retain-available (byte) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 25 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*retain-available (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 25 01 25 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 v0 s1 'p' 02 28 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 28 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*wildcard-subscription-available (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 28 01 28 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"subscription-identifier-available (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 v0 s1 'p' 02 29 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier-available (byte) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 29 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*subscription-identifier-available (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 29 01 29 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"shared-subscription-available (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 v0 s1 'p' 02 2A 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"shared-subscription-available (byte) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 2A s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*shared-subscription-available (byte)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 2A 01 2A 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 13 0001 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 13 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*server-keep-alive (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 v0 s1 'p' 06 13 0001 13 0001 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"receive-maximum (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 21 0001 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 21 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*receive-maximum (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 v0 s1 'p' 06 21 0001 21 0001 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 22 0001 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 22 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*topic-alias-maximum (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 v0 s1 'p' 06 22 0001 22 0001 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"topic-alias (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 23 0001 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias (two byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 23 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*topic-alias (two byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 v0 s1 'p' 06 23 0001 23 0001 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 06 k10 v0 s1 'p' 05 11 L1 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 11 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*session-expiry-interval (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r31 s4 'MQTT' 05 06 k10 v0 s1 'p' 0A 11 L1 11 L1 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 06 k10 v0 s1 'p' 05 27 L1 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 27 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*maximum-packet-size (four byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r31 s4 'MQTT' 05 06 k10 v0 s1 'p' 0A 27 L1 27 L1 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 12 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*assigned-client-identifier (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 v0 s1 'p' 08 12 s1 'p' 12 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 12 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 12 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 15 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*authentication-method (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 v0 s1 'p' 08 15 s1 'p' 15 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 15 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 15 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"response-information (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 1A s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*response-information (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 v0 s1 'p' 08 1A s1 'p' 1A s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 1A s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 1A 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"server-reference (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 1C s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*server-reference (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 v0 s1 'p' 08 1C s1 'p' 1C s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"server-reference (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 1C s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"server-reference (UTF-8 string) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 1C 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"reason-string (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 1F s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*reason-string (UTF-8 string)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 v0 s1 'p' 08 1F s1 'p' 1F s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"reason-string (UTF-8 string) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 1F s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"reason-string (UTF-8 string) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 1F 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"authentication-data (binary)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 v0 s1 'p' 04 16 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*authentication-data (binary)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 v0 s1 'p' 08 16 s1 'p' 16 s1 'p' s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"authentication-data (binary) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 16 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"authentication-data (binary) empty\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 v0 s1 'p' 03 16 0000 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"subscription-identifier (variable byte integer)\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 v0 s1 'p' 02 0B 01 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 82 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier (variable byte integer) missing\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 v0 s1 'p' 01 0B s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r3 00 81 00\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/DISCONNECT.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 DISCONNECT\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"E0 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r0\"}]},\n\t\t\t{ \"name\": \"E0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"E0 r268435456\"}]},\n\t\t\t{ \"name\": \"E0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 00\"}]},\n\t\t\t{ \"name\": \"E0 valid\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r0\"}]},\n\t\t\t{ \"name\": \"E1 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E1 r0\"}]},\n\t\t\t{ \"name\": \"E2 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E2 r0\"}]},\n\t\t\t{ \"name\": \"E4 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E4 r0\"}]},\n\t\t\t{ \"name\": \"E8 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E8 r0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   DISCONNECT\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"E0 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r0\"}]},\n\t\t\t{ \"name\": \"E0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"E0 r268435456\"}]},\n\t\t\t{ \"name\": \"E0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 00\"}]},\n\t\t\t{ \"name\": \"E0 valid\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r0\"}]},\n\t\t\t{ \"name\": \"E1 [MQTT-3.14.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E1 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E2 [MQTT-3.14.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E2 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E4 [MQTT-3.14.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E4 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E8 [MQTT-3.14.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E8 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 RC=0x00 (normal disconnection)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 00\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x01 (qos 1 - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 01\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x04 (disconnect with will)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 04\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x05 (invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 05\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x80 (unspecified error)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 80\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x81 (malformed packet)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 81\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x82 (protocol error)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 82\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x83 (implementation specific error)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 83\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x87 (not authorised - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 87\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x89 (server busy - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 89\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x8B (server shutting down - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 8B\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x8D (keep alive timeout - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 8D\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x8E (session taken over - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 8E\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x8F (topic filter invalid - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 8F\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x90 (topic name invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 90\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x93 (receive maximum exceeded)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 93\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x94 (topic alias invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 94\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x95 (packet too large)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 95\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x96 (message rate too high)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 96\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x97 (quota exceeded)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 97\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x98 (administrative action)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 98\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x99 (payload format invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 99\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9A (retain not supported - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9A\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9B (qos not supported - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9B\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9C (use another server - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9C\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9D (server moved - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9D\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9E (shared subs not supported - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9E\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9F (connection rate exceeded - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9F\"}]},\n\t\t\t{ \"name\": \"E0 RC=0xA0 (maximum connect time - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 A0\"}]},\n\t\t\t{ \"name\": \"E0 RC=0xA1 (subscription ids not supported - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 A1\"}]},\n\t\t\t{ \"name\": \"E0 RC=0xA2 (wildcard subs not supported - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 A2\"}]},\n\n\t\t\t{ \"name\": \"E0 RC=0x82 PL=0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r2 82 00\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=1 P=0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 0001 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=1 P=0x11\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 0001 11\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=2 P=0x11\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 0002 1100\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=3 P=0x11\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r5 0003 110000\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=4 P=0x11\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 0004 11000000\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=5 P=0x11\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r7 0005 1100000000\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 non-zero session expiry\", \"connect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 11 L0 s1 'p'\", \"comment\":\"CONNECT with session expiry=0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r7 00 05 11 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   DISCONNECT ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"E0 with reason-string property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 04 1F s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with 2*reason-string property (invalid)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r10 00 08 1F s1 'p' 1F s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with reason-string property missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 1F\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with reason-string property empty\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r5 00 03 1F 0000\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with user-property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r9 00 07 26 s1 'p' s1 'q'\"}]},\n\t\t\t{ \"name\": \"E0 with user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 00 04 26 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 26\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with user-property empty key\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r8 00 06 26 0000 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with user-property empty key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r8 00 06 26 s1 'p' 0000\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with user-property empty key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r7 00 05 26 0000 0000\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with session-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r7 00 05 1100000000\"}]},\n\t\t\t{ \"name\": \"E0 with 2*session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r12 00 0A 1100000000 1100000000\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 11\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with server-reference (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 04 1C s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with 2*server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r10 00 08 1C s1 'p' 1C s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 1C\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with server-reference (UTF-8 string) empty\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r5 00 03 1C 0000\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   DISCONNECT DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"E0 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0100\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 1700\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 2400\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 2500\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 2800\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 2900\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 2A00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 17\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 24\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 25\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 28\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 29\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 2A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r7 00 05 0200000001\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r7 00 05 1800000001\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r7 00 05 2700000001\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 02\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 18\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 27\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 00 04 03 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 00 04 08 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 00 04 12 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 00 04 15 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 00 04 1A s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 03\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 08\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 12\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 15\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 1A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 00 04 09 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 00 04 16 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 0109\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 0116\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0B01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 0B\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r5 00 03 130101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r5 00 03 210101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r5 00 03 220101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r5 00 03 230101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 13\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 21\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 22\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r3 00 01 23\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"E0 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0001\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0401\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0501\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0601\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0701\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0A01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0C01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0D01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0E01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 0F01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 1001\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 1401\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 1B01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 1D01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 1E01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 2001\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r4 00 02 7F01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r5 00 03 800001\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r5 00 03 800101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r5 00 03 FF7F01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 00 04 80800101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r6 00 04 FFFF7F01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r7 00 05 8080800101\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"E0 r7 00 05 FFFFFF7F01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/FLOW.json",
    "content": "[\n\t{\n\t\t\"comment\": \"FLOW TESTS ARE INCOMPLETE\",\n\t\t\"group\": \"v3.1.1 FLOW\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"QoS 0 self receive ok\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 01\", \"comment\":\"SUBSCRIBE, 'p' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r10 s1 'p' 'message'\", \"comment\":\"PUBLISH send\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r10 s1 'p' 'message'\", \"comment\":\"PUBLISH receive\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 1 receive ok\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 01\", \"comment\":\"SUBSCRIBE, 'p' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":1, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"32 r12 s1 'p' m1 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r2 m1\", \"comment\":\"PUBACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 1 PUBLISH-PUBREC\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 01\", \"comment\":\"SUBSCRIBE, 'p' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":1, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"32 r12 s1 'p' m1 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\", \"comment\":\"PUBREC\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 1 PUBLISH-PUBCOMP\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 01\", \"comment\":\"SUBSCRIBE, 'p' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":1, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"32 r12 s1 'p' m1 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r2 m1\", \"comment\":\"PUBCOMP\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 2 receive ok\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 02\", \"comment\":\"SUBSCRIBE, 'p' qos2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 02\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":2, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"34 r12 s1 'p' m1 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\", \"comment\":\"PUBREC\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\", \"comment\":\"PUBREL\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r2 m1\", \"comment\":\"PUBCOMP\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 2 PUBLISH-PUBACK\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 02\", \"comment\":\"SUBSCRIBE, 'p' qos2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 02\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":2, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"34 r12 s1 'p' m1 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r2 m1\", \"comment\": \"PUBACK (should be PUBREC)\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 2 PUBLISH-PUBCOMP\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 02\", \"comment\":\"SUBSCRIBE, 'p' qos2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 02\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":2, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"34 r12 s1 'p' m1 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r2 m1\", \"comment\": \"PUBCOMP (should be PUBREC)\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 2 PUBLISH-PUBREC-PUBREL-PUBACK\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 02\", \"comment\":\"SUBSCRIBE, 'p' qos2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 02\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":2, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"34 r12 s1 'p' m1 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\", \"comment\": \"PUBREC)\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\", \"comment\": \"PUBREL)\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r2 m1\", \"comment\": \"PUBACK (should be PUBCOMP))\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 2 PUBLISH-PUBREC-PUBREL-PUBREC\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 02\", \"comment\":\"SUBSCRIBE, 'p' qos2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 02\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":2, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"34 r12 s1 'p' m1 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\", \"comment\": \"PUBREC)\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\", \"comment\": \"PUBREL)\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\", \"comment\": \"PUBREC (should be PUBCOMP))\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\", \"comment\": \"PUBREL)\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   FLOW\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"QoS 0 self receive ok\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 00 s1 'p' 01\", \"comment\":\"SUBSCRIBE, 'p' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r11 s1 'p' 00 'message'\", \"comment\":\"PUBLISH send\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r11 s1 'p' 00 'message'\", \"comment\":\"PUBLISH receive\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 1 receive ok\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 00 s1 'p' 01\", \"comment\":\"SUBSCRIBE, 'p' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":1, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"32 r13 s1 'p' m1 00 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r2 m1\", \"comment\":\"PUBACK\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 1 PUBLISH-PUBREC\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 00 s1 'p' 01\", \"comment\":\"SUBSCRIBE, 'p' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":1, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"32 r13 s1 'p' m1 00 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\", \"comment\":\"PUBREC\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 1 PUBLISH-PUBCOMP\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 00 s1 'p' 01\", \"comment\":\"SUBSCRIBE, 'p' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":1, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"32 r13 s1 'p' m1 00 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r2 m1\", \"comment\":\"PUBCOMP\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 2 receive ok\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 00 s1 'p' 02\", \"comment\":\"SUBSCRIBE, 'p' qos2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 02\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":2, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"34 r13 s1 'p' m1 00 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\", \"comment\":\"PUBREC\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\", \"comment\":\"PUBREL\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r2 m1\", \"comment\":\"PUBCOMP\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 2 PUBLISH-PUBACK\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 00 s1 'p' 02\", \"comment\":\"SUBSCRIBE, 'p' qos2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 02\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":2, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"34 r13 s1 'p' m1 00 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r2 m1\", \"comment\": \"PUBACK (should be PUBREC)\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 2 PUBLISH-PUBCOMP\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 00 s1 'p' 02\", \"comment\":\"SUBSCRIBE, 'p' qos2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 02\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":2, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"34 r13 s1 'p' m1 00 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r2 m1\", \"comment\": \"PUBCOMP (should be PUBREC)\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 2 PUBLISH-PUBREC-PUBREL-PUBACK\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 00 s1 'p' 02\", \"comment\":\"SUBSCRIBE, 'p' qos2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 02\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":2, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"34 r13 s1 'p' m1 00 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\", \"comment\": \"PUBREC)\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\", \"comment\": \"PUBREL)\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r2 m1\", \"comment\": \"PUBACK (should be PUBCOMP))\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"QoS 2 PUBLISH-PUBREC-PUBREL-PUBREC\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 00 s1 'p' 02\", \"comment\":\"SUBSCRIBE, 'p' qos2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 02\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"publish\", \"topic\":\"p\", \"qos\":2, \"payload\":\"message\", \"comment\":\"helper\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"34 r13 s1 'p' m1 00 'message'\", \"comment\":\"PUBLISH receive\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\", \"comment\": \"PUBREC)\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\", \"comment\": \"PUBREL)\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\", \"comment\": \"PUBREC (should be PUBCOMP))\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\", \"comment\": \"PUBREL)\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   FLOW WITH PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"payload-format-indicator=1 (byte)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1234 00 s5 'topic' 01\", \"comment\":\"SUBSCRIBE, 'topic' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r17 s5 'topic' 02 0101 'payload'\", \"comment\": \"PUBLISH send\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r17 s5 'topic' 02 0101 'payload'\", \"comment\": \"PUBLISH recv\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"message-expiry-interval=1 (four byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1234 00 s5 'topic' 01\", \"comment\":\"SUBSCRIBE, 'topic' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r20 s5 'topic' 05 02 L1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r20 s5 'topic' 05 02 L1 'payload'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias\", \"expect_disconnect\":false, \"comment\":\"broker doesn't initiate topic alias\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1234 00 s5 'topic' 01\", \"comment\":\"SUBSCRIBE, 'topic' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r18 s5 'topic' 03 23 s1 'payload'\", \"comment\":\"PUBLISH with topic alias 1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r15 s5 'topic' 00 'payload'\", \"comment\":\"PUBLISH receive 1\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r13 s0 03 23 s1 'payload'\", \"comment\":\"PUBLISH with topic alias 1, no topic\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r15 s5 'topic' 00 'payload'\", \"comment\":\"PUBLISH receive 2\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1234 00 s5 'topic' 01\", \"comment\":\"SUBSCRIBE, 'topic' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r19 s5 'topic' 04 08 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r19 s5 'topic' 04 08 s1 'p' 'payload'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"correlation-data\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1234 00 s5 'topic' 01\", \"comment\":\"SUBSCRIBE, 'topic' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r19 s5 'topic' 04 09 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r19 s5 'topic' 04 09 s1 'p' 'payload'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1234 00 s5 'topic' 01\", \"comment\":\"SUBSCRIBE, 'topic' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r22 s5 'topic' 07 26 s1 'p' s1 'q' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r22 s5 'topic' 07 26 s1 'p' s1 'q' 'payload'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r13 m1234 02 0B01 s5 'topic' 01\", \"comment\":\"SUBSCRIBE, 'topic' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 'topic' 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r17 s5 'topic' 02 0B01 'payload'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"content-type\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1234 00 s5 'topic' 01\", \"comment\":\"SUBSCRIBE, 'topic' qos1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 01\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r19 s5 'topic' 04 03 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r19 s5 'topic' 04 03 s1 'p' 'payload'\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/FORBIDDEN.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 FORBIDDEN\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"00 first packet\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"00 r0\"}]},\n\t\t\t{ \"name\": \"00 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"00 r268435456\"}]},\n\t\t\t{ \"name\": \"01 first packet\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"01 r0\"}]},\n\t\t\t{ \"name\": \"02 first packet\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"02 r0\"}]},\n\t\t\t{ \"name\": \"04 first packet\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"04 r0\"}]},\n\t\t\t{ \"name\": \"08 first packet\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"08 r0\"}]},\n\t\t\t{ \"name\": \"00 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"00 r1 00\"}]},\n\t\t\t{ \"name\": \"00\", \"msgs\": [{\"type\":\"send\", \"payload\":\"00 r0\"}]},\n\t\t\t{ \"name\": \"01\", \"msgs\": [{\"type\":\"send\", \"payload\":\"01 r0\"}]},\n\t\t\t{ \"name\": \"02\", \"msgs\": [{\"type\":\"send\", \"payload\":\"02 r0\"}]},\n\t\t\t{ \"name\": \"04\", \"msgs\": [{\"type\":\"send\", \"payload\":\"04 r0\"}]},\n\t\t\t{ \"name\": \"08\", \"msgs\": [{\"type\":\"send\", \"payload\":\"08 r0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   FORBIDDEN\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"00 first packet\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"00 r0\"}]},\n\t\t\t{ \"name\": \"00 remaining length 5 bytes\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"00 r268435456\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\", \"comment\":\"DISCONNECT protocol error\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"01 first packet\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"01 r0\"}]},\n\t\t\t{ \"name\": \"02 first packet\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"02 r0\"}]},\n\t\t\t{ \"name\": \"04 first packet\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"04 r0\"}]},\n\t\t\t{ \"name\": \"08 first packet\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"08 r0\"}]},\n\t\t\t{ \"name\": \"00 long\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"00 r1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\", \"comment\":\"DISCONNECT protocol error\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"00\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"00 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\", \"comment\":\"DISCONNECT protocol error\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"01\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"01 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\", \"comment\":\"DISCONNECT protocol error\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"02\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"02 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\", \"comment\":\"DISCONNECT protocol error\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"04\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"04 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\", \"comment\":\"DISCONNECT protocol error\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"08\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"08 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\", \"comment\":\"DISCONNECT protocol error\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"0A with illegal length C0\", \"ver\":5, \"expect_disconnect\":true, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"0A C0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\", \"comment\":\"DISCONNECT protocol error\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/PINGREQ.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PINGREQ\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"C0 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 r0\"}]},\n\t\t\t{ \"name\": \"C0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"C0 r268435456\"}]},\n\t\t\t{ \"name\": \"C0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C00100\"}]},\n\t\t\t{ \"name\": \"C0 valid\", \"expect_disconnect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 r0\"}, {\"type\":\"recv\", \"payload\":\"D0 00\"}]},\n\t\t\t{ \"name\": \"C1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C1 r0\"}]},\n\t\t\t{ \"name\": \"C2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C2 r0\"}]},\n\t\t\t{ \"name\": \"C4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C4 r0\"}]},\n\t\t\t{ \"name\": \"C8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C8 r0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PINGREQ\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"C0 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 r0\"}]},\n\t\t\t{ \"name\": \"C0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"C0 r268435456\"}]},\n\t\t\t{ \"name\": \"C0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 r1 00\"}]},\n\t\t\t{ \"name\": \"C0 valid\", \"expect_disconnect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 r0\"}, {\"type\":\"recv\", \"payload\":\"D0 00\"}]},\n\t\t\t{ \"name\": \"C1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"C1 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"C2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"C2 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"C4\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"C4 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"C8\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"C8 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/PINGRESP.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PINGRESP\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"D0 [MQTT-3.1.0-1]\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r0\"}]},\n\t\t\t{ \"name\": \"D0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"D0 r268435456\"}]},\n\t\t\t{ \"name\": \"D0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r1 00\"}]},\n\t\t\t{ \"name\": \"D0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r0\"}]},\n\t\t\t{ \"name\": \"D1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D1 r0\"}]},\n\t\t\t{ \"name\": \"D2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D2 r0\"}]},\n\t\t\t{ \"name\": \"D4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D4 r0\"}]},\n\t\t\t{ \"name\": \"D8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D8 r0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PINGRESP\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"D0 [MQTT-3.1.0-1]\", \"connect\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r0\"}]},\n\t\t\t{ \"name\": \"D0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"D0 r268435456\"}]},\n\t\t\t{ \"name\": \"D0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r1 00\"}]},\n\t\t\t{ \"name\": \"D0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"D0 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"D1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"D1 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"D2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"D2 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"D4\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"D4 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"D8\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"D8 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/PUBACK.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PUBACK\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"40 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m1\"}]},\n\t\t\t{ \"name\": \"40 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"40 r268435456\"}]},\n\t\t\t{ \"name\": \"40 unsolicited long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"40 unsolicited mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m0\"}]},\n\t\t\t{ \"name\": \"40 unsolicited short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r0\"}]},\n\t\t\t{ \"name\": \"40 unsolicited short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r1 01\"}]},\n\t\t\t{ \"name\": \"40 unsolicited\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m1\"}]},\n\t\t\t{ \"name\": \"41 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"41 r2 m1\"}]},\n\t\t\t{ \"name\": \"42 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"42 r2 m1\"}]},\n\t\t\t{ \"name\": \"44 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"44 r2 m1\"}]},\n\t\t\t{ \"name\": \"48 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"48 r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBACK\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"40 [MQTT-3.1.0-1] (no reason code)\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m1\"}]},\n\t\t\t{ \"name\": \"40 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"40 r268435456\"}]},\n\t\t\t{ \"name\": \"40 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"40 unsolicited long\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 unsolicited mid 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r3 m0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 unsolicited short 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 unsolicited short 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 unsolicited len=2\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m1\"}]},\n\t\t\t{ \"name\": \"40 unsolicited len=3\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"40 unsolicited len=3 fail\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r3 m1 80\"}]},\n\t\t\t{ \"name\": \"40 unsolicited len=4 ok\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r4 m1 00 00\"}]},\n\t\t\t{ \"name\": \"40 unsolicited len=4 rc=fail\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r4 m1 80 00\"}]},\n\t\t\t{ \"name\": \"40 unsolicited len=4 rc=unknown\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r4 m1 FF 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 unsolicited len=4 short\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r4 m1 00 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"41 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"41 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"42 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"42 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"44 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"44 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"48 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"48 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBACK ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"40 with reason-string property\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 1F s1 'p'\"}]},\n\t\t\t{ \"name\": \"40 with 2*reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r12 m1 00 v8 1F s1 'p' 1F s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with reason-string property missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 m1 1F\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with reason-string property incomplete string\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 1F 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with reason-string property empty string\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 1F s0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r11 m1 00 v7 26 s1 'p' s1 'q'\"}]},\n\t\t\t{ \"name\": \"40 with 2*user-property\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r18 m1 00 v14 26 s1 'p' s1 'q' 26 s1 'p' s1 'q'\"}]},\n\t\t\t{ \"name\": \"40 with user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 26 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 26\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property empty key\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r10 m1 00 v6 26 s0 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property empty value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r10 m1 00 v6 26 s1 'p' s0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 26 s0 s0\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBACK DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"40 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 01 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 17 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 24 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 25 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 28 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 29 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 2A i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 17\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 24\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 25\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 28\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 29\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 2A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 02 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 11 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 18 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 27 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 02\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 11\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 18\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 27\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 03 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 08 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 12 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 15 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 1A s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 1C s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 03\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 08\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 12\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 15\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 1A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 1C\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 09 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 16 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 09\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 16\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0B v1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 0B\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 13 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 21 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 22 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 23 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 13\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 21\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 22\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 23\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"40 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 00 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 04 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 05 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 06 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 07 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0A i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0C i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0D i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0E i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 10 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 14 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 1B i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 1D i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 1E i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 20 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 8000 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 8001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 FF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 808001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 FFFF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 80808001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 FFFFFF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/PUBCOMP.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PUBCOMP\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"70 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r2 m1\"}]},\n\t\t\t{ \"name\": \"70 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"70 r268435456\"}]},\n\t\t\t{ \"name\": \"70 unsolicited long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"70 unsolicited mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r2 m0\"}]},\n\t\t\t{ \"name\": \"70 unsolicited short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r0\"}]},\n\t\t\t{ \"name\": \"70 unsolicited short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r1 01\"}]},\n\t\t\t{ \"name\": \"70 unsolicited\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r2 m1\"}]},\n\t\t\t{ \"name\": \"71 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"71 r2 m1\"}]},\n\t\t\t{ \"name\": \"72 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"72 r2 m1\"}]},\n\t\t\t{ \"name\": \"74 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"74 r2 m1\"}]},\n\t\t\t{ \"name\": \"78 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"78 r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBCOMP\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"70 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r2 m1\"}]},\n\t\t\t{ \"name\": \"70 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"70 r268435456\"}]},\n\t\t\t{ \"name\": \"70 unsolicited long\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 unsolicited mid 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r2 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 unsolicited short 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 unsolicited short 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 unsolicited short 3\", \"FIXME\":\"strictly, a short 3 should be malformed\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r3 m1 80\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 unsolicited\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r2 m1\"}]},\n\t\t\t{ \"name\": \"70 unsolicited rc\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"70 unsolicited rc=92\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r3 m1 92\"}]},\n\t\t\t{ \"name\": \"70 unsolicited rc=20\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r3 m1 20\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 unsolicited rc=FF\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r3 m1 FF\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 unsolicited rc,properties\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r4 m1 00 00\"}]},\n\t\t\t{ \"name\": \"71 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"71 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"72 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"72 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"74 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"74 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"78 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"78 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"71 unsolicited rc\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"71 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"72 unsolicited rc\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"72 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"74 unsolicited rc\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"74 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"78 unsolicited rc\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"78 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"71 unsolicited rc,properties\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"71 r4 m1 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"72 unsolicited rc,properties\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"72 r4 m1 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"74 unsolicited rc,properties\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"74 r4 m1 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"78 unsolicited rc,properties\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"78 r4 m1 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBCOMP ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"70 with reason-string property\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r8 m1 00 04 1F s1 'p'\"}]},\n\t\t\t{ \"name\": \"70 with 2*reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r12 m1 00 08 1F s1 'p' 1F s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with reason-string property missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 01 1F\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with reason-string property empty\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r7 m1 00 03 1F s0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with user-property\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r11 m1 00 07 26 s1 'p' s1 'q'\"}]},\n\t\t\t{ \"name\": \"70 with 2*user-property\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"70 r18 m1 00 0E 26 s1 'p' s1 'q' 26 s1 'p' s1 'q'\"}]},\n\t\t\t{ \"name\": \"70 with user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 04 26 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 01 26\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with user-property empty key\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r10 m1 00 06 26 s0 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with user-property empty value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r10 m1 00 06 26 s1 'p' s0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r9 m1 00 05 26 s0 s0\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBCOMP DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"70 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 01 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 17 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 24 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 25 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 28 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 29 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 2A i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 17\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 24\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 25\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 28\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 29\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 2A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r9 m1 00 v5 02 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r9 m1 00 v5 11 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r9 m1 00 v5 18 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r9 m1 00 v5 27 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 02\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 11\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 18\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 27\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 v4 03 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 v4 08 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 v4 12 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 v4 15 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 v4 1A s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 v4 1C s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 03\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 08\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 12\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 15\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 1A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 1C\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 v4 09 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 v4 16 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 09\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 16\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 0B v1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 0B\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r7 m1 00 v3 13 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r7 m1 00 v3 21 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r7 m1 00 v3 22 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r7 m1 00 v3 23 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 13\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 21\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 22\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r5 m1 00 v1 23\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"70 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 00 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 04 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 05 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 06 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 07 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 0A i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 0C i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 0D i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 0E i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 0F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 10 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 14 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 1B i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 1D i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 1E i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 20 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r6 m1 00 v2 7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r7 m1 00 v3 8000 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r7 m1 00 v3 8001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r7 m1 00 v3 FF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 v4 808001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r8 m1 00 v4 FFFF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r9 m1 00 v5 80808001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"70 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"70 r9 m1 00 v5 FFFFFF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/PUBLISH.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PUBLISH\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"30 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 'topic' 'payload'\"}]},\n\t\t\t{ \"name\": \"30 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"30 r268435456\"}]},\n\t\t\t{ \"name\": \"30\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 'topic' 'payload'\"}]},\n\t\t\t{ \"name\": \"31 retain 1\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"31 r14 s5 'topic' 'payload'\"}]},\n\t\t\t{ \"name\": \"31 retain 1 zero length\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"31 r7 s5 'topic'\"}]},\n\t\t\t{ \"name\": \"30 topic 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r9 s0 'payload'\"}]},\n\t\t\t{ \"name\": \"38 QoS 0 Dup 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"38 r14 s5 'topic' 'payload'\"}]},\n\t\t\t{ \"name\": \"36 QoS 3 (no mid) [MQTT-3.3.1-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"36 r14 s5 'topic' 'payload'\"}]},\n\t\t\t{ \"name\": \"36 QoS 3 (with mid) [MQTT-3.3.1-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"36 r16 s5 'topic' m1234 'payload'\"}]},\n\t\t\t{ \"name\": \"32 QoS 1 Mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"32 r16 s5 'topic' m0 'payload'\"}]},\n\t\t\t{ \"name\": \"34 QoS 2 Mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"34 r16 s5 'topic' m0 'payload'\"}]},\n\t\t\t{ \"name\": \"32 QoS 1 Dup 0\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r16 s5 'topic' m1234 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"3A QoS 1 Dup 1\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"3A r16 s5 'topic' m1234 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"34 QoS 2 Dup 0\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"34 r16 s5 'topic' m1234 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r2 m1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"3C QoS 2 Dup 1\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"3C r16 s5 'topic' m1234 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r2 m1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 746F700000 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 746FEDA080 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 746F700170 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 746F701F70 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 746F707F70 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 746FC29F70 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 746FEDBFBF 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+2A6D4 (section 1.5.3.1)\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 41F0AA9B94 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with + [MQTT-3.3.2-2]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 '+opic' 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with # [MQTT-3.3.2-2]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 '#opic' 'payload'\"}]},\n\t\t\t{ \"name\": \"34 QoS 2 repeated with/without 'payload'\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"34 r9 s5 'topic' m1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"34 r10 s5 'topic' m1234 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"34 r9 s5 'topic' m1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBLISH\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"30 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r15 s5 'topic' 00 'payload'\"}]},\n\t\t\t{ \"name\": \"30 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"30 r268435456\"}]},\n\t\t\t{ \"name\": \"30\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r15 s5 'topic' 00 'payload'\"}]},\n\t\t\t{ \"name\": \"31 retain 1\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"31 r15 s5 'topic' v0 'payload'\"}]},\n\t\t\t{ \"name\": \"31 retain 1 zero length\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"31 r8 s5 'topic' v0\"}]},\n\t\t\t{ \"name\": \"30 topic 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r10 s0 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"38 QoS 0 Dup 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"38 r15 s5 'topic' v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"36 QoS 3 (no mid) [MQTT-3.3.1-4]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"36 r15 s5 'topic' v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"36 QoS 3 (with mid) [MQTT-3.3.1-4]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"3611 s5 'topic' m1234 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"32 QoS 1 Mid 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r17 s5 'topic' m0 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"34 QoS 2 Mid 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"34 r17 s5 'topic' m0 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"32 QoS 1 Dup 0\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r17 s5 'topic' m1234 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"3A QoS 1 Dup 1\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"3A r17 s5 'topic' m1234 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"34 QoS 2 Dup 0\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"34 r17 s5 'topic' m1234 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r2 m1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"3C QoS 2 Dup 1\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"3C r17 s5 'topic' m1234 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r2 m1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with 0x0000\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746F700000 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+D800\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746FEDA080 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+0001\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746F700170 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+001F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746F701F70 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+007F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746F707F70 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+009F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746FC29F70 v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+FFFF\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746FEDBFBF v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+2A6D4 (section 1.5.3.1)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 41F0AA9B94 v0 'payload'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with + [MQTT-3.3.2-2]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 '+opic' v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with # [MQTT-3.3.2-2]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 '#opic' v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBLISH ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"maximum packet size\", \"connect\":false, \"expect_disconnect\":false, \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 v5 2700000014 s1 'p'\", \"comment\":\"CONNECT with max-packet-size 20\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"20 r14 00000B 22 H10 27 L2000000 21 H20\", \"comment\": \"CONNACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1234 v0 s5 'topic' 00\", \"comment\":\"SUBSCRIBE topic\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 00\", \"comment\":\"SUBACK\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r22 s5 'topic' v0 'payloadpayload'\", \"comment\":\"PUBLISH with size > 20\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 'topic' v0 'payload'\", \"comment\":\"PUBLISH with size < 20\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"30 r15 s5 'topic' v0 'payload'\", \"comment\":\"PUBLISH with size < 20, returned\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"payload-format-indicator=0 (byte)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 01 i0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"payload-format-indicator=1 (byte)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 01 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"payload-format-indicator=2 (byte, invalid)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 01 i2 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*payload-format-indicator=1 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 01 i1 01 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 01 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"message-expiry-interval=0 (four byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 02 L0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"message-expiry-interval=1 (four byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 02 L1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*message-expiry-interval=1 (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r27 s5 'topic' m1234 v10 02 L1 02 L1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 02 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"topic alias > max topic alias\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r18 s5 'topic' v3 23 H11 'payload'\", \"comment\":\"PUBLISH with topic alias 11 (server has set max topic alias=10)\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 94\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias (two byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 23 H1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 23 H1 23 H1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*topic-alias different (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 23 H1 23 H2 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 23 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"response-topic (UTF-8 string)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 08 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string, with wildcard)\", \"ver\":5, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 08 s1 '#' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r25 s5 'topic' m1234 v8 08 s1 'p' 08 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 08 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) empty\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 08 s0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"correlation-data (binary data)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 09 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r25 s5 'topic' m1234 v8 09 s1 'p' 09 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 09 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"correlation-data (binary data) empty\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 09 H0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r24 s5 'topic' m1234 v7 26 s1 'p' s1 'q' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r31 s5 'topic' m1234 v14 26 s1 'p' s1 'q' 26 s1 'p' s1 'q' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 26 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 26 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty key\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 26 s0 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 26 s1 'p' s0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 26 s0 s0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 03 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r25 s5 'topic' m1234 v8 03 s1 'p' 03 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 03 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) empty\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 03 s0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r3 m1234 10\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBLISH DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"subscription-identifier=0 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0B v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x7F (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0B 7F 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x8000 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 0B 8000 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x8001 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 0B 8001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0xFF7F (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 0B FF7F 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x808001 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 0B 808001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0xFFFF7F (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 0B FFFF7F 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x80808001 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 0B 80808001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0xFFFFFF7F (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 0B FFFFFF7F 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x8080808001 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 0B 8080808001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*subscription-identifier=1 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 0B v1 0B v1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 0B 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 1F s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 1700 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 2400 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 2500 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 2800 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 2900 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 2A00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 17 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 24 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 25 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 28 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 29 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 2A 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 1100000001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 1800000001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 2700000001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v4 11 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v4 18 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v4 27 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 12 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 15 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 1A s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 1C s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 12 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 15 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 1A 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 1C 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 16 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 16 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 13 H5 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 21 H5 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 22 H5 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 13 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 21 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 22 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 00 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 04 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 05 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 06 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 07 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0A i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0C i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0D i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0E i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0F i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 10 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 14 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 1B i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 1D i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 1E i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 20 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 7F i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 8000 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 8001 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 FF7F i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 808001 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 FFFF7F i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 80808001 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 FFFFFF7F i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x8080808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 8080808001 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/PUBREC.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PUBREC\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r2 m1\"}]},\n\t\t\t{ \"name\": \"50 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"50 r268435456\"}]},\n\t\t\t{ \"name\": \"50 unsolicited\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t] },\n\t\t\t{ \"name\": \"50 unsolicited long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"50 unsolicited mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r2 m0\"}]},\n\t\t\t{ \"name\": \"50 unsolicited short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r0\"}]},\n\t\t\t{ \"name\": \"50 unsolicited short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r1 01\"}]},\n\t\t\t{ \"name\": \"51 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"51 r2 m1\"}]},\n\t\t\t{ \"name\": \"52 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"52 r2 m1\"}]},\n\t\t\t{ \"name\": \"54 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"54 r2 m1\"}]},\n\t\t\t{ \"name\": \"58 unsolicited\", \"msgs\": [{\"type\":\"send\", \"payload\":\"58 r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREC\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50 [MQTT-3.1.0-1] (no reason code)\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r2 m1\"}]},\n\t\t\t{ \"name\": \"50 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"50 r268435456\"}]},\n\t\t\t{ \"name\": \"50 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"50 unsolicited long\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 unsolicited mid 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r3 m0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 unsolicited short 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 unsolicited short 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 unsolicited len=2\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 unsolicited len=3\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 unsolicited len=3 fail\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r3 m1 80\"}]},\n\t\t\t{ \"name\": \"50 unsolicited len=4 ok\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r4 m1 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 unsolicited len=4 rc=fail\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r4 m1 80 00\"}]},\n\t\t\t{ \"name\": \"50 unsolicited len=4 rc=unknown\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r4 m1 FF 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 unsolicited len=4 short\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r4 m1 00 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"51 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"51 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"52 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"52 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"54 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"54 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"58 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"58 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREC ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50 with reason-string property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 1F s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with 2*reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r12 m1 00 v8 1F s1 'p' 1F s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with reason-string property missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 1F\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with reason-string property empty\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 1F s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r11 m1 00 v7 26 s1 'p' s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with 2*user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r18 m1 00 v14 26 s1 'p' s1 'q' 26 s1 'p' s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 26 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 26\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with user-property empty key\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r10 m1 00 v6 26 s0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with user-property empty value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r10 m1 00 v6 26 s1 'p' s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 26 s0 s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREC DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 01 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 17 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 24 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 25 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 28 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 29 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 2A i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 17\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 24\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 25\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 28\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 29\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 2A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 02 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 11 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 18 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 27 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 02\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 11\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 18\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 27\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 03 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 08 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 12 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 15 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 1A s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 1C s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 03\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 08\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 12\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 15\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 1A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 1C\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 09 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 16 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 09\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 16\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0B v1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 0B\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 13 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 21 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 22 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 23 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 13\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 21\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 22\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 23\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"50 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 00 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 04 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 05 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 06 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 07 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0A i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0C i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0D i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0E i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 10 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 14 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 1B i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 1D i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 1E i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 20 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 8000 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 8001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 FF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 808001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 FFFF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 80808001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 FFFFFF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/PUBREL.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PUBREL\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"62 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"62 r2 m1\"}]},\n\t\t\t{ \"name\": \"62 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"62 r268435456\"}]},\n\t\t\t{ \"name\": \"62 unsolicited\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"62 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"62 unsolicited mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"62 r2 m0\"}]},\n\t\t\t{ \"name\": \"62 unsolicited short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"62 r0\"}]},\n\t\t\t{ \"name\": \"62 unsolicited short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"62 r1 01\"}]},\n\t\t\t{ \"name\": \"63 unsolicited [MQTT-3.6.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"63 r2 m1\"}]},\n\t\t\t{ \"name\": \"64 unsolicited [MQTT-3.6.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"64 r2 m1\"}]},\n\t\t\t{ \"name\": \"66 unsolicited [MQTT-3.6.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"66 r2 m1\"}]},\n\t\t\t{ \"name\": \"6A unsolicited [MQTT-3.6.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"6A r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREL\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"62 [MQTT-3.1.0-1] (no reason code)\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"62 r2 m1\"}]},\n\t\t\t{ \"name\": \"62 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"62 r268435456\"}]},\n\t\t\t{ \"name\": \"62 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"62 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"62 unsolicited long\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited mid 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r3 m0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited short 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited short 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited len=2\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited len=3\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited len=3 fail\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r3 m1 92\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited len=4 ok\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r4 m1 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited len=4 rc=fail\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r4 m1 92 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited len=4 rc=unknown\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r4 m1 FF 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 unsolicited len=4 short\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r4 m1 00 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"63 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"63 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"64 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"64 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"66 unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"66 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"6A unsolicited\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"6A r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREL ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"62 with reason-string property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 04 1F s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with 2*reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r12 m1 00 08 1F s1 'p' 1F s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with reason-string property missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 01 1F\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with reason-string property empty\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r7 m1 00 03 1F s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r11 m1 00 07 26 s1 'p' s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with 2*user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r18 m1 00 0E 26 s1 'p' s1 'q' 26 s1 'p' s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 04 26 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 01 26\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with user-property empty key\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r10 m1 00 06 26 s0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with user-property empty value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r10 m1 00 06 26 s1 'p' s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r9 m1 00 05 26 s0 s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREL DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"62 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 01 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 17 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 24 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 25 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 28 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 29 i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 2A i0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 17\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 24\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 25\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 28\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 29\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 2A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r9 m1 00 v5 02 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r9 m1 00 v5 11 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r9 m1 00 v5 18 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r9 m1 00 v5 27 L1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 02\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 11\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 18\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 27\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 v4 03 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 v4 08 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 v4 12 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 v4 15 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 v4 1A s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 v4 1C s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 03\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 08\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 12\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 15\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 1A\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 1C\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 v4 09 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 v4 16 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 09\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 16\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 0B v1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 0B\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r7 m1 00 v3 13 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r7 m1 00 v3 21 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r7 m1 00 v3 22 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r7 m1 00 v3 23 H5\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 13\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 21\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 22\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r5 m1 00 v1 23\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"62 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 00 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 04 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 05 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 06 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 07 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 0A i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 0C i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 0D i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 0E i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 0F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 10 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 14 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 1B i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 1D i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 1E i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 20 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r6 m1 00 v2 7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r7 m1 00 v3 8000 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r7 m1 00 v3 8001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r7 m1 00 v3 FF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 v4 808001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r8 m1 00 v4 FFFF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r9 m1 00 v5 80808001 i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"62 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r9 m1 00 v5 FFFFFF7F i1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/REGRESSION.json",
    "content": "[\n\t{\n\t\t\"group\": \"REGRESSIONS\",\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"subscribe-unsubscribe-crash part 1\", \"ver\":4, \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r38 m1234 s9 'drash/1/#' 00 s9 'erash/2/#' 00 s9 'crash/3/#' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r5 m1234 00 00 00\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r13 m1234 s9 'drash/1/#'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r2 m1234\"}\n\t\t\t], \"comment\": \"Must be used with part 2 immediately after\",\n\t\t\t\"comment2\": \"Requires WITH_ASAN=yes\"},\n\t\t\t{ \"name\": \"subscribe-unsubscribe-crash part 2\", \"ver\":4, \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r14 m1234 s9 'crash/3/#' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 00\"}\n\t\t\t], \"comment\": \"https://github.com/eclipse/mosquitto/issues/2885\"}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"REGRESSIONS\",\n\t\t\"tests\": [\n\t\t\t{\n\t\t\t\t\"name\": \"mismatched-shared-normal-subscribe-unsubscribe-leak\", \"ver\":4, \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r26 m1 s21 '$share/sharename/test' 01\"},\n\t\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1 01\"},\n\t\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m2 s4 'test' 00\"},\n\t\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m2 00\"},\n\t\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m7 s4 'test'\"},\n\t\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r2 m7\"}\n\t\t\t\t],\n\t\t\t\t\"comment\": \"Also part one of the next two tests\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"acl-check-uaf\", \"ver\":4, \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r13 s4 'test' 'payload'\"}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"shared-sub-uaf\", \"ver\":4, \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r26 m1 s21 '$share/sharename/test' 01\"},\n\t\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1 01\"},\n\t\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m2 s4 'test' 00\"},\n\t\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m2 00\"},\n\t\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m7 s4 'test'\"},\n\t\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r2 m7\"}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/SUBACK.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 SUBACK\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"90 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"90 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"90 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"90 r268435456\"}]},\n\t\t\t{ \"name\": \"90 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"90 r3 m0 00\"}]},\n\t\t\t{ \"name\": \"90\", \"msgs\": [{\"type\":\"send\", \"payload\":\"90 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"90 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"90 r0\"}]},\n\t\t\t{ \"name\": \"90 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"90 r1 01\"}]},\n\t\t\t{ \"name\": \"90 short 2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"90 r2 m1\"}]},\n\t\t\t{ \"name\": \"91\", \"msgs\": [{\"type\":\"send\", \"payload\":\"91 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"92\", \"msgs\": [{\"type\":\"send\", \"payload\":\"92 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"94\", \"msgs\": [{\"type\":\"send\", \"payload\":\"94 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"98\", \"msgs\": [{\"type\":\"send\", \"payload\":\"98 r3 m1 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   SUBACK\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"90 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"90 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"90 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"90 r268435456\"}]},\n\t\t\t{ \"name\": \"90 long\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 short 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 short 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 short 2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 short 3\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"91\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"91 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"92\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"92 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"94\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"94 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"98\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"98 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 04 1F s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0x01 qos 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0x02 qos 2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 02\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0x11 no sub\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 11\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0x80 unspecified error\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 80\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0x83 implementation specific error\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 83\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0x87 not authorised\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 87\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0x8F topic filter invalid\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 8F\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0x91 packet identifier in use\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 91\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0x97 quota exceeded\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 97\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0x9E shared subs not supported\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 9E\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0xA1 sub ids not supported\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 A1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0xA2 wildcards not supported\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 A2\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 reason code 0xFF unknown\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r4 m1 00 FF\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   SUBACK ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"90 with reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 04 1F s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with 2*reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r12 m1 0008 1F s1 'p' 1F s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with reason-string property missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 01 1F 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with user-property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r11 m1 07 26 s1 'p' s1 'q' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 04 26 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 01 26 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with user-property empty key\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r10 m1 06 26 s0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with user-property empty value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r10 m1 06 26 s1 'p' s0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with user-property empty key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r9 m1 05 26 s0 s0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   SUBACK DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"90 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 01 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 17 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 24 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 25 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 28 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 29 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 2A i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 01 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 17 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 24 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 25 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 28 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 29 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 2A 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r9 m1 v5 02 L1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r9 m1 v5 11 L1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r9 m1 v5 18 L1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r9 m1 v5 27 L1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 02 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 11 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 18 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 27 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 v4 03 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 v4 08 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 v4 12 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 v4 15 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 v4 1A s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 v4 1C s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 03 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 08 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 12 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 15 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 1A00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 1C00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 v4 09 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 v4 16 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 09 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 16 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 0B v1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 0B 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r7 m1 v3 13 H5 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r7 m1 v3 21 H5 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r7 m1 v3 22 H5 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r7 m1 v3 23 H5 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 13 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 21 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 22 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r5 m1 v1 23 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"90 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 00 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 04 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 05 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 06 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 07 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 0A i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 0C i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 0D i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 0E i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 0F i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 10 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 14 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 19 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 1D i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 1E i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 20 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r6 m1 v2 7F i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r7 m1 v3 8000 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r7 m1 v3 8001 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r7 m1 v3 FF7F i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 v4 808001 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r8 m1 v4 FFFF7F i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r9 m1 v5 80808001 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"90 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"90 r9 m1 v5 FFFFFF7F i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/SUBSCRIBE.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 SUBSCRIBE\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"82 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"82 r268435456\"}]},\n\t\t\t{ \"name\": \"80\", \"msgs\": [{\"type\":\"send\", \"payload\":\"80061234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"83 [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"83 r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"84 [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"84 r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"86 [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"86 r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"8A [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"8A r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 QoS 3 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 03\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x04 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 04\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x08 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 08\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x10 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 10\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x20 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 20\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x40 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 40\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x80 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 80\"}]},\n\t\t\t{ \"name\": \"82 topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746F700000 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746FEDA080 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746F700170 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746F701F70 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746F707F70 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746FC29F70 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746FEDBFBF 00\"} ] },\n\t\t\t{ \"name\": \"82 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 s1 'p' 00 00\"}]},\n\t\t\t{ \"name\": \"82 short 5 [MQTT-3.8.3-3]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"82 short 4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r4 m1234 0000\"}]},\n\t\t\t{ \"name\": \"82 short 3\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r3 m1234 00\"}]},\n\t\t\t{ \"name\": \"82 short 2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r2 1234\"}]},\n\t\t\t{ \"name\": \"82 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r1 12\"}]},\n\t\t\t{ \"name\": \"82 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r0\"}]},\n\t\t\t{ \"name\": \"82 single topic len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r5 m1234 s0 00\"}]},\n\t\t\t{ \"name\": \"82 multiple topic 1 len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1234 s0 00 s1 'q' 00\"}]},\n\t\t\t{ \"name\": \"82 multiple topic 2 len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1234 s1 'q' 00 s0 00\"}]},\n\t\t\t{ \"name\": \"82 multiple topic 1,2 len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r8 m1234 s0 00 s0 00\"}]},\n\t\t\t{ \"name\": \"82 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 0 [MQTT-3.8.4-1]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 single ok QoS 1 [MQTT-3.8.4-1]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 01\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 single ok QoS 2 [MQTT-3.8.4-1]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 02\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 02\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 multiple ok [MQTT-3.8.4-4]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1234 s1 'p' 00 s1 'q' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 00 00\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   SUBSCRIBE\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"82 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 0 [MQTT-3.8.4-1]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 single ok QoS 1 [MQTT-3.8.4-1]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 01\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 single ok QoS 2 [MQTT-3.8.4-1]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 02\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 02\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"80\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"80 r7 m1234 v0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"83 [MQTT-3.8.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"83 r7 m1234 v0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"84 [MQTT-3.8.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"84 r7 m1234 v0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"86 [MQTT-3.8.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"86 r7 m1234 v0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"8A [MQTT-3.8.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"8A r7 m1234 v0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 3 [MQTT-3-8.3-2] (no qos)\", \"ver\":5, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 v0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 3 [MQTT-3-8.3-2] (no topic or qos)\", \"ver\":5, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r3 m1234 v0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 3 [MQTT-3-8.3-4]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 03\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 0 no local 0x04\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 04\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 0 retain as published 0x08\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 08\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 0 retain handling=1 0x10\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 10\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 0 retain handling=2 0x20\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 20\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 0 retain handling=3 0x30 [MQTT-3-8.3-4]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 30\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 0 options=0x40 [MQTT-3-8.3-5]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 40\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 0 options=0x80 [MQTT-3-8.3-5]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 80\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 QoS 0 options=0xF0 [MQTT-3-8.3-5]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' F0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 topic with 0x0000\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746F700000 'payload' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 topic with U+D800\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746FEDA080 'payload' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 topic with U+0001\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746F700170 'payload' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 topic with U+001F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746F701F70 'payload' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 topic with U+007F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746F707F70 'payload' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 topic with U+009F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746FC29F70 'payload' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 topic with U+FFFF\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746FEDBFBF 'payload' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 long\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r8 m1234 v0 s1 'p' 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 short 5 [MQTT-3.8.3-3]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 v0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 short 5\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r5 m1234 v0 0000\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 short 4\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r4 m1234 v0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 short 3\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r3 m1234 v0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 short 2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r2 1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 short 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r1 12\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 short 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 single topic len 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 v0 s0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 multiple topic 1 len 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1234 v0 s0 00 s1 'q' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 multiple topic 2 len 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1234 v0 s1 'q' 00 s0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 multiple topic 1,2 len 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1234 v0 s0 00 s0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 single ok QoS 0 [MQTT-3.8.4-1]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 single ok QoS 1 [MQTT-3.8.4-1]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 01\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 single ok QoS 2 [MQTT-3.8.4-1]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 02\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 02\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 multiple ok [MQTT-3.8.4-4]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1234 v0 s1 'p' 00 s1 'q' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r5 m1234 v0 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 mid 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m0 00 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 shared sub with + in sharename\", \"ver\":5, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r16 m1234 v0 s10 '$share/+/p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 shared sub with # in sharename\", \"ver\":5, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r16 m1234 v0 s10 '$share/#/p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 shared sub with no topic part 1\", \"ver\":5, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r15 m1234 v0 s9 '$share/p/' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 shared sub with no topic part 2\", \"ver\":5, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r14 m1234 v0 s8 '$share/p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 shared sub with no local set\", \"ver\":5, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r16 m1234 v0 s10 '$share/p/p' 04\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   SUBSCRIBE ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"82 with user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r14 m1 v7 26 s1 'p' s1 'q' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with 2*user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r21 m1 v14 26 s1 'p' s1 'q' 26 s1 'p' s1 'q' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 26 s1 'p' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r8 m1 v1 26 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with user-property empty key\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r13 m1 v6 26 s0 s1 'p' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with user-property empty value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r13 m1 v6 26 s1 'p' s0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 26 s0 s0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"82 with subscription-identifier missing (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r8 m1 v1 0B s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0B v0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=1 (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0B v1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with 2*subscription-identifier=1 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 0B v1 0B v1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x7F (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0B 7F s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x8000 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 0B 8000 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x8001 (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 0B 8001 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0xFF7F (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 0B FF7F s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x808001 (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 0B 808001 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0xFFFF7F (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 0B FFFF7F s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x80808001 (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 0B 80808001 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0xFFFFFF7F (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 0B FFFFFF7F s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x8080808001 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r13 m1 v6 0B 8080808001 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   SUBSCRIBE DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"82 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 01 i0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 17 i0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 24 i0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 25 i0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 28 i0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 29 i0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 2A i0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"82 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 02 L1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 11 L1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 18 L1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 27 L1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"82 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 03 s1 'p' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 08 s1 'p' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 12 s1 'p' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 15 s1 'p' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 1A s1 'p' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 1C s1 'p' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"82 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 09 s1 'p' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 16 s1 'p' s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"82 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 13 H5 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 21 H5 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 22 H5 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 23 H5 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"82 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 00 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 04 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 05 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 06 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 07 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0A i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0C i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0D i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0E i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0F i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 10 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 14 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 1B i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 1D i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 1E i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 20 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 7F i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 8000 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 8001 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 FF7F i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 808001 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 FFFF7F i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 80808001 i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"82 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 FFFFFF7F i1 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/UNSUBACK.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 UNSUBACK\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"B0 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r2 m1\"}]},\n\t\t\t{ \"name\": \"B0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"B0 r268435456\"}]},\n\t\t\t{ \"name\": \"B0 multi\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B0 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r0\"}]},\n\t\t\t{ \"name\": \"B0 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r1 01\"}]},\n\t\t\t{ \"name\": \"B0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r2 m1\"}]},\n\t\t\t{ \"name\": \"B1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B1 r2 m1\"}]},\n\t\t\t{ \"name\": \"B2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B2 r2 m1\"}]},\n\t\t\t{ \"name\": \"B4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B4 r2 m1\"}]},\n\t\t\t{ \"name\": \"B8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B8 r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBACK\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"B0 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"B0 r268435456\"}]},\n\t\t\t{ \"name\": \"B0 multi\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r4 m1 v0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 short 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 short 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r1 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 short 2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r3 m1 v0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B1 r3 m1 v0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B2 r3 m1 v0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B4\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B4 r3 m1 v0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B8\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B8 r3 m1 v0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r7 m1 04 1F s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 reason code 0x11 no sub\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r4 m1 v0 11\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 reason code 0x80 unspecified error\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r4 m1 v0 80\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 reason code 0x83 implementation specific error\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r4 m1 v0 83\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 reason code 0x87 not authorised\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r4 m1 v0 87\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 reason code 0x8F topic filter invalid\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r4 m1 v0 8F\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 reason code 0x91 packet identifier in use\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r4 m1 v0 91\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 reason code 0xFF unknown\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r4 m1 v0 FF\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBACK ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"B0 with reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 1F s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with reason-string property missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 1F 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with user-property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r11 m1 v7 26 s1 'p' s1 'q' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 26 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 26 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with user-property empty key\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r10 m1 v6 26 s0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with user-property empty value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r10 m1 v6 26 s1 'p' s0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with user-property empty key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 26 s0 s0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBACK DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"B0 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 01 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 17 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 24 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 25 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 28 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 29 i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 2A i0 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 01 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 17 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 24 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 25 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 28 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 29 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 2A 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 02 L1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 11 L1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 18 L1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 27 L1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 02 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 11 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 18 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 27 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 03 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 08 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 12 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 15 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 1A s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 1C s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 03 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 08 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 12 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 15 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 1A 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 1C 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 09 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 16 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 09 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 16 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v2 0B v1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r4 m1 v1 0B\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 13 H5 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 21 H5 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 22 H5 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 23 H5 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 13 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 21 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 22 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 23 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"B0 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 00 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 04 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 05 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 06 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 07 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 0A i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 0C i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 0D i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 0E i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 0F i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 10 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 14 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 1B i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 1D i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 1E i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 20 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 7F i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 8000 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 8001 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 FF7F i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 808001 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 FFFF7F i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 80808001 i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 FFFFFF7F i1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/UNSUBSCRIBE.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 UNSUBSCRIBE\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"A2 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"A2 r268435456\"}]},\n\t\t\t{ \"name\": \"A2 (no subscribe) [MQTT-3.10.4-5]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r5 m1234 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 (with subscribe) [MQTT-3.10.4-5]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r3 m1234 00\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r5 m1234 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 multiple [MQTT-3.10.4-6]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1234 s1 'p' s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 multiple zero 1st\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r7 m1234 0000 s1 'q'\"}]},\n\t\t\t{ \"name\": \"A2 multiple zero 2nd\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r7 m1234 s1 'q' 0000\"}]},\n\t\t\t{ \"name\": \"A2 short 4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r4 m1234 0001\"}]},\n\t\t\t{ \"name\": \"A2 short 3\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r3 m1234 01\"}]},\n\t\t\t{ \"name\": \"A2 short 2 [MQTT-3.10.3-2]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r2 m1234\"}]},\n\t\t\t{ \"name\": \"A2 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r1 12\"}]},\n\t\t\t{ \"name\": \"A2 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r0\"}]},\n\t\t\t{ \"name\": \"A0 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A0 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A3 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A3 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A4 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A4 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A6 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A6 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"AA [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"AA r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746F700000\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746F700170\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"A2 zero mid\", \"msgs\": [ {\"type\":\"send\", \"payload\":\"A2 r8 0000 s1 'p' s1 'q'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBSCRIBE\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"A2 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r6 m1234 00 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"A2 r268435456\"}]},\n\t\t\t{ \"name\": \"A2 (no subscribe) [MQTT-3.10.4-5]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r6 m1234 v0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r4 m1234 v0 11\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 (with subscribe) [MQTT-3.10.4-5]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"90 r4 m1234 v0 00\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r6 m1234 v0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r4 m1234 v0 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 multiple zero 1st\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1234 v0 0000 s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 multiple zero 2nd\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1234 v0 s1 'q' 0000\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 short 5\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r5 m1234 v0 0001\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 short 4\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r4 m1234 v0 01\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 short 3 [MQTT-3.10.3-2]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r3 m1234 v0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 short 2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r1 1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 short 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r1 12\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 short 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A0 [MQTT-3.10.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A0 r6 m1234 v0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A3 [MQTT-3.10.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A3 r6 m1234 v0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A4 [MQTT-3.10.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A4 r6 m1234 v0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A6 [MQTT-3.10.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A6 r6 m1234 v0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"AA [MQTT-3.10.1-1]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"AA r6 m1234 v0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 topic with 0x0000\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1234 v0 s5 746F700000\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 topic with U+D800\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1234 v0 s5 746FEDA080\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 topic with U+0001\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1234 v0 s5 746F700170\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 topic with U+001F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1234 v0 s5 746F701F70\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 topic with U+007F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1234 v0 s5 746F707F70\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 topic with U+009F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1234 v0 s5 746FC29F70\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 topic with U+FFFF\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1234 v0 s5 746FEDBFBF\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 multiple [MQTT-3.10.4-6]\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r9 m1234 v0 s1 'p' s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r5 m1234 v0 11 11\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 zero mid\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r6 m0 00 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBSCRIBE ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"A2 with user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r13 m1 v7 26 s1 'p' s1 'q' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r4 m1 00 11\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with 2*user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r20 m1 v14 26 s1 'p' s1 'q' 26 s1 'p' s1 'q' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r4 m1 00 11\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 26 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r7 m1 v1 26 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with user-property empty key\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r12 m1 v6 26 s0 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r4 m1 00 11\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with user-property empty value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r12 m1 v6 26 s1 'p' s0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r4 m1 00 11\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 26 s0 s0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"B0 r4 m1 00 11\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBSCRIBE DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"A2 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 01 i0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 17 i0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 24 i0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 25 i0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 28 i0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 29 i0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 2A i0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"A2 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 02 L1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 11 L1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 18 L1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 27 L1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"A2 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 03 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 08 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 12 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 15 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 1A s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 1C s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"A2 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 09 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 16 s1 'p' s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"A2 with subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0B v1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"A2 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 13 H5 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 21 H5 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 22 H5 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 23 H5 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"A2 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 00 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 04 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 05 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 06 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 07 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0A i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0C i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0D i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0E i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0F i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 10 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 14 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 1B i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 1D i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 1E i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 20 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 7F i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 8000 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 8001 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 FF7F i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 808001 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 FFFF7F i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 80808001 i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 FFFFFF7F i1 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/data/ZZ-broker-check.json",
    "content": "[\n\t{\n\t\t\"group\": \"BROKER CHECK\",\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"END OF TEST\", \"ver\":4, \"expect_disconnect\": false, \"msgs\": []}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/broker/dynamic-security-init.json",
    "content": "{\n\t\"clients\":\t[{\n\t\t\t\"username\":\t\"admin\",\n\t\t\t\"textName\":\t\"Dynsec admin user\",\n\t\t\t\"password\":\t\"Rko31yHY12ryMoyZTBNIUsCPb5SDa4WmUP3Xe2+V6P+QOSW3Gj6IDmpl6zQsAjutb476zEYdBeTw9tU7WZ1new==\",\n\t\t\t\"salt\":\t\"Ezuo4G1TqYtTQDL/\",\n\t\t\t\"iterations\":\t101,\n\t\t\t\"roles\":\t[{\n\t\t\t\t\t\"rolename\":\t\"admin\"\n\t\t\t\t}]\n\t\t}],\n\t\"roles\":\t[{\n\t\t\t\"rolename\":\t\"admin\",\n\t\t\t\"acls\":\t[{\n\t\t\t\t\t\"acltype\":\t\"publishClientSend\",\n\t\t\t\t\t\"topic\":\t\"$CONTROL/dynamic-security/#\",\n\t\t\t\t\t\"allow\":\ttrue\n\t\t\t\t}, {\n\t\t\t\t\t\"acltype\":\t\"publishClientReceive\",\n\t\t\t\t\t\"topic\":\t\"$CONTROL/dynamic-security/#\",\n\t\t\t\t\t\"allow\":\ttrue\n\t\t\t\t}, {\n\t\t\t\t\t\"acltype\":\t\"subscribePattern\",\n\t\t\t\t\t\"topic\":\t\"$CONTROL/dynamic-security/#\",\n\t\t\t\t\t\"allow\":\ttrue\n\t\t\t\t}, {\n\t\t\t\t\t\"acltype\":\t\"publishClientReceive\",\n\t\t\t\t\t\"topic\":\t\"$SYS/#\",\n\t\t\t\t\t\"allow\":\ttrue\n\t\t\t\t}, {\n\t\t\t\t\t\"acltype\":\t\"subscribePattern\",\n\t\t\t\t\t\"topic\":\t\"$SYS/#\",\n\t\t\t\t\t\"allow\":\ttrue\n\t\t\t\t}, {\n\t\t\t\t\t\"acltype\":\t\"publishClientReceive\",\n\t\t\t\t\t\"topic\":\t\"#\",\n\t\t\t\t\t\"allow\":\ttrue\n\t\t\t\t}, {\n\t\t\t\t\t\"acltype\":\t\"subscribePattern\",\n\t\t\t\t\t\"topic\":\t\"#\",\n\t\t\t\t\t\"allow\":\ttrue\n\t\t\t\t}, {\n\t\t\t\t\t\"acltype\":\t\"unsubscribePattern\",\n\t\t\t\t\t\"topic\":\t\"#\",\n\t\t\t\t\t\"allow\":\ttrue\n\t\t\t\t}]\n\t\t}],\n\t\"defaultACLAccess\":\t{\n\t\t\"publishClientSend\":\tfalse,\n\t\t\"publishClientReceive\":\ttrue,\n\t\t\"subscribe\":\tfalse,\n\t\t\"unsubscribe\":\ttrue\n\t}\n}"
  },
  {
    "path": "test/broker/dynsec_helper.py",
    "content": "import json\nimport mosq_test\n\n\ndef command_check(sock, command_payload, expected_response, msg=\"\"):\n    command_packet = mosq_test.gen_publish(topic=\"$CONTROL/dynamic-security/v1\", qos=0, payload=json.dumps(command_payload))\n    sock.send(command_packet)\n    response = json.loads(mosq_test.read_publish(sock))\n    if response != expected_response:\n        if msg != \"\":\n            print(msg)\n        print(expected_response)\n        print(response)\n        raise ValueError(response)\n\ndef check_details(sock, client_count, group_count, role_count, change_index):\n    command = {\"commands\":[{ \"command\": \"getDetails\"}]}\n    response = {'responses': [{'command': 'getDetails', 'data':{\n        'clientCount':client_count,\n        'groupCount':group_count,\n        'roleCount':role_count,\n        'changeIndex': change_index\n    }}]}\n    command_check(sock, command, response)\n"
  },
  {
    "path": "test/broker/mosq_test_helper.py",
    "content": "import inspect, os, sys\n\n# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder\ncmd_subfolder = os.path.realpath(\n    os.path.abspath(\n        os.path.join(os.path.split(inspect.getfile(inspect.currentframe()))[0], \"..\")\n    )\n)\nif cmd_subfolder not in sys.path:\n    sys.path.insert(0, cmd_subfolder)\n\nimport mosq_test\nimport mqtt5_opts\nimport mqtt5_props\nimport mqtt5_rc\n\nimport socket\nimport ssl\nimport struct\nimport subprocess\nimport time\nimport errno\nfrom pathlib import Path\n\nsource_dir = Path(__file__).resolve().parent\nssl_dir = source_dir.parent / \"ssl\"\n\nimport importlib\n\n\ndef persist_module():\n    if len(sys.argv) > 1:\n        mod = sys.argv.pop(1).replace(\".py\", \"\")\n    else:\n        raise RuntimeError(\"Not enough command line arguments - need persist module\")\n    return importlib.import_module(mod)\n\n\ndef do_test_broker_failure(\n    conf_file: str,\n    config: list,\n    port: int,\n    rc_expected: int,\n    error_log_entry: str = None,\n    stdout_entry: str = None,\n    cmd_args: list = [],\n    with_test_config: bool = True,\n):\n    rc = 1\n\n    use_conf_file = len(conf_file) > 0\n\n    cmd_args = cmd_args.copy()\n    if with_test_config and use_conf_file:\n        cmd_args.insert(0, \"--test-config\")\n\n    create_conf_file = use_conf_file and len(config)\n    if create_conf_file:\n        with open(conf_file, \"w\") as f:\n            f.write(\"\\n\".join(config))\n            f.write(\"\\n\")\n    try:\n        broker = None\n        broker = mosq_test.start_broker(\n            conf_file,\n            port=port,\n            use_conf=use_conf_file,\n            expect_fail=True,\n            cmd_args=cmd_args,\n        )\n        (stdo, stde) = broker.communicate()\n        if broker.returncode != rc_expected:\n            print(f\"Expected broker return code {rc_expected}, got {broker.returncode}\")\n            print(stde.decode(\"utf-8\"))\n            return rc\n\n        if error_log_entry is not None:\n            error_log = stde.decode(\"utf-8\")\n            if error_log_entry not in error_log:\n                print(\n                    f\"Error log entry: '{error_log_entry}' not found in '{error_log}'\"\n                )\n                return rc\n\n        if stdout_entry is not None:\n            stdout_log = stdo.decode(\"utf-8\")\n            if stdout_entry not in stdout_log:\n                print(\n                    f\"Error stdout entry: '{stdout_entry}' not found in '{stdout_log}'\"\n                )\n                return rc\n\n        rc = 0\n    except subprocess.TimeoutExpired:\n        if broker is not None:\n            mosq_test.wait_for_subprocess(broker, timeout=1)\n        return rc\n    except Exception as e:\n        print(e)\n        return rc\n    finally:\n        if create_conf_file:\n            try:\n                os.remove(conf_file)\n            except FileNotFoundError:\n                pass\n        if rc:\n            print(\n                f\"While testing 'config {chr(10).join(config) if len(config) else ''}'{', args '+ ' '.join(cmd_args) if cmd_args is not None else ''}\"\n            )\n            exit(rc)\n\n    return rc\n"
  },
  {
    "path": "test/broker/msg_sequence_test.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a valid CONNECT results in the correct CONNACK packet.\n\nfrom mosq_test_helper import *\nimport importlib\nfrom os import walk\nimport socket\nimport json\nfrom collections import deque\nimport mosq_test\n\nsend = 1\nrecv = 2\ndisconnected_check = 3\nconnected_check = 4\npublish = 5\n\n\nclass SingleMsg(object):\n    __slots__ = 'action', 'message', 'comment'\n    def __init__(self, action, message, comment=''):\n        self.action = action\n        self.message = message\n        self.comment = comment\n\nclass MsgSequence(object):\n    __slots__ = 'name', 'msgs', 'msgs_all', 'expect_disconnect', 'port', 'protocol'\n\n    def __init__(self, name, default_connect=True, port=1888, protocol='mqtt', proto_ver=4, expect_disconnect=True):\n        self.name = name\n        self.msgs_all = deque()\n        self.expect_disconnect = expect_disconnect\n        self.port = port\n        self.protocol = protocol\n        if default_connect:\n            self.add_default_connect(proto_ver=proto_ver)\n\n    def add_default_connect(self, proto_ver):\n        self.add_send(mosq_test.gen_connect(self.name, proto_ver=proto_ver))\n        self.add_recv(mosq_test.gen_connack(rc=0, proto_ver=proto_ver), \"default connack\")\n\n    def add_send(self, message):\n        self._add(send, message)\n\n    def add_recv(self, message, comment):\n        self._add(recv, message, comment)\n\n    def add_publish(self, message, comment):\n        self._add(publish, message, comment)\n\n    def add_connected_check(self):\n        self._add(connected_check, b\"\")\n\n    def add_disconnected_check(self):\n        self._add(disconnected_check, b\"\")\n\n    def _add(self, action, message, comment=\"\"):\n        msg = SingleMsg(action, message, comment)\n        self.msgs_all.append(msg)\n\n    def _connected_check(self, sock):\n        try:\n            mosq_test.do_ping(sock)\n        except (BrokenPipeError, mosq_test.TestError):\n            raise ValueError(\"connection failed\")\n\n    def _send_message(self, sock, msg):\n        sock.send(msg.message)\n\n    def _publish_message(self, msg):\n        sock = mosq_test.client_connect_only(hostname=\"localhost\", port=self.port, timeout=2, protocol=self.protocol)\n        sock.send(mosq_test.gen_connect(\"helper\"))\n        mosq_test.expect_packet(sock, \"connack\", mosq_test.gen_connack(rc=0))\n\n        m = msg.message\n        if m['qos'] == 0:\n            sock.send(mosq_test.gen_publish(topic=m['topic'], payload=m['payload']))\n        elif m['qos'] == 1:\n            sock.send(mosq_test.gen_publish(mid=1, qos=1, topic=m['topic'], payload=m['payload']))\n            mosq_test.expect_packet(sock, \"helper puback\", mosq_test.gen_puback(mid=1))\n        elif m['qos'] == 2:\n            sock.send(mosq_test.gen_publish(mid=1, qos=2, topic=m['topic'], payload=m['payload']))\n            mosq_test.expect_packet(sock, \"helper pubrec\", mosq_test.gen_pubrec(mid=1))\n            sock.send(mosq_test.gen_pubrel(mid=1))\n            mosq_test.expect_packet(sock, \"helper pubcomp\", mosq_test.gen_pubcomp(mid=1))\n        sock.close()\n\n    def _recv_message(self, sock, msg):\n        data = sock.recv(len(msg.message))\n        if data != msg.message:\n            raise ValueError(\"Receive message %s | rec:%s | exp:%s\" % (msg.comment, data, msg.message))\n\n\n    def _disconnected_check(self, sock):\n        try:\n            data = sock.recv(1)\n            if len(data) == 1 and self.expect_disconnect:\n                raise ValueError(\"Still connected\")\n        except (ConnectionResetError, BlockingIOError):\n            if self.expect_disconnect:\n                pass\n            else:\n                raise\n\n    def _process_message(self, sock, msg):\n        if msg.action == send:\n            self._send_message(sock, msg)\n        elif msg.action == recv:\n            self._recv_message(sock, msg)\n        elif msg.action == publish:\n            self._publish_message(msg)\n        elif msg.action == disconnected_check:\n            self._disconnected_check(sock)\n        elif msg.action == connected_check:\n            self._connected_check(sock)\n\n    def process_next(self, sock):\n        msg = self.msgs.popleft()\n        self._process_message(sock, msg)\n\n    def process_all(self, sock):\n        self.msgs = self.msgs_all.copy()\n        while len(self.msgs):\n            self.process_next(sock)\n        if self.expect_disconnect:\n            self._disconnected_check(sock)\n        else:\n            self._connected_check(sock)\n\n\ndef parse_message(message):\n    b = bytes()\n    parts = message.split(\" \")\n    for i in range(0, len(parts)):\n        if len(parts[i]) == 0:\n            continue\n        elif parts[i][0] in ['i']:\n            # General 8-bit unsigned decimal\n            b += int(parts[i][1:]).to_bytes(length=1, byteorder='big', signed=False)\n        elif parts[i][0] in ['H', 'k', 'm', 's']:\n            # General 16-bit unsigned decimal\n            # Or 'k' keepalive specific\n            # Or 'm' mid specific\n            # Or 's' string specific\n            b += int(parts[i][1:]).to_bytes(length=2, byteorder='big', signed=False)\n        elif parts[i][0] == \"L\":\n            # 32-bit unsigned decimal\n            b += int(parts[i][1:]).to_bytes(length=4, byteorder='big', signed=False)\n        elif parts[i][0] == \"'\":\n            s = parts[i][1:]\n            while s[-1] != \"'\" and i < len(parts)-1:\n                i += 1\n                s += \" \" + parts[i]\n            if s[-1] != \"'\":\n                raise ValueError(f\"message {message} has incomplete string type\")\n            b += bytes(s[0:-1].encode('utf-8'))\n        elif parts[i][0] in ['v', 'r']:\n            # General variable length integer\n            # Or 'r' remaining length\n            v = int(parts[i][1:])\n\n            # This allows non-compliant values >=2^28\n            while True:\n                byte = v % 128\n                v = v // 128\n\n                if v > 0:\n                    byte = byte | 0x80\n                b += byte.to_bytes(length=1, byteorder='big', signed=False)\n                if v == 0:\n                    break\n        else:\n            # hex\n            try:\n                b += bytes.fromhex(parts[i])\n            except ValueError:\n                raise ValueError(f\"message {message} has invalid hex bytes\")\n\n    return b\n\n\ndef do_test(hostname, port, protocol):\n    data_path=Path(__file__).resolve().parent/\"data\"\n    rc = 0\n    sequences = []\n    for (_, _, filenames) in walk(data_path):\n        sequences.extend(filenames)\n        break\n\n    total = 0\n    succeeded = 0\n    test = None\n    failed_tests = []\n    for seq in sorted(sequences):\n        if seq[-5:] != \".json\":\n            continue\n\n        with open(data_path/seq, \"r\") as f:\n            test_file = json.load(f)\n\n        for g in test_file:\n            group_name = g[\"group\"]\n            try:\n                disabled = g[\"disable\"]\n                if disabled:\n                    continue\n            except KeyError:\n                pass\n            try:\n                g_proto_ver = g[\"ver\"]\n            except KeyError:\n                g_proto_ver = 4\n            try:\n                g_connect = g[\"connect\"]\n            except KeyError:\n                g_connect = True\n            try:\n                g_expect_disconnect = g[\"expect_disconnect\"]\n            except KeyError:\n                g_expect_disconnect = True\n            tests = g[\"tests\"]\n\n            for t in tests:\n                tname = group_name + \" \" + t[\"name\"]\n                try:\n                    proto_ver = t[\"ver\"]\n                except KeyError:\n                    proto_ver = g_proto_ver\n                try:\n                    connect = t[\"connect\"]\n                except KeyError:\n                    connect = g_connect\n                try:\n                    expect_disconnect = t[\"expect_disconnect\"]\n                except KeyError:\n                    expect_disconnect = g_expect_disconnect\n\n                this_test = MsgSequence(tname,\n                        port=port,\n                        protocol=protocol,\n                        proto_ver=proto_ver,\n                        expect_disconnect=expect_disconnect,\n                        default_connect=connect)\n\n                for m in t[\"msgs\"]:\n                    try:\n                        c = m[\"comment\"]\n                    except KeyError:\n                        c = \"\"\n                    if m[\"type\"] == \"send\":\n                        this_test.add_send(parse_message(m[\"payload\"]))\n                    elif m[\"type\"] == \"recv\":\n                        this_test.add_recv(parse_message(m[\"payload\"]), c)\n                    elif m[\"type\"] == \"publish\":\n                        this_test.add_publish(m, c)\n\n                total += 1\n                try:\n                    failed_tests.append(this_test)\n                    sock = mosq_test.client_connect_only(hostname=hostname, port=port, timeout=2, protocol=protocol)\n                    this_test.process_all(sock)\n                    #print(\"\\033[32m\" + tname + \"\\033[0m\")\n                    succeeded += 1\n                    sock.close()\n                    failed_tests.pop()\n                except ValueError as e:\n                    print(\"\\033[31m\" + tname + \" failed: \" + str(e) + \"\\033[0m\")\n                    rc = 1\n                    sock.close()\n                except ConnectionResetError as e:\n                    print(\"\\033[31m\" + tname + \" failed: \" + str(e) + \"\\033[0m\")\n                    rc = 1\n                    sock.close()\n                except socket.timeout as e:\n                    print(\"\\033[31m\" + tname + \" failed: \" + str(e) + \"\\033[0m\")\n                    rc = 1\n                    sock.close()\n                    exit()\n                except mosq_test.TestError as e:\n                    print(\"\\033[31m\" + tname + \" failed: \" + str(e) + \"\\033[0m\")\n                    rc = 1\n                    sock.close()\n\n    # Option to replay failed tests to make them easier to analyse.\n    if False:\n        for t in failed_tests:\n            try:\n                sock = mosq_test.client_connect_only(hostname=hostname, port=port, timeout=2, protocol=protocol)\n                t.process_all(sock)\n                length = len(data)\n                print(\"\\033[32m\" + t.name + \"\\033[0m\")\n                sock.close()\n            except ValueError as e:\n                print(\"\\033[31m\" + t.name + \" failed: \" + str(e) + \"\\033[0m\")\n                rc = 1\n                sock.close()\n            except ConnectionResetError as e:\n                print(\"\\033[31m\" + t.name + \" failed: \" + str(e) + \"\\033[0m\")\n                rc = 1\n                sock.close()\n            except socket.timeout as e:\n                print(\"\\033[31m\" + t.name + \" failed: \" + str(e) + \"\\033[0m\")\n                rc = 1\n                sock.close()\n            except mosq_test.TestError as e:\n                print(\"\\033[31m\" + t.name + \" failed: \" + str(e) + \"\\033[0m\")\n                rc = 1\n                sock.close()\n\n    print(\"%d tests total\\n%d tests succeeded\" % (total, succeeded))\n    return rc\n\n\ndef write_config(filename, port, protocol):\n    with open(filename, 'w') as f:\n        f.write(f'listener {port}\\n')\n        f.write(f'protocol {protocol}\\n')\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\"log_type all\\n\")\n\n\ndef main(protocol):\n    hostname = \"localhost\"\n    port = mosq_test.get_port()\n    conf_file = 'msg_sequence_test.conf'\n    write_config(conf_file, port, protocol)\n    broker = mosq_test.start_broker(filename=conf_file, port=port, use_conf=True, nolog=True)\n\n    rc = 0\n    try:\n        rc = do_test(hostname=hostname, port=port, protocol=protocol)\n    finally:\n        broker.terminate()\n        os.remove(conf_file)\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        if broker.returncode != 0:\n            rc = broker.returncode\n            print(f\"Broker exited with code {rc}. If there are no obvious errors this may be due to an ASAN build having leaks, which must be fixed.\")\n            print(\"The easiest way to reproduce this is to run the broker with `mosquitto -p 1888`, rerun the test, then quit the broker.\")\n        (stdo, stde) = broker.communicate()\n    if rc:\n        #print(stde.decode('utf-8'))\n        exit(rc)\n\nif __name__ == \"__main__\":\n    #main(protocol=\"websockets\")\n    main(protocol=\"mqtt\")\n"
  },
  {
    "path": "test/broker/ntest.py",
    "content": "#!/usr/bin/env python3\n\nimport mosq_test_helper\nimport mosq_test\nimport os\nimport ptest\nimport threading\nimport importlib\nimport time\n\ntests = [\n    '01-connect-bad-packet',\n    '01-connect-disconnect-v5',\n    '01-connect-duplicate',\n    '01-connect-invalid-id-0',\n    '01-connect-invalid-id-missing',\n    '01-connect-invalid-id-utf8',\n    '01-connect-invalid-protonum',\n    '01-connect-invalid-reserved',\n    '01-connect-success',\n    '01-connect-uname-invalid-utf8',\n    '01-connect-uname-no-flag',\n    '01-connect-uname-pwd-no-flag',\n\n    '02-shared-nolocal',\n    '02-shared-qos0-v5',\n    '02-subhier-crash',\n    '02-subpub-b2c-topic-alias',\n    '02-subpub-qos0-long-topic',\n    '02-subpub-qos0-retain-as-publish',\n    '02-subpub-qos0-send-retain',\n    '02-subpub-qos0-subscription-id',\n    '02-subpub-qos0-topic-alias-unknown',\n    '02-subpub-qos0-topic-alias',\n    '02-subpub-qos0',\n    '02-subpub-qos1-bad-pubcomp',\n    '02-subpub-qos1-bad-pubrec',\n    '02-subpub-qos1-message-expiry-retain',\n    '02-subpub-qos1-message-expiry-will',\n    '02-subpub-qos1-message-expiry',\n    '02-subpub-qos1-nolocal',\n    '02-subpub-qos1',\n    '02-subpub-qos2-1322',\n    '02-subpub-qos2-bad-puback-1',\n    '02-subpub-qos2-bad-puback-2',\n    '02-subpub-qos2-bad-pubcomp',\n    '02-subpub-qos2-pubrec-error',\n    '02-subpub-qos2-receive-maximum-1',\n    '02-subpub-qos2-receive-maximum-2',\n    '02-subpub-qos2',\n    '02-subscribe-dollar-v5',\n    '02-subscribe-invalid-utf8',\n    '02-subscribe-long-topic',\n    '02-subscribe-persistence-flipflop',\n    '02-subscribe-qos0',\n    '02-subscribe-qos1',\n    '02-subscribe-qos2',\n    '02-unsubscribe-invalid-no-topic',\n    '02-unsubscribe-qos0',\n    '02-unsubscribe-qos1',\n    '02-unsubscribe-qos2-multiple',\n    '02-unsubscribe-qos2',\n\n    ##'03-pattern-matching',\n    '03-publish-b2c-disconnect-qos1',\n    '03-publish-b2c-disconnect-qos2',\n    '03-publish-b2c-qos1-len',\n    '03-publish-b2c-qos2-len',\n    '03-publish-c2b-disconnect-qos2',\n    '03-publish-c2b-qos2-len',\n    '03-publish-dollar-v5',\n    '03-publish-dollar',\n    '03-publish-invalid-utf8',\n    '03-publish-long-topic',\n    '03-publish-qos1-no-subscribers-v5',\n    '03-publish-qos1',\n    '03-publish-qos2',\n\n    '04-retain-qos0-clear',\n    '04-retain-qos0-fresh',\n    '04-retain-qos0-repeated',\n    '04-retain-qos0',\n    '04-retain-qos1-qos0',\n\n    '05-clean-session-qos1',\n    '05-session-expiry-v5',\n\n    '06-bridge-no-local',\n\n    '07-will-delay',\n    '07-will-delay-reconnect',\n    '07-will-delay-recover',\n    '07-will-delay-session-expiry',\n    '07-will-delay-session-expiry2',\n    '07-will-disconnect-with-will',\n    '07-will-invalid-utf8',\n    '07-will-no-flag',\n    '07-will-null',\n    '07-will-null-topic',\n    '07-will-properties',\n    '07-will-qos0',\n    '07-will-reconnect-1273',\n    '07-will-takeover',\n\n    '09-auth-bad-method',\n    '09-extended-auth-unsupported',\n\n    '12-prop-assigned-client-identifier',\n    '12-prop-maximum-packet-size-connect',\n    '12-prop-maximum-packet-size-publish',\n    '12-prop-maximum-packet-size-publish-qos1',\n    '12-prop-maximum-packet-size-publish-qos2',\n    '12-prop-response-topic',\n    '12-prop-response-topic-correlation-data',\n    '12-prop-session-expiry-invalid',\n    '12-prop-subpub-content-type',\n    '12-prop-subpub-payload-format',\n    '12-prop-topic-alias-invalid',\n\n    '13-malformed-subscribe-v5',\n    '13-malformed-unsubscribe-v5',\n    ]\n\ndef single_test(name):\n    start_time = time.time()\n    try:\n        mod = importlib.import_module(name)\n    except ModuleNotFoundError:\n        print(\"------ : \\033[31m%s\\033[0m test not found\" % (name))\n        return 1\n\n    rc = mod.all_tests()\n    runtime = time.time() - start_time\n    if rc:\n        print(\"%0.3fs : \\033[31m%s\\033[0m\" % (runtime, name))\n    else:\n        print(\"%0.3fs : \\033[32m%s\\033[0m\" % (runtime, name))\n    return rc\n\n\nport = mosq_test.get_port()\nbroker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, nolog=True)\n\nrc = 0\ntry:\n    # FIXME - use Queue instead to limit max threads\n    threads = []\n    for test in tests:\n        t = threading.Thread(target=single_test, args=(test,), name=test)\n        threads.append(t)\n        t.start()\n\n    # FIXME - return code\n    for t in threads:\n        t.join()\n\nfinally:\n    broker.terminate()\n    if mosq_test.wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        if rc == 0: rc=1\n    (stdo, stde) = broker.communicate()\n    if rc:\n        print(stde.decode('utf-8'))\n"
  },
  {
    "path": "test/broker/persist_module_helper.py",
    "content": "#!/usr/bin/env python3\n\nimport socket\nimport mosq_test\nimport mqtt5_props\n\nfrom typing import Any, Optional\nfrom types import ModuleType\n\n\ndef connect_client(\n    port: int,\n    client_id: str,\n    username: str,\n    proto_ver: int,\n    session_expiry: int,\n    session_present: bool = False,\n    subscribe_topic: Optional[str] = None,\n    qos: int = 1,\n    **connect_params: Any,\n):\n    connect_packet = mosq_test.gen_connect(\n        client_id=client_id,\n        username=username,\n        proto_ver=proto_ver,\n        clean_session=session_expiry == 0,\n        session_expiry=session_expiry,\n        **connect_params,\n    )\n    connack_packet = mosq_test.gen_connack(\n        rc=0, proto_ver=proto_ver, flags=1 if session_present else 0\n    )\n    sock = mosq_test.do_client_connect(\n        connect_packet, connack_packet, timeout=5, port=port\n    )\n    if subscribe_topic is not None:\n        mid = 1\n        subscribe_packet = mosq_test.gen_subscribe(\n            mid, subscribe_topic, qos, proto_ver=proto_ver\n        )\n        suback_packet = mosq_test.gen_suback(mid, qos=qos, proto_ver=proto_ver)\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n    return sock\n\n\ndef publish_messages(\n    sock: socket,\n    proto_ver: int,\n    topic: str,\n    start: int,\n    end: int,\n    retain_end=0,\n    message_expiry: int = 0,\n    qos: int = 1,\n):\n    for i in range(start, end):\n        payload = f\"queued message {i:3}\"\n        mid = 10 + i\n        props = (\n            mqtt5_props.gen_uint32_prop(\n                mqtt5_props.MESSAGE_EXPIRY_INTERVAL, message_expiry\n            )\n            if message_expiry > 0\n            else b\"\"\n        )\n        publish_packet = mosq_test.gen_publish(\n            topic,\n            mid=mid,\n            qos=qos,\n            payload=payload.encode(\"UTF-8\"),\n            retain=True if i < retain_end else False,\n            proto_ver=proto_ver,\n            properties=props,\n        )\n        puback_packet = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver)\n        mosq_test.do_send_receive(sock, publish_packet, puback_packet, \"puback\")\n\n\ndef check_db(\n    persist_help: ModuleType,\n    port: int,\n    username: str,\n    subscription_topic: str,\n    client_msg_counts: dict[str, int],\n    publisher_id: str,\n    num_published_msgs: int,\n    retain_end: int = 0,\n    message_expiry: int = 0,\n    qos: int = 1,\n    check_session_expiry_time: bool = True,\n):\n    count_list = [v for v in client_msg_counts.values() if v is not None] + [0]\n    num_base_msgs = max(count_list)\n    num_subscriptions = sum(1 for c in client_msg_counts.values() if c is not None)\n    num_client_msgs_out = sum(count_list)\n    persist_help.check_counts(\n        port,\n        clients=len(client_msg_counts),\n        client_msgs_out=num_client_msgs_out,\n        base_msgs=num_base_msgs if num_base_msgs > 0 or retain_end == 0 else 1,\n        retain_msgs=1 if retain_end > 0 else 0,\n        subscriptions=num_subscriptions,\n    )\n\n    # Check client\n    for client_id, num_messages_for_client in client_msg_counts.items():\n        persist_help.check_client(\n            port,\n            client_id,\n            username=username,\n            will_delay_time=0,\n            session_expiry_time=60 if check_session_expiry_time else None,\n            listener_port=None,  # persist-lmdb reset listener port to 0 on disconnect\n            max_packet_size=0,\n            max_qos=2,\n            retain_available=1,\n            session_expiry_interval=60,\n            will_delay_interval=0,\n        )\n        # Check subscription\n        if num_messages_for_client is not None:\n            persist_help.check_subscription(port, client_id, subscription_topic, qos, 0)\n\n    # Check stored message\n    for i in range(num_base_msgs):\n        msg_id = num_published_msgs - num_base_msgs + i\n        payload = f\"queued message {msg_id:3}\"\n        payload_b = payload.encode(\"UTF-8\")\n        mid = 10 + msg_id\n        store_id = persist_help.check_base_msg(\n            port,\n            message_expiry,\n            subscription_topic,\n            payload_b,\n            publisher_id,\n            username,\n            len(payload_b),\n            mid,\n            port,\n            qos,\n            retain=1 if i < retain_end else 0,\n            idx=i,\n        )\n        # Check client msg\n        for client_id, num_messages_for_client in client_msg_counts.items():\n            if num_messages_for_client is None:\n                continue\n            client_msg_start = num_published_msgs - num_messages_for_client\n            if msg_id < client_msg_start:\n                continue\n            cmsg_id = 1 + msg_id - client_msg_start\n            subscriber_mid = cmsg_id\n            persist_help.check_client_msg(\n                port,\n                client_id,\n                cmsg_id,\n                store_id,\n                0,\n                persist_help.dir_out,\n                subscriber_mid,\n                qos,\n                0,\n                persist_help.ms_queued,\n            )\n"
  },
  {
    "path": "test/broker/persist_sqlite.py",
    "content": "import os\nfrom pathlib import Path\nimport sqlite3\nimport mosq_test\n\ndir_in = 0\ndir_out = 1\n\nms_invalid = 0\nms_publish_qos0 = 1\nms_publish_qos1 = 2\nms_wait_for_puback = 3\nms_publish_qos2 = 4\nms_wait_for_pubrec = 5\nms_resend_pubrel = 6\nms_wait_for_pubrel = 7\nms_resend_pubcomp = 8\nms_wait_for_pubcomp = 9\nms_send_pubrec = 10\nms_queued = 11\n\n\ndef write_config(filename, port, additional_config_entries: dict = {}):\n    with open(filename, \"w\") as f:\n        f.write(\"listener %d\\n\" % (port))\n        f.write(\"allow_anonymous true\\n\")\n        f.write(\n            f\"plugin {mosq_test.get_build_root()}/plugins/persist-sqlite/mosquitto_persist_sqlite.so\\n\"\n        )\n        f.write(\"plugin_opt_db_file %d/mosquitto.sqlite3\\n\" % (port))\n        for entry, value in additional_config_entries.items():\n            f.write(f\"{entry} {value}\\n\")\n\n\n# The create_db_of_version contains the database schema version introduced with Pro Mosquitto 2.8.\n# The list of supported version includes artificial versions, which are used for databases created\n# prior to the current version\n# 0.9.0: DB from Mosquitto without version info table\n# 1.0.0: DB created with latest version of plugin\ndef init(port, create_db_of_version: list[int] = None):\n    try:\n        os.mkdir(str(port))\n    except FileExistsError:\n        try:\n            os.remove(f\"{port}/mosquitto.sqlite3\")\n        except FileNotFoundError:\n            pass\n    if create_db_of_version is not None:\n        con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n        cursor = con.cursor()\n        for statement in [\n            \"PRAGMA page_size=4096;\",\n            \"PRAGMA journal_mode=WAL;\",\n            \"PRAGMA foreign_keys = ON;\",\n            \"PRAGMA synchronous=1;\",\n            \"CREATE TABLE base_msgs (store_id INT64 PRIMARY KEY,expiry_time INT64,topic STRING NOT NULL,payload BLOB,source_id STRING,source_username STRING,payloadlen INTEGER,source_mid INTEGER,source_port INTEGER,qos INTEGER,retain INTEGER,properties STRING);\",\n            \"CREATE TABLE retains (topic STRING PRIMARY KEY,store_id INT64);\",\n            \"CREATE TABLE clients (client_id TEXT PRIMARY KEY,username TEXT,connection_time INT64,will_delay_time INT64,session_expiry_time INT64,listener_port INT,max_packet_size INT,max_qos INT,retain_available INT,session_expiry_interval INT,will_delay_interval INT);\",\n            \"CREATE TABLE subscriptions (client_id TEXT NOT NULL,topic TEXT NOT NULL,subscription_options INTEGER,subscription_identifier INTEGER,PRIMARY KEY (client_id, topic) );\",\n        ]:\n            cursor.execute(statement)\n        if create_db_of_version[0] == 0 and create_db_of_version[1] == 9:\n            for statement in [\n                \"CREATE TABLE client_msgs (client_id TEXT NOT NULL,cmsg_id INT64,store_id INT64,dup INTEGER,direction INTEGER,mid INTEGER,qos INTEGER,retain INTEGER,state INTEGER,subscription_identifier INTEGER);\",\n            ]:\n                cursor.execute(statement)\n        elif create_db_of_version[0] >= 1:\n            for statement in [\n                \"CREATE TABLE client_msgs (client_id TEXT NOT NULL,cmsg_id INT64,store_id INT64,dup INTEGER,direction INTEGER,mid INTEGER,qos INTEGER,retain INTEGER,state INTEGER,subscription_identifier INTEGER);\",\n                \"CREATE TABLE version_info (component TEXT NOT NULL,major INTEGER NOT NULL,minor INTEGER NOT NULL,patch INTEGER NOT NULL);\",\n                f\"INSERT INTO version_info(component,major,minor,patch) VALUES ('database_schema',{','.join([str(i) for i in create_db_of_version])});\",\n            ]:\n                cursor.execute(statement)\n            if create_db_of_version[1] >= 1:\n                for statement in [\n                    \"CREATE TABLE wills(client_id TEXT PRIMARY KEY,payload BLOB,topic STRING NOT NULL,payloadlen INTEGER,qos INTEGER,retain INTEGER,properties STRING);\"\n                ]:\n                    cursor.execute(statement)\n\n        cursor.close()\n        con.commit()\n        con.close()\n        # We need to set write permission to everybody as broker will start with privilege drop\n        os.chmod(f\"{port}/mosquitto.sqlite3\", 0o666)\n\n\ndef cleanup(port):\n    rc = 1\n    try:\n        os.remove(f\"{port}/mosquitto.sqlite3\")\n    except FileNotFoundError:\n        pass\n    try:\n        os.rmdir(f\"{port}\")\n        rc = 0\n    except OSError as e:\n        if Path(str(port), \"mosquitto.sqlite3-wal\").stat().st_size == 0:\n            # some versions of sqlite3 do not remove the wal file\n            # thus we make sure that the file is at least empty (no pending db transactions)\n            rc = 0\n        else:\n            print(f\"ERROR sqlite3 file not removed after shutdown\")\n        try:\n            os.remove(f\"{port}/mosquitto.sqlite3-shm\")\n        except FileNotFoundError:\n            pass\n        try:\n            os.remove(f\"{port}/mosquitto.sqlite3-wal\")\n        except FileNotFoundError:\n            pass\n        os.rmdir(f\"{port}\")\n    return rc\n\n\ndef check_version_infos(port, database_schema_version):\n    con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n    cur = con.cursor()\n    cur.execute(\n        \"SELECT major,minor,patch FROM version_info WHERE component = 'database_schema';\"\n    )\n    row = cur.fetchone()\n\n    if len(row) != len(database_schema_version):\n        raise ValueError(\"Could not fetch db version info from DB\")\n    for i in range(len(row)):\n        if row[i] != database_schema_version[i]:\n            raise ValueError(\n                f\"DB version info {'.'.join([str(v) for v in row])} != expected {'.'.join([str(v) for v in database_schema_version])}\"\n            )\n    con.close()\n\n\ndef check_counts(\n    port,\n    clients=0,\n    client_msgs_in=0,\n    client_msgs_out=0,\n    base_msgs=0,\n    retain_msgs=0,\n    subscriptions=0,\n    wills=None\n):\n    con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n    cur = con.cursor()\n    cur.execute(\"SELECT COUNT(*) FROM clients\")\n    row = cur.fetchone()\n    if row[0] != clients:\n        raise ValueError(\"Found %d clients, expected %d\" % (row[0], clients))\n\n    cur.execute(\"SELECT COUNT(*) FROM client_msgs WHERE direction=0\")\n    row = cur.fetchone()\n    if row[0] != client_msgs_in:\n        raise ValueError(\n            \"Found %d client_msgs_in, expected %d\" % (row[0], client_msgs_in)\n        )\n\n    cur.execute(\"SELECT COUNT(*) FROM client_msgs WHERE direction=1\")\n    row = cur.fetchone()\n    if row[0] != client_msgs_out:\n        raise ValueError(\n            \"Found %d client_msgs_out, expected %d\" % (row[0], client_msgs_out)\n        )\n\n    cur.execute(\"SELECT COUNT(*) FROM subscriptions\")\n    row = cur.fetchone()\n    if row[0] != subscriptions:\n        raise ValueError(\n            \"Found %d subscriptions, expected %d\" % (row[0], subscriptions)\n        )\n\n    cur.execute(\"SELECT COUNT(*) FROM base_msgs\")\n    row = cur.fetchone()\n    if row[0] != base_msgs:\n        raise ValueError(\"Found %d base_msgs, expected %d\" % (row[0], base_msgs))\n\n    cur.execute(\"SELECT COUNT(*) FROM retains\")\n    row = cur.fetchone()\n    if row[0] != retain_msgs:\n        raise ValueError(\"Found %d retain_msgs, expected %d\" % (row[0], retain_msgs))\n\n    if wills is not None:\n        cur.execute(\"SELECT COUNT(*) FROM wills\")\n        row = cur.fetchone()\n        if row[0] != wills:\n            raise ValueError(\"Found %d wills, expected %d\" % (row[0], wills))\n        \n    \n    con.close()\n\n\ndef check_client(\n    port,\n    client_id,\n    username,\n    will_delay_time,\n    session_expiry_time,\n    listener_port,\n    max_packet_size,\n    max_qos,\n    retain_available,\n    session_expiry_interval,\n    will_delay_interval,\n):\n    # \"Fix\" the infinite session expiry interval as mangled by an int32 conversion.\n    if session_expiry_interval == 4294967295:\n        session_expiry_interval = -1\n\n    con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n    cur = con.cursor()\n    cur.execute(\n        \"SELECT client_id, username, will_delay_time, session_expiry_time, \"\n        + \"listener_port, max_packet_size, max_qos, retain_available, \"\n        + \"session_expiry_interval, will_delay_interval \"\n        + \"FROM clients \"\n        + f\"WHERE client_id = '{client_id}'\"\n    )\n    row = cur.fetchone()\n\n    if row is None:\n        raise ValueError(f\"Cannot find client {client_id} in db\")\n\n    if row[0] != client_id:\n        raise ValueError(\"Invalid client_id %s / %s\" % (row[0], client_id))\n\n    if username is not None and row[1] != username:\n        raise ValueError(\"Invalid username %s / %s\" % (row[1], username))\n\n    if (will_delay_time == 0 and row[2] != 0) or (will_delay_time != 0 and row[2] == 0):\n        raise ValueError(\"Invalid will_delay_time %d / %d\" % (row[2], will_delay_time))\n\n    if session_expiry_time and (\n        (session_expiry_time == 0 and row[3] != 0)\n        or (session_expiry_time != 0 and row[3] == 0)\n    ):\n        raise ValueError(\n            \"Invalid session_expiry_time %d / %d for client %s\"\n            % (row[3], session_expiry_time, client_id)\n        )\n\n    if listener_port is not None and row[4] != listener_port:\n        raise ValueError(\"Invalid listener_port %d / %d\" % (row[4], listener_port))\n\n    if row[5] != max_packet_size:\n        raise ValueError(\"Invalid max_packet_size %d / %d\" % (row[5], max_packet_size))\n\n    if row[6] != max_qos:\n        raise ValueError(\"Invalid max_qos %d / %d\" % (row[6], max_qos))\n\n    if row[7] != retain_available:\n        raise ValueError(\n            \"Invalid retain_available %d / %d\" % (row[7], retain_available)\n        )\n\n    if row[8] != session_expiry_interval:\n        raise ValueError(\n            \"Invalid session_expiry_interval %d / %d\"\n            % (row[8], session_expiry_interval)\n        )\n\n    if row[9] != will_delay_interval:\n        raise ValueError(\n            \"Invalid will_delay_interval %d / %d\" % (row[9], will_delay_interval)\n        )\n    con.close()\n\n\ndef modify_client(port: int, client_id: str, sub_expiry_time: int):\n    num_modified_rows = 0\n    con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n    try:\n        cur = con.cursor()\n        cur.execute(\n            \"UPDATE clients\"\n            + f\" SET session_expiry_time = session_expiry_time - {sub_expiry_time}\"\n            + f\" WHERE client_id = ?\",\n            (client_id,),\n        )\n        num_modified_rows = cur.rowcount\n        con.commit()\n    finally:\n        con.close()\n\n    return num_modified_rows\n\n\ndef check_subscription(\n    port, client_id, topic, subscription_options, subscription_identifier\n):\n    con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n    cur = con.cursor()\n    cur.execute(\n        \"SELECT client_id, topic, subscription_options, subscription_identifier \"\n        + \"FROM subscriptions \"\n        + f\"WHERE client_id = '{client_id}'\"\n    )\n    row = cur.fetchone()\n\n    if row is None:\n        raise ValueError(f\"Cannot find client {client_id} in db\")\n\n    if row[0] != client_id:\n        raise ValueError(\"Invalid client_id %s / %s\" % (row[0], client_id))\n\n    if row[1] != topic:\n        raise ValueError(\"Invalid topic %s / %s\" % (row[1], topic))\n\n    if row[2] != subscription_options:\n        raise ValueError(\n            \"Invalid subscription_options %d / %d\" % (row[2], subscription_options)\n        )\n\n    if row[3] != subscription_identifier:\n        raise ValueError(\n            \"Invalid subscription_identifier %d / %d\"\n            % (row[3], subscription_identifier)\n        )\n    con.close()\n\n\ndef check_client_msg(\n    port, client_id, cmsg_id, store_id, dup, direction, mid, qos, retain, state\n):\n    con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n    try:\n        cur = con.cursor()\n        cur.execute(\n            \"SELECT client_id,cmsg_id,store_id,dup,direction,mid,qos,retain,state \"\n            + \"FROM client_msgs \"\n            + f\"WHERE client_id = '{client_id}' AND cmsg_id = {cmsg_id}\"\n        )\n        row = cur.fetchone()\n\n        msg_id = f\"client_id={client_id},cmsg_id={cmsg_id}\"\n        if row is None:\n            raise ValueError(\n                f\"Cannot find client message client_id = {client_id} cmsg_id = {msg_id} in db.\"\n            )\n\n        if row[0] != client_id:\n            raise ValueError(\n                \"Invalid client_id %s / %s for message %s\" % (row[0], client_id, msg_id)\n            )\n\n        if row[1] != cmsg_id:\n            raise ValueError(\n                \"Invalid cmsg_id %s / %s for message %s\" % (row[1], cmsg_id, msg_id)\n            )\n\n        if row[2] != store_id:\n            raise ValueError(\n                \"Invalid store_id %d / %d for message %s\" % (row[2], store_id, msg_id)\n            )\n\n        if row[3] != dup:\n            raise ValueError(\n                \"Invalid dup %d / %d for message %s\" % (row[3], dup, msg_id)\n            )\n\n        if row[4] != direction:\n            raise ValueError(\n                \"Invalid direction %d / %d for message %s\" % (row[4], direction, msg_id)\n            )\n\n        if row[5] != mid:\n            raise ValueError(\n                \"Invalid mid %d / %d for message %s\" % (row[5], mid, msg_id)\n            )\n\n        if row[6] != qos:\n            raise ValueError(\n                \"Invalid qos %d / %d for message %s\" % (row[6], qos, msg_id)\n            )\n\n        if row[7] != retain:\n            raise ValueError(\n                \"Invalid retain %d / %d for message %s\" % (row[7], retain, msg_id)\n            )\n\n        if row[8] != state:\n            raise ValueError(\n                \"Invalid state %d / %d for message %s\" % (row[8], state, msg_id)\n            )\n    finally:\n        con.close()\n\n\ndef check_base_msg(\n    port,\n    expiry_time,\n    topic,\n    payload,\n    source_id,\n    source_username,\n    payloadlen,\n    source_mid,\n    source_port,\n    qos,\n    retain,\n    idx=0,\n):\n    con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n    try:\n        cur = con.cursor()\n        cur.execute(\n            \"SELECT store_id,expiry_time,topic,payload,source_id,source_username, \"\n            + \"payloadlen, source_mid, source_port, qos, retain \"\n            + \"FROM base_msgs \"\n        )\n        for i in range(0, idx + 1):\n            row = cur.fetchone()\n\n        if row is None:\n            raise ValueError(f\"no base messages\")\n\n        if row[0] == 0:\n            raise ValueError(\"Invalid store_id %d / %d\" % (row[0], store_id))\n\n        if (expiry_time == 0 and row[1] != 0) or (expiry_time != 0 and row[1] == 0):\n            raise ValueError(\"Invalid expiry_time %d / %d\" % (row[1], expiry_time))\n\n        if row[2] != topic:\n            raise ValueError(\"Invalid topic %s / %s\" % (row[2], topic))\n\n        if row[3] != payload:\n            raise ValueError(\"Invalid payload %s / %s\" % (row[3], payload))\n\n        if row[4] != source_id:\n            raise ValueError(\"Invalid source_id %s / %s\" % (row[4], source_id))\n\n        if row[5] != source_username:\n            raise ValueError(\n                \"Invalid source_username %s / %s\" % (row[5], source_username)\n            )\n\n        if row[6] != payloadlen or (payloadlen != 0 and row[6] != len(row[3])):\n            raise ValueError(\"Invalid payloadlen %d / %d\" % (row[6], payloadlen))\n\n        if row[7] != source_mid:\n            raise ValueError(\"Invalid source_mid %d / %d\" % (row[7], source_mid))\n\n        if row[8] != source_port:\n            raise ValueError(\"Invalid source_port %d / %d\" % (row[8], source_port))\n\n        if row[9] != qos:\n            raise ValueError(\"Invalid qos %d / %d\" % (row[9], qos))\n\n        if row[10] != retain:\n            raise ValueError(\"Invalid retain %d / %d\" % (row[10], retain))\n    except ValueError as err:\n        raise ValueError(str(err) + f\" at index {idx}\") from err\n    finally:\n        con.close()\n\n    return row[0]\n\n\ndef modify_base_msgs(\n    port: int,\n    sub_expiry_time: int,\n):\n    num_modified_rows = 0\n    con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n    try:\n        cur = con.cursor()\n        cur.execute(\n            \"UPDATE base_msgs\" + f\" SET expiry_time = expiry_time - {sub_expiry_time}\"\n        )\n        num_modified_rows = cur.rowcount\n        con.commit()\n    finally:\n        con.close()\n    return num_modified_rows\n\n\ndef check_retain(port, topic, store_id):\n    con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n    cur = con.cursor()\n    cur.execute(\"SELECT store_id FROM retains WHERE topic=?\", (topic,))\n    row = cur.fetchone()\n\n    if row[0] != store_id:\n        raise ValueError(\"Invalid store_id %d / %d\" % (row[0], store_id))\n    con.close()\n\n\ndef check_will(\n    port,\n    client_id: str,\n    payload: bytes,\n    topic: str,\n    qos: int,\n    retain: int,\n    properties: str,\n    idx=0,\n):\n    con = sqlite3.connect(f\"{port}/mosquitto.sqlite3\")\n    try:\n        cur = con.cursor()\n        cur.execute(\n            \"SELECT client_id,topic,payload,payloadlen,qos,retain,properties \"\n            \"FROM wills\",\n        )\n        for i in range(0, idx + 1):\n            row = cur.fetchone()\n\n        if row is None:\n            raise ValueError(f\"no will at index {idx}\")\n\n        if row[0] != client_id:\n            raise ValueError(f\"Invalid client_id {row[0]} / {client_id}\")\n\n        if row[1] != topic:\n            raise ValueError(\"Invalid topic %s / %s\" % (row[2], topic))\n\n        if row[2] != payload:\n            raise ValueError(\"Invalid payload %s / %s\" % (row[2], payload))\n\n        if row[3] != len(payload):\n            raise ValueError(\"Invalid payloadlen %d / %d\" % (row[3], len(payload)))\n\n        if row[4] != qos:\n            raise ValueError(\"Invalid qos %d / %d\" % (row[4], qos))\n\n        if row[5] != retain:\n            raise ValueError(\"Invalid retain %d / %d\" % (row[5], retain))\n\n        if row[6] != properties:\n            raise ValueError(\"Invalid properties %s / %s\" % (row[6], properties))\n\n    except ValueError as err:\n        raise ValueError(str(err) + f\" at index {idx}\") from err\n    finally:\n        con.close()\n\n    return row[0]\n"
  },
  {
    "path": "test/broker/prop_subpub_helper.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client subscribed to a topic receives its own message sent to that topic.\n# Does a given property get sent through?\n# MQTT v5\n\nfrom mosq_test_helper import *\n\ndef prop_subpub_helper(start_broker, test_name, props_out, props_in, expect_proto_error=False):\n    rc = 1\n    mid = 53\n    connect_packet = mosq_test.gen_connect(test_name, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"%s/subpub/qos0\" % (test_name), 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    publish_packet_out = mosq_test.gen_publish(\"%s/subpub/qos0\" % (test_name), qos=0, payload=\"message\", proto_ver=5, properties=props_out)\n\n    publish_packet_expected = mosq_test.gen_publish(\"%s/subpub/qos0\" % (test_name), qos=0, payload=\"message\", proto_ver=5, properties=props_in)\n\n    disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.PROTOCOL_ERROR, proto_ver=5)\n\n    port = mosq_test.get_port()\n    if start_broker:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port)\n\n        mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, \"suback\")\n        if expect_proto_error:\n            mosq_test.do_send_receive(sock, publish_packet_out, disconnect_packet, \"publish\")\n        else:\n            mosq_test.do_send_receive(sock, publish_packet_out, publish_packet_expected, \"publish\")\n\n        rc = 0\n\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if start_broker:\n            broker.terminate()\n            if mosq_test.wait_for_subprocess(broker):\n                print(\"broker not terminated\")\n                if rc == 0: rc=1\n            (stdo, stde) = broker.communicate()\n            if rc:\n                print(stde.decode('utf-8'))\n                exit(rc)\n        else:\n            return rc\n"
  },
  {
    "path": "test/broker/proxy_helper.py",
    "content": "import socket\n\nPROXY_VER = 0x20\nPROXY_CMD_LOCAL = 0x00\nPROXY_CMD_PROXY = 0x01\nPROXY_FAM_UNSPEC = 0x00\nPROXY_FAM_IPV4 = 0x10\nPROXY_FAM_IPV6 = 0x20\nPROXY_FAM_UNIX = 0x30\nPROXY_PROTO_UNSPEC = 0x00\nPROXY_PROTO_TCP = 0x01\nPROXY_PROTO_UDP = 0x02\n\ndef do_connect(port, data):\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.settimeout(5)\n    sock.connect((\"localhost\", port))\n    sock.send(data)\n    return sock\n\ndef do_proxy_v2_connect(port, ver, cmd, fam, data):\n    proxy_header = b\"\\x0d\\x0a\\x0d\\x0a\\x00\\x0d\\x0a\\x51\\x55\\x49\\x54\\x0a\"\n    l = len(data)\n    proxy_header += bytes([ver | cmd, fam, (l&0xFF00)>>8, l&0xFF])\n    proxy_header += data\n    return do_connect(port, proxy_header)\n\ndef do_proxy_v1_connect(port, data):\n    return do_connect(port, data)\n"
  },
  {
    "path": "test/broker/readme.txt",
    "content": "----- Broker Tests -----\n\nThis folder contains a number of tests to exercise the functionality of the\nbroker. Feel free to add more.\n\nNumbering is as follows:\n\n01: Connection tests\n02: Subscribe/unsubscribe tests\n03: Publish tests\n04: Retained message tests\n05: Session management tests\n06: Bridge tests\n07: Will tests\n08: TLS tests\n09: Auth tests\n10: Listener tests\n11: Persistence tests\n12: Property tests\n13: Malformed tests\n14: Dynamic security tests\n"
  },
  {
    "path": "test/broker/test.py",
    "content": "#!/usr/bin/env python3\n\nimport sys\nsys.path.insert(0, \"..\")\nimport ptest\n\ntests = [\n    #(ports required, 'path'),\n    (1, './msg_sequence_test.py'),\n    (1, './01-bad-initial-packets.py'),\n    (1, './01-connect-575314.py'),\n    (1, './01-connect-accept-protocol.py'),\n    (1, './01-connect-allow-anonymous.py'),\n    (2, './01-connect-auto-id.py'),\n    (1, './01-connect-disconnect-v5.py'),\n    (1, './01-connect-global-max-clients.py'),\n    (1, './01-connect-global-max-connections.py'),\n    (2, './01-connect-listener-allow-anonymous.py'),\n    (1, './01-connect-max-connections.py'),\n    (1, './01-connect-max-keepalive.py'),\n    (1, './01-connect-take-over.py'),\n    (1, './01-connect-uname-no-password-denied.py'),\n    (1, './01-connect-uname-or-anon.py'),\n    (1, './01-connect-uname-password-denied-no-will.py'),\n    (1, './01-connect-uname-password-denied.py'),\n    (1, './01-connect-unix-socket.py'),\n    (1, './01-connect-windows-line-endings.py'),\n    (2, './01-connect-zero-length-id.py'),\n    (1, './01-plugin-connect-uname-password-denied.py'),\n\n    (1, './02-shared-nolocal.py'),\n    (1, './02-shared-qos0-v5.py'),\n    (1, './02-subhier-crash.py'),\n    (1, './02-subpub-b2c-topic-alias.py'),\n    (1, './02-subpub-qos0-long-topic.py'),\n    (1, './02-subpub-qos0-oversize-payload.py'),\n    (1, './02-subpub-qos0-queued-bytes.py'),\n    (1, './02-subpub-qos0-retain-as-publish.py'),\n    (1, './02-subpub-qos0-send-retain.py'),\n    (1, './02-subpub-qos0-subscription-id.py'),\n    (1, './02-subpub-qos0-topic-alias-unknown.py'),\n    (1, './02-subpub-qos0-topic-alias.py'),\n    (1, './02-subpub-qos1-message-expiry-retain.py'),\n    (1, './02-subpub-qos1-message-expiry-will.py'),\n    (1, './02-subpub-qos1-message-expiry.py'),\n    (1, './02-subpub-qos1-nolocal.py'),\n    (1, './02-subpub-qos1-oversize-payload.py'),\n    (1, './02-subpub-qos1.py'),\n    (1, './02-subpub-qos2-1322.py'),\n    (1, './02-subpub-qos2-max-inflight-bytes.py'),\n    (1, './02-subpub-qos2-pubrec-error.py'),\n    (1, './02-subpub-qos2-receive-maximum-1.py'),\n    (1, './02-subpub-qos2-receive-maximum-2.py'),\n    (1, './02-subpub-qos2.py'),\n    (1, './02-subpub-recover-subscriptions.py'),\n    (1, './02-subscribe-dollar-v5.py'),\n    (1, './02-subscribe-invalid-utf8.py'),\n    (1, './02-subscribe-long-topic.py'),\n    (1, './02-subscribe-persistence-flipflop.py'),\n\n    #(1, './03-publish-qos1-queued-bytes.py'),\n    (1, './03-pattern-matching.py'),\n    (1, './03-publish-bad-flags.py'),\n    (1, './03-publish-b2c-disconnect-qos1.py'),\n    (1, './03-publish-b2c-disconnect-qos2.py'),\n    (1, './03-publish-b2c-qos1-len.py'),\n    (1, './03-publish-b2c-qos2-len.py'),\n    (1, './03-publish-c2b-disconnect-qos2.py'),\n    (1, './03-publish-c2b-qos2-len.py'),\n    (1, './03-publish-dollar-v5.py'),\n    (1, './03-publish-dollar.py'),\n    (1, './03-publish-invalid-utf8.py'),\n    (1, './03-publish-long-topic.py'),\n    (1, './03-publish-qos1-max-inflight-expire.py'),\n    (1, './03-publish-qos1-max-inflight.py'),\n    (1, './03-publish-qos1-no-subscribers-v5.py'),\n    (1, './03-publish-qos1-retain-disabled.py'),\n    (1, './03-publish-qos1.py'),\n    (1, './03-publish-qos2-dup.py'),\n    #(1, './03-publish-qos2-max-inflight-exceeded.py'),\n    (1, './03-publish-qos2-max-inflight.py'),\n    (1, './03-publish-qos2-reuse-mid.py'),\n    (1, './03-publish-qos2.py'),\n\n    (1, './04-retain-check-source-persist.py'),\n    (1, './04-retain-check-source.py'),\n    (1, './04-retain-clear-multiple.py'),\n    (1, './04-retain-qos0-clear.py'),\n    (1, './04-retain-qos0-fresh.py'),\n    (1, './04-retain-qos0-repeated.py'),\n    (1, './04-retain-qos0.py'),\n    (1, './04-retain-qos1-qos0.py'),\n    (1, './04-retain-upgrade-outgoing-qos.py'),\n    (2, './04-retain-check-source-persist-diff-port.py'),\n\n    (1, './05-clean-session-qos1.py'),\n    (1, './05-session-expiry-v5.py'),\n    (1, './05-session-expiry-kick.py'),\n\n    (2, './06-bridge-b2br-disconnect-qos1.py'),\n    (2, './06-bridge-b2br-disconnect-qos2.py'),\n    (2, './06-bridge-b2br-late-connection-retain.py'),\n    (2, './06-bridge-b2br-late-connection.py'),\n    (2, './06-bridge-b2br-remapping.py'),\n    (2, './06-bridge-br2b-disconnect-qos1.py'),\n    (2, './06-bridge-br2b-disconnect-qos2.py'),\n    (2, './06-bridge-br2b-remapping.py'),\n    (2, './06-bridge-clean-session-csF-lcsF.py'),\n    (2, './06-bridge-clean-session-csF-lcsN.py'),\n    (2, './06-bridge-clean-session-csF-lcsT.py'),\n    (2, './06-bridge-clean-session-csT-lcsF.py'),\n    (2, './06-bridge-clean-session-csT-lcsN.py'),\n    (2, './06-bridge-clean-session-csT-lcsT.py'),\n    (2, './06-bridge-fail-persist-resend-qos1.py'),\n    (2, './06-bridge-fail-persist-resend-qos2.py'),\n    (1, './06-bridge-no-local.py'),\n    (2, './06-bridge-outgoing-retain.py'),\n    (2, './06-bridge-per-listener-settings.py'),\n    (2, './06-bridge-reconnect-local-out.py'),\n    (2, './06-bridge-remote-shutdown.py'),\n    (2, './06-bridge-config-reload.py'),\n    (2, './06-bridge-remap-receive-wildcard.py'),\n\n    (1, './07-will-control.py'),\n    (1, './07-will-delay-invalid-573191.py'),\n    (1, './07-will-delay-reconnect.py'),\n    (1, './07-will-delay-recover.py'),\n    (1, './07-will-delay-session-expiry-0.py'),\n    (1, './07-will-delay-session-expiry.py'),\n    (1, './07-will-delay-session-expiry2.py'),\n    (1, './07-will-delay.py'),\n    (1, './07-will-disconnect-with-will.py'),\n    (1, './07-will-invalid-utf8.py'),\n    (1, './07-will-no-flag.py'),\n    (1, './07-will-null-topic.py'),\n    (1, './07-will-null.py'),\n    (1, './07-will-oversize-payload.py'),\n    (1, './07-will-per-listener.py'),\n    (1, './07-will-properties.py'),\n    (1, './07-will-qos0.py'),\n    (1, './07-will-reconnect-1273.py'),\n    (1, './07-will-takeover.py'),\n\n    (2, './08-ssl-bridge.py'),\n    (2, './08-ssl-connect-cert-auth-crl.py'),\n    (2, './08-ssl-connect-cert-auth-expired.py'),\n    (2, './08-ssl-connect-cert-auth-expired-allowed.py'),\n    (2, './08-ssl-connect-cert-auth-revoked.py'),\n    (2, './08-ssl-connect-cert-auth-without.py'),\n    (2, './08-ssl-connect-cert-auth.py'),\n    (2, './08-ssl-connect-dhparam.py'),\n    (2, './08-ssl-connect-identity.py'),\n    (2, './08-ssl-connect-no-auth-wrong-ca.py'),\n    (2, './08-ssl-connect-no-auth.py'),\n    (2, './08-ssl-connect-no-identity.py'),\n    (1, './08-ssl-hup-disconnect.py'),\n    (2, './08-tls-psk-pub.py'),\n    (3, './08-tls-psk-bridge.py'),\n\n    (1, './09-acl-access-variants.py'),\n    (1, './09-acl-change.py'),\n    (1, './09-acl-empty-file.py'),\n    (1, './09-auth-bad-method.py'),\n    (1, './09-extended-auth-change-username.py'),\n    (1, './09-extended-auth-multistep-reauth.py'),\n    (1, './09-extended-auth-multistep.py'),\n    (1, './09-extended-auth-reauth.py'),\n    (1, './09-extended-auth-single.py'),\n    (1, './09-plugin-acl-access-variants.py'),\n    (1, './09-plugin-acl-change.py'),\n    (1, './09-plugin-auth-acl-pub.py'),\n    (1, './09-plugin-auth-acl-pub-prop.py'),\n    (1, './09-plugin-auth-acl-sub-denied.py'),\n    (1, './09-plugin-auth-acl-sub.py'),\n    (1, './09-plugin-auth-context-params.py'),\n    (1, './09-plugin-auth-defer-unpwd-fail.py'),\n    (1, './09-plugin-auth-defer-unpwd-success.py'),\n    (1, './09-plugin-auth-msg-params.py'),\n    (1, './09-plugin-auth-unpwd-fail.py'),\n    (1, './09-plugin-auth-unpwd-success.py'),\n    (1, './09-plugin-auth-v2-unpwd-fail.py'),\n    (1, './09-plugin-auth-v2-unpwd-success.py'),\n    (1, './09-plugin-auth-v3-unpwd-fail.py'),\n    (1, './09-plugin-auth-v3-unpwd-success.py'),\n    (1, './09-plugin-auth-v4-unpwd-fail.py'),\n    (1, './09-plugin-auth-v4-unpwd-success.py'),\n    (1, './09-plugin-auth-v5-unpwd-fail.py'),\n    (1, './09-plugin-auth-v5-unpwd-success.py'),\n    (1, './09-plugin-bad.py'),\n    (1, './09-plugin-change-id.py'),\n    (1, './09-plugin-evt-client-offline.py'),\n    (1, './09-plugin-evt-message-in.py'),\n    (1, './09-plugin-evt-message-out.py'),\n    (1, './09-plugin-evt-psk-key.py'),\n    (1, './09-plugin-evt-reload.py'),\n    (1, './09-plugin-evt-subscribe.py'),\n    (1, './09-plugin-evt-tick.py'),\n    (1, './09-plugin-evt-unsubscribe.py'),\n    (1, './09-plugin-delayed-auth.py'),\n    (2, './09-plugin-load-acl.py'),\n    (3, './09-plugin-load-basic-auth.py'),\n    (2, './09-plugin-load-extended-auth.py'),\n    (1, './09-plugin-publish.py'),\n    (1, './09-plugin-unsupported.py'),\n    (1, './09-pwfile-parse-invalid.py'),\n\n    (2, './10-listener-mount-point.py'),\n\n    (1, './11-message-expiry.py'),\n    (1, './11-persistence-autosave-changes.py'),\n    (1, './11-persistent-subscription.py'),\n    (1, './11-persistent-subscription-no-local.py'),\n    (1, './11-pub-props.py'),\n    (1, './11-subscription-id.py'),\n\n    (1, './12-prop-assigned-client-identifier.py'),\n    (1, './12-prop-maximum-packet-size-broker.py'),\n    (1, './12-prop-maximum-packet-size-publish-qos1.py'),\n    (1, './12-prop-maximum-packet-size-publish-qos2.py'),\n    (1, './12-prop-response-topic-correlation-data.py'),\n    (1, './12-prop-response-topic.py'),\n    (1, './12-prop-server-keepalive.py'),\n    (1, './12-prop-subpub-content-type.py'),\n    (1, './12-prop-subpub-payload-format.py'),\n\n    (1, './13-websocket-bad-origin.py'),\n\n    (1, './14-dynsec-acl.py'),\n    (1, './14-dynsec-allow-wildcard.py'),\n    (1, './14-dynsec-anon-group.py'),\n    (1, './14-dynsec-auth.py'),\n    (1, './14-dynsec-client-invalid.py'),\n    (1, './14-dynsec-client.py'),\n    (1, './14-dynsec-config-init-env.py'),\n    (1, './14-dynsec-config-init-file.py'),\n    (1, './14-dynsec-config-init-random.py'),\n    (1, './14-dynsec-default-access.py'),\n    (1, './14-dynsec-disable-client.py'),\n    (1, './14-dynsec-group-invalid.py'),\n    (1, './14-dynsec-group.py'),\n    (1, './14-dynsec-modify-client.py'),\n    (1, './14-dynsec-modify-group.py'),\n    (1, './14-dynsec-modify-role.py'),\n    (1, './14-dynsec-plugin-invalid.py'),\n    (1, './14-dynsec-role-invalid.py'),\n    (1, './14-dynsec-role.py'),\n\n    (2, './15-persist-bridge-queue.py',  'persist_sqlite'),\n    (1, './15-persist-client-msg-in-v3-1-1.py', 'persist_sqlite'),\n    (1, './15-persist-client-msg-in-v5-0.py', 'persist_sqlite'),\n    (1, './15-persist-client-msg-out-clear-v3-1-1.py', 'persist_sqlite'),\n    (1, './15-persist-client-msg-out-dup-v3-1-1.py', 'persist_sqlite'),\n    (1, './15-persist-client-msg-out-queue-v3-1-1.py', 'persist_sqlite'),\n    (1, './15-persist-client-msg-out-v3-1-1-db.py', 'persist_sqlite'),\n    (1, './15-persist-client-msg-out-v3-1-1.py', 'persist_sqlite'),\n    (1, './15-persist-client-msg-out-v5-0.py', 'persist_sqlite'),\n    (1, './15-persist-client-v3-1-1.py', 'persist_sqlite'),\n    (1, './15-persist-client-v5-0.py', 'persist_sqlite'),\n    (1, './15-persist-publish-properties-v5-0.py', 'persist_sqlite'),\n    (1, './15-persist-retain-clear.py', 'persist_sqlite'),\n    (1, './15-persist-retain-v3-1-1.py', 'persist_sqlite'),\n    (1, './15-persist-retain-v5-0.py', 'persist_sqlite'),\n    (1, './15-persist-subscription-v3-1-1.py', 'persist_sqlite'),\n    (1, './15-persist-subscription-v5-0.py', 'persist_sqlite'),\n\n    (1, './16-cmd-args.py'),\n    (4, './16-config-huge.py'),\n    (1, './16-config-includedir.py'),\n    (1, './16-config-missing.py'),\n    (1, './16-config-parse-errors-tls.py'),\n    (1, './16-config-parse-errors-tls-psk.py'),\n    (1, './16-config-parse-errors-without-tls.py'),\n\n    (4, './17-control-list-listeners.py'),\n    (1, './17-control-list-plugins.py'),\n    (1, './17-control-missing-endpoint.py'),\n\n    (1, './20-sparkplug-compliance.py'),\n    (1, './20-sparkplug-aware.py'),\n\n    (1, './21-proxy-bad-version.py'),\n    (1, './21-proxy-v1-bad.py'),\n    (1, './21-proxy-v1-success.py'),\n    (1, './21-proxy-v2-bad-config.py'),\n    (1, './21-proxy-v2-bad-header.py'),\n    (1, './21-proxy-v2-local.py'),\n    (1, './21-proxy-v2-ipv4.py'),\n    (1, './21-proxy-v2-ipv6.py'),\n    (1, './21-proxy-v2-unix.py'),\n    (1, './21-proxy-v2-websockets.py'),\n    (1, './21-proxy-v2-long-tlv.py'),\n    (1, './21-proxy-v2-ssl-require-cert-failure.py'),\n    (1, './21-proxy-v2-ssl-require-cert-success.py'),\n    (1, './21-proxy-v2-ssl-common-name-failure.py'),\n    (1, './21-proxy-v2-ssl-common-name-success.py'),\n    (1, './21-proxy-v2-lost-connection.py'),\n    (1, './21-proxy-v2-ssl-cipher.py'),\n    (1, './21-proxy-v2-ssl-require-tls-failure.py'),\n    (1, './21-proxy-v2-ssl-require-tls-success.py'),\n\n    (2, './22-http-api-acl.py'),\n    (3, './22-http-api-api.py'),\n    (2, './22-http-api-auth.py'),\n    (2, './22-http-api-file.py'),\n    (2, './22-http-api-tls.py'),\n\n    (2, './23-security-acl-file-reload.py'),\n]\n\nif __name__ == \"__main__\":\n    test = ptest.PTest()\n    if len(sys.argv) == 2 and sys.argv[1] == \"--rerun-failed\":\n        test.run_failed_tests()\n    else:\n        test.run_tests(tests)\n"
  },
  {
    "path": "test/broker/test.supp",
    "content": "{\n   openssl_CRYPTO_get_ex_new_index\n   Memcheck:Leak\n   match-leak-kinds: reachable\n   ...\n   fun:CRYPTO_get_ex_new_index\n   ...\n}\n{\n   dl_reachable_leaks\n   Memcheck:Leak\n   match-leak-kinds: reachable\n   ...\n   fun:_dl_catch_error\n}\n{\n   openssl_CRYPTO_THREAD_run_once\n   Memcheck:Leak\n   match-leak-kinds: reachable\n   ...\n   fun:CRYPTO_THREAD_run_once\n   ...\n}\n{\n   dl_open_reachable\n   Memcheck:Leak\n   match-leak-kinds: reachable\n   ...\n   fun:_dl_open\n   ...\n}\n{\n   MHD_quick_close\n   CoreError:FdBadClose\n   fun:__syscall_cancel\n   fun:close\n   fun:MHD_start_daemon_va\n   fun:MHD_start_daemon\n   ...\n}\n{\n   openssl_LH_new\n   Memcheck:Leak\n   match-leak-kinds: reachable\n   fun:malloc\n   fun:CRYPTO_zalloc\n   fun:OPENSSL_LH_new\n   ...\n   fun:__pthread_once_slow\n}\n{\n   openssl_THREAD_lock_new\n   Memcheck:Leak\n   match-leak-kinds: reachable\n   fun:malloc\n   fun:CRYPTO_zalloc\n   fun:CRYPTO_THREAD_lock_new\n   ...\n   fun:__pthread_once_slow\n}\n{\n   openssl_CRYPTO_set_ex_data\n   Memcheck:Leak\n   match-leak-kinds: reachable\n   fun:malloc\n   fun:CRYPTO_zalloc\n   ...\n   fun:CRYPTO_set_ex_data\n   ...\n   fun:__pthread_once_slow\n}\n"
  },
  {
    "path": "test/client/02-subscribe-argv-errors-tls-psk.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(args, stderr_expected, rc_expected):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub'] + args\n\n    sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n    if mosq_test.wait_for_subprocess(sub):\n        print(\"sub not terminated\")\n        raise mosq_test.TestError(1)\n    (stdo, stde) = sub.communicate()\n    if sub.returncode != rc_expected:\n        raise mosq_test.TestError(sub.returncode)\n    if stderr_expected is not None and stde.decode('utf-8') != stderr_expected:\n        raise mosq_test.TestError(stde)\n\n\nif __name__ == '__main__':\n    helps = \"\\nUse 'mosquitto_sub --help' to see usage.\\n\"\n\n    # Missing args for TLS-PSK related options\n    do_test(['--psk'], \"Error: --psk argument given but no key specified.\\n\\n\" + helps, 1)\n    do_test(['--psk-identity'], \"Error: --psk-identity argument given but no identity specified.\\n\\n\" + helps, 1)\n"
  },
  {
    "path": "test/client/02-subscribe-argv-errors-tls.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(args, stderr_expected, rc_expected):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub'] + args\n\n    sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n    if mosq_test.wait_for_subprocess(sub):\n        print(\"sub not terminated\")\n        raise mosq_test.TestError(1)\n    (stdo, stde) = sub.communicate()\n    if sub.returncode != rc_expected:\n        raise mosq_test.TestError(sub.returncode)\n    if stderr_expected is not None and stde.decode('utf-8') != stderr_expected:\n        raise mosq_test.TestError(stde)\n\n\nif __name__ == '__main__':\n    helps = \"\\nUse 'mosquitto_sub --help' to see usage.\\n\"\n\n    # Missing args for TLS related options\n    do_test(['--cafile'], \"Error: --cafile argument given but no file specified.\\n\\n\" + helps, 1)\n    do_test(['--capath'], \"Error: --capath argument given but no directory specified.\\n\\n\" + helps, 1)\n    do_test(['--cert'], \"Error: --cert argument given but no file specified.\\n\\n\" + helps, 1)\n    do_test(['--ciphers'], \"Error: --ciphers argument given but no ciphers specified.\\n\\n\" + helps, 1)\n    do_test(['--key'], \"Error: --key argument given but no file specified.\\n\\n\" + helps, 1)\n    do_test(['--keyform'], \"Error: --keyform argument given but no keyform specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-alpn'], \"Error: --tls-alpn argument given but no protocol specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-engine'], \"Error: --tls-engine argument given but no engine_id specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-engine-kpass-sha1'], \"Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-version'], \"Error: --tls-version argument given but no version specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-keylog'], \"Error: --tls-keylog argument given but no file specified.\\n\\n\" + helps, 1)\n"
  },
  {
    "path": "test/client/02-subscribe-argv-errors-without-tls.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(args, stderr_expected, rc_expected):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub'] + args\n\n    sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n    if mosq_test.wait_for_subprocess(sub):\n        print(\"sub not terminated\")\n        raise mosq_test.TestError(1)\n    (stdo, stde) = sub.communicate()\n    if sub.returncode != rc_expected:\n        raise mosq_test.TestError(sub.returncode)\n    if stderr_expected is not None and stde.decode('utf-8') != stderr_expected:\n        raise mosq_test.TestError(stde)\n\n\nif __name__ == '__main__':\n    helps = \"\\nUse 'mosquitto_sub --help' to see usage.\\n\"\n\n    # Usage and version, ignore actual text though.\n    do_test(['--help'], None, 1)\n    do_test(['--version'], None, 1)\n\n    # Missing args\n    do_test(['-A'], \"Error: -A argument given but no address specified.\\n\\n\" + helps, 1)\n    do_test(['-C'], \"Error: -C argument given but no count specified.\\n\\n\" + helps, 1)\n    do_test(['-h'], \"Error: -h argument given but no host specified.\\n\\n\" + helps, 1)\n    do_test(['-i'], \"Error: -i argument given but no id specified.\\n\\n\" + helps, 1)\n    do_test(['-I'], \"Error: -I argument given but no id prefix specified.\\n\\n\" + helps, 1)\n    do_test(['-k'], \"Error: -k argument given but no keepalive specified.\\n\\n\" + helps, 1)\n    do_test(['-L'], \"Error: -L argument given but no URL specified.\\n\\n\" + helps, 1)\n    do_test(['-M'], \"Error: -M argument given but max_inflight not specified.\\n\\n\" + helps, 1)\n    do_test(['-o'], \"Error: -o argument given but no options file specified.\\n\\n\" + helps, 1)\n    do_test(['-p'], \"Error: -p argument given but no port specified.\\n\\n\" + helps, 1)\n    do_test(['-P'], \"Error: -P argument given but no password specified.\\n\\n\" + helps, 1)\n    do_test(['--proxy'], \"Error: --proxy argument given but no proxy url specified.\\n\\n\" + helps, 1)\n    do_test(['--random-filter'], \"Error: --random-filter argument given but no chance specified.\\n\\n\" + helps, 1)\n    do_test(['-q'], \"Error: -q argument given but no QoS specified.\\n\\n\" + helps, 1)\n    do_test(['-t'], \"Error: -t argument given but no topic specified.\\n\\n\" + helps, 1)\n    do_test(['-u'], \"Error: -u argument given but no username specified.\\n\\n\" + helps, 1)\n    do_test(['--unix'], \"Error: --unix argument given but no socket path specified.\\n\\n\" + helps, 1)\n    do_test(['-V'], \"Error: --protocol-version argument given but no version specified.\\n\\n\" + helps, 1)\n    do_test(['--will-payload'], \"Error: --will-payload argument given but no will payload specified.\\n\\n\" + helps, 1)\n    do_test(['--will-qos'], \"Error: --will-qos argument given but no will QoS specified.\\n\\n\" + helps, 1)\n    do_test(['--will-topic'], \"Error: --will-topic argument given but no will topic specified.\\n\\n\" + helps, 1)\n    do_test(['-x'], \"Error: -x argument given but no session expiry interval specified.\\n\\n\" + helps, 1)\n    do_test(['-F'], \"Error: -F argument given but no format specified.\\n\\n\" + helps, 1)\n    do_test(['-o'], \"Error: -o argument given but no options file specified.\\n\\n\" + helps, 1)\n    do_test(['-T'], \"Error: -T argument given but no topic filter specified.\\n\\n\" + helps, 1)\n    do_test(['-U'], \"Error: -U argument given but no unsubscribe topic specified.\\n\\n\" + helps, 1)\n    do_test(['-W'], \"Error: -W argument given but no timeout specified.\\n\\n\" + helps, 1)\n    do_test(['--will-payload', 'payload'], \"Error: Will payload given, but no will topic given.\\n\" + helps, 1)\n    # No -t or -U\n    do_test([], \"Error: You must specify a topic to subscribe to (-t) or unsubscribe from (-U).\\n\" + helps, 1)\n\n    # Invalid combinations\n    do_test(['-i', 'id', '-I', 'id-prefix'], \"Error: -i and -I argument cannot be used together.\\n\\n\" + helps, 1)\n    do_test(['-I', 'id-prefix', '-i', 'id'], \"Error: -i and -I argument cannot be used together.\\n\\n\" + helps, 1)\n\n    # Duplicate options\n    do_test(['-o', 'file1', '-o', 'file2'], \"Error: Duplicate -o argument given.\\n\\n\" + helps, 1)\n\n    # Invalid output format\n    do_test(['-F', '%'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%0'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%-'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%1'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%.'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%.1'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%Z'], \"Error: Invalid format specifier 'Z'.\\n\" + helps, 1)\n    do_test(['-F', '@'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '\\\\'], \"Error: Incomplete escape specifier.\\n\" + helps, 1)\n    do_test(['-F', '\\\\Z'], \"Error: Invalid escape specifier 'Z'.\\n\" + helps, 1)\n\n    # Invalid values\n    do_test(['-k', '-1'], \"Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\\n\\n\" + helps, 1)\n    do_test(['-k', '65536'], \"Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\\n\\n\" + helps, 1)\n    do_test(['-M', '0'], \"Error: Maximum inflight messages must be greater than 0.\\n\\n\" + helps, 1)\n    do_test(['-p', '-1'], \"Error: Invalid port given: -1\\n\" + helps, 1)\n    do_test(['-p', '65536'], \"Error: Invalid port given: 65536\\n\" + helps, 1)\n    do_test(['-q', '-1'], \"Error: Invalid QoS given: -1\\n\" + helps, 1)\n    do_test(['-q', '3'], \"Error: Invalid QoS given: 3\\n\" + helps, 1)\n    do_test(['-C', '0'], \"Error: Invalid message count \\\"0\\\".\\n\\n\" + helps, 1)\n    do_test(['-L', 'invalid://'], \"Error: Unsupported URL scheme.\\n\\n\" + helps, 1)\n    do_test(['-L', 'mqtt://localhost'], \"Error: Invalid URL for -L argument specified - topic missing.\\n\" + helps, 1)\n    do_test(['-L', 'mqtts://localhost'], \"Error: Invalid URL for -L argument specified - topic missing.\\n\" + helps, 1)\n    do_test(['-L', 'ws://localhost'], \"Error: Invalid URL for -L argument specified - topic missing.\\n\" + helps, 1)\n    do_test(['-L', 'wss://localhost'], \"Error: Invalid URL for -L argument specified - topic missing.\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'request-problem-information', '-1'], \"Error: Property value (-1) out of range for property request-problem-information.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'request-problem-information', '256'], \"Error: Property value (256) out of range for property request-problem-information.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'receive-maximum', '-1'], \"Error: Property value (-1) out of range for property receive-maximum.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'receive-maximum', '65536'], \"Error: Property value (65536) out of range for property receive-maximum.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'session-expiry-interval', '-1'], \"Error: Property value (-1) out of range for property session-expiry-interval.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'session-expiry-interval', '4294967296'], \"Error: Property value (4294967296) out of range for property session-expiry-interval.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'subscribe', 'subscription-identifier', '-1'], \"Error: Property value (-1) out of range for property subscription-identifier.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'subscribe', 'subscription-identifier', '4294967296'], \"Error: Property value (4294967296) out of range for property subscription-identifier.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'subscribe', 'topic-alias', '1'], \"Error: topic-alias property not allowed for subscribe in --property argument.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'auth', 'authentication-method', '1'], \"Error: authentication-method property not supported for auth in --property argument.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'puback', 'reason-string', '1'], \"Error: reason-string property not supported for puback in --property argument.\\n\\n\" + helps, 1)\n    do_test(['-t', '++'], \"Error: Invalid subscription topic '++', are all '+' and '#' wildcards correct?\\n\" + helps, 1)\n    do_test(['-T', '++'], \"Error: Invalid filter topic '++', are all '+' and '#' wildcards correct?\\n\" + helps, 1)\n    do_test(['-U', '++'], \"Error: Invalid unsubscribe topic '++', are all '+' and '#' wildcards correct?\\n\" + helps, 1)\n    do_test(['-V', '0'], \"Error: Invalid protocol version argument given.\\n\\n\" + helps, 1)\n    do_test(['-W', '0'], \"Error: Invalid timeout \\\"0\\\".\\n\\n\" + helps, 1)\n    do_test(['--will-qos', '-1'], \"Error: Invalid will QoS -1.\\n\\n\" + helps, 1)\n    do_test(['--will-qos', '3'], \"Error: Invalid will QoS 3.\\n\\n\" + helps, 1)\n    do_test(['--will-topic', '+'], \"Error: Invalid will topic '+', does it contain '+' or '#'?\\n\" + helps, 1)\n    do_test(['-x', 'A'], \"Error: session-expiry-interval not a number.\\n\\n\" + helps, 1)\n    do_test(['-x', '-2'], \"Error: session-expiry-interval out of range.\\n\\n\" + helps, 1)\n    do_test(['-x', '4294967296'], \"Error: session-expiry-interval out of range.\\n\\n\" + helps, 1)\n    do_test(['--retain-handling', 'invalid'], \"Error: Unknown value 'invalid' for --retain-handling.\\n\\n\" + helps, 1)\n\n    # Unknown options\n    do_test(['--unknown'], \"Error: Unknown option '--unknown'.\\n\" + helps, 1)\n    do_test(['-l'], \"Error: Unknown option '-l'.\\n\" + helps, 1)\n    do_test(['-m'], \"Error: Unknown option '-m'.\\n\" + helps, 1)\n    do_test(['-n'], \"Error: Unknown option '-n'.\\n\" + helps, 1)\n    do_test(['-r'], \"Error: Unknown option '-r'.\\n\" + helps, 1)\n    do_test(['--repeat'], \"Error: Unknown option '--repeat'.\\n\" + helps, 1)\n    do_test(['--repeat-delay'], \"Error: Unknown option '--repeat-delay'.\\n\" + helps, 1)\n    do_test(['-s'], \"Error: Unknown option '-s'.\\n\" + helps, 1)\n"
  },
  {
    "path": "test/client/02-subscribe-env.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver, env):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [mosq_test.get_build_root() + '/client/mosquitto_sub',\n            '-p', str(port),\n            '-q', '1',\n            '-V', V,\n            '-C', '1'\n            ]\n\n    payload = \"message\"\n    publish_packet_s = mosq_test.gen_publish(\"env/config/file/sub\", qos=1, mid=1, payload=payload, proto_ver=proto_ver)\n    publish_packet_r = mosq_test.gen_publish(\"env/config/file/sub\", qos=1, mid=2, payload=payload, proto_ver=proto_ver)\n    puback_packet_s = mosq_test.gen_puback(1, proto_ver=proto_ver)\n    puback_packet_r = mosq_test.gen_puback(2, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.pub_helper(port=port, proto_ver=proto_ver)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        time.sleep(0.1)\n        sock.send(publish_packet_s)\n        mosq_test.expect_packet(sock, \"puback\", puback_packet_s)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        if stdo.decode('utf-8') == payload + '\\n':\n            rc = sub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (_, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\nenv = {'HOME': str(source_dir / 'data')}\ndo_test(proto_ver=3, env=env)\ndo_test(proto_ver=4, env=env)\ndo_test(proto_ver=5, env=env)\n\nenv = {'XDG_CONFIG_HOME': str(source_dir / 'data/.config')}\ndo_test(proto_ver=3, env=env)\ndo_test(proto_ver=4, env=env)\ndo_test(proto_ver=5, env=env)\n"
  },
  {
    "path": "test/client/02-subscribe-filter-out.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(port),\n            '-q', '0',\n            '-t', '02/sub/filter-out/#',\n            '-T', '02/sub/filter-out/filtered',\n            '-V', V,\n            '-C', '2'\n            ]\n\n    publish_packet1 = mosq_test.gen_publish(\"02/sub/filter-out/recv\", qos=0, payload=\"recv\", proto_ver=proto_ver)\n    publish_packet2 = mosq_test.gen_publish(\"02/sub/filter-out/filtered\", qos=0, payload=\"filtered\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.pub_helper(port=port, proto_ver=proto_ver)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        time.sleep(0.1)\n        sock.send(publish_packet1)\n        sock.send(publish_packet2)\n        sock.send(publish_packet1)\n        sock.send(publish_packet2)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        if stdo.decode('utf-8') == 'recv\\nrecv\\n':\n            rc = sub_terminate_rc\n        else:\n            print(stdo.decode('utf-8'))\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/02-subscribe-format-json-properties.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\nimport json\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(port),\n            '-q', '1',\n            '-F', '%j',\n            '-t', '02/sub/format/json/properties/test',\n            '-V', V,\n            '-C', '1',\n            '-D', 'subscribe', 'subscription-identifier', '99',\n            '-D', 'connect', 'topic-alias-maximum', '100'\n            ]\n\n    props = mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 1)\n    props += mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"plain/text\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"/dev/null\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"2357289375902345\")\n    props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name\", \"value4\")\n    props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name\", \"value3\")\n    props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name\", \"value1\")\n    props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name\", \"value2\")\n    publish_packet = mosq_test.gen_publish(\"02/sub/format/json/properties/test\", mid=1, qos=1, payload=\"message\", proto_ver=proto_ver, properties=props)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    expected = {\n        \"tst\": \"\",\n        \"topic\": \"02/sub/format/json/properties/test\",\n        \"qos\": 1,\n        \"retain\": 0,\n        \"payloadlen\": 7,\n        \"mid\": 1,\n        \"properties\": {\n            \"payload-format-indicator\": 1,\n            \"content-type\": \"plain/text\",\n            \"response-topic\": \"/dev/null\",\n            \"correlation-data\": \"2357289375902345\",\n            \"user-properties\": [\n                {\"name\": \"value4\"},\n                {\"name\": \"value3\"},\n                {\"name\": \"value1\"},\n                {\"name\": \"value2\"}\n            ],\n            \"topic-alias\": 1,\n            \"subscription-identifier\": 99\n        },\n        \"payload\": \"message\"\n    }\n\n    try:\n        sock = mosq_test.pub_helper(port=port, proto_ver=proto_ver)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        time.sleep(0.1)\n        sock.send(publish_packet)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        j = json.loads(stdo.decode('utf-8'))\n        j['tst'] = \"\"\n\n        if j == expected:\n            rc = sub_terminate_rc\n        else:\n            print(json.dumps(j))\n            print(json.dumps(expected))\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/02-subscribe-format-json-qos0.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\nimport json\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(port),\n            '-F', '%j',\n            '-t', '02/sub/format/json/test',\n            '-V', V,\n            '-C', '1'\n            ]\n\n    publish_packet = mosq_test.gen_publish(\"02/sub/format/json/test\", qos=0, payload=\"message\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    expected = {\"tst\": \"\", \"topic\": \"02/sub/format/json/test\", \"qos\": 0, \"retain\": 0, \"payloadlen\": 7, \"payload\": \"message\"}\n\n    try:\n        sock = mosq_test.pub_helper(port=port, proto_ver=proto_ver)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        time.sleep(0.1)\n        sock.send(publish_packet)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        j = json.loads(stdo.decode('utf-8'))\n        j['tst'] = \"\"\n\n        if j == expected:\n            rc = sub_terminate_rc\n        else:\n            print(json.dumps(j))\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/02-subscribe-format-json-qos1.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\nimport json\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(port),\n            '-q', '1',\n            '-F', '%j',\n            '-t', '02/sub/format/json/qos1/test',\n            '-V', V,\n            '-C', '1'\n            ]\n\n    publish_packet = mosq_test.gen_publish(\"02/sub/format/json/qos1/test\", mid=1, qos=1, payload=\"message\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    expected = {\"tst\": \"\", \"topic\": \"02/sub/format/json/qos1/test\", \"qos\": 1, \"mid\": 1, \"retain\": 0, \"payloadlen\": 7, \"payload\": \"message\"}\n\n    try:\n        sock = mosq_test.pub_helper(port=port, proto_ver=proto_ver)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        time.sleep(0.1)\n        sock.send(publish_packet)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        j = json.loads(stdo.decode('utf-8'))\n        j['tst'] = \"\"\n\n        if j == expected:\n            rc = sub_terminate_rc\n        else:\n            print(json.dumps(expected))\n            print(json.dumps(j))\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/02-subscribe-format-json-retain.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\nimport json\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(port),\n            '-F', '%j',\n            '-t', '02/sub/format/json/retain/test',\n            '-V', V,\n            '-C', '1'\n            ]\n\n    publish_packet = mosq_test.gen_publish(\"02/sub/format/json/retain/test\", qos=0, payload=\"message\", proto_ver=proto_ver, retain=True)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    expected = {\"tst\": \"\", \"topic\": \"02/sub/format/json/retain/test\", \"qos\": 0, \"retain\": 1, \"payloadlen\": 7, \"payload\": \"message\"}\n\n    try:\n        sock = mosq_test.pub_helper(port=port, proto_ver=proto_ver)\n        sock.send(publish_packet)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        j = json.loads(stdo.decode('utf-8'))\n        j['tst'] = \"\"\n\n        if j == expected:\n            rc = sub_terminate_rc\n        else:\n            print(json.dumps(j))\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/02-subscribe-format.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\nimport platform\n\ndef do_test(format_str, expected_output, proto_ver=4, payload=\"message\"):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '02/sub/format/test',\n            '-C', '1',\n            '-V', V,\n            '-F', format_str\n            ]\n\n    if proto_ver == 5:\n        cmd += ['-D', 'subscribe', 'subscription-identifier', '56']\n\n    props = mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 1)\n    props += mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 3600)\n    props += mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"plain/text\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"/dev/null\")\n    #props += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"2357289375902345\")\n    props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name1\", \"value1\")\n    props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name2\", \"value2\")\n    props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name3\", \"value3\")\n    props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"name4\", \"value4\")\n    if proto_ver == 5:\n        publish_packet = mosq_test.gen_publish(\"02/sub/format/test\", qos=0, payload=payload, properties=props, proto_ver=proto_ver)\n    else:\n        publish_packet = mosq_test.gen_publish(\"02/sub/format/test\", qos=0, payload=payload, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.pub_helper(port=port, proto_ver=proto_ver)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        time.sleep(0.1)\n        sock.send(publish_packet)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        if stdo.decode('utf-8') == expected_output:\n            rc = sub_terminate_rc\n        else:\n            print(\"expected: (%d) %s\" % (len(expected_output), expected_output))\n            print(\"actual:   (%d) %s\"  % (len(stdo.decode('utf-8')), stdo.decode('utf-8')))\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test('%%', '%\\n')\ndo_test('%A', '\\n') # missing\ndo_test('%C', '\\n') # missing\ndo_test('%2C', '  \\n') # missing\ndo_test('%C', 'plain/text\\n', proto_ver=5)\ndo_test('%D', '\\n') # missing\ndo_test('%E', '\\n') # missing\ndo_test('%E', '3600\\n', proto_ver=5)\ndo_test('%F', '\\n') # missing\ndo_test('%F', '1\\n', proto_ver=5)\ndo_test('%l', '7\\n') # strlen(\"message\")\ndo_test('%02l', '07\\n') # strlen(\"message\")\ndo_test('%2l', ' 7\\n') # strlen(\"message\")\ndo_test('%-2l', '7 \\n') # strlen(\"message\")\ndo_test('%m', '0\\n')\ndo_test('%P', '\\n') # missing\ndo_test('%P', 'name1:value1 name2:value2 name3:value3 name4:value4\\n', proto_ver=5)\ndo_test('%p', 'message\\n')\ndo_test('%-12p', 'message     \\n')\ndo_test('%q', '0\\n')\ndo_test('%R', '\\n') # missing\ndo_test('%r', '0\\n')\ndo_test('%S', '\\n') # missing\ndo_test('%S', '56\\n', proto_ver=5)\ndo_test('%t', '02/sub/format/test\\n')\ndo_test('%.20t', '02/sub/format/test\\n')\ndo_test('%-.20t', '02/sub/format/test\\n')\ndo_test('%20t', '  02/sub/format/test\\n')\ndo_test('%-20t', '02/sub/format/test  \\n')\ndo_test('%10.10t', '02/sub/for\\n')\ndo_test('%20.10t', '          02/sub/for\\n')\ndo_test('%-20.10t', '02/sub/for          \\n')\ndo_test('%x', '6d657373616765\\n')\ndo_test('%.1x', '6 d 6 5 7 3 7 3 6 1 6 7 6 5\\n')\ndo_test('%.2x', '6d 65 73 73 61 67 65\\n')\ndo_test('%.2:x', '6d:65:73:73:61:67:65\\n')\ndo_test('%18x', '    6d657373616765\\n')\ndo_test('%-18x', '6d657373616765    \\n')\ndo_test('%X', '6D657373616765\\n')\ndo_test('\\\\\\\\', '\\\\\\n')\ndo_test('\\\\a', '\\a\\n')\n#do_test('\\\\e', '\\e\\n')\ndo_test('\\\\n', '\\n\\n')\ndo_test('\\\\r', '\\r\\n')\ndo_test('\\\\t', '\\t\\n')\ndo_test('\\\\v', '\\v\\n')\ndo_test('@@', '@\\n')\ndo_test('text', 'text\\n')\nif platform.system() != 'Darwin':\n    do_test('%.3d', '2.718\\n', payload=struct.pack('BBBBBBBB', 0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0x05, 0x40))\n    do_test('%.3f', '0.707\\n', payload=struct.pack('BBBB', 0xF4, 0xFD, 0x34, 0x3F))\n"
  },
  {
    "path": "test/client/02-subscribe-null.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '02/sub/null/test',\n            '-V', V,\n            '-C', '1',\n            '-v'\n            ]\n\n    topic = \"02/sub/null/test\"\n    payload = \"\"\n    publish_packet_s = mosq_test.gen_publish(topic, qos=1, mid=1, payload=payload, proto_ver=proto_ver)\n    publish_packet_r = mosq_test.gen_publish(topic, qos=1, mid=2, payload=payload, proto_ver=proto_ver)\n    puback_packet_s = mosq_test.gen_puback(1, proto_ver=proto_ver)\n    puback_packet_r = mosq_test.gen_puback(2, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.pub_helper(port=port, proto_ver=proto_ver)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        time.sleep(0.1)\n        sock.send(publish_packet_s)\n        mosq_test.expect_packet(sock, \"puback\", puback_packet_s)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        expected_output = topic + ' (null)\\n'\n        if stdo.decode('utf-8') == expected_output:\n            rc = sub_terminate_rc\n        else:\n            print(\"expected: %s\" % expected_output)\n            print(\"actual:   %s\"  % stdo.decode('utf-8'))\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/02-subscribe-qos1-ws.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port1}\\n\")\n        f.write(\"protocol websockets\\n\")\n        f.write(f\"listener {port2}\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n\n    ports = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(ports[0]),\n            '-q', '1',\n            '-t', '02/sub/qos1/test',\n            '-V', V,\n            '-C', '1',\n            '--ws'\n            ]\n\n    payload = \"message\"\n    publish_packet_s = mosq_test.gen_publish(\"02/sub/qos1/test\", qos=1, mid=1, payload=payload, proto_ver=proto_ver)\n    puback_packet_s = mosq_test.gen_puback(1, proto_ver=proto_ver)\n\n    write_config(conf_file, ports[0], ports[1])\n\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=ports[1], use_conf=True)\n\n        sock = mosq_test.pub_helper(port=ports[1], proto_ver=proto_ver)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        time.sleep(0.1)\n        sock.send(publish_packet_s)\n        mosq_test.expect_packet(sock, \"puback\", puback_packet_s)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        if stdo.decode('utf-8') == payload + '\\n':\n            rc = sub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        os.remove(conf_file)\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/02-subscribe-qos1.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '02/sub/qos1/test',\n            '-V', V,\n            '-C', '1'\n            ]\n\n    payload = \"message\"\n    publish_packet_s = mosq_test.gen_publish(\"02/sub/qos1/test\", qos=1, mid=1, payload=payload, proto_ver=proto_ver)\n    publish_packet_r = mosq_test.gen_publish(\"02/sub/qos1/test\", qos=1, mid=2, payload=payload, proto_ver=proto_ver)\n    puback_packet_s = mosq_test.gen_puback(1, proto_ver=proto_ver)\n    puback_packet_r = mosq_test.gen_puback(2, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.pub_helper(port=port, proto_ver=proto_ver)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        time.sleep(0.1)\n        sock.send(publish_packet_s)\n        mosq_test.expect_packet(sock, \"puback\", puback_packet_s)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        if stdo.decode('utf-8') == payload + '\\n':\n            rc = sub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/02-subscribe-retain-handling.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test():\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(port),\n            '-q', '0',\n            '-t', 'retain-handling',\n            '-V', '5',\n            '-C', '1'\n            ]\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    sock.settimeout(10)\n    sock.bind(('', port))\n    sock.listen(5)\n\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 1)\n    connect_packet = mosq_test.gen_connect(\"\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet_always = mosq_test.gen_subscribe(mid=1, topic=\"retain-handling\", qos=0x00, proto_ver=5)\n    subscribe_packet_new = mosq_test.gen_subscribe(mid=1, topic=\"retain-handling\", qos=0x10, proto_ver=5)\n    subscribe_packet_never = mosq_test.gen_subscribe(mid=1, topic=\"retain-handling\", qos=0x20, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid=1, qos=0)\n\n    publish_packet = mosq_test.gen_publish(\"retain-handling\", qos=0, payload=\"m\", proto_ver=5)\n\n    client_terminate_rc = 0\n\n    try:\n        for subscribe_packet, handling in [\n                (subscribe_packet_always, 'always'),\n                (subscribe_packet_new, 'new'),\n                (subscribe_packet_never, 'never')]:\n\n            client_cmd = cmd + ['--retain-handling', handling]\n            client = subprocess.Popen(client_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=env)\n\n            (conn, address) = sock.accept()\n            conn.settimeout(5)\n\n            mosq_test.expect_packet(conn, \"connect\", connect_packet)\n            conn.send(connack_packet)\n\n            mosq_test.expect_packet(conn, f\"subscribe {handling}\", subscribe_packet)\n            conn.send(suback_packet)\n            conn.send(publish_packet)\n\n            if mosq_test.wait_for_subprocess(client):\n                print(\"client not terminated\")\n                client_terminate_rc = 1\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        sock.close()\n        client.terminate()\n        exit(client_terminate_rc)\n\n\ndo_test()\n"
  },
  {
    "path": "test/client/02-subscribe-verbose.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_sub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '02/sub/verbose/test',\n            '-V', V,\n            '-C', '1',\n            '-v'\n            ]\n\n    topic = \"02/sub/verbose/test\"\n    payload = \"message\"\n    publish_packet_s = mosq_test.gen_publish(topic, qos=1, mid=1, payload=payload, proto_ver=proto_ver)\n    publish_packet_r = mosq_test.gen_publish(topic, qos=1, mid=2, payload=payload, proto_ver=proto_ver)\n    puback_packet_s = mosq_test.gen_puback(1, proto_ver=proto_ver)\n    puback_packet_r = mosq_test.gen_puback(2, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.pub_helper(port=port, proto_ver=proto_ver)\n\n        sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        time.sleep(0.1)\n        sock.send(publish_packet_s)\n        mosq_test.expect_packet(sock, \"puback\", puback_packet_s)\n        sub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(sub):\n            print(\"sub not terminated\")\n            sub_terminate_rc = 1\n        (stdo, stde) = sub.communicate()\n        expected_output = topic + ' ' + payload + '\\n'\n        if stdo.decode('utf-8') == expected_output:\n            rc = sub_terminate_rc\n        else:\n            print(\"expected: %s\" % expected_output)\n            print(\"actual:   %s\"  % stdo.decode('utf-8'))\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-argv-errors-tls-psk.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(args, stderr_expected, rc_expected):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub'] + args\n\n    pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n    if mosq_test.wait_for_subprocess(pub):\n        print(\"pub not terminated\")\n        raise mosq_test.TestError(1)\n    (stdo, stde) = pub.communicate()\n    if pub.returncode != rc_expected:\n        raise mosq_test.TestError(pub.returncode)\n    if stderr_expected is not None and stde.decode('utf-8') != stderr_expected:\n        raise mosq_test.TestError(stde)\n\n\nif __name__ == '__main__':\n    helps = \"\\nUse 'mosquitto_pub --help' to see usage.\\n\"\n\n    # Missing args\n    do_test(['--psk'], \"Error: --psk argument given but no key specified.\\n\\n\" + helps, 1)\n    do_test(['--psk-identity'], \"Error: --psk-identity argument given but no identity specified.\\n\\n\" + helps, 1)\n\n    # Invalid combinations\n    do_test(['--cafile', 'file', '--psk', 'key'], \"Error: Only one of --psk or --cafile/--capath may be used at once.\\n\" + helps, 1)\n    do_test(['--capath', 'dir', '--psk', 'key'], \"Error: Only one of --psk or --cafile/--capath may be used at once.\\n\" + helps, 1)\n    do_test(['--psk', 'key'], \"Error: --psk-identity required if --psk used.\\n\" + helps, 1)\n    \n"
  },
  {
    "path": "test/client/03-publish-argv-errors-tls.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(args, stderr_expected, rc_expected):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub'] + args\n\n    pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n    if mosq_test.wait_for_subprocess(pub):\n        print(\"pub not terminated\")\n        raise mosq_test.TestError(1)\n    (stdo, stde) = pub.communicate()\n    if pub.returncode != rc_expected:\n        raise mosq_test.TestError(pub.returncode)\n    if stderr_expected is not None and stde.decode('utf-8') != stderr_expected:\n        raise mosq_test.TestError(stde.decode('utf-8'))\n\n\nif __name__ == '__main__':\n    helps = \"\\nUse 'mosquitto_pub --help' to see usage.\\n\"\n\n    # Missing args\n    do_test(['--cafile'], \"Error: --cafile argument given but no file specified.\\n\\n\" + helps, 1)\n    do_test(['--capath'], \"Error: --capath argument given but no directory specified.\\n\\n\" + helps, 1)\n    do_test(['--cert'], \"Error: --cert argument given but no file specified.\\n\\n\" + helps, 1)\n    do_test(['--ciphers'], \"Error: --ciphers argument given but no ciphers specified.\\n\\n\" + helps, 1)\n    do_test(['--key'], \"Error: --key argument given but no file specified.\\n\\n\" + helps, 1)\n    do_test(['--keyform'], \"Error: --keyform argument given but no keyform specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-alpn'], \"Error: --tls-alpn argument given but no protocol specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-engine'], \"Error: --tls-engine argument given but no engine_id specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-engine-kpass-sha1'], \"Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-version'], \"Error: --tls-version argument given but no version specified.\\n\\n\" + helps, 1)\n\n    # Invalid combinations\n    do_test(['--cert', 'file'], \"Error: Both certfile and keyfile must be provided if one of them is set.\\n\" + helps, 1)\n    do_test(['--key', 'file'], \"Error: Both certfile and keyfile must be provided if one of them is set.\\n\" + helps, 1)\n    do_test(['--keyform', 'file'], \"Error: If keyform is set, keyfile must be also specified.\\n\" + helps, 1)\n    do_test(['--tls-engine-kpass-sha1', 'hash'], \"Error: when using tls-engine-kpass-sha1, both tls-engine and keyform must also be provided.\\n\" + helps, 1)\n\n    # Invalid values\n    do_test(['--tls-keylog', 'keylog', '-t','topic','-m','1', '--cafile', 'missing'], \"Error: Problem setting TLS options: File not found.\\n\", 1)\n\n"
  },
  {
    "path": "test/client/03-publish-argv-errors-without-tls.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(args, stderr_expected, rc_expected):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n       'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub'] + args\n\n    pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n    if mosq_test.wait_for_subprocess(pub):\n        print(\"pub not terminated\")\n        raise mosq_test.TestError(1)\n    (stdo, stde) = pub.communicate()\n    if pub.returncode != rc_expected:\n        raise mosq_test.TestError(pub.returncode)\n    if stderr_expected is not None and stde.decode('utf-8') != stderr_expected:\n        raise mosq_test.TestError(stde)\n\n\nif __name__ == '__main__':\n    helps = \"\\nUse 'mosquitto_pub --help' to see usage.\\n\"\n\n    # Usage, version, ignore actual text though.\n    do_test(['--help'], None, 1)\n    do_test(['--version'], None, 1)\n\n    # Missing args\n    do_test(['-A'], \"Error: -A argument given but no address specified.\\n\\n\" + helps, 1)\n    do_test(['-f'], \"Error: -f argument given but no file specified.\\n\\n\" + helps, 1)\n    do_test(['-h'], \"Error: -h argument given but no host specified.\\n\\n\" + helps, 1)\n    do_test(['-i'], \"Error: -i argument given but no id specified.\\n\\n\" + helps, 1)\n    do_test(['-I'], \"Error: -I argument given but no id prefix specified.\\n\\n\" + helps, 1)\n    do_test(['-k'], \"Error: -k argument given but no keepalive specified.\\n\\n\" + helps, 1)\n    do_test(['-L'], \"Error: -L argument given but no URL specified.\\n\\n\" + helps, 1)\n    do_test(['-M'], \"Error: -M argument given but max_inflight not specified.\\n\\n\" + helps, 1)\n    do_test(['-m'], \"Error: -m argument given but no message specified.\\n\\n\" + helps, 1)\n    do_test(['-o'], \"Error: -o argument given but no options file specified.\\n\\n\" + helps, 1)\n    do_test(['-p'], \"Error: -p argument given but no port specified.\\n\\n\" + helps, 1)\n    do_test(['-P'], \"Error: -P argument given but no password specified.\\n\\n\" + helps, 1)\n    do_test(['--proxy'], \"Error: --proxy argument given but no proxy url specified.\\n\\n\" + helps, 1)\n    do_test(['-q'], \"Error: -q argument given but no QoS specified.\\n\\n\" + helps, 1)\n    do_test(['--repeat'], \"Error: --repeat argument given but no count specified.\\n\\n\" + helps, 1)\n    do_test(['--repeat-delay'], \"Error: --repeat-delay argument given but no time specified.\\n\\n\" + helps, 1)\n    do_test(['-t'], \"Error: -t argument given but no topic specified.\\n\\n\" + helps, 1)\n    do_test(['-u'], \"Error: -u argument given but no username specified.\\n\\n\" + helps, 1)\n    do_test(['--unix'], \"Error: --unix argument given but no socket path specified.\\n\\n\" + helps, 1)\n    do_test(['-V'], \"Error: --protocol-version argument given but no version specified.\\n\\n\" + helps, 1)\n    do_test(['--will-payload'], \"Error: --will-payload argument given but no will payload specified.\\n\\n\" + helps, 1)\n    do_test(['--will-qos'], \"Error: --will-qos argument given but no will QoS specified.\\n\\n\" + helps, 1)\n    do_test(['--will-topic'], \"Error: --will-topic argument given but no will topic specified.\\n\\n\" + helps, 1)\n    do_test(['-x'], \"Error: -x argument given but no session expiry interval specified.\\n\\n\" + helps, 1)\n\n    do_test(['-V', '5', '-D'], \"Error: --property argument given but not enough arguments specified.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect'], \"Error: --property argument given but not enough arguments specified.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'receive-maximum'], \"Error: --property argument given but not enough arguments specified.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'invalid', 'receive-maximum', '1'], \"Error: Invalid command invalid given in --property argument.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'invalid', '1'], \"Error: Invalid property name invalid given in --property argument.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'will-delay-interval', '1'], \"Error: will-delay-interval property not allowed for connect in --property argument.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'user-property', 'key'], \"Error: --property argument given but not enough arguments specified.\\n\\n\" + helps, 1)\n\n    # Invalid combinations\n    do_test(['-i', 'id', '-I', 'id-prefix'], \"Error: -i and -I argument cannot be used together.\\n\\n\" + helps, 1)\n    do_test(['-I', 'id-prefix', '-i', 'id'], \"Error: -i and -I argument cannot be used together.\\n\\n\" + helps, 1)\n    do_test(['--will-payload', 'payload'], \"Error: Will payload given, but no will topic given.\\n\" + helps, 1)\n    do_test(['--will-retain'], \"Error: Will retain given, but no will topic given.\\n\" + helps, 1)\n    do_test(['-V', 'mqttv5', '-x', '-1'], \"Error: You must provide a client id if you are using an infinite session expiry interval.\\n\" + helps, 1)\n    do_test(['-V', 'mqttv311', '-c'], \"Error: You must provide a client id if you are using the -c option.\\n\" + helps, 1)\n\n\n    # Mixed message types\n    do_test(['-m', 'message', '-f', 'file'], \"Error: Only one type of message can be sent at once.\\n\\n\" + helps, 1)\n    do_test(['-m', 'message', '-l'], \"Error: Only one type of message can be sent at once.\\n\\n\" + helps, 1)\n    do_test(['-l', '-m', 'message'], \"Error: Only one type of message can be sent at once.\\n\\n\" + helps, 1)\n    do_test(['-l', '-n'], \"Error: Only one type of message can be sent at once.\\n\\n\" + helps, 1)\n    do_test(['-l', '-s'], \"Error: Only one type of message can be sent at once.\\n\\n\" + helps, 1)\n\n    # Invalid values\n    do_test(['-t', 'topic', '-f', 'missing'], \"Error: Unable to read file \\\"missing\\\": No such file or directory.\\nError loading input file \\\"missing\\\".\\n\", 1)\n    do_test(['-k', '-1'], \"Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\\n\\n\" + helps, 1)\n    do_test(['-k', '65536'], \"Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\\n\\n\" + helps, 1)\n    do_test(['-M', '0'], \"Error: Maximum inflight messages must be greater than 0.\\n\\n\" + helps, 1)\n    do_test(['-p', '-1'], \"Error: Invalid port given: -1\\n\" + helps, 1)\n    do_test(['-p', '65536'], \"Error: Invalid port given: 65536\\n\" + helps, 1)\n    do_test(['-q', '-1'], \"Error: Invalid QoS given: -1\\n\" + helps, 1)\n    do_test(['-q', '3'], \"Error: Invalid QoS given: 3\\n\" + helps, 1)\n    do_test(['--repeat-delay', '-1'], \"Error: --repeat-delay argument must be >=0.0.\\n\\n\" + helps, 1)\n    do_test(['-t', 'topic/+'], \"Error: Invalid publish topic 'topic/+', does it contain '+' or '#'?\\n\" + helps, 1)\n    do_test(['-t', 'topic/#'], \"Error: Invalid publish topic 'topic/#', does it contain '+' or '#'?\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'request-problem-information', '-1'], \"Error: Property value (-1) out of range for property request-problem-information.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'request-problem-information', '256'], \"Error: Property value (256) out of range for property request-problem-information.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'receive-maximum', '-1'], \"Error: Property value (-1) out of range for property receive-maximum.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'receive-maximum', '65536'], \"Error: Property value (65536) out of range for property receive-maximum.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'session-expiry-interval', '-1'], \"Error: Property value (-1) out of range for property session-expiry-interval.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'session-expiry-interval', '4294967296'], \"Error: Property value (4294967296) out of range for property session-expiry-interval.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'subscription-identifier', '1'], \"Error: subscription-identifier property not allowed for connect in --property argument.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'publish', 'subscription-identifier', '1'], \"Error: subscription-identifier property not supported for publish in --property argument.\\n\\n\" + helps, 1)\n\n    # Unknown options\n    do_test(['--unknown'], \"Error: Unknown option '--unknown'.\\n\" + helps, 1)\n    do_test(['-C', '1'], \"Error: Unknown option '-C'.\\n\" + helps, 1)\n    do_test(['-e', 'response-topic'], \"Error: Unknown option '-e'.\\n\" + helps, 1)\n    do_test(['-E'], \"Error: Unknown option '-E'.\\n\" + helps, 1)\n    do_test(['-F', '%p'], \"Error: Unknown option '-F'.\\n\" + helps, 1)\n    do_test(['-N'], \"Error: Unknown option '-N'.\\n\" + helps, 1)\n    do_test(['--pretty'], \"Error: Unknown option '--pretty'.\\n\" + helps, 1)\n    do_test(['-R'], \"Error: Unknown option '-R'.\\n\" + helps, 1)\n    do_test(['--random-filter'], \"Error: Unknown option '--random-filter'.\\n\" + helps, 1)\n    do_test(['--remove-retained'], \"Error: Unknown option '--remove-retained'.\\n\" + helps, 1)\n    do_test(['--retain-as-published'], \"Error: Unknown option '--retain-as-published'.\\n\" + helps, 1)\n    do_test(['--retain-handling', 'invalid'], \"Error: Unknown option '--retain-handling'.\\n\" + helps, 1)\n    do_test(['--retained-only'], \"Error: Unknown option '--retained-only'.\\n\" + helps, 1)\n    do_test(['-T'], \"Error: Unknown option '-T'.\\n\" + helps, 1)\n    do_test(['-U'], \"Error: Unknown option '-U'.\\n\" + helps, 1)\n    do_test(['-v'], \"Error: Unknown option '-v'.\\n\" + helps, 1)\n    do_test(['-W'], \"Error: Unknown option '-W'.\\n\" + helps, 1)\n    do_test(['-w'], \"Error: Unknown option '-w'.\\n\" + helps, 1)\n"
  },
  {
    "path": "test/client/03-publish-env.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver, env):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [mosq_test.get_build_root() + '/client/mosquitto_pub',\n            '-p', str(port),\n            '-q', '1',\n            '-V', V\n            ]\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"env/config/file/pub\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=1, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\nenv = {'HOME': str(source_dir / 'data')}\ndo_test(proto_ver=3, env=env)\ndo_test(proto_ver=4, env=env)\ndo_test(proto_ver=5, env=env)\n\nenv = {'XDG_CONFIG_HOME': str(source_dir / 'data/.config')}\ndo_test(proto_ver=3, env=env)\ndo_test(proto_ver=4, env=env)\ndo_test(proto_ver=5, env=env)\n"
  },
  {
    "path": "test/client/03-publish-file-empty.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef write_file(filename):\n    with open(filename, 'w') as f:\n        pass\n\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n    data_file = os.path.basename(__file__).replace('.py', '.data')\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '03/pub/file/empty/test',\n            '-f', data_file,\n            '-V', V\n            ]\n\n    publish_packet = mosq_test.gen_publish(\"03/pub/file/empty/test\", qos=0, payload=\"\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    write_file(data_file)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=0, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        os.remove(data_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-file.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef write_file(filename):\n    with open(filename, 'w') as f:\n        f.write(\"line1\\n\")\n        f.write(\"line2\\n\")\n\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n    data_file = os.path.basename(__file__).replace('.py', '.data')\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '03/pub/file/test',\n            '-f', data_file,\n            '-V', V\n            ]\n\n    publish_packet = mosq_test.gen_publish(\"03/pub/file/test\", qos=0, payload=\"line1\\nline2\\n\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    write_file(data_file)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=0, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        os.remove(data_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-options-file.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port, V):\n    with open(filename, 'w') as f:\n        f.write(\"-p %d\\n\" % (port))\n        f.write(\"-V %s\\n\" % (V))\n        f.write(\"-q 1\\n\")\n        f.write(\"-t 03/pub/qos1/test\\n\")\n        f.write(\"-m message\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-o', conf_file\n            ]\n\n    write_config(conf_file, port, V)\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/pub/qos1/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=1, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        os.remove(conf_file)\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-qos0-empty.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-p', str(port),\n            '-q', '0',\n            '-t', '03/pub/qos0/test',\n            '-n',\n            '-V', V\n            ]\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/pub/qos0/test\", qos=0, mid=mid, payload=\"\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=0, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-qos1-properties.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '03/pub/qos1/test/properties',\n            '-m', 'message',\n            '-V', V,\n\t        '-D', 'publish', 'content-type', 'application/json',\n\t        '-D', 'publish', 'correlation-data', 'some-data',\n\t        '-D', 'publish', 'message-expiry-interval', '59',\n\t        '-D', 'publish', 'payload-format-indicator', '1',\n\t        '-D', 'publish', 'response-topic', '/dev/null',\n\t        '-D', 'publish', 'topic-alias', '4',\n\t        '-D', 'publish', 'user-property', 'publish', 'up'\n            ]\n\n    mid = 1\n    props = mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"application/json\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"some-data\")\n    props += mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 1)\n    props += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"/dev/null\")\n    props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"publish\", \"up\")\n    props += mqtt5_props.gen_uint32_prop(mqtt5_props.MESSAGE_EXPIRY_INTERVAL, 59)\n    publish_packet = mosq_test.gen_publish(\"03/pub/qos1/test/properties\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver, properties=props)\n    puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=1, proto_ver=5)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-qos1-ws-large.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port1}\\n\")\n        f.write(\"protocol websockets\\n\")\n        f.write(f\"listener {port2}\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n\n    ports = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n\n    payload = \"abcdefghijklmnopqrstuvwxyz0123456789\"*1821\n\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-p', str(ports[0]),\n            '-q', '1',\n            '-t', '03/pub/qos1/test',\n            '-m', payload,\n            '-V', V,\n            '--ws'\n            ]\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/pub/qos1/test\", qos=1, mid=mid, payload=payload, proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    write_config(conf_file, ports[0], ports[1])\n\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=ports[1], use_conf=True)\n        sock = mosq_test.sub_helper(port=ports[1], topic=\"#\", qos=1, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        os.remove(conf_file)\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-qos1-ws.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port1}\\n\")\n        f.write(\"protocol websockets\\n\")\n        f.write(f\"listener {port2}\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n\n    ports = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-p', str(ports[0]),\n            '-q', '1',\n            '-t', '03/pub/qos1/test',\n            '-m', 'message',\n            '-V', V,\n            '--ws'\n            ]\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/pub/qos1/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    write_config(conf_file, ports[0], ports[1])\n\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=ports[1], use_conf=True)\n        sock = mosq_test.sub_helper(port=ports[1], topic=\"#\", qos=1, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        os.remove(conf_file)\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-qos1.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '03/pub/qos1/test',\n            '-m', 'message',\n            '-V', V\n            ]\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/pub/qos1/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=1, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-repeat.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '03/pub/repeat/test',\n            '-m', 'message',\n            '-V', V,\n            '--repeat', '2',\n            '--repeat-delay', '0.1',\n            ]\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/pub/repeat/test\", qos=0, mid=mid, payload=\"message\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=0, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n\n        mosq_test.expect_packet(sock, \"publish 1\", publish_packet)\n        mosq_test.expect_packet(sock, \"publish 2\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-socks-auth-failed.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver, host):\n    rc = 1\n\n    (port1, port2) = mosq_test.get_port(2)\n\n    cmd = ['microsocks', '-?']\n    try:\n        proxy = subprocess.run(cmd, capture_output=True)\n    except FileNotFoundError:\n        print(\"microsocks not found, skipping test\")\n        sys.exit(0)\n\n    cmd = ['microsocks', '-1', '-i', host, '-u', 'user', '-P', 'pass:word', '-p', str(port1)]\n    if b\"bindaddr\" in proxy.stderr:\n        cmd += ['-b', host]\n    else:\n        cmd += ['-b']\n\n    try:\n        proxy = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n    except FileNotFoundError:\n        print(\"microsocks not found, skipping test\")\n        sys.exit(0)\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [\n            f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-h', host,\n            '-p', str(port2),\n            '-q', '1',\n            '-t', '03/pub/proxy/test',\n            '-m', 'message',\n            '-V', V,\n            '--proxy', f'socks5h://wrong:auth@{host}:{port1}'\n            ]\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, checkhost=host)\n\n    try:\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        if stde.decode('utf-8') == 'Error: Authorisation failed\\n':\n            rc = 0\n        else:\n            rc = pub_terminate_rc\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        proxy.terminate()\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3, host=\"localhost\")\ndo_test(proto_ver=4, host=\"localhost\")\ndo_test(proto_ver=5, host=\"localhost\")\n"
  },
  {
    "path": "test/client/03-publish-socks-no-auth.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver, host):\n    rc = 1\n\n    (port1, port2) = mosq_test.get_port(2)\n\n    cmd = ['microsocks', '-?']\n    try:\n        proxy = subprocess.run(cmd, capture_output=True)\n    except FileNotFoundError:\n        print(\"microsocks not found, skipping test\")\n        sys.exit(0)\n\n    cmd = ['microsocks', '-i', host, '-p', str(port1)]\n    if b\"bindaddr\" in proxy.stderr:\n        cmd += ['-b', host]\n    else:\n        cmd += ['-b']\n\n    try:\n        proxy = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n    except FileNotFoundError:\n        print(\"microsocks not found, skipping test\")\n        sys.exit(0)\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [\n            f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-h', host,\n            '-p', str(port2),\n            '-q', '1',\n            '-t', '03/pub/proxy/test',\n            '-m', 'message',\n            '-V', V,\n            '--proxy', f'socks5h://{host}:{port1}'\n            ]\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/pub/proxy/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, checkhost=host)\n\n    try:\n        sock = mosq_test.sub_helper(port=port2, topic=\"#\", qos=1, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        proxy.terminate()\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3, host=\"localhost\")\ndo_test(proto_ver=4, host=\"localhost\")\ndo_test(proto_ver=5, host=\"localhost\")\ndo_test(proto_ver=5, host=\"ip6-localhost\")\ndo_test(proto_ver=5, host=\"127.0.0.1\")\n#do_test(proto_ver=5, host=\"::1\")\n"
  },
  {
    "path": "test/client/03-publish-socks.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver, host):\n    rc = 1\n\n    (port1, port2) = mosq_test.get_port(2)\n\n    cmd = ['microsocks', '-?']\n    try:\n        proxy = subprocess.run(cmd, capture_output=True)\n    except FileNotFoundError:\n        print(\"microsocks not found, skipping test\")\n        sys.exit(0)\n\n    cmd = ['microsocks', '-1', '-i', host, '-u', 'user', '-P', 'pass:word', '-p', str(port1)]\n    if b\"bindaddr\" in proxy.stderr:\n        cmd += ['-b', host]\n    else:\n        cmd += ['-b']\n\n    try:\n        proxy = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n    except FileNotFoundError:\n        print(\"microsocks not found, skipping test\")\n        sys.exit(0)\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [\n            f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-h', host,\n            '-p', str(port2),\n            '-q', '1',\n            '-t', '03/pub/proxy/test',\n            '-m', 'message',\n            '-V', V,\n            '--proxy', f'socks5h://user:pass%3Aword@{host}:{port1}'\n            ]\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"03/pub/proxy/test\", qos=1, mid=mid, payload=\"message\", proto_ver=proto_ver)\n    if proto_ver == 5:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.NO_MATCHING_SUBSCRIBERS)\n    else:\n        puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, checkhost=host)\n\n    try:\n        sock = mosq_test.sub_helper(port=port2, topic=\"#\", qos=1, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        proxy.terminate()\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3, host=\"localhost\")\ndo_test(proto_ver=4, host=\"localhost\")\ndo_test(proto_ver=5, host=\"localhost\")\ndo_test(proto_ver=5, host=\"ip6-localhost\")\ndo_test(proto_ver=5, host=\"127.0.0.1\")\n#do_test(proto_ver=5, host=\"::1\")\n"
  },
  {
    "path": "test/client/03-publish-stdin-file.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '03/pub/stdin/file/test',\n            '-s',\n            '-V', V\n            ]\n\n    publish_packet = mosq_test.gen_publish(\"03/pub/stdin/file/test\", qos=0, payload=\"message1\\nmessage2\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=0, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=env)\n        pub.stdin.write(b'message1\\nmessage2')\n        pub.stdin.close()\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-stdin-line.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-p', str(port),\n            '-q', '2',\n            '-t', '03/pub/stdin/line/test',\n            '-l',\n            '-V', V\n            ]\n\n    publish_packet1 = mosq_test.gen_publish(\"03/pub/stdin/line/test\", qos=0, payload=\"message1\", proto_ver=proto_ver)\n    publish_packet2 = mosq_test.gen_publish(\"03/pub/stdin/line/test\", qos=0, payload=\"message2\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=0, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=env)\n        pub.stdin.write(b'message1\\nmessage2')\n        pub.stdin.close()\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet1)\n        mosq_test.expect_packet(sock, \"publish\", publish_packet2)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/03-publish-tls.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\nsource_dir = Path(__file__).resolve().parent\nssl_dir = source_dir.parent / \"ssl\"\n\ndef do_test(address, insecure_option, expect_ssl_fail):\n    rc = 1\n\n    port = mosq_test.get_port()\n    port = 8883\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing',\n        'SSLKEYLOGFILE':'/home/roger/keylog'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '--cafile', f\"{ssl_dir}/all-ca.crt\",\n            '-d',\n            '-h', address,\n            '-p', str(port),\n            '-t', '03/pub/tls/test',\n            '-m', 'message',\n            ]\n    if insecure_option is not None:\n        cmd.append(insecure_option)\n\n    connect_packet = mosq_test.gen_connect(\"\", clean_session=True)\n    connack_packet = mosq_test.gen_connack(rc=0)\n    publish_packet = mosq_test.gen_publish(\"03/pub/tls/test\", qos=0, payload=\"message\")\n\n    broker = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    broker.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=f\"{ssl_dir}/all-ca.crt\")\n    context.minimum_version = ssl.TLSVersion.TLSv1_2\n    context.load_cert_chain(certfile=f\"{ssl_dir}/server-san.crt\", keyfile=f\"{ssl_dir}/server-san.key\")\n    sbroker = context.wrap_socket(broker, server_side=True)\n    sbroker.settimeout(20)\n    sbroker.bind(('', port))\n    sbroker.listen(5)\n\n    try:\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n\n        (pub_sock, address) = sbroker.accept()\n        pub_sock.settimeout(5)\n\n        mosq_test.expect_packet(pub_sock, \"connect\", connect_packet)\n        pub_sock.send(connack_packet)\n        mosq_test.expect_packet(pub_sock, \"publish\", publish_packet)\n\n        if expect_ssl_fail:\n            raise mosq_test.TestError\n\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        rc = pub_terminate_rc\n        pub_sock.close()\n    except mosq_test.TestError:\n        pass\n    except ssl.SSLError as e:\n        if expect_ssl_fail and e.reason == \"SSLV3_ALERT_BAD_CERTIFICATE\":\n            rc = 0\n            pass\n        else:\n            raise mosq_test.TestError\n    except Exception as e:\n        print(e)\n    finally:\n        broker.close()\n        if rc:\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test(\"127.0.0.1\", None, False)\ndo_test(mosq_test.get_non_loopback_ip(), None, True)\ndo_test(mosq_test.get_non_loopback_ip(), \"--insecure\", False)\n"
  },
  {
    "path": "test/client/03-publish-url.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_pub',\n            '-L', f'mqtt://localhost:{port}/03/pub/url/test',\n            '-m', 'message',\n            '-V', V\n            ]\n\n    publish_packet = mosq_test.gen_publish(\"03/pub/url/test\", qos=0, payload=\"message\", proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"#\", qos=0, proto_ver=proto_ver)\n\n        pub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n        pub_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(pub):\n            print(\"pub not terminated\")\n            pub_terminate_rc = 1\n        (stdo, stde) = pub.communicate()\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet)\n        rc = pub_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/04-rr-argv-errors-tls-psk.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(args, stderr_expected, rc_expected):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_rr'] + args\n\n    sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n    sub.wait()\n    (stdo, stde) = sub.communicate()\n    if sub.returncode != rc_expected:\n        raise mosq_test.TestError(sub.returncode)\n    if stderr_expected is not None and stde.decode('utf-8') != stderr_expected:\n        raise mosq_test.TestError(stde)\n\n\nif __name__ == '__main__':\n    helps = \"\\nUse 'mosquitto_rr --help' to see usage.\\n\"\n\n    # Missing args for TLS-PSK related options\n    do_test(['--psk'], \"Error: --psk argument given but no key specified.\\n\\n\" + helps, 1)\n    do_test(['--psk-identity'], \"Error: --psk-identity argument given but no identity specified.\\n\\n\" + helps, 1)\n"
  },
  {
    "path": "test/client/04-rr-argv-errors-tls.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(args, stderr_expected, rc_expected):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_rr'] + args\n\n    sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n    sub.wait()\n    (stdo, stde) = sub.communicate()\n    if sub.returncode != rc_expected:\n        raise mosq_test.TestError(sub.returncode)\n    if stderr_expected is not None and stde.decode('utf-8') != stderr_expected:\n        raise mosq_test.TestError(stde)\n\n\nif __name__ == '__main__':\n    helps = \"\\nUse 'mosquitto_rr --help' to see usage.\\n\"\n\n    # Missing args for TLS related options\n    do_test(['--cafile'], \"Error: --cafile argument given but no file specified.\\n\\n\" + helps, 1)\n    do_test(['--capath'], \"Error: --capath argument given but no directory specified.\\n\\n\" + helps, 1)\n    do_test(['--cert'], \"Error: --cert argument given but no file specified.\\n\\n\" + helps, 1)\n    do_test(['--ciphers'], \"Error: --ciphers argument given but no ciphers specified.\\n\\n\" + helps, 1)\n    do_test(['--key'], \"Error: --key argument given but no file specified.\\n\\n\" + helps, 1)\n    do_test(['--keyform'], \"Error: --keyform argument given but no keyform specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-alpn'], \"Error: --tls-alpn argument given but no protocol specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-engine'], \"Error: --tls-engine argument given but no engine_id specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-engine-kpass-sha1'], \"Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\\n\\n\" + helps, 1)\n    do_test(['--tls-version'], \"Error: --tls-version argument given but no version specified.\\n\\n\" + helps, 1)\n"
  },
  {
    "path": "test/client/04-rr-argv-errors-without-tls.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(args, stderr_expected, rc_expected):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_rr'] + args\n\n    sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n    sub.wait()\n    (stdo, stde) = sub.communicate()\n    if sub.returncode != rc_expected:\n        raise mosq_test.TestError(sub.returncode)\n    if stderr_expected is not None and stde.decode('utf-8') != stderr_expected:\n        raise mosq_test.TestError(stde)\n\n\nif __name__ == '__main__':\n    helps = \"\\nUse 'mosquitto_rr --help' to see usage.\\n\"\n\n    # Usage, version, ignore actual text though.\n    do_test(['--help'], None, 1)\n    do_test(['--version'], None, 1)\n\n    # Missing args\n    do_test(['-A'], \"Error: -A argument given but no address specified.\\n\\n\" + helps, 1)\n    do_test(['-e'], \"Error: -e argument given but no response topic specified.\\n\\n\" + helps, 1)\n    do_test(['-h'], \"Error: -h argument given but no host specified.\\n\\n\" + helps, 1)\n    do_test(['-i'], \"Error: -i argument given but no id specified.\\n\\n\" + helps, 1)\n    do_test(['-I'], \"Error: -I argument given but no id prefix specified.\\n\\n\" + helps, 1)\n    do_test(['-k'], \"Error: -k argument given but no keepalive specified.\\n\\n\" + helps, 1)\n    do_test(['-L'], \"Error: -L argument given but no URL specified.\\n\\n\" + helps, 1)\n    do_test(['-M'], \"Error: -M argument given but max_inflight not specified.\\n\\n\" + helps, 1)\n    do_test(['-m'], \"Error: -m argument given but no message specified.\\n\\n\" + helps, 1)\n    do_test(['-o'], \"Error: -o argument given but no options file specified.\\n\\n\" + helps, 1)\n    do_test(['-p'], \"Error: -p argument given but no port specified.\\n\\n\" + helps, 1)\n    do_test(['-P'], \"Error: -P argument given but no password specified.\\n\\n\" + helps, 1)\n    do_test(['--proxy'], \"Error: --proxy argument given but no proxy url specified.\\n\\n\" + helps, 1)\n    do_test(['-q'], \"Error: -q argument given but no QoS specified.\\n\\n\" + helps, 1)\n    do_test(['-t'], \"Error: -t argument given but no topic specified.\\n\\n\" + helps, 1)\n    do_test(['-u'], \"Error: -u argument given but no username specified.\\n\\n\" + helps, 1)\n    do_test(['--unix'], \"Error: --unix argument given but no socket path specified.\\n\\n\" + helps, 1)\n    do_test(['-V'], \"Error: --protocol-version argument given but no version specified.\\n\\n\" + helps, 1)\n    do_test(['--will-payload'], \"Error: --will-payload argument given but no will payload specified.\\n\\n\" + helps, 1)\n    do_test(['--will-qos'], \"Error: --will-qos argument given but no will QoS specified.\\n\\n\" + helps, 1)\n    do_test(['--will-topic'], \"Error: --will-topic argument given but no will topic specified.\\n\\n\" + helps, 1)\n    do_test(['-x'], \"Error: -x argument given but no session expiry interval specified.\\n\\n\" + helps, 1)\n    do_test(['-F'], \"Error: -F argument given but no format specified.\\n\\n\" + helps, 1)\n    do_test(['-o'], \"Error: -o argument given but no options file specified.\\n\\n\" + helps, 1)\n    do_test(['-W'], \"Error: -W argument given but no timeout specified.\\n\\n\" + helps, 1)\n    do_test(['--will-payload', 'payload'], \"Error: Will payload given, but no will topic given.\\n\" + helps, 1)\n    # No -t or -U\n    do_test([], \"Error: All of topic, message, and response topic must be supplied.\\n\" + helps, 1)\n\n    # Invalid combinations\n    do_test(['-i', 'id', '-I', 'id-prefix'], \"Error: -i and -I argument cannot be used together.\\n\\n\" + helps, 1)\n    do_test(['-I', 'id-prefix', '-i', 'id'], \"Error: -i and -I argument cannot be used together.\\n\\n\" + helps, 1)\n\n    # Invalid output format\n    do_test(['-F', '%'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%0'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%-'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%1'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%.'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%.1'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '%Z'], \"Error: Invalid format specifier 'Z'.\\n\" + helps, 1)\n    do_test(['-F', '@'], \"Error: Incomplete format specifier.\\n\" + helps, 1)\n    do_test(['-F', '\\\\'], \"Error: Incomplete escape specifier.\\n\" + helps, 1)\n    do_test(['-F', '\\\\Z'], \"Error: Invalid escape specifier 'Z'.\\n\" + helps, 1)\n\n    # Invalid values\n    do_test(['-e', 'topic/+'], \"Error: Invalid response topic 'topic/+', does it contain '+' or '#'?\\n\" + helps, 1)\n    do_test(['-k', '-1'], \"Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\\n\\n\" + helps, 1)\n    do_test(['-k', '65536'], \"Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\\n\\n\" + helps, 1)\n    do_test(['-M', '0'], \"Error: Maximum inflight messages must be greater than 0.\\n\\n\" + helps, 1)\n    do_test(['-p', '-1'], \"Error: Invalid port given: -1\\n\" + helps, 1)\n    do_test(['-p', '65536'], \"Error: Invalid port given: 65536\\n\" + helps, 1)\n    do_test(['-q', '-1'], \"Error: Invalid QoS given: -1\\n\" + helps, 1)\n    do_test(['-q', '3'], \"Error: Invalid QoS given: 3\\n\" + helps, 1)\n    do_test(['-L', 'invalid://'], \"Error: Unsupported URL scheme.\\n\\n\" + helps, 1)\n    do_test(['-L', 'mqtt://localhost'], \"Error: Invalid URL for -L argument specified - topic missing.\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'request-problem-information', '-1'], \"Error: Property value (-1) out of range for property request-problem-information.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'request-problem-information', '256'], \"Error: Property value (256) out of range for property request-problem-information.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'receive-maximum', '-1'], \"Error: Property value (-1) out of range for property receive-maximum.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'receive-maximum', '65536'], \"Error: Property value (65536) out of range for property receive-maximum.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'session-expiry-interval', '-1'], \"Error: Property value (-1) out of range for property session-expiry-interval.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'connect', 'session-expiry-interval', '4294967296'], \"Error: Property value (4294967296) out of range for property session-expiry-interval.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'subscribe', 'subscription-identifier', '-1'], \"Error: Property value (-1) out of range for property subscription-identifier.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'subscribe', 'subscription-identifier', '4294967296'], \"Error: Property value (4294967296) out of range for property subscription-identifier.\\n\\n\" + helps, 1)\n    do_test(['-V', '5', '-D', 'subscribe', 'topic-alias', '1'], \"Error: topic-alias property not allowed for subscribe in --property argument.\\n\\n\" + helps, 1)\n    do_test(['-V', '0'], \"Error: Invalid protocol version argument given.\\n\\n\" + helps, 1)\n    do_test(['-W', '0'], \"Error: Invalid timeout \\\"0\\\".\\n\\n\" + helps, 1)\n    do_test(['--will-qos', '-1'], \"Error: Invalid will QoS -1.\\n\\n\" + helps, 1)\n    do_test(['--will-qos', '3'], \"Error: Invalid will QoS 3.\\n\\n\" + helps, 1)\n    do_test(['--will-topic', '+'], \"Error: Invalid will topic '+', does it contain '+' or '#'?\\n\" + helps, 1)\n    do_test(['-x', 'A'], \"Error: session-expiry-interval not a number.\\n\\n\" + helps, 1)\n    do_test(['-x', '-2'], \"Error: session-expiry-interval out of range.\\n\\n\" + helps, 1)\n    do_test(['-x', '4294967296'], \"Error: session-expiry-interval out of range.\\n\\n\" + helps, 1)\n    do_test(['--retain-handling', 'invalid'], \"Error: Unknown value 'invalid' for --retain-handling.\\n\\n\" + helps, 1)\n\n    # Mixed message types\n    do_test(['-m', 'message', '-f', 'file'], \"Error: Only one type of message can be sent at once.\\n\\n\" + helps, 1)\n\n    # Unknown options\n    do_test(['--unknown'], \"Error: Unknown option '--unknown'.\\n\" + helps, 1)\n    do_test(['-l'], \"Error: Unknown option '-l'.\\n\" + helps, 1)\n    do_test(['-r'], \"Error: Unknown option '-r'.\\n\" + helps, 1)\n    do_test(['--repeat'], \"Error: Unknown option '--repeat'.\\n\" + helps, 1)\n    do_test(['--repeat-delay'], \"Error: Unknown option '--repeat-delay'.\\n\" + helps, 1)\n"
  },
  {
    "path": "test/client/04-rr-env.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver, env):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = mosq_test.env_add_ld_library_path(env)\n\n    payload = \"message\"\n    cmd = [mosq_test.get_build_root() + '/client/mosquitto_rr',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '04/rr/qos1/test/request',\n            '-e', '04/rr/qos1/test/response',\n            '-V', V,\n            '-m', payload\n            ]\n\n    if proto_ver == 5:\n        props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"04/rr/qos1/test/response\")\n    else:\n        props = None\n    publish_packet_req = mosq_test.gen_publish(\"04/rr/qos1/test/request\", qos=1, mid=1, payload=payload, proto_ver=proto_ver, properties=props)\n    payload = \"the response\"\n    publish_packet_resp = mosq_test.gen_publish(\"04/rr/qos1/test/response\", qos=1, mid=2, payload=payload, proto_ver=proto_ver)\n    puback_packet_req = mosq_test.gen_puback(1, proto_ver=proto_ver)\n    puback_packet_resp = mosq_test.gen_puback(2, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"04/rr/qos1/test/request\", qos=1, proto_ver=proto_ver)\n\n        rr = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet_req)\n        sock.send(puback_packet_req)\n\n        sock.send(publish_packet_resp)\n        mosq_test.expect_packet(sock, \"puback\", puback_packet_resp)\n\n        time.sleep(0.1)\n        rr_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(rr):\n            print(\"rr not terminated\")\n            rr_terminate_rc = 1\n        (stdo, stde) = rr.communicate()\n        if stdo.decode('utf-8') == payload + '\\n':\n            rc = rr_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\nenv = {'HOME': str(source_dir / 'data')}\ndo_test(proto_ver=3, env=env)\ndo_test(proto_ver=4, env=env)\ndo_test(proto_ver=5, env=env)\n\nenv = {'XDG_CONFIG_HOME': str(source_dir / 'data/.config')}\ndo_test(proto_ver=3, env=env)\ndo_test(proto_ver=4, env=env)\ndo_test(proto_ver=5, env=env)\n"
  },
  {
    "path": "test/client/04-rr-qos1-ws.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef write_config(filename, port1, port2):\n    with open(filename, 'w') as f:\n        f.write(\"allow_anonymous true\\n\")\n        f.write(f\"listener {port1}\\n\")\n        f.write(\"protocol websockets\\n\")\n        f.write(f\"listener {port2}\\n\")\n\ndef do_test(proto_ver):\n    rc = 1\n\n    ports = mosq_test.get_port(2)\n    conf_file = os.path.basename(__file__).replace('.py', '.conf')\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    payload = \"message\"\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_rr',\n            '-p', str(ports[0]),\n            '-q', '1',\n            '-t', '04/rr/qos1/test/request',\n            '-e', '04/rr/qos1/test/response',\n            '-V', V,\n            '-m', payload,\n            '--ws'\n            ]\n\n    if proto_ver == 5:\n        props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"04/rr/qos1/test/response\")\n    else:\n        props = None\n    publish_packet_req = mosq_test.gen_publish(\"04/rr/qos1/test/request\", qos=1, mid=1, payload=payload, proto_ver=proto_ver, properties=props)\n    payload = \"the response\"\n    publish_packet_resp = mosq_test.gen_publish(\"04/rr/qos1/test/response\", qos=1, mid=2, payload=payload, proto_ver=proto_ver)\n    puback_packet_req = mosq_test.gen_puback(1, proto_ver=proto_ver)\n    puback_packet_resp = mosq_test.gen_puback(2, proto_ver=proto_ver)\n\n    write_config(conf_file, ports[0], ports[1])\n\n    try:\n        broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=ports[1], use_conf=True)\n        sock = mosq_test.sub_helper(port=ports[1], topic=\"04/rr/qos1/test/request\", qos=1, proto_ver=proto_ver)\n\n        rr = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet_req)\n        sock.send(puback_packet_req)\n\n        sock.send(publish_packet_resp)\n        mosq_test.expect_packet(sock, \"puback\", puback_packet_resp)\n\n        time.sleep(0.1)\n        rr_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(rr):\n            print(\"rr not terminated\")\n            rr_terminate_rc = 1\n        (stdo, stde) = rr.communicate()\n        if stdo.decode('utf-8') == payload + '\\n':\n            rc = rr_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        os.remove(conf_file)\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/04-rr-qos1.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test(proto_ver):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    if proto_ver == 5:\n        V = 'mqttv5'\n    elif proto_ver == 4:\n        V = 'mqttv311'\n    else:\n        V = 'mqttv31'\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    payload = \"message\"\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_rr',\n            '-p', str(port),\n            '-q', '1',\n            '-t', '04/rr/qos1/test/request',\n            '-e', '04/rr/qos1/test/response',\n            '-V', V,\n            '-m', payload\n            ]\n\n    if proto_ver == 5:\n        props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"04/rr/qos1/test/response\")\n    else:\n        props = None\n    publish_packet_req = mosq_test.gen_publish(\"04/rr/qos1/test/request\", qos=1, mid=1, payload=payload, proto_ver=proto_ver, properties=props)\n    payload = \"the response\"\n    publish_packet_resp = mosq_test.gen_publish(\"04/rr/qos1/test/response\", qos=1, mid=2, payload=payload, proto_ver=proto_ver)\n    puback_packet_req = mosq_test.gen_puback(1, proto_ver=proto_ver)\n    puback_packet_resp = mosq_test.gen_puback(2, proto_ver=proto_ver)\n\n    broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)\n\n    try:\n        sock = mosq_test.sub_helper(port=port, topic=\"04/rr/qos1/test/request\", qos=1, proto_ver=proto_ver)\n\n        rr = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)\n\n        mosq_test.expect_packet(sock, \"publish\", publish_packet_req)\n        sock.send(puback_packet_req)\n\n        sock.send(publish_packet_resp)\n        mosq_test.expect_packet(sock, \"puback\", puback_packet_resp)\n\n        time.sleep(0.1)\n        rr_terminate_rc = 0\n        if mosq_test.wait_for_subprocess(rr):\n            print(\"rr not terminated\")\n            rr_terminate_rc = 1\n        (stdo, stde) = rr.communicate()\n        if stdo.decode('utf-8') == payload + '\\n':\n            rc = rr_terminate_rc\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        broker.terminate()\n        if mosq_test.wait_for_subprocess(broker):\n            print(\"broker not terminated\")\n            if rc == 0: rc=1\n        (stdo, stde) = broker.communicate()\n        if rc:\n            print(stde.decode('utf-8'))\n            print(\"proto_ver=%d\" % (proto_ver))\n            exit(rc)\n\n\ndo_test(proto_ver=3)\ndo_test(proto_ver=4)\ndo_test(proto_ver=5)\n"
  },
  {
    "path": "test/client/04-rr-retain-handling.py",
    "content": "#!/usr/bin/env python3\n\n#\n\nfrom mosq_test_helper import *\n\ndef do_test():\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    env = {\n        'XDG_CONFIG_HOME':'/tmp/missing'\n    }\n    env = mosq_test.env_add_ld_library_path(env)\n    cmd = [f'{mosq_test.get_build_root()}/client/mosquitto_rr',\n            '-p', str(port),\n            '-q', '0',\n            '-e', 'retain-handling',\n            '-t', 'retain-handling',\n            '-m', 'm',\n            '-V', '5',\n            ]\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    sock.settimeout(5)\n    sock.bind(('', port))\n    sock.listen(5)\n\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 1)\n    connect_packet = mosq_test.gen_connect(\"\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    subscribe_packet_always = mosq_test.gen_subscribe(mid=1, topic=\"retain-handling\", qos=0x00, proto_ver=5)\n    subscribe_packet_new = mosq_test.gen_subscribe(mid=1, topic=\"retain-handling\", qos=0x10, proto_ver=5)\n    subscribe_packet_never = mosq_test.gen_subscribe(mid=1, topic=\"retain-handling\", qos=0x20, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid=1, qos=0, proto_ver=5)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"retain-handling\")\n    publish_packet_in = mosq_test.gen_publish(\"retain-handling\", qos=0, payload=\"m\", proto_ver=5, properties=props)\n    publish_packet_out = mosq_test.gen_publish(\"retain-handling\", qos=0, payload=\"m\", proto_ver=5)\n\n    client_terminate_rc = 0\n\n    try:\n        for subscribe_packet, handling in [\n                (subscribe_packet_always, 'always'),\n                (subscribe_packet_new, 'new'),\n                (subscribe_packet_never, 'never')]:\n\n            client_cmd = cmd + ['--retain-handling', handling]\n            client = subprocess.Popen(client_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=env)\n\n            (conn, address) = sock.accept()\n            conn.settimeout(5)\n\n            mosq_test.expect_packet(conn, \"connect\", connect_packet)\n            conn.send(connack_packet)\n\n            mosq_test.expect_packet(conn, f\"subscribe {handling}\", subscribe_packet)\n            conn.send(suback_packet)\n\n            mosq_test.expect_packet(conn, \"publish}\", publish_packet_in)\n            conn.send(publish_packet_out)\n\n            if mosq_test.wait_for_subprocess(client):\n                print(\"client not terminated\")\n                client_terminate_rc = 1\n        sock.close()\n    except mosq_test.TestError:\n        pass\n    except Exception as e:\n        print(e)\n    finally:\n        sock.close()\n        client.terminate()\n        exit(client_terminate_rc)\n\n\ndo_test()\n"
  },
  {
    "path": "test/client/CMakeLists.txt",
    "content": "file(GLOB PY_TEST_FILES [0-9][0-9]-*.py)\n\nset(EXCLUDE_LIST\n    # none\n)\n\nforeach(PY_TEST_FILE ${PY_TEST_FILES})\n    get_filename_component(PY_TEST_NAME ${PY_TEST_FILE} NAME_WE)\n    if(${PY_TEST_NAME} IN_LIST EXCLUDE_LIST)\n        continue()\n    endif()\n    add_test(NAME client-${PY_TEST_NAME}\n        COMMAND ${PY_TEST_FILE}\n    )\n    set_tests_properties(client-${PY_TEST_NAME}\n        PROPERTIES\n            ENVIRONMENT \"BUILD_ROOT=${CMAKE_BINARY_DIR}\"\n    )\nendforeach()\n"
  },
  {
    "path": "test/client/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\n.PHONY: all check test test-compile ptest clean\n.NOTPARALLEL:\n\nall :\n\ncheck : test\ntest : 02 03 04\n\t./test.sh\n\t./test-ws.sh\n\ntest-compile:\n\n02 :\n\t./02-subscribe-argv-errors-without-tls.py\nifeq ($(WITH_TLS),yes)\n\t./02-subscribe-argv-errors-tls.py\nifeq ($(WITH_TLS_PSK),yes)\n\t./02-subscribe-argv-errors-tls-psk.py\nendif\nendif\n\t./02-subscribe-env.py\n\t./02-subscribe-filter-out.py\n\t./02-subscribe-format.py\n\t./02-subscribe-format-json-qos0.py\n\t./02-subscribe-format-json-qos1.py\n\t./02-subscribe-format-json-properties.py\n\t./02-subscribe-format-json-retain.py\n\t./02-subscribe-qos1.py\nifeq ($(WITH_WEBSOCKETS),yes)\n\t./02-subscribe-qos1-ws.py\nendif\n\t./02-subscribe-retain-handling.py\n\t./02-subscribe-format.py\n\t./02-subscribe-null.py\n\t./02-subscribe-verbose.py\n\n03 :\n\t./03-publish-argv-errors-without-tls.py\nifeq ($(WITH_TLS),yes)\n\t./03-publish-argv-errors-tls.py\nifeq ($(WITH_TLS_PSK),yes)\n\t./03-publish-argv-errors-tls-psk.py\nendif\nendif\n\t./03-publish-env.py\n\t./03-publish-file-empty.py\n\t./03-publish-file.py\n\t./03-publish-options-file.py\n\t./03-publish-qos0-empty.py\n\t./03-publish-qos1-properties.py\n\t./03-publish-qos1.py\nifeq ($(WITH_WEBSOCKETS),yes)\n\t./03-publish-qos1-ws.py\n\t./03-publish-qos1-ws-large.py\nendif\n\t./03-publish-repeat.py\n\t./03-publish-socks.py\n\t./03-publish-socks-auth-failed.py\n\t./03-publish-socks-no-auth.py\n\t./03-publish-stdin-file.py\n\t./03-publish-stdin-line.py\n\t./03-publish-tls.py\n\t./03-publish-url.py\n\n04 :\n\t./04-rr-argv-errors-without-tls.py\nifeq ($(WITH_TLS),yes)\n\t./04-rr-argv-errors-tls.py\nifeq ($(WITH_TLS_PSK),yes)\n\t./04-rr-argv-errors-tls-psk.py\nendif\nendif\n\t./04-rr-env.py\n\t./04-rr-qos1.py\nifeq ($(WITH_WEBSOCKETS),yes)\n\t./04-rr-qos1-ws.py\nendif\n\t./04-rr-retain-handling.py\n\nptest :\n\t./test.sh\n\t./test-ws.sh\n\t./test.py\n\nclean:\n"
  },
  {
    "path": "test/client/data/.config/mosquitto_pub",
    "content": "-t env/config/file/pub\n-m message\n"
  },
  {
    "path": "test/client/data/.config/mosquitto_sub",
    "content": "-t env/config/file/sub\n"
  },
  {
    "path": "test/client/mosq_test_helper.py",
    "content": "import inspect, os, sys\n\n# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder\ncmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],\"..\")))\nif cmd_subfolder not in sys.path:\n    sys.path.insert(0, cmd_subfolder)\n\nimport mosq_test\nimport mqtt5_opts\nimport mqtt5_props\nimport mqtt5_rc\n\nimport socket\nimport ssl\nimport struct\nimport subprocess\nimport time\nimport errno\n\nfrom pathlib import Path\n\nsource_dir = Path(__file__).resolve().parent\n"
  },
  {
    "path": "test/client/test-ws.sh",
    "content": "#!/bin/bash\n\n# Very basic client testing.\n\n\nset -e\n\nexport BASE_PATH=../../\nexport LD_LIBRARY_PATH=${BASE_PATH}/lib\nexport CONFIG=ws.conf\nexport PORT=1888\nexport SUB_TIMEOUT=1\n\n# Start broker\n../../src/mosquitto -c ${CONFIG} 2>/dev/null &\nexport MOSQ_PID=$!\nsleep 0.5\n\n# Kill broker on exit\ntrap \"kill $MOSQ_PID\" EXIT\n\n\n# Simple subscribe test - single message from $SYS\n${BASE_PATH}/client/mosquitto_sub --ws -p ${PORT} -W ${SUB_TIMEOUT} -C 1 -t '$SYS/broker/version' >/dev/null\necho \"Simple subscribe ok\"\n\n# Simple publish/subscribe test - single message from mosquitto_pub\n${BASE_PATH}/client/mosquitto_sub --ws -p ${PORT} -W ${SUB_TIMEOUT} -C 1 -t 'single/test' >/dev/null &\nexport SUB_PID=$!\n${BASE_PATH}/client/mosquitto_pub --ws -p ${PORT} -t 'single/test' -m 'single-test'\nkill ${SUB_PID} 2>/dev/null || true\necho \"Simple publish/subscribe ok\"\n\n# Publish a file and subscribe, do we get at least that many lines?\nexport TEST_LINES=$(wc -l test.sh | cut -d' ' -f1)\n${BASE_PATH}/client/mosquitto_sub --ws -p ${PORT} -W ${SUB_TIMEOUT} -C ${TEST_LINES} -t 'file-publish' >/dev/null &\nexport SUB_PID=$!\n${BASE_PATH}/client/mosquitto_pub --ws -p ${PORT} -t 'file-publish' -f ./test.sh\nkill ${SUB_PID} 2>/dev/null || true\necho \"File publish ok\"\n\n# Publish a file from stdin and subscribe, do we get at least that many lines?\nexport TEST_LINES=$(wc -l test.sh | cut -d' ' -f1)\n${BASE_PATH}/client/mosquitto_sub --ws -p ${PORT} -W ${SUB_TIMEOUT} -C ${TEST_LINES} -t 'file-publish' >/dev/null &\nexport SUB_PID=$!\n${BASE_PATH}/client/mosquitto_pub --ws -p ${PORT} -t 'file-publish' -l < ./test.sh\nkill ${SUB_PID} 2>/dev/null || true\necho \"stdin publish ok\"\n"
  },
  {
    "path": "test/client/test.py",
    "content": "#!/usr/bin/env python3\n\nimport sys\nsys.path.insert(0, \"..\")\nimport ptest\n\ntests = [\n    #(ports required, 'path'),\n    (1, './02-subscribe-argv-errors-tls-psk.py'),\n    (1, './02-subscribe-argv-errors-tls.py'),\n    (1, './02-subscribe-argv-errors-without-tls.py'),\n    (1, './02-subscribe-env.py'),\n    (1, './02-subscribe-filter-out.py'),\n    (1, './02-subscribe-format.py'),\n\t(1, './02-subscribe-format-json-qos0.py'),\n\t(1, './02-subscribe-format-json-qos1.py'),\n    (1, './02-subscribe-format-json-properties.py'),\n    (1, './02-subscribe-format-json-retain.py'),\n    (1, './02-subscribe-null.py'),\n    (1, './02-subscribe-qos1.py'),\n    (2, './02-subscribe-qos1-ws.py'),\n    (1, './02-subscribe-retain-handling.py'),\n    (1, './02-subscribe-verbose.py'),\n\n    (1, './03-publish-argv-errors-tls-psk.py'),\n    (1, './03-publish-argv-errors-tls.py'),\n    (1, './03-publish-argv-errors-without-tls.py'),\n    (1, './03-publish-env.py'),\n    (1, './03-publish-file-empty.py'),\n    (1, './03-publish-file.py'),\n    (1, './03-publish-options-file.py'),\n    (1, './03-publish-qos0-empty.py'),\n    (1, './03-publish-qos1-properties.py'),\n    (1, './03-publish-qos1.py'),\n    (2, './03-publish-qos1-ws.py'),\n    (2, './03-publish-qos1-ws-large.py'),\n    (1, './03-publish-repeat.py'),\n    (1, './03-publish-url.py'),\n    (1, './03-publish-tls.py'),\n\n    (2, './03-publish-socks.py'),\n    (2, './03-publish-socks-auth-failed.py'),\n    (2, './03-publish-socks-no-auth.py'),\n    (1, './03-publish-stdin-file.py'),\n    (1, './03-publish-stdin-line.py'),\n\n    (1, './04-rr-argv-errors-tls-psk.py'),\n    (1, './04-rr-argv-errors-tls.py'),\n    (1, './04-rr-argv-errors-without-tls.py'),\n    (1, './04-rr-env.py'),\n    (1, './04-rr-qos1.py'),\n    (2, './04-rr-qos1-ws.py'),\n    (1, './04-rr-retain-handling.py'),\n    ]\n\nif __name__ == \"__main__\":\n    test = ptest.PTest()\n    test.run_tests(tests)\n"
  },
  {
    "path": "test/client/test.sh",
    "content": "#!/bin/bash\n\n# Very basic client testing.\n\n\nset -e\n\nexport BASE_PATH=../../\nexport LD_LIBRARY_PATH=${BASE_PATH}/lib\nexport PORT=1888\nexport SUB_TIMEOUT=1\n\n# Start broker\n../../src/mosquitto -p ${PORT} 2>/dev/null &\nexport MOSQ_PID=$!\nsleep 0.5\n\n# Kill broker on exit\ntrap \"kill $MOSQ_PID\" EXIT\n\n\n# Simple subscribe test - single message from $SYS\n${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C 1 -t '$SYS/broker/version' >/dev/null\necho \"Simple subscribe ok\"\n\n# Simple publish/subscribe test - single message from mosquitto_pub\n${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C 1 -t 'single/test' >/dev/null &\nexport SUB_PID=$!\n${BASE_PATH}/client/mosquitto_pub -p ${PORT} -t 'single/test' -m 'single-test'\nkill ${SUB_PID} 2>/dev/null || true\necho \"Simple publish/subscribe ok\"\n\n# Publish a file and subscribe, do we get at least that many lines?\nexport TEST_LINES=$(wc -l test.sh | cut -d' ' -f1)\n${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C ${TEST_LINES} -t 'file-publish' >/dev/null &\nexport SUB_PID=$!\n${BASE_PATH}/client/mosquitto_pub -p ${PORT} -t 'file-publish' -f ./test.sh\nkill ${SUB_PID} 2>/dev/null || true\necho \"File publish ok\"\n\n# Publish a file from stdin and subscribe, do we get at least that many lines?\nexport TEST_LINES=$(wc -l test.sh | cut -d' ' -f1)\n${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C ${TEST_LINES} -t 'file-publish' >/dev/null &\nexport SUB_PID=$!\n${BASE_PATH}/client/mosquitto_pub -p ${PORT} -t 'file-publish' -l < ./test.sh\nkill ${SUB_PID} 2>/dev/null || true\necho \"stdin publish ok\"\n"
  },
  {
    "path": "test/client/ws.conf",
    "content": "listener 1888\nprotocol websockets\nallow_anonymous true\n"
  },
  {
    "path": "test/lib/01-con-discon-success-v5.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect and subsequent disconnect.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id 01-con-discon-success\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a DISCONNECT\n# message. If rc!=0, the client should exit with an error.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 1000)\n    props += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n    connect_packet = mosq_test.gen_connect(\"01-con-discon-success-v5\", proto_ver=5, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/01-con-discon-success-v5.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-con-discon-success-v5.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-con-discon-success.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect and subsequent disconnect.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id 01-con-discon-success\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a DISCONNECT\n# message. If rc!=0, the client should exit with an error.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"01-con-discon-success\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\nmosq_test.client_test(\"c/01-con-discon-success.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-con-discon-success.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-con-discon-will-clear.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect and subsequent disconnect, with setting a will then clearing it.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"01-con-discon-will\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/01-con-discon-will-clear.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-con-discon-will-clear.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-con-discon-will-v5.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect and subsequent disconnect, with a will, MQTT v5\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    props = mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 0x01)\n    connect_packet = mosq_test.gen_connect(\"01-con-discon-will\", will_topic=\"will/topic\", will_payload=b\"will-payload\", will_qos=1, will_retain=True, will_properties=props, proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/01-con-discon-will-v5.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-con-discon-will-v5.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-con-discon-will.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect and subsequent disconnect, with a will.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"01-con-discon-will\", will_topic=\"will/topic\", will_payload=b\"will-payload\", will_qos=1, will_retain=True)\n    connack_packet = mosq_test.gen_connack(rc=0)\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/01-con-discon-will.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-con-discon-will.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-extended-auth-continue.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport mqtt5_rc\n\ndef do_test(conn, data):\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 1000)\n    props += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n    connect_packet = mosq_test.gen_connect(\"01-extended-auth\", proto_ver=5, properties=props)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"test-method\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"test-request\") # This is really a binary property\n    auth_continue_b2c = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"test-method\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"test-reply\") # This is really a binary property\n    auth_continue_c2b = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, auth_continue_b2c, \"auth_b2c\")\n    mosq_test.do_receive_send(conn, auth_continue_c2b, connack_packet, \"connack\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/01-extended-auth-continue.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-extended-auth-continue.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-extended-auth-failure.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\nimport mqtt5_rc\n\ndef do_test(conn, data):\n    props = mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 1000)\n    props += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n    connect_packet = mosq_test.gen_connect(\"01-extended-auth\", proto_ver=5, properties=props)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_METHOD, \"test-method\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.AUTHENTICATION_DATA, \"test-request\") # This is really a binary property\n    auth_continue_b2c = mosq_test.gen_auth(reason_code=mqtt5_rc.CONTINUE_AUTHENTICATION, properties=props)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, auth_continue_b2c, \"auth_b2c\")\n    p = conn.recv(1)\n    if len(p) == 1:\n        exit(1)\n\n\nmosq_test.client_test(\"c/01-extended-auth-failure.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-extended-auth-failure.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-keepalive-pingreq.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a pingreq after the keepalive time\n\n# The client should connect to port 1888 with keepalive=4, clean session set,\n# and client id 01-keepalive-pingreq\n# The client should send a PINGREQ message after the appropriate amount of time\n# (4 seconds after no traffic).\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    keepalive = 5\n    connect_packet = mosq_test.gen_connect(\"01-keepalive-pingreq\", keepalive=keepalive)\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    pingreq_packet = mosq_test.gen_pingreq()\n    pingresp_packet = mosq_test.gen_pingresp()\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.expect_packet(conn, \"pingreq\", pingreq_packet)\n    time.sleep(1.0)\n    conn.send(pingresp_packet)\n\n    mosq_test.expect_packet(conn, \"pingreq\", pingreq_packet)\n\n\nmosq_test.client_test(\"c/01-keepalive-pingreq.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-keepalive-pingreq.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-no-clean-session.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect with clean session not set.\n\n# The client should connect to port 1888 with keepalive=60, clean session not\n# set, and client id 01-no-clean-session.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"01-no-clean-session\", clean_session=False)\n\n    mosq_test.expect_packet(conn, \"connect\", connect_packet)\n\n\nmosq_test.client_test(\"c/01-no-clean-session.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-no-clean-session.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-pre-connect-callback.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether the pre-connect callback is triggered and allows us to set a username and password.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# client id 01-pre-connect, username set to uname and password set to ;'[08gn=#\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"01-pre-connect\", username=\"uname\", password=\";'[08gn=#\")\n\n    mosq_test.expect_packet(conn, \"connect\", connect_packet)\n\n\nmosq_test.client_test(\"c/01-pre-connect-callback.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-pre-connect-callback.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-server-keepalive-pingreq.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a pingreq after the keepalive time\n# Client sets a keepalive of 60 seconds, but receives a server keepalive to set\n# it back to 4 seconds.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    keepalive = 60\n    server_keepalive = 4\n    connect_packet = mosq_test.gen_connect(\"01-server-keepalive-pingreq\", keepalive=keepalive, proto_ver=5)\n\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.SERVER_KEEP_ALIVE, server_keepalive)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n    pingreq_packet = mosq_test.gen_pingreq()\n    pingresp_packet = mosq_test.gen_pingresp()\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.expect_packet(conn, \"pingreq\", pingreq_packet)\n    time.sleep(1.0)\n    conn.send(pingresp_packet)\n\n    mosq_test.expect_packet(conn, \"pingreq\", pingreq_packet)\n\n\nmosq_test.client_test(\"c/01-server-keepalive-pingreq.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-server-keepalive-pingreq.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-unpwd-set.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect with a username and password.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# client id 01-unpwd-set, username set to uname and password set to ;'[08gn=#\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"01-unpwd-set\", username=\"uname\", password=\";'[08gn=#\")\n\n    mosq_test.expect_packet(conn, \"connect\", connect_packet)\n\n\nmosq_test.client_test(\"c/01-unpwd-set.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-unpwd-set.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-will-set.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect with a will.\n# Will QoS=1, will retain=1.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# client id 01-will-set will topic set to topic/on/unexpected/disconnect , will\n# payload set to \"will message\", will qos set to 1 and will retain set.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"01-will-set\", will_topic=\"topic/on/unexpected/disconnect\", will_qos=1, will_retain=True, will_payload=b\"will message\")\n\n    mosq_test.expect_packet(conn, \"connect\", connect_packet)\n\n\nmosq_test.client_test(\"c/01-will-set.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-will-set.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/01-will-unpwd-set.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect with a will, username and password.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# client id 01-will-unpwd-set , will topic set to \"will-topic\", will payload\n# set to \"will message\", will qos=2, will retain not set, username set to\n# \"oibvvwqw\" and password set to \"#'^2hg9a&nm38*us\".\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"01-will-unpwd-set\",\n            username=\"oibvvwqw\", password=\"#'^2hg9a&nm38*us\",\n            will_topic=\"will-topic\", will_qos=2, will_payload=b\"will message\")\n\n    mosq_test.expect_packet(conn, \"connect\", connect_packet)\n\n\nmosq_test.client_test(\"c/01-will-unpwd-set.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/01-will-unpwd-set.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/02-subscribe-helper-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct SUBSCRIBE to a topic with QoS 2.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id subscribe-qos2-test\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE\n# message to subscribe to topic \"qos2/test\" with QoS=2. If rc!=0, the client\n# should exit with an error.\n# Upon receiving the correct SUBSCRIBE message, the test will reply with a\n# SUBACK message with the accepted QoS set to 2. On receiving the SUBACK\n# message, the client should send a DISCONNECT message.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"subscribe-qos2-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"qos2/test\", 2)\n    suback_packet = mosq_test.gen_suback(mid, 2)\n\n    publish_packet = mosq_test.gen_publish(\"qos2/test\", 0, \"message\")\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, subscribe_packet, suback_packet, \"subscribe\")\n    conn.send(publish_packet)\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/02-subscribe-helper-simple-qos2.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/02-subscribe-helper-simple-qos2.test\", [], do_test, None)\nmosq_test.client_test(\"c/02-subscribe-helper-callback-qos2.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/02-subscribe-helper-callback-qos2.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/02-subscribe-qos0.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct SUBSCRIBE to a topic with QoS 0.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id subscribe-qos0-test\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE\n# message to subscribe to topic \"qos0/test\" with QoS=0. If rc!=0, the client\n# should exit with an error.\n# Upon receiving the correct SUBSCRIBE message, the test will reply with a\n# SUBACK message with the accepted QoS set to 0. On receiving the SUBACK\n# message, the client should send a DISCONNECT message.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"subscribe-qos0-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"qos0/test\", 0)\n    suback_packet = mosq_test.gen_suback(mid, 0)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, subscribe_packet, suback_packet, \"subscribe\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/02-subscribe-qos0.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/02-subscribe-qos0.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/02-subscribe-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct SUBSCRIBE to a topic with QoS 1.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id subscribe-qos1-test\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE\n# message to subscribe to topic \"qos1/test\" with QoS=1. If rc!=0, the client\n# should exit with an error.\n# Upon receiving the correct SUBSCRIBE message, the test will reply with a\n# SUBACK message with the accepted QoS set to 1. On receiving the SUBACK\n# message, the client should send a DISCONNECT message.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"subscribe-qos1-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"qos1/test\", 1)\n    suback_packet = mosq_test.gen_suback(mid, 1)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, subscribe_packet, suback_packet, \"subscribe\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/02-subscribe-qos1.test\", [], do_test, None)\nmosq_test.client_test(\"c/02-subscribe-qos1-async1.test\", [], do_test, None)\nmosq_test.client_test(\"c/02-subscribe-qos1-async2.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/02-subscribe-qos1.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/02-subscribe-qos1-async1.test\", [], do_test, None)\n# FIXME - CI fails here, connection refused mosq_test.client_test(\"cpp/02-subscribe-qos1-async2.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/02-subscribe-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct SUBSCRIBE to a topic with QoS 2.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id subscribe-qos2-test\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE\n# message to subscribe to topic \"qos2/test\" with QoS=2. If rc!=0, the client\n# should exit with an error.\n# Upon receiving the correct SUBSCRIBE message, the test will reply with a\n# SUBACK message with the accepted QoS set to 2. On receiving the SUBACK\n# message, the client should send a DISCONNECT message.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"subscribe-qos2-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"qos2/test\", 2)\n    suback_packet = mosq_test.gen_suback(mid, 2)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, subscribe_packet, suback_packet, \"subscribe\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\nmosq_test.client_test(\"c/02-subscribe-qos2.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/02-subscribe-qos2.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/02-unsubscribe-multiple-v5.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a v5 client sends a correct UNSUBSCRIBE packet with multiple\n# topics, and handles the UNSUBACK.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"unsubscribe-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"unsubscribe/test\", 2, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5)\n\n    mid = 2\n    unsubscribe_packet = mosq_test.gen_unsubscribe_multiple(mid, [\"unsubscribe/test\", \"no-sub\"], proto_ver=5)\n    unsuback_packet = mosq_test.gen_unsuback(mid, reason_code=[0, 17], proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, subscribe_packet, suback_packet, \"subscribe\")\n    mosq_test.do_receive_send(conn, unsubscribe_packet, unsuback_packet, \"unsubscribe\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/02-unsubscribe-multiple-v5.test\", [], do_test, None)\n# FIXME - missing func in lib mosq_test.client_test(\"cpp/02-unsubscribe-multiple-v5.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/02-unsubscribe-v5.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a v5 client sends a correct UNSUBSCRIBE packet, and handles the UNSUBACK.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"unsubscribe-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mid = 1\n    props = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\n    unsubscribe_packet = mosq_test.gen_unsubscribe(mid, \"unsubscribe/test\", proto_ver=5, properties=props)\n    unsuback_packet = mosq_test.gen_unsuback(mid, proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, unsubscribe_packet, unsuback_packet, \"unsubscribe\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/02-unsubscribe-v5.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/02-unsubscribe-v5.test\", [], do_test, None)\n# FIXME - missing lib function mosq_test.client_test(\"c/02-unsubscribe2-v5.test\", [], do_test, None)\n# FIXME - missing lib function mosq_test.client_test(\"cpp/02-unsubscribe2-v5.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/02-unsubscribe.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct UNSUBSCRIBE packet.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"unsubscribe-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 1\n    unsubscribe_packet = mosq_test.gen_unsubscribe(mid, \"unsubscribe/test\")\n    unsuback_packet = mosq_test.gen_unsuback(mid)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, unsubscribe_packet, unsuback_packet, \"unsubscribe\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/02-unsubscribe.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/02-unsubscribe.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-b2c-qos1-unexpected-puback.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    keepalive = 5\n    connect_packet = mosq_test.gen_connect(\"publish-qos1-test\", keepalive=keepalive)\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 13423\n    puback_packet = mosq_test.gen_puback(mid)\n    pingreq_packet = mosq_test.gen_pingreq()\n\n    mosq_test.expect_packet(conn, \"connect\", connect_packet)\n    conn.send(connack_packet)\n    conn.send(puback_packet)\n\n    mosq_test.expect_packet(conn, \"pingreq\", pingreq_packet)\n\n\nmosq_test.client_test(\"c/03-publish-b2c-qos1-unexpected-puback.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-b2c-qos1-unexpected-puback.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-b2c-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client responds correctly to a PUBLISH with QoS 1.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id publish-qos1-test\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK the client should verify that rc==0.\n# The test will send the client a PUBLISH message with topic\n# \"pub/qos1/receive\", payload of \"message\", QoS=1 and mid=123. The client\n# should handle this as per the spec by sending a PUBACK message.\n# The client should then exit with return code==0.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos1-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 123\n    publish_packet = mosq_test.gen_publish(\"pub/qos1/receive\", qos=1, mid=mid, payload=\"message\")\n    puback_packet = mosq_test.gen_puback(mid)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_send_receive(conn, publish_packet, puback_packet, \"puback\")\n\n\nmosq_test.client_test(\"c/03-publish-b2c-qos1.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-b2c-qos1.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-b2c-qos2-len.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether a v5 client handles a v5 PUBREL with all combinations\n# of with/without reason code and properties.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"len/qos2/test\", qos=2, mid=data['mid'], payload=\"message\", proto_ver=5)\n    pubrec_packet = mosq_test.gen_pubrec(data['mid'], proto_ver=5)\n    pubcomp_packet = mosq_test.gen_pubcomp(data['mid'], proto_ver=5)\n\n    mosq_test.expect_packet(conn, \"connect\", connect_packet)\n    conn.send(connack_packet)\n\n    mosq_test.do_send_receive(conn, publish_packet, pubrec_packet, \"pubrec\")\n    mosq_test.do_send_receive(conn, data['pubrel_packet'], pubcomp_packet, \"pubcomp\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\ndata = {}\ndata['mid'] = 56\n# No reason code, no properties\ndata['pubrel_packet'] = mosq_test.gen_pubrel(data['mid'])\ndata['label'] = \"qos2 len 2\"\nmosq_test.client_test(\"c/03-publish-b2c-qos2-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-b2c-qos2-len.test\", [], do_test, data)\n\n# Reason code, no properties\ndata['pubrel_packet'] = mosq_test.gen_pubrel(data['mid'], proto_ver=5, reason_code=0x00)\ndata['label'] = \"qos2 len 3\"\nmosq_test.client_test(\"c/03-publish-b2c-qos2-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-b2c-qos2-len.test\", [], do_test, data)\n\n# Reason code, empty properties\ndata['pubrel_packet'] = mosq_test.gen_pubrel(data['mid'], proto_ver=5, reason_code=0x00, properties=\"\")\ndata['label'] = \"qos2 len 4\"\nmosq_test.client_test(\"c/03-publish-b2c-qos2-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-b2c-qos2-len.test\", [], do_test, data)\n\n# Reason code, one property\nprops = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\ndata['pubrel_packet'] = mosq_test.gen_pubrel(data['mid'], proto_ver=5, reason_code=0x00, properties=props)\ndata['label'] = \"qos2 len >5\"\nmosq_test.client_test(\"c/03-publish-b2c-qos2-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-b2c-qos2-len.test\", [], do_test, data)\n"
  },
  {
    "path": "test/lib/03-publish-b2c-qos2-unexpected-pubcomp.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    keepalive = 5\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\", keepalive=keepalive)\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 13423\n    pubcomp_packet = mosq_test.gen_pubcomp(mid)\n    pingreq_packet = mosq_test.gen_pingreq()\n\n    mosq_test.expect_packet(conn, \"connect\", connect_packet)\n    conn.send(connack_packet)\n    conn.send(pubcomp_packet)\n\n    mosq_test.expect_packet(conn, \"pingreq\", pingreq_packet)\n\n\nmosq_test.client_test(\"c/03-publish-b2c-qos2-unexpected-pubcomp.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-b2c-qos2-unexpected-pubcomp.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-b2c-qos2-unexpected-pubrel.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    pubrel_unexpected = mosq_test.gen_pubrel(1000)\n    pubcomp_unexpected = mosq_test.gen_pubcomp(1000)\n\n    mid = 13423\n    publish_packet = mosq_test.gen_publish(\"pub/qos2/receive\", qos=2, mid=mid, payload=\"message\")\n    pubrec_packet = mosq_test.gen_pubrec(mid)\n    pubrel_packet = mosq_test.gen_pubrel(mid)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid)\n\n    publish_quit_packet = mosq_test.gen_publish(\"quit\", qos=0, payload=\"quit\")\n\n    mosq_test.expect_packet(conn, \"connect\", connect_packet)\n    conn.send(connack_packet)\n\n    conn.send(pubrel_unexpected)\n    mosq_test.expect_packet(conn, \"pubcomp\", pubcomp_unexpected)\n\n    conn.send(publish_packet)\n\n    mosq_test.expect_packet(conn, \"pubrec\", pubrec_packet)\n    conn.send(pubrel_packet)\n\n    mosq_test.expect_packet(conn, \"pubcomp\", pubcomp_packet)\n    conn.send(publish_quit_packet)\n    conn.close()\n\n\nmosq_test.client_test(\"c/03-publish-b2c-qos2-unexpected-pubrel.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-b2c-qos2-unexpected-pubrel.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-b2c-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client responds correctly to a PUBLISH with QoS 1.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id publish-qos2-test\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK the client should verify that rc==0.\n# The test will send the client a PUBLISH message with topic\n# \"pub/qos2/receive\", payload of \"message\", QoS=2 and mid=13423. The client\n# should handle this as per the spec by sending a PUBREC message.\n# The test will not respond to the first PUBREC message, so the client must\n# resend the PUBREC message with dup=1. Note that to keep test durations low, a\n# message retry timeout of less than 10 seconds is required for this test.\n# On receiving the second PUBREC with dup==1, the test will send the correct\n# PUBREL message. The client should respond to this with the correct PUBCOMP\n# message and then exit with return code=0.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 13423\n    publish_packet = mosq_test.gen_publish(\"pub/qos2/receive\", qos=2, mid=mid, payload=\"message\")\n    pubrec_packet = mosq_test.gen_pubrec(mid)\n    pubrel_packet = mosq_test.gen_pubrel(mid)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_send_receive(conn, publish_packet, pubrec_packet, \"pubrec\")\n    mosq_test.do_send_receive(conn, pubrel_packet, pubcomp_packet, \"pubcomp\")\n\n\nmosq_test.client_test(\"c/03-publish-b2c-qos2.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-b2c-qos2.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos1-disconnect.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct PUBLISH to a topic with QoS 1, then responds correctly to a disconnect.\n\nfrom mosq_test_helper import *\n\ndef do_test(client_cmd):\n    port = mosq_test.get_port()\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"publish-qos1-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"pub/qos1/test\", qos=1, mid=mid, payload=\"message\")\n    publish_packet_dup = mosq_test.gen_publish(\"pub/qos1/test\", qos=1, mid=mid, payload=\"message\", dup=True)\n    puback_packet = mosq_test.gen_puback(mid)\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    sock.settimeout(10)\n    sock.bind(('', port))\n    sock.listen(5)\n\n    client_args = [client_cmd, str(port)]\n    env = mosq_test.env_add_ld_library_path()\n\n    client = mosq_test.start_client(filename=client_cmd.replace('/', '-'), cmd=client_args, env=env)\n\n    try:\n        (conn, address) = sock.accept()\n        conn.settimeout(15)\n\n        mosq_test.expect_packet(conn, \"connect\", connect_packet)\n        conn.send(connack_packet)\n\n        mosq_test.expect_packet(conn, \"publish\", publish_packet)\n        # Disconnect client. It should reconnect.\n        conn.close()\n\n        (conn, address) = sock.accept()\n        conn.settimeout(15)\n\n        mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n        mosq_test.do_receive_send(conn, publish_packet_dup, puback_packet, \"retried publish\")\n        mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n        rc = 0\n\n        conn.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        sock.close()\n        if mosq_test.wait_for_subprocess(client):\n            print(\"test client not finished\")\n            rc=1\n            exit(1)\n\ndo_test(\"c/03-publish-c2b-qos1-disconnect.test\")\ndo_test(\"cpp/03-publish-c2b-qos1-disconnect.test\")\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos1-len.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether a v5 client handles a v5 PUBACK with all combinations\n# of with/without reason code and properties.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos1-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"pub/qos1/test\", qos=1, mid=data['mid'], payload=\"message\", proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, publish_packet, data['puback_packet'], \"publish\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\ndata = {}\ndata['mid'] = 1\n# No reason code, no properties\ndata['puback_packet'] = mosq_test.gen_puback(data['mid'])\ndata['label'] = \"qos1 len 2\"\nmosq_test.client_test(\"c/03-publish-c2b-qos1-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos1-len.test\", [], do_test, data)\n\n# Reason code, no properties\ndata['puback_packet'] = mosq_test.gen_puback(data['mid'], proto_ver=5, reason_code=0x00)\ndata['label'] = \"qos1 len 3\"\nmosq_test.client_test(\"c/03-publish-c2b-qos1-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos1-len.test\", [], do_test, data)\n\n# Reason code, empty properties\ndata['puback_packet'] = mosq_test.gen_puback(data['mid'], proto_ver=5, reason_code=0x00, properties=\"\")\ndata['label'] = \"qos1 len 4\"\nmosq_test.client_test(\"c/03-publish-c2b-qos1-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos1-len.test\", [], do_test, data)\n\n# Reason code, one property\nprops = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\ndata['puback_packet'] = mosq_test.gen_puback(data['mid'], proto_ver=5, reason_code=0x00, properties=props)\ndata['label'] = \"qos1 len >5\"\nmosq_test.client_test(\"c/03-publish-c2b-qos1-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos1-len.test\", [], do_test, data)\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos1-receive-maximum.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client responds correctly to multiple PUBLISH with QoS 1, with\n# receive maximum set to 3.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos1-test\", proto_ver=5)\n\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 3)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mid = 1\n    publish_1_packet = mosq_test.gen_publish(\"topic\", qos=1, mid=mid, payload=\"12345\", proto_ver=5)\n    puback_1_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    mid = 2\n    publish_2_packet = mosq_test.gen_publish(\"topic\", qos=1, mid=mid, payload=\"12345\", proto_ver=5)\n    puback_2_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    mid = 3\n    publish_3_packet = mosq_test.gen_publish(\"topic\", qos=1, mid=mid, payload=\"12345\", proto_ver=5)\n    puback_3_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    mid = 4\n    publish_4_packet = mosq_test.gen_publish(\"topic\", qos=1, mid=mid, payload=\"12345\", proto_ver=5)\n    puback_4_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    mid = 5\n    publish_5_packet = mosq_test.gen_publish(\"topic\", qos=1, mid=mid, payload=\"12345\", proto_ver=5)\n    puback_5_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    mid = 6\n    publish_6_packet = mosq_test.gen_publish(\"topic\", qos=1, mid=mid, payload=\"12345\", proto_ver=5)\n    puback_6_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.expect_packet(conn, \"publish 1\", publish_1_packet)\n    mosq_test.expect_packet(conn, \"publish 2\", publish_2_packet)\n    mosq_test.expect_packet(conn, \"publish 3\", publish_3_packet)\n    conn.send(puback_1_packet)\n    conn.send(puback_2_packet)\n\n    mosq_test.expect_packet(conn, \"publish 4\", publish_4_packet)\n    mosq_test.expect_packet(conn, \"publish 5\", publish_5_packet)\n    conn.send(puback_3_packet)\n\n    mosq_test.expect_packet(conn, \"publish 6\", publish_6_packet)\n    conn.send(puback_4_packet)\n    conn.send(puback_5_packet)\n    conn.send(puback_6_packet)\n\n\nmosq_test.client_test(\"c/03-publish-c2b-qos1-receive-maximum.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos1-receive-maximum.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos2-disconnect.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct PUBLISH to a topic with QoS 2 and responds to a disconnect.\n\nfrom mosq_test_helper import *\n\ndef do_test(client_cmd):\n    port = mosq_test.get_port()\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"pub/qos2/test\", qos=2, mid=mid, payload=\"message\")\n    publish_dup_packet = mosq_test.gen_publish(\"pub/qos2/test\", qos=2, mid=mid, payload=\"message\", dup=True)\n    pubrec_packet = mosq_test.gen_pubrec(mid)\n    pubrel_packet = mosq_test.gen_pubrel(mid)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid)\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    sock.settimeout(10)\n    sock.bind(('', port))\n    sock.listen(5)\n\n    client_args = [client_cmd, str(port)]\n    env = mosq_test.env_add_ld_library_path()\n\n    client = mosq_test.start_client(filename=client_cmd.replace('/', '-'), cmd=client_args, env=env)\n\n    try:\n        (conn, address) = sock.accept()\n        conn.settimeout(10)\n\n        mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n        mosq_test.expect_packet(conn, \"publish\", publish_packet)\n        # Disconnect client. It should reconnect.\n        conn.close()\n\n        (conn, address) = sock.accept()\n        conn.settimeout(15)\n\n        mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n        mosq_test.do_receive_send(conn, publish_dup_packet, pubrec_packet, \"retried publish\")\n\n        mosq_test.expect_packet(conn, \"pubrel\", pubrel_packet)\n        # Disconnect client. It should reconnect.\n        conn.close()\n\n        (conn, address) = sock.accept()\n        conn.settimeout(15)\n\n        # Complete connection and message flow.\n        mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n        mosq_test.do_receive_send(conn, pubrel_packet, pubcomp_packet, \"retried pubrel\")\n\n        mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n        rc = 0\n\n        conn.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        sock.close()\n        if mosq_test.wait_for_subprocess(client):\n            print(\"test client not finished\")\n            rc=1\n            exit(1)\n\ndo_test(\"c/03-publish-c2b-qos2-disconnect.test\")\ndo_test(\"cpp/03-publish-c2b-qos2-disconnect.test\")\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos2-len.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether a v5 client handles a v5 PUBREC, PUBCOMP with all combinations\n# of with/without reason code and properties.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"pub/qos2/test\", qos=2, mid=data['mid'], payload=\"message\", proto_ver=5)\n    pubrel_packet = mosq_test.gen_pubrel(data['mid'], proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, publish_packet, data['pubrec_packet'], \"publish\")\n    mosq_test.do_receive_send(conn, pubrel_packet, data['pubcomp_packet'], \"pubrel\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\ndata = {}\ndata['mid'] = 1\n# No reason code, no properties\ndata['pubrec_packet'] = mosq_test.gen_pubrec(data['mid'])\ndata['pubcomp_packet'] = mosq_test.gen_pubcomp(data['mid'])\nmosq_test.client_test(\"c/03-publish-c2b-qos2-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos2-len.test\", [], do_test, data)\n\n# Reason code, no properties\ndata['pubrec_packet'] = mosq_test.gen_pubrec(data['mid'], proto_ver=5, reason_code=0x00)\ndata['pubcomp_packet'] = mosq_test.gen_pubcomp(data['mid'], proto_ver=5, reason_code=0x00)\nmosq_test.client_test(\"c/03-publish-c2b-qos2-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos2-len.test\", [], do_test, data)\n\n# Reason code, empty properties\ndata['pubrec_packet'] = mosq_test.gen_pubrec(data['mid'], proto_ver=5, reason_code=0x00, properties=\"\")\ndata['pubcomp_packet'] = mosq_test.gen_pubcomp(data['mid'], proto_ver=5, reason_code=0x00, properties=\"\")\nmosq_test.client_test(\"c/03-publish-c2b-qos2-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos2-len.test\", [], do_test, data)\n\n# Reason code, one property\nprops = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\ndata['pubrec_packet'] = mosq_test.gen_pubrec(data['mid'], proto_ver=5, reason_code=0x00, properties=props)\nprops = mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"key\", \"value\")\ndata['pubcomp_packet'] = mosq_test.gen_pubcomp(data['mid'], proto_ver=5, reason_code=0x00, properties=props)\nmosq_test.client_test(\"c/03-publish-c2b-qos2-len.test\", [], do_test, data)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos2-len.test\", [], do_test, data)\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos2-maximum-qos-0.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client correctly handles sending a message with QoS > maximum QoS.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\", proto_ver=5)\n\n    props = mqtt5_props.gen_byte_prop(mqtt5_props.MAXIMUM_QOS, 0)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    publish_1_packet = mosq_test.gen_publish(\"maximum/qos/qos0\", qos=0, payload=\"message\", proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.expect_packet(conn, \"publish 1\", publish_1_packet)\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/03-publish-c2b-qos2-maximum-qos-0.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos2-maximum-qos-0.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos2-maximum-qos-1.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client correctly handles sending a message with QoS > maximum QoS.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\", proto_ver=5)\n\n    props = mqtt5_props.gen_byte_prop(mqtt5_props.MAXIMUM_QOS, 1)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mid = 1\n    publish_1_packet = mosq_test.gen_publish(\"maximum/qos/qos1\", qos=1, mid=mid, payload=\"message\", proto_ver=5)\n    puback_1_packet = mosq_test.gen_puback(mid, proto_ver=5)\n\n    publish_2_packet = mosq_test.gen_publish(\"maximum/qos/qos0\", qos=0, payload=\"message\", proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, publish_1_packet, puback_1_packet, \"publish 1\")\n\n    mosq_test.expect_packet(conn, \"publish 2\", publish_2_packet)\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/03-publish-c2b-qos2-maximum-qos-1.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos2-maximum-qos-1.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos2-pubrec-error.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client responds correctly when sending multiple PUBLISH with\n# QoS 2, with the broker rejecting the first PUBLISH by setting the reason code\n# in PUBACK to >= 0x80.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\", proto_ver=5)\n\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 1)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mid = 1\n    publish_1_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"rejected\", proto_ver=5)\n    pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5, reason_code=0x80)\n\n    mid = 2\n    publish_2_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"accepted\", proto_ver=5)\n    pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, publish_1_packet, pubrec_1_packet, \"publish 1\")\n    mosq_test.do_receive_send(conn, publish_2_packet, pubrec_2_packet, \"publish 2\")\n    mosq_test.do_receive_send(conn, pubrel_2_packet, pubcomp_2_packet, \"pubrel 2\")\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/03-publish-c2b-qos2-pubrec-error.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos2-pubrec-error.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos2-receive-maximum-1.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client responds correctly to multiple PUBLISH with QoS 2, with\n# receive maximum set to 1.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\", proto_ver=5)\n\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 1)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mid = 1\n    publish_1_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"12345\", proto_ver=5)\n    pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_1_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_1_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 2\n    publish_2_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"12345\", proto_ver=5)\n    pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 3\n    publish_3_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"12345\", proto_ver=5)\n    pubrec_3_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_3_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_3_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 4\n    publish_4_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"12345\", proto_ver=5)\n    pubrec_4_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_4_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_4_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 5\n    publish_5_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"12345\", proto_ver=5)\n    pubrec_5_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_5_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_5_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.do_receive_send(conn, publish_1_packet, pubrec_1_packet, \"publish 1\")\n    mosq_test.do_receive_send(conn, pubrel_1_packet, pubcomp_1_packet, \"pubrel 1\")\n\n    mosq_test.do_receive_send(conn, publish_2_packet, pubrec_2_packet, \"publish 2\")\n    mosq_test.do_receive_send(conn, pubrel_2_packet, pubcomp_2_packet, \"pubrel 2\")\n\n    mosq_test.do_receive_send(conn, publish_3_packet, pubrec_3_packet, \"publish 3\")\n    mosq_test.do_receive_send(conn, pubrel_3_packet, pubcomp_3_packet, \"pubrel 3\")\n\n    mosq_test.do_receive_send(conn, publish_4_packet, pubrec_4_packet, \"publish 4\")\n    mosq_test.do_receive_send(conn, pubrel_4_packet, pubcomp_4_packet, \"pubrel 4\")\n\n    mosq_test.do_receive_send(conn, publish_5_packet, pubrec_5_packet, \"publish 5\")\n    mosq_test.do_receive_send(conn, pubrel_5_packet, pubcomp_5_packet, \"pubrel 5\")\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/03-publish-c2b-qos2-receive-maximum.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos2-receive-maximum.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos2-receive-maximum-2.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client responds correctly to multiple PUBLISH with QoS 2, with\n# receive maximum set to 2.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\", proto_ver=5)\n\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 2)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mid = 1\n    publish_1_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"12345\", proto_ver=5)\n    pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_1_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_1_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 2\n    publish_2_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"12345\", proto_ver=5)\n    pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 3\n    publish_3_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"12345\", proto_ver=5)\n    pubrec_3_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_3_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_3_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 4\n    publish_4_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"12345\", proto_ver=5)\n    pubrec_4_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_4_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_4_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mid = 5\n    publish_5_packet = mosq_test.gen_publish(\"topic\", qos=2, mid=mid, payload=\"12345\", proto_ver=5)\n    pubrec_5_packet = mosq_test.gen_pubrec(mid, proto_ver=5)\n    pubrel_5_packet = mosq_test.gen_pubrel(mid, proto_ver=5)\n    pubcomp_5_packet = mosq_test.gen_pubcomp(mid, proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.expect_packet(conn, \"publish 1\", publish_1_packet)\n    mosq_test.expect_packet(conn, \"publish 2\", publish_2_packet)\n    conn.send(pubrec_1_packet)\n    conn.send(pubrec_2_packet)\n\n    mosq_test.expect_packet(conn, \"pubrel 1\", pubrel_1_packet)\n    mosq_test.expect_packet(conn, \"pubrel 2\", pubrel_2_packet)\n    conn.send(pubcomp_1_packet)\n    conn.send(pubcomp_2_packet)\n\n    mosq_test.expect_packet(conn, \"publish 3\", publish_3_packet)\n    mosq_test.expect_packet(conn, \"publish 4\", publish_4_packet)\n    conn.send(pubrec_3_packet)\n    conn.send(pubrec_4_packet)\n\n    mosq_test.expect_packet(conn, \"pubrel 3\", pubrel_3_packet)\n    mosq_test.expect_packet(conn, \"pubrel 4\", pubrel_4_packet)\n    conn.send(pubcomp_3_packet)\n    conn.send(pubcomp_4_packet)\n\n    mosq_test.expect_packet(conn, \"publish 5\", publish_5_packet)\n    conn.send(pubrec_5_packet)\n\n    mosq_test.expect_packet(conn, \"pubrel 5\", pubrel_5_packet)\n    conn.send(pubcomp_5_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/03-publish-c2b-qos2-receive-maximum.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos2-receive-maximum.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-c2b-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct PUBLISH to a topic with QoS 2.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id publish-qos2-test\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK the client should verify that rc==0. If not, it should exit with\n# return code=1.\n# On a successful CONNACK, the client should send a PUBLISH message with topic\n# \"pub/qos2/test\", payload \"message\" and QoS=2.\n# The test will not respond to the first PUBLISH message, so the client must\n# resend the PUBLISH message with dup=1. Note that to keep test durations low, a\n# message retry timeout of less than 10 seconds is required for this test.\n# On receiving the second PUBLISH message, the test will send the correct\n# PUBREC response. On receiving the correct PUBREC response, the client should\n# send a PUBREL message.\n# The test will not respond to the first PUBREL message, so the client must\n# resend the PUBREL message with dup=1. On receiving the second PUBREL message,\n# the test will send the correct PUBCOMP response. On receiving the correct\n# PUBCOMP response, the client should send a DISCONNECT message.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos2-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mid = 1\n    publish_packet = mosq_test.gen_publish(\"pub/qos2/test\", qos=2, mid=mid, payload=\"message\")\n    publish_dup_packet = mosq_test.gen_publish(\"pub/qos2/test\", qos=2, mid=mid, payload=\"message\", dup=True)\n    pubrec_packet = mosq_test.gen_pubrec(mid)\n    pubrel_packet = mosq_test.gen_pubrel(mid)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, publish_packet, pubrec_packet, \"publish\")\n    mosq_test.do_receive_send(conn, pubrel_packet, pubcomp_packet, \"pubrel\")\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/03-publish-c2b-qos2.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-c2b-qos2.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-loop.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"loop-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe_packet = mosq_test.gen_subscribe(mid, \"loop/test\", 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    publish_packet = mosq_test.gen_publish(\"loop/test\", qos=0, payload=\"message\", proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_receive_send(conn, subscribe_packet, suback_packet, \"subscribe\")\n    conn.send(publish_packet)\n    mosq_test.expect_packet(conn, \"publish\", publish_packet)\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n\nmosq_test.client_test(\"c/03-publish-loop.test\", [], do_test, None)\nmosq_test.client_test(\"c/03-publish-loop-forever.test\", [], do_test, None)\nmosq_test.client_test(\"c/03-publish-loop-manual.test\", [], do_test, None)\nmosq_test.client_test(\"c/03-publish-loop-start.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-loop.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-loop-forever.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-loop-manual.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-loop-start.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-qos0-no-payload.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct PUBLISH to a topic with QoS 0 and no payload.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id publish-qos0-test-np\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a PUBLISH message\n# to topic \"pub/qos0/no-payload/test\" with zero length payload and QoS=0. If\n# rc!=0, the client should exit with an error.\n# After sending the PUBLISH message, the client should send a DISCONNECT message.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos0-test-np\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    publish_packet = mosq_test.gen_publish(\"pub/qos0/no-payload/test\", qos=0)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.expect_packet(conn, \"publish\", publish_packet)\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/03-publish-qos0-no-payload.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-qos0-no-payload.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-publish-qos0.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct PUBLISH to a topic with QoS 0.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id publish-qos0-test\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a PUBLISH message\n# to topic \"pub/qos0/test\" with payload \"message\" and QoS=0. If rc!=0, the\n# client should exit with an error.\n# After sending the PUBLISH message, the client should send a DISCONNECT message.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos0-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    publish_packet = mosq_test.gen_publish(\"pub/qos0/test\", qos=0, payload=\"message\")\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.expect_packet(conn, \"publish\", publish_packet)\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/03-publish-qos0.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/03-publish-qos0.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/03-request-response-correlation.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(testdir):\n    port = mosq_test.get_port()\n\n    resp_topic = \"response/topic\"\n    pub_topic = \"request/topic\"\n\n    rc = 1\n    connect1_packet = mosq_test.gen_connect(\"request-test\", proto_ver=5)\n    connect2_packet = mosq_test.gen_connect(\"response-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe1_packet = mosq_test.gen_subscribe(mid, resp_topic, 0, proto_ver=5)\n    subscribe2_packet = mosq_test.gen_subscribe(mid, pub_topic, 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, resp_topic)\n    props += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"corridor\")\n    publish1_packet_incoming = mosq_test.gen_publish(pub_topic, qos=0, payload=\"action\", proto_ver=5, properties=props)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, resp_topic)\n    props += mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"corridor\")\n    props += mqtt5_props.gen_string_pair_prop(mqtt5_props.USER_PROPERTY, \"user\", \"data\")\n    publish1_packet_outgoing = mosq_test.gen_publish(pub_topic, qos=0, payload=\"action\", proto_ver=5, properties=props)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.CORRELATION_DATA, \"corridor\")\n    publish2_packet = mosq_test.gen_publish(resp_topic, qos=0, payload=\"a response\", proto_ver=5, properties=props)\n    publish2_packet_outgoing = mosq_test.gen_publish(pub_topic, qos=0, payload=\"action\", proto_ver=5, properties=props)\n\n\n    sock = mosq_test.listen_sock(port);\n\n    env = dict(os.environ)\n    client1 = mosq_test.start_client(filename=f\"{testdir}-03-request-response-correlation-1.log\", cmd=[f\"{testdir}/03-request-response-correlation-1.test\", str(port)])\n\n    try:\n        (conn1, address) = sock.accept()\n        conn1.settimeout(10)\n\n        client2 = mosq_test.start_client(filename=f\"{testdir}-03-request-response-2.log\", cmd=[f\"{testdir}/03-request-response-2.test\", str(port)])\n        (conn2, address) = sock.accept()\n        conn2.settimeout(10)\n\n        mosq_test.do_receive_send(conn1, connect1_packet, connack_packet, \"connect1\")\n        mosq_test.do_receive_send(conn2, connect2_packet, connack_packet, \"connect2\")\n\n        mosq_test.do_receive_send(conn1, subscribe1_packet, suback_packet, \"subscribe1\")\n        mosq_test.do_receive_send(conn2, subscribe2_packet, suback_packet, \"subscribe2\")\n\n        mosq_test.expect_packet(conn1, \"publish1\", publish1_packet_incoming)\n        conn2.send(publish1_packet_outgoing)\n\n        mosq_test.expect_packet(conn2, \"publish2\", publish2_packet)\n        conn1.send(publish2_packet_outgoing)\n        rc = 0\n\n        conn1.close()\n        conn2.close()\n    except mosq_test.TestError:\n        pass\n        if mosq_test.wait_for_subprocess(client1):\n            print(\"client1 not terminated\")\n            if rc == 0: rc=1\n        if mosq_test.wait_for_subprocess(client2):\n            print(\"client2 not terminated\")\n            if rc == 0: rc=1\n        if rc:\n            (stdo, stde) = client1.communicate()\n            print(stde)\n            (stdo, stde) = client2.communicate()\n            print(stde)\n            exit(1)\n\ndo_test(\"c\")\ndo_test(\"cpp\")\n"
  },
  {
    "path": "test/lib/03-request-response.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(testdir):\n    port = mosq_test.get_port()\n\n    resp_topic = \"response/topic\"\n    pub_topic = \"request/topic\"\n\n    rc = 1\n    connect1_packet = mosq_test.gen_connect(\"request-test\", proto_ver=5)\n    connect2_packet = mosq_test.gen_connect(\"response-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    subscribe1_packet = mosq_test.gen_subscribe(mid, resp_topic, 0, proto_ver=5)\n    subscribe2_packet = mosq_test.gen_subscribe(mid, pub_topic, 0, proto_ver=5)\n    suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, resp_topic)\n    publish1_packet = mosq_test.gen_publish(pub_topic, qos=0, payload=\"action\", proto_ver=5, properties=props)\n\n    publish2_packet = mosq_test.gen_publish(resp_topic, qos=0, payload=\"a response\", proto_ver=5)\n\n    sock = mosq_test.listen_sock(port);\n\n    client1 = mosq_test.start_client(filename=f\"{testdir}-03-request-response-1.log\", cmd=[f\"{testdir}/03-request-response-1.test\", str(port)])\n\n    try:\n        (conn1, address) = sock.accept()\n        conn1.settimeout(10)\n\n        client2 = mosq_test.start_client(filename=f\"{testdir}-03-request-response-2.log\", cmd=[f\"{testdir}/03-request-response-2.test\", str(port)])\n        (conn2, address) = sock.accept()\n        conn2.settimeout(10)\n\n        mosq_test.do_receive_send(conn1, connect1_packet, connack_packet, \"connect1\")\n        mosq_test.do_receive_send(conn2, connect2_packet, connack_packet, \"connect2\")\n\n        mosq_test.do_receive_send(conn1, subscribe1_packet, suback_packet, \"subscribe1\")\n        mosq_test.do_receive_send(conn2, subscribe2_packet, suback_packet, \"subscribe2\")\n\n        mosq_test.expect_packet(conn1, \"publish1\", publish1_packet)\n        conn2.send(publish1_packet)\n\n        mosq_test.expect_packet(conn2, \"publish2\", publish2_packet)\n        conn1.send(publish2_packet)\n        rc = 0\n\n        conn1.close()\n        conn2.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        sock.close()\n        if mosq_test.wait_for_subprocess(client1):\n            print(\"client1 not terminated\")\n            if rc == 0: rc=1\n        if mosq_test.wait_for_subprocess(client2):\n            print(\"client2 not terminated\")\n            if rc == 0: rc=1\n        if rc:\n            (stdo, stde) = client1.communicate()\n            print(stde)\n            (stdo, stde) = client2.communicate()\n            print(stde)\n            exit(1)\n\ndo_test(\"c\")\ndo_test(\"cpp\")\n"
  },
  {
    "path": "test/lib/04-retain-qos0.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct retained PUBLISH to a topic with QoS 0.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"retain-qos0-test\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n\n    mid = 16\n    publish_packet = mosq_test.gen_publish(\"retain/qos0/test\", qos=0, payload=\"retained message\", retain=True)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.expect_packet(conn, \"publish\", publish_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/04-retain-qos0.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/04-retain-qos0.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/08-ssl-bad-cacert.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef do_test(client_cmd):\n    rc = 1\n\n    port = mosq_test.get_port()\n\n    client_args = [mosq_test.get_build_root() + \"/test/lib/\" + client_cmd, str(port)]\n    client = mosq_test.start_client(filename=client_cmd.replace('/', '-'), cmd=client_args)\n\n    if mosq_test.wait_for_subprocess(client):\n        print(\"test client not finished\")\n        rc=1\n    else:\n        rc=client.returncode\n\n    if rc:\n        (o, e) = client.communicate()\n        print(o)\n        print(e)\n        exit(rc)\n\ndo_test(\"c/08-ssl-bad-cacert.test\")\ndo_test(\"cpp/08-ssl-bad-cacert.test\")\n"
  },
  {
    "path": "test/lib/08-ssl-connect-cert-auth-enc.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect and subsequent disconnect when using SSL.\n# Client must provide a certificate.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id 08-ssl-connect-crt-auth\n# It should use the CA certificate ssl/test-root-ca.crt for verifying the server.\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a DISCONNECT\n# message. If rc!=0, the client should exit with an error.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef do_test(client_cmd):\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"08-ssl-connect-crt-auth-enc\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    port = mosq_test.get_port()\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=f\"{ssl_dir}/all-ca.crt\")\n    context.load_cert_chain(certfile=f\"{ssl_dir}/server.crt\", keyfile=f\"{ssl_dir}/server.key\")\n    context.verify_mode = ssl.CERT_REQUIRED\n    ssock = context.wrap_socket(sock, server_side=True)\n    ssock.settimeout(10)\n    ssock.bind(('', port))\n    ssock.listen(5)\n\n    client_args = [mosq_test.get_build_root() + \"/test/lib/\" + client_cmd, str(port)]\n    client = mosq_test.start_client(filename=client_cmd.replace('/', '-'), cmd=client_args)\n\n    try:\n        (conn, address) = ssock.accept()\n        conn.settimeout(10)\n\n        mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n        mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n        rc = 0\n\n        conn.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if mosq_test.wait_for_subprocess(client):\n            print(\"test client not finished\")\n            rc=1\n        ssock.close()\n        if rc:\n            (stdo, stde) = client.communicate()\n            print(stde.decode('utf-8'))\n            exit(rc)\n\n\ndo_test('c/08-ssl-connect-cert-auth-enc.test')\ndo_test('cpp/08-ssl-connect-cert-auth-enc.test')\n"
  },
  {
    "path": "test/lib/08-ssl-connect-cert-auth.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect and subsequent disconnect when using SSL.\n# Client must provide a certificate.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id 08-ssl-connect-crt-auth\n# It should use the CA certificate ssl/test-root-ca.crt for verifying the server.\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a DISCONNECT\n# message. If rc!=0, the client should exit with an error.\n\nfrom mosq_test_helper import *\n\ndef do_test(client_cmd):\n    port = mosq_test.get_port()\n\n    if sys.version < '2.7':\n        print(\"WARNING: SSL not supported on Python 2.6\")\n        exit(0)\n\n    rc = 1\n    keepalive = 60\n    connect_packet = mosq_test.gen_connect(\"08-ssl-connect-crt-auth\", keepalive=keepalive)\n    connack_packet = mosq_test.gen_connack(rc=0)\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=f\"{ssl_dir}/all-ca.crt\")\n    context.load_cert_chain(certfile=f\"{ssl_dir}/server.crt\", keyfile=f\"{ssl_dir}/server.key\")\n    context.verify_mode = ssl.CERT_REQUIRED\n    ssock = context.wrap_socket(sock, server_side=True)\n    ssock.settimeout(10)\n    ssock.bind(('', port))\n    ssock.listen(5)\n\n    client_args = [mosq_test.get_build_root() + \"/test/lib/\" + client_cmd, str(port)]\n    client = mosq_test.start_client(filename=client_cmd.replace('/', '-'), cmd=client_args)\n\n    try:\n        (conn, address) = ssock.accept()\n        conn.settimeout(10)\n\n        mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n        mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n        rc = 0\n\n        conn.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if mosq_test.wait_for_subprocess(client):\n            print(\"test client not finished\")\n            rc=1\n        ssock.close()\n    if rc:\n        print(f\"Fail: {client}\")\n        exit(rc)\n\ndo_test(\"c/08-ssl-connect-cert-auth.test\")\ndo_test(\"c/08-ssl-connect-cert-auth-custom-ssl-ctx.test\")\ndo_test(\"c/08-ssl-connect-cert-auth-custom-ssl-ctx-default.test\")\ndo_test(\"cpp/08-ssl-connect-cert-auth.test\")\ndo_test(\"cpp/08-ssl-connect-cert-auth-custom-ssl-ctx.test\")\ndo_test(\"cpp/08-ssl-connect-cert-auth-custom-ssl-ctx-default.test\")\n"
  },
  {
    "path": "test/lib/08-ssl-connect-no-auth.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect and subsequent disconnect when using SSL.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id 08-ssl-connect-no-auth\n# It should use the CA certificate ssl/test-ca.crt for verifying the server.\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a DISCONNECT\n# message. If rc!=0, the client should exit with an error.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef do_test(client_cmd):\n    port = mosq_test.get_port()\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"08-ssl-connect-no-auth\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=f\"{ssl_dir}/all-ca.crt\")\n    context.load_cert_chain(certfile=f\"{ssl_dir}/server.crt\", keyfile=f\"{ssl_dir}/server.key\")\n    ssock = context.wrap_socket(sock, server_side=True)\n    ssock.settimeout(10)\n    ssock.bind(('', port))\n    ssock.listen(5)\n\n    client_args = [mosq_test.get_build_root() + \"/test/lib/\" + client_cmd, str(port)]\n    client = mosq_test.start_client(filename=client_cmd.replace('/', '-'), cmd=client_args)\n\n    try:\n        (conn, address) = ssock.accept()\n        conn.settimeout(100)\n\n        mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n        mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n        rc = 0\n\n        conn.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if mosq_test.wait_for_subprocess(client):\n            print(\"test client not finished\")\n            rc=1\n        ssock.close()\n        if rc:\n            exit(rc)\n\ndo_test(\"c/08-ssl-connect-no-auth.test\")\ndo_test(\"cpp/08-ssl-connect-no-auth.test\")\n"
  },
  {
    "path": "test/lib/08-ssl-connect-san.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client produces a correct connect and subsequent disconnect when using SSL.\n\n# Certificate uses subjectAltName\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id 08-ssl-connect-san\n# It should use the CA certificate ssl/test-ca.crt for verifying the server.\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a DISCONNECT\n# message. If rc!=0, the client should exit with an error.\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef do_test(client_cmd, host):\n    port = mosq_test.get_port()\n\n    rc = 1\n    connect_packet = mosq_test.gen_connect(\"08-ssl-connect-san\")\n    connack_packet = mosq_test.gen_connack(rc=0)\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=f\"{ssl_dir}/all-ca.crt\")\n    context.load_cert_chain(certfile=f\"{ssl_dir}/server-san.crt\", keyfile=f\"{ssl_dir}/server-san.key\")\n    ssock = context.wrap_socket(sock, server_side=True)\n    ssock.settimeout(10)\n    ssock.bind(('', port))\n    ssock.listen(5)\n\n    client_args = [mosq_test.get_build_root() + \"/test/lib/\" + client_cmd, str(port), host]\n    client = mosq_test.start_client(filename=client_cmd.replace('/', '-'), cmd=client_args)\n\n    try:\n        (conn, address) = ssock.accept()\n        conn.settimeout(10)\n\n        mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n        mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n        rc = 0\n\n        conn.close()\n    except mosq_test.TestError:\n        pass\n    finally:\n        if mosq_test.wait_for_subprocess(client):\n            print(\"test client not finished\")\n            rc=1\n        ssock.close()\n        if rc:\n            exit(rc)\n\ndo_test(\"c/08-ssl-connect-san.test\", \"localhost\")\ndo_test(\"cpp/08-ssl-connect-san.test\", \"localhost\")\ndo_test(\"c/08-ssl-connect-san.test\", \"127.0.0.1\")\ndo_test(\"cpp/08-ssl-connect-san.test\", \"127.0.0.1\")\n"
  },
  {
    "path": "test/lib/08-ssl-fake-cacert.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\nif sys.version < '2.7':\n    print(\"WARNING: SSL not supported on Python 2.6\")\n    exit(0)\n\ndef do_test(client_cmd):\n    port = mosq_test.get_port()\n\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=f\"{ssl_dir}/all-ca.crt\")\n    context.load_cert_chain(certfile=f\"{ssl_dir}/server.crt\", keyfile=f\"{ssl_dir}/server.key\")\n    context.verify_mode = ssl.CERT_REQUIRED\n    ssock = context.wrap_socket(sock, server_side=True)\n    ssock.settimeout(10)\n    ssock.bind(('', port))\n    ssock.listen(5)\n\n    client_args = [mosq_test.get_build_root() + \"/test/lib/\" + client_cmd, str(port)]\n    client = mosq_test.start_client(filename=client_cmd.replace('/', '-'), cmd=client_args)\n\n    try:\n        (conn, address) = ssock.accept()\n\n        conn.close()\n    except ssl.SSLError:\n        # Expected error due to ca certs not matching.\n        pass\n    except mosq_test.TestError:\n        pass\n    finally:\n        time.sleep(1.0)\n        if mosq_test.wait_for_subprocess(client):\n            print(\"test client not finished\")\n            rc=1\n        ssock.close()\n\n    if client.returncode == 0:\n        exit(0)\n    else:\n        exit(1)\n\ndo_test(\"c/08-ssl-fake-cacert.test\")\ndo_test(\"cpp/08-ssl-fake-cacert.test\")\n\n"
  },
  {
    "path": "test/lib/09-util-topic-tokenise.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(client):\n    port = mosq_test.get_port()\n\n    rc = 1\n\n    client_args = [client, str(port)]\n    client = mosq_test.start_client(filename=client.replace('/', '-'), cmd=client_args)\n\n    if mosq_test.wait_for_subprocess(client):\n        print(\"test client not finished\")\n        rc=1\n    else:\n        rc=client.returncode\n    if rc:\n        print(f\"Fail: {client}\")\n        exit(rc)\n\ndo_test(\"c/09-util-topic-tokenise.test\")\ndo_test(\"cpp/09-util-topic-tokenise.test\")\n"
  },
  {
    "path": "test/lib/11-prop-oversize-packet.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client publishing an oversize packet correctly.\n# The client should try to publish a message that is too big, then the one below which is ok.\n# It should also try to subscribe with a too large topic\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"publish-qos0-test\", proto_ver=5)\n    props = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10)\n    props += mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 30)\n    props += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False)\n\n    bad_publish_packet = mosq_test.gen_publish(\"pub/test\", qos=0, payload=\"123456789012345678\", proto_ver=5)\n    publish_packet = mosq_test.gen_publish(\"pub/test\", qos=0, payload=\"12345678901234567\", proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect()\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.expect_packet(conn, \"publish\", publish_packet)\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/11-prop-oversize-packet.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/11-prop-oversize-packet.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/11-prop-recv-qos0.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether the v5 message callback gets the properties\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"prop-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"plain/text\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"msg/123\")\n    publish_packet = mosq_test.gen_publish(\"prop/test\", qos=0, payload=\"message\", proto_ver=5, properties=props)\n\n    ok_packet = mosq_test.gen_publish(\"ok\", qos=0, payload=\"ok\", proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_send_receive(conn, publish_packet, ok_packet, \"ok\")\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/11-prop-recv.test\", [\"0\"], do_test, None)\nmosq_test.client_test(\"cpp/11-prop-recv.test\", [\"0\"], do_test, None)\n"
  },
  {
    "path": "test/lib/11-prop-recv-qos1.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether the v5 message callback gets the properties\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"prop-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    props = mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"plain/text\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"msg/123\")\n    publish_packet = mosq_test.gen_publish(\"prop/test\", mid=mid, qos=1, payload=\"message\", proto_ver=5, properties=props)\n    puback_packet = mosq_test.gen_puback(mid=mid, proto_ver=5)\n\n    ok_packet = mosq_test.gen_publish(\"ok\", qos=0, payload=\"ok\", proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    conn.send(publish_packet)\n    mosq_test.expect_packet(conn, \"puback\", puback_packet)\n    mosq_test.expect_packet(conn, \"ok\", ok_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/11-prop-recv.test\", [\"1\"], do_test, None)\nmosq_test.client_test(\"cpp/11-prop-recv.test\", [\"1\"], do_test, None)\n"
  },
  {
    "path": "test/lib/11-prop-recv-qos2.py",
    "content": "#!/usr/bin/env python3\n\n# Check whether the v5 message callback gets the properties\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"prop-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    mid = 1\n    props = mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"plain/text\")\n    props += mqtt5_props.gen_string_prop(mqtt5_props.RESPONSE_TOPIC, \"msg/123\")\n    publish_packet = mosq_test.gen_publish(\"prop/test\", mid=mid, qos=2, payload=\"message\", proto_ver=5, properties=props)\n    pubrec_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=5)\n    pubrel_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=5)\n    pubcomp_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=5)\n\n    ok_packet = mosq_test.gen_publish(\"ok\", qos=0, payload=\"ok\", proto_ver=5)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n    mosq_test.do_send_receive(conn, publish_packet, pubrec_packet, \"pubrec\")\n    mosq_test.do_send_receive(conn, pubrel_packet, pubcomp_packet, \"pubcomp\")\n    mosq_test.expect_packet(conn, \"ok\", ok_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/11-prop-recv.test\", [\"2\"], do_test, None)\nmosq_test.client_test(\"cpp/11-prop-recv.test\", [\"2\"], do_test, None)\n"
  },
  {
    "path": "test/lib/11-prop-send-content-type.py",
    "content": "#!/usr/bin/env python3\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"prop-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    props = mqtt5_props.gen_string_prop(mqtt5_props.CONTENT_TYPE, \"application/json\")\n    publish_packet = mosq_test.gen_publish(\"prop/qos0\", qos=0, payload=\"message\", proto_ver=5, properties=props)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.expect_packet(conn, \"publish\", publish_packet)\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/11-prop-send-content-type.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/11-prop-send-content-type.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/11-prop-send-payload-format.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a client sends a correct PUBLISH to a topic with QoS 0.\n\n# The client should connect to port 1888 with keepalive=60, clean session set,\n# and client id publish-qos0-test\n# The test will send a CONNACK message to the client with rc=0. Upon receiving\n# the CONNACK and verifying that rc=0, the client should send a PUBLISH message\n# to topic \"pub/qos0/test\" with payload \"message\" and QoS=0. If rc!=0, the\n# client should exit with an error.\n# After sending the PUBLISH message, the client should send a DISCONNECT message.\n\nfrom mosq_test_helper import *\n\ndef do_test(conn, data):\n    connect_packet = mosq_test.gen_connect(\"prop-test\", proto_ver=5)\n    connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)\n\n    props = mqtt5_props.gen_byte_prop(mqtt5_props.PAYLOAD_FORMAT_INDICATOR, 0x01)\n    publish_packet = mosq_test.gen_publish(\"prop/qos0\", qos=0, payload=\"message\", proto_ver=5, properties=props)\n\n    disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)\n\n    mosq_test.do_receive_send(conn, connect_packet, connack_packet, \"connect\")\n\n    mosq_test.expect_packet(conn, \"publish\", publish_packet)\n    mosq_test.expect_packet(conn, \"disconnect\", disconnect_packet)\n\n    conn.close()\n\n\nmosq_test.client_test(\"c/11-prop-send-payload-format.test\", [], do_test, None)\nmosq_test.client_test(\"cpp/11-prop-send-payload-format.test\", [], do_test, None)\n"
  },
  {
    "path": "test/lib/CMakeLists.txt",
    "content": "add_subdirectory(c)\nadd_subdirectory(cpp)\n\n\nfile(GLOB PY_TEST_FILES [0-9][0-9]-*.py)\nlist(APPEND PY_TEST_FILES \"${CMAKE_CURRENT_SOURCE_DIR}/msg_sequence_test.py\")\n\nset(EXCLUDE_LIST\n    03-publish-c2b-qos1-timeout\n    03-publish-c2b-qos2-timeout\n)\n\nforeach(PY_TEST_FILE ${PY_TEST_FILES})\n    get_filename_component(PY_TEST_NAME ${PY_TEST_FILE} NAME_WE)\n    if(${PY_TEST_NAME} IN_LIST EXCLUDE_LIST)\n        continue()\n    endif()\n    add_test(NAME lib-${PY_TEST_NAME}\n        COMMAND ${PY_TEST_FILE}\n    )\n    set_tests_properties(lib-${PY_TEST_NAME}\n        PROPERTIES\n            ENVIRONMENT \"BUILD_ROOT=${CMAKE_BINARY_DIR}\"\n    )\nendforeach()\n"
  },
  {
    "path": "test/lib/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\n.PHONY: all check test test-compile test-compile-c test-compile-cpp\n.NOTPARALLEL:\n\nLD_LIBRARY_PATH=${R}/lib\n\nall :\n\ncheck : test\n\nptest : test-compile\n\t./test.py\n\nmsg_sequence_test: test-compile-c\n\t./msg_sequence_test.py\n\ntest-compile : test-compile-c test-compile-cpp\n\ntest-compile-c :\n\t$(MAKE) -C c\n\ntest-compile-cpp :\n\t$(MAKE) -C cpp\n\ntest : test-compile\n\t./01-con-discon-success.py\n\t./01-con-discon-success-v5.py\n\t./01-con-discon-will.py\n\t./01-con-discon-will-v5.py\n\t./01-con-discon-will-clear.py\n\t./01-extended-auth-continue.py\n\t./01-extended-auth-failure.py\n\t./01-keepalive-pingreq.py\n\t./01-no-clean-session.py\n\t./01-server-keepalive-pingreq.py\n\t./01-pre-connect-callback.py\n\t./01-unpwd-set.py\n\t./01-will-set.py\n\t./01-will-unpwd-set.py\n\t./02-subscribe-qos0.py\n\t./02-subscribe-qos1.py\n\t./02-subscribe-qos2.py\n\t./02-subscribe-helper-qos2.py\n\t./02-unsubscribe-multiple-v5.py\n\t./02-unsubscribe-v5.py\n\t./02-unsubscribe.py\n\t./03-publish-b2c-qos1.py\n\t./03-publish-b2c-qos1-unexpected-puback.py\n\t./03-publish-b2c-qos2-len.py\n\t./03-publish-b2c-qos2.py\n\t./03-publish-b2c-qos2-unexpected-pubrel.py\n\t./03-publish-b2c-qos2-unexpected-pubcomp.py\n\t./03-publish-c2b-qos1-disconnect.py\n\t./03-publish-c2b-qos1-len.py\n\t./03-publish-c2b-qos1-receive-maximum.py\n\t./03-publish-c2b-qos2-disconnect.py\n\t./03-publish-c2b-qos2-len.py\n\t./03-publish-c2b-qos2-maximum-qos-0.py\n\t./03-publish-c2b-qos2-maximum-qos-1.py\n\t./03-publish-c2b-qos2-pubrec-error.py\n\t./03-publish-c2b-qos2-receive-maximum-1.py\n\t./03-publish-c2b-qos2-receive-maximum-2.py\n\t./03-publish-c2b-qos2.py\n\t./03-publish-loop.py\n\t./03-publish-qos0-no-payload.py\n\t./03-publish-qos0.py\n\t./03-request-response-correlation.py\n\t./03-request-response.py\n\t./04-retain-qos0.py\nifeq ($(WITH_TLS),yes)\n\t./08-ssl-fake-cacert.py\n\t./08-ssl-bad-cacert.py\n\t./08-ssl-connect-cert-auth-enc.py\n\t./08-ssl-connect-cert-auth.py\n\t./08-ssl-connect-no-auth.py\n\t./08-ssl-connect-san.py\nendif\n\t./09-util-topic-tokenise.py\n\t./11-prop-oversize-packet.py\n\t./11-prop-send-content-type.py\n\t./11-prop-send-payload-format.py\n\t./11-prop-recv-qos0.py\n\t./11-prop-recv-qos1.py\n\t./11-prop-recv-qos2.py\n\nclean :\n\t$(MAKE) -C c clean\n\t$(MAKE) -C cpp clean\n"
  },
  {
    "path": "test/lib/c/01-con-discon-success-v5.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)rc;\n\t(void)flags;\n\t(void)properties;\n\n\t/* FIXME - should verify flags and all properties here. */\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)properties;\n\n\t/* FIXME - should verify flags and all properties here. */\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\tmosquitto_property *props = NULL;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-con-discon-success-v5\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, 5);\n\tmosquitto_connect_v5_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_v5_callback_set(mosq, on_disconnect);\n\n\tmosquitto_property_add_int32(&props, MQTT_PROP_MAXIMUM_PACKET_SIZE, 1000);\n\trc = mosquitto_connect_bind_v5(mosq, \"localhost\", port, 60, NULL, props);\n\tmosquitto_property_free_all(&props);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-con-discon-success.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\nstatic int mydata = 42;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\tint *obj_i = obj;\n\n\tif(rc || obj_i != &mydata || *obj_i != mydata || obj != mosquitto_userdata(mosq)){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-con-discon-success\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_user_data_set(mosq, &mydata);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-con-discon-will-clear.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-con-discon-will\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_will_set(mosq, \"will/topic\", strlen(\"will-payload\"), \"will-payload\", 1, true);\n\tmosquitto_will_clear(mosq);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-con-discon-will-v5.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto/mqtt_protocol.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\tmosquitto_property *proplist = NULL;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-con-discon-will\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tabort();\n\t}\n\t/* Set twice, so it has to clear the old settings */\n\tmosquitto_will_set_v5(mosq, \"will/topic\", strlen(\"will-payload\"), \"will-payload\", 1, true, proplist);\n\tproplist = NULL;\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tabort();\n\t}\n\tmosquitto_will_set_v5(mosq, \"will/topic\", strlen(\"will-payload\"), \"will-payload\", 1, true, proplist);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-con-discon-will.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-con-discon-will\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\t/* Set twice, so it has to clear the old settings */\n\tmosquitto_will_set(mosq, \"will/topic\", strlen(\"will-payload\"), \"will-payload\", 1, true);\n\tmosquitto_will_set(mosq, \"will/topic\", strlen(\"will-payload\"), \"will-payload\", 1, true);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-extended-auth-continue.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)rc;\n\t(void)flags;\n\t(void)properties;\n\n\t/* FIXME - should verify flags and all properties here. */\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic int on_ext_auth(struct mosquitto *mosq, void *obj, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)auth_data;\n\t(void)auth_data_len;\n\t(void)properties;\n\n\tif(strcmp(auth_method, \"test-method\")){\n\t\trun = 1;\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\tif(auth_data_len == 0 || (!auth_data || strcmp(auth_data, \"test-request\"))){\n\t\trun = 1;\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\treturn mosquitto_ext_auth_continue(mosq, auth_method, strlen(\"test-reply\"), \"test-reply\", NULL);\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)properties;\n\n\t/* FIXME - should verify flags and all properties here. */\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\tmosquitto_property *props = NULL;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-extended-auth\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, 5);\n\tmosquitto_connect_v5_callback_set(mosq, on_connect);\n\tmosquitto_ext_auth_callback_set(mosq, on_ext_auth);\n\tmosquitto_disconnect_v5_callback_set(mosq, on_disconnect);\n\n\tmosquitto_property_add_int32(&props, MQTT_PROP_MAXIMUM_PACKET_SIZE, 1000);\n\trc = mosquitto_connect_bind_v5(mosq, \"localhost\", port, 60, NULL, props);\n\tmosquitto_property_free_all(&props);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-extended-auth-failure.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)rc;\n\t(void)flags;\n\t(void)properties;\n\n\t/* FIXME - should verify flags and all properties here. */\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic int on_ext_auth(struct mosquitto *mosq, void *obj, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)auth_method;\n\t(void)auth_data;\n\t(void)auth_data_len;\n\t(void)properties;\n\n\treturn MOSQ_ERR_AUTH;\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)properties;\n\n\t/* FIXME - should verify flags and all properties here. */\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\tmosquitto_property *props = NULL;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-extended-auth\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, 5);\n\tmosquitto_connect_v5_callback_set(mosq, on_connect);\n\tmosquitto_ext_auth_callback_set(mosq, on_ext_auth);\n\tmosquitto_disconnect_v5_callback_set(mosq, on_disconnect);\n\n\tmosquitto_property_add_int32(&props, MQTT_PROP_MAXIMUM_PACKET_SIZE, 1000);\n\trc = mosquitto_connect_bind_v5(mosq, \"localhost\", port, 60, NULL, props);\n\tmosquitto_property_free_all(&props);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-keepalive-pingreq.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-keepalive-pingreq\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 5);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, -1, 1);\n\t\tif(rc != 0){\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-no-clean-session.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-no-clean-session\", false, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-pre-connect-callback.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\n\nstatic void on_pre_connect(struct mosquitto *mosq, void *userdata)\n{\n\t(void)userdata;\n\n\tmosquitto_username_pw_set(mosq, \"uname\", \";'[08gn=#\");\n}\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-pre-connect\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_pre_connect_callback_set(mosq, on_pre_connect);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-server-keepalive-pingreq.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-server-keepalive-pingreq\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, -1, 1);\n\t\tif(rc){\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-unpwd-set.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tprintf(\"conn %d\\n\", rc);\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-unpwd-set\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_username_pw_set(mosq, \"uname\", \";'[08gn=#\");\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-will-set.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-will-set\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_will_set(mosq, \"topic/on/unexpected/disconnect\", strlen(\"will message\"), \"will message\", 1, true);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/01-will-unpwd-set.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"01-will-unpwd-set\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_username_pw_set(mosq, \"oibvvwqw\", \"#'^2hg9a&nm38*us\");\n\tmosquitto_will_set(mosq, \"will-topic\", strlen(\"will message\"), \"will message\", 2, false);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/02-subscribe-helper-callback-qos2.c",
    "content": "#include <assert.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n\n#define QOS 2\n\n\nint cb(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg)\n{\n\t(void)mosq;\n\t(void)userdata;\n\n\tassert(msg);\n\tassert(!strcmp(msg->topic, \"qos2/test\"));\n\treturn 1;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosquitto_subscribe_callback(\n\t\t\tcb, NULL, \"qos2/test\", QOS, \"localhost\", port,\n\t\t\t\"subscribe-qos2-test\", 60, true, NULL, NULL, NULL, NULL);\n\n\tmosquitto_lib_cleanup();\n\treturn 0;\n}\n"
  },
  {
    "path": "test/lib/c/02-subscribe-helper-simple-qos2.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\n#define QOS 2\n\n\nint main(int argc, char *argv[])\n{\n\tint port;\n\tstruct mosquitto_message *messages;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosquitto_subscribe_simple(&messages, 1,\n\t\t\ttrue, \"qos2/test\", QOS, \"localhost\", port,\n\t\t\t\"subscribe-qos2-test\", 60, true, NULL, NULL, NULL, NULL);\n\n\tmosquitto_message_free(&messages);\n\n\tmosquitto_lib_cleanup();\n\treturn 0;\n}\n"
  },
  {
    "path": "test/lib/c/02-subscribe-qos0.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\n#define QOS 0\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe(mosq, NULL, \"qos0/test\", QOS);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nstatic void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\t(void)obj;\n\t(void)mid;\n\n\tif(qos_count != 1 || granted_qos[0] != QOS){\n\t\tabort();\n\t}\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"subscribe-qos0-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/02-subscribe-qos1-async1.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <mosquitto.h>\n\n/* mosquitto_connect_async() test, with mosquitto_loop_start() called before mosquitto_connect_async(). */\n\n#define QOS 1\n\nstatic int run = -1;\nstatic bool should_run = true;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe(mosq, NULL, \"qos1/test\", QOS);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nstatic void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)mid;\n\n\tif(qos_count != 1 || granted_qos[0] != QOS){\n\t\tabort();\n\t}\n\tshould_run = false;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"subscribe-qos1-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe);\n\n\trc = mosquitto_loop_start(mosq);\n\tif(rc){\n\t\tprintf(\"loop_start failed: %s\\n\", mosquitto_strerror(rc));\n\t\treturn rc;\n\t}\n\n\trc = mosquitto_connect_async(mosq, \"localhost\", port, 60);\n\tif(rc){\n\t\tprintf(\"connect_async failed: %s\\n\", mosquitto_strerror(rc));\n\t\treturn rc;\n\t}\n\n\t/* 50 millis to be system polite */\n\tstruct timespec tv = { 0, 50e6 };\n\twhile(should_run){\n\t\tnanosleep(&tv, NULL);\n\t}\n\n\tmosquitto_disconnect(mosq);\n\tmosquitto_loop_stop(mosq, false);\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/02-subscribe-qos1-async2.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <mosquitto.h>\n\n/* mosquitto_connect_async() test, with mosquitto_loop_start() called after mosquitto_connect_async(). */\n\n#define QOS 1\n\nstatic int run = -1;\nstatic bool should_run = true;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe(mosq, NULL, \"qos1/test\", QOS);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nstatic void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)mid;\n\n\tif(qos_count != 1 || granted_qos[0] != QOS){\n\t\tabort();\n\t}\n\tshould_run = false;\n}\n\n\nstatic const char *loglevel_as_str(int level)\n{\n\tswitch(level){\n\t\tcase MOSQ_LOG_INFO:\n\t\t\treturn \"INFO\";\n\t\tcase MOSQ_LOG_NOTICE:\n\t\t\treturn \"NOTICE\";\n\t\tcase MOSQ_LOG_WARNING:\n\t\t\treturn \"WARNING\";\n\t\tcase MOSQ_LOG_ERR:\n\t\t\treturn \"ERROR\";\n\t\tcase MOSQ_LOG_DEBUG:\n\t\t\treturn \"DEBUG\";\n\t}\n\treturn \"UNKNOWN\";\n}\n\n\nstatic void on_log(struct mosquitto *mosq, void *user_data, int level, const char *msg)\n{\n\t(void)mosq;\n\t(void)user_data;\n\tfprintf(stderr, \"%s: %s\\n\", loglevel_as_str(level), msg);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"subscribe-qos1-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_log_callback_set(mosq, &on_log);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe);\n\n\trc = mosquitto_connect_async(mosq, \"localhost\", port, 60);\n\tif(rc){\n\t\tprintf(\"connect_async failed: %s\\n\", mosquitto_strerror(rc));\n\t}\n\n\trc = mosquitto_loop_start(mosq);\n\tif(rc){\n\t\tprintf(\"loop_start failed: %s\\n\", mosquitto_strerror(rc));\n\t}\n\n\t/* 50 millis to be system polite */\n\tstruct timespec tv = { 0, 50e6 };\n\twhile(should_run){\n\t\tnanosleep(&tv, NULL);\n\t}\n\n\tmosquitto_disconnect(mosq);\n\tmosquitto_loop_stop(mosq, false);\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/02-subscribe-qos1.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\n#define QOS 1\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe(mosq, NULL, \"qos1/test\", QOS);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nstatic void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\t(void)obj;\n\t(void)mid;\n\n\tif(qos_count != 1 || granted_qos[0] != QOS){\n\t\tabort();\n\t}\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"subscribe-qos1-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/02-subscribe-qos2.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\n#define QOS 2\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe(mosq, NULL, \"qos2/test\", QOS);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nstatic void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\t(void)obj;\n\t(void)mid;\n\n\tif(qos_count != 1 || granted_qos[0] != QOS){\n\t\tabort();\n\t}\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"subscribe-qos2-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/02-unsubscribe-multiple-v5.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\n#define QOS 2\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe_v5(mosq, NULL, \"unsubscribe/test\", QOS, 0, NULL);\n\t}\n}\n\n\nstatic void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int sub_count, const int *subs)\n{\n\tchar *const unsubs[] = {\"unsubscribe/test\", \"no-sub\"};\n\n\t(void)obj;\n\t(void)mid;\n\n\tif(sub_count != 1 || subs[0] != QOS){\n\t\tabort();\n\t}\n\n\tmosquitto_unsubscribe_multiple(mosq, NULL, 2, unsubs, NULL);\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nstatic void on_unsubscribe(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\t(void)mid;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"unsubscribe-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe);\n\tmosquitto_unsubscribe_callback_set(mosq, on_unsubscribe);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/02-unsubscribe-v5.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\tint rc2;\n\tmosquitto_property *proplist = NULL;\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc2 = mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, \"key\", \"value\");\n\t\tif(rc2 != MOSQ_ERR_SUCCESS){\n\t\t\tabort();\n\t\t}\n\t\tmosquitto_unsubscribe_v5(mosq, NULL, \"unsubscribe/test\", proplist);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nstatic void on_unsubscribe(struct mosquitto *mosq, void *obj, int mid, const mosquitto_property *props)\n{\n\t(void)obj;\n\t(void)mid;\n\t(void)props;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"unsubscribe-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_unsubscribe_v5_callback_set(mosq, on_unsubscribe);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/02-unsubscribe.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_unsubscribe(mosq, NULL, \"unsubscribe/test\");\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nstatic void on_unsubscribe(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\t(void)mid;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"unsubscribe-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_unsubscribe_callback_set(mosq, on_unsubscribe);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/02-unsubscribe2-v5.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\tint rc2;\n\tmosquitto_property *proplist = NULL;\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc2 = mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, \"key\", \"value\");\n\t\tif(rc2 != MOSQ_ERR_SUCCESS){\n\t\t\tabort();\n\t\t}\n\t\tmosquitto_unsubscribe_v5(mosq, NULL, \"unsubscribe/test\", proplist);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nstatic void on_unsubscribe(struct mosquitto *mosq, void *obj, int mid, int reason_code_count, const int *reason_codes, const mosquitto_property *props)\n{\n\t(void)obj;\n\t(void)mid;\n\t(void)props;\n\n\tfor(int i=0; i<reason_code_count; i++){\n\t\tif(reason_codes[i] != 0){\n\t\t\texit(1);\n\t\t}\n\t}\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"unsubscribe-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_unsubscribe2_v5_callback_set(mosq, on_unsubscribe);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-b2c-qos1-unexpected-puback.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(rc){\n\t\tprintf(\"Connect error: %d\\n\", rc);\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos1-test\", true, &run);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 5);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, 300, 1);\n\t\tif(rc){\n\t\t\texit(0);\n\t\t}\n\t}\n\n\tmosquitto_lib_cleanup();\n\treturn 0;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-b2c-qos1.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nstatic void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(msg->mid != 123){\n\t\tprintf(\"Invalid mid (%d)\\n\", msg->mid);\n\t\texit(1);\n\t}\n\tif(msg->qos != 1){\n\t\tprintf(\"Invalid qos (%d)\\n\", msg->qos);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->topic, \"pub/qos1/receive\")){\n\t\tprintf(\"Invalid topic (%s)\\n\", msg->topic);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->payload, \"message\")){\n\t\tprintf(\"Invalid payload (%s)\\n\", (char *)msg->payload);\n\t\texit(1);\n\t}\n\tif(msg->payloadlen != 7){\n\t\tprintf(\"Invalid payloadlen (%d)\\n\", msg->payloadlen);\n\t\texit(1);\n\t}\n\tif(msg->retain != false){\n\t\tprintf(\"Invalid retain (%d)\\n\", msg->retain);\n\t\texit(1);\n\t}\n\n\texit(0);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos1-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_message_callback_set(mosq, on_message);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(1){\n\t\tif(mosquitto_loop(mosq, 300, 1)){\n\t\t\tbreak;\n\t\t}\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn 1;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-b2c-qos2-len.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nstatic void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)\n{\n\t(void)obj;\n\n\tif(msg->mid != 56){\n\t\tprintf(\"Invalid mid (%d)\\n\", msg->mid);\n\t\texit(1);\n\t}\n\tif(msg->qos != 2){\n\t\tprintf(\"Invalid qos (%d)\\n\", msg->qos);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->topic, \"len/qos2/test\")){\n\t\tprintf(\"Invalid topic (%s)\\n\", msg->topic);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->payload, \"message\")){\n\t\tprintf(\"Invalid payload (%s)\\n\", (char *)msg->payload);\n\t\texit(1);\n\t}\n\tif(msg->payloadlen != 7){\n\t\tprintf(\"Invalid payloadlen (%d)\\n\", msg->payloadlen);\n\t\texit(1);\n\t}\n\tif(msg->retain != false){\n\t\tprintf(\"Invalid retain (%d)\\n\", msg->retain);\n\t\texit(1);\n\t}\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)rc;\n\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, &run);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_message_callback_set(mosq, on_message);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 100, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-b2c-qos2-unexpected-pubcomp.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(rc){\n\t\tprintf(\"Connect error: %d\\n\", rc);\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, &run);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 5);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, 300, 1);\n\t\tif(rc){\n\t\t\texit(0);\n\t\t}\n\t}\n\n\tmosquitto_lib_cleanup();\n\treturn 0;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-b2c-qos2-unexpected-pubrel.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nstatic void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(!strcmp(msg->topic, \"quit\")){\n\t\trun = 0;\n\t\treturn;\n\t}\n\tif(msg->mid != 13423){\n\t\tprintf(\"Invalid mid (%d)\\n\", msg->mid);\n\t\texit(1);\n\t}\n\tif(msg->qos != 2){\n\t\tprintf(\"Invalid qos (%d)\\n\", msg->qos);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->topic, \"pub/qos2/receive\")){\n\t\tprintf(\"Invalid topic (%s)\\n\", msg->topic);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->payload, \"message\")){\n\t\tprintf(\"Invalid payload (%s)\\n\", (char *)msg->payload);\n\t\texit(1);\n\t}\n\tif(msg->payloadlen != 7){\n\t\tprintf(\"Invalid payloadlen (%d)\\n\", msg->payloadlen);\n\t\texit(1);\n\t}\n\tif(msg->retain != false){\n\t\tprintf(\"Invalid retain (%d)\\n\", msg->retain);\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, &run);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_message_callback_set(mosq, on_message);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, 300, 1);\n\t\tif(rc){\n\t\t\tprintf(\"%d:%s\\n\", rc, mosquitto_strerror(rc));\n\t\t\texit(1);\n\t\t}\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-b2c-qos2.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nstatic void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(msg->mid != 13423){\n\t\tprintf(\"Invalid mid (%d)\\n\", msg->mid);\n\t\texit(1);\n\t}\n\tif(msg->qos != 2){\n\t\tprintf(\"Invalid qos (%d)\\n\", msg->qos);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->topic, \"pub/qos2/receive\")){\n\t\tprintf(\"Invalid topic (%s)\\n\", msg->topic);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->payload, \"message\")){\n\t\tprintf(\"Invalid payload (%s)\\n\", (char *)msg->payload);\n\t\texit(1);\n\t}\n\tif(msg->payloadlen != 7){\n\t\tprintf(\"Invalid payloadlen (%d)\\n\", msg->payloadlen);\n\t\texit(1);\n\t}\n\tif(msg->retain != false){\n\t\tprintf(\"Invalid retain (%d)\\n\", msg->retain);\n\t\texit(1);\n\t}\n\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, &run);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_message_callback_set(mosq, on_message);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 300, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-c2b-qos1-disconnect.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\nstatic int first_connection = 1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tif(first_connection == 1){\n\t\t\tmosquitto_publish(mosq, NULL, \"pub/qos1/test\", strlen(\"message\"), \"message\", 1, false);\n\t\t\tfirst_connection = 0;\n\t\t}\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\t(void)mid;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\tmosquitto_reconnect(mosq);\n\t}else{\n\t\trun = 0;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos1-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 300, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-c2b-qos1-len.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_publish(mosq, NULL, \"pub/qos1/test\", strlen(\"message\"), \"message\", 1, false);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\t(void)mid;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)rc;\n\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos1-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 300, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-c2b-qos1-receive-maximum.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties)\n{\n\tint i;\n\n\t(void)obj;\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}\n\n\tfor(i=0; i<6; i++){\n\t\tmosquitto_publish_v5(mosq, NULL, \"topic\", 5, \"12345\", 1, false, NULL);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)reason_code;\n\t(void)properties;\n\n\tif(mid == 6){\n\t\tmosquitto_disconnect(mosq);\n\t\trun = 0;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos1-test\", true, &run);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosquitto_connect_v5_callback_set(mosq, on_connect);\n\tmosquitto_publish_v5_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect_bind_v5(mosq, \"localhost\", port, 60, NULL, NULL);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 300, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-c2b-qos2-disconnect.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\nstatic int first_connection = 1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tif(first_connection == 1){\n\t\t\tmosquitto_publish(mosq, NULL, \"pub/qos2/test\", strlen(\"message\"), \"message\", 2, false);\n\t\t\tfirst_connection = 0;\n\t\t}\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\t(void)mid;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\tmosquitto_reconnect(mosq);\n\t}else{\n\t\trun = 0;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 300, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-c2b-qos2-len.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_publish(mosq, NULL, \"pub/qos2/test\", strlen(\"message\"), \"message\", 2, false);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)mid;\n\t(void)reason_code;\n\t(void)properties;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)rc;\n\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_publish_v5_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 300, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-c2b-qos2-maximum-qos-0.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc = mosquitto_publish(mosq, NULL, \"maximum/qos/qos2\", strlen(\"message\"), \"message\", 2, false);\n\t\tif(rc != MOSQ_ERR_QOS_NOT_SUPPORTED){\n\t\t\trun = 1;\n\t\t}\n\t\trc = mosquitto_publish(mosq, NULL, \"maximum/qos/qos1\", strlen(\"message\"), \"message\", 1, false);\n\t\tif(rc != MOSQ_ERR_QOS_NOT_SUPPORTED){\n\t\t\trun = 1;\n\t\t}\n\t\trc = mosquitto_publish(mosq, NULL, \"maximum/qos/qos0\", strlen(\"message\"), \"message\", 0, false);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\trun = 1;\n\t\t}\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\n\tif(mid == 1){\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)rc;\n\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 50, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-c2b-qos2-maximum-qos-1.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc = mosquitto_publish(mosq, NULL, \"maximum/qos/qos2\", strlen(\"message\"), \"message\", 2, false);\n\t\tif(rc != MOSQ_ERR_QOS_NOT_SUPPORTED){\n\t\t\trun = 1;\n\t\t}\n\t\trc = mosquitto_publish(mosq, NULL, \"maximum/qos/qos1\", strlen(\"message\"), \"message\", 1, false);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\trun = 1;\n\t\t}\n\t\trc = mosquitto_publish(mosq, NULL, \"maximum/qos/qos0\", strlen(\"message\"), \"message\", 0, false);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\trun = 1;\n\t\t}\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\n\tif(mid == 2){\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)rc;\n\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 50, 1);\n\t}\n\tmosquitto_loop(mosq, 50, 1);\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-c2b-qos2-pubrec-error.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}\n\tmosquitto_publish_v5(mosq, NULL, \"topic\", strlen(\"rejected\"), \"rejected\", 2, false, NULL);\n\tmosquitto_publish_v5(mosq, NULL, \"topic\", strlen(\"accepted\"), \"accepted\", 2, false, NULL);\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)reason_code;\n\t(void)properties;\n\n\tif(mid == 2){\n\t\trun = 0;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, &run);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosquitto_connect_v5_callback_set(mosq, on_connect);\n\tmosquitto_publish_v5_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect_bind_v5(mosq, \"localhost\", port, 60, NULL, NULL);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 100, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-c2b-qos2-receive-maximum.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties)\n{\n\tint i;\n\n\t(void)obj;\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}\n\n\tfor(i=0; i<5; i++){\n\t\tmosquitto_publish_v5(mosq, NULL, \"topic\", 5, \"12345\", 2, false, NULL);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)reason_code;\n\t(void)properties;\n\n\tif(mid == 5){\n\t\tmosquitto_disconnect(mosq);\n\t\trun = 0;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, &run);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosquitto_connect_v5_callback_set(mosq, on_connect);\n\tmosquitto_publish_v5_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect_bind_v5(mosq, \"localhost\", port, 60, NULL, NULL);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 300, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-c2b-qos2.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_publish(mosq, NULL, \"pub/qos2/test\", strlen(\"message\"), \"message\", 2, false);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\t(void)mid;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)rc;\n\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos2-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 300, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-loop-forever.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect_v5(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe_v5(mosq, NULL, \"loop/test\", 0, 0, NULL);\n\t}\n}\n\n\nstatic void on_disconnect_v5(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)properties;\n\n\trun = rc;\n}\n\n\nstatic void on_subscribe_v5(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props)\n{\n\t(void)obj;\n\t(void)mid;\n\t(void)qos_count;\n\t(void)granted_qos;\n\t(void)props;\n\n\tmosquitto_publish_v5(mosq, NULL, \"loop/test\", strlen(\"message\"), \"message\", 0, false, NULL);\n}\n\n\nstatic void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)msg;\n\t(void)properties;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"loop-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosquitto_connect_v5_callback_set(mosq, on_connect_v5);\n\tmosquitto_disconnect_v5_callback_set(mosq, on_disconnect_v5);\n\tmosquitto_subscribe_v5_callback_set(mosq, on_subscribe_v5);\n\tmosquitto_message_v5_callback_set(mosq, on_message_v5);\n\n\trc = mosquitto_connect_bind_v5(mosq, \"localhost\", port, 60, NULL, NULL);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\tmosquitto_loop_forever(mosq, -1, 1);\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn 1;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-loop-manual.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <sys/select.h>\n\nstatic int run = -1;\n\n\nstatic void do_loop(struct mosquitto *mosq)\n{\n\tint sock;\n\tstruct timeval tv;\n\tfd_set readfds, writefds;\n\tint fdcount;\n\n\tsock = mosquitto_socket(mosq);\n\n\tif(sock < 0){\n\t\texit(1);\n\t}\n\n\tFD_ZERO(&readfds);\n\tFD_ZERO(&writefds);\n\n\tFD_SET(sock, &readfds);\n\n\twhile(run == -1){\n\t\ttv.tv_sec = 0;\n\t\ttv.tv_usec = 100000;\n\n\t\tFD_SET(sock, &readfds);\n\t\tif(mosquitto_want_write(mosq)){\n\t\t\tFD_SET(sock, &writefds);\n\t\t}else{\n\t\t\tFD_CLR(sock, &writefds);\n\t\t}\n\n\t\tfdcount = select(sock+1, &readfds, &writefds, NULL, &tv);\n\t\tif(fdcount < 0){\n\t\t\texit(1);\n\t\t}\n\t\tif(FD_ISSET(sock, &readfds)){\n\t\t\tmosquitto_loop_read(mosq, 1);\n\t\t}\n\t\tif(FD_ISSET(sock, &writefds)){\n\t\t\tmosquitto_loop_write(mosq, 1);\n\t\t}\n\t\tmosquitto_loop_misc(mosq);\n\t}\n}\n\n\nstatic void on_connect_v5(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe_v5(mosq, NULL, \"loop/test\", 0, 0, NULL);\n\t}\n}\n\n\nstatic void on_disconnect_v5(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)properties;\n\n\trun = rc;\n}\n\n\nstatic void on_subscribe_v5(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props)\n{\n\t(void)obj;\n\t(void)mid;\n\t(void)qos_count;\n\t(void)granted_qos;\n\t(void)props;\n\n\tmosquitto_publish_v5(mosq, NULL, \"loop/test\", strlen(\"message\"), \"message\", 0, false, NULL);\n}\n\n\nstatic void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)msg;\n\t(void)properties;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"loop-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosquitto_connect_v5_callback_set(mosq, on_connect_v5);\n\tmosquitto_disconnect_v5_callback_set(mosq, on_disconnect_v5);\n\tmosquitto_subscribe_v5_callback_set(mosq, on_subscribe_v5);\n\tmosquitto_message_v5_callback_set(mosq, on_message_v5);\n\n\trc = mosquitto_connect_bind_v5(mosq, \"localhost\", port, 60, NULL, NULL);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\tdo_loop(mosq);\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn 1;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-loop-start.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect_v5(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe_v5(mosq, NULL, \"loop/test\", 0, 0, NULL);\n\t}\n}\n\n\nstatic void on_disconnect_v5(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)properties;\n\n\trun = rc;\n}\n\n\nstatic void on_subscribe_v5(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props)\n{\n\t(void)obj;\n\t(void)mid;\n\t(void)qos_count;\n\t(void)granted_qos;\n\t(void)props;\n\n\tmosquitto_publish_v5(mosq, NULL, \"loop/test\", strlen(\"message\"), \"message\", 0, false, NULL);\n}\n\n\nstatic void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)msg;\n\t(void)properties;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"loop-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosquitto_connect_v5_callback_set(mosq, on_connect_v5);\n\tmosquitto_disconnect_v5_callback_set(mosq, on_disconnect_v5);\n\tmosquitto_subscribe_v5_callback_set(mosq, on_subscribe_v5);\n\tmosquitto_message_v5_callback_set(mosq, on_message_v5);\n\n\trc = mosquitto_connect_bind_v5(mosq, \"localhost\", port, 60, NULL, NULL);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\tmosquitto_loop_start(mosq);\n\tstruct timespec tv = { 0, 50e6 };\n\twhile(run == -1){\n\t\tnanosleep(&tv, NULL);\n\t}\n\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn 1;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-loop.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect_v5(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe_v5(mosq, NULL, \"loop/test\", 0, 0, NULL);\n\t}\n}\n\n\nstatic void on_disconnect_v5(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)properties;\n\n\trun = rc;\n}\n\n\nstatic void on_subscribe_v5(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props)\n{\n\t(void)obj;\n\t(void)mid;\n\t(void)qos_count;\n\t(void)granted_qos;\n\t(void)props;\n\n\tmosquitto_publish_v5(mosq, NULL, \"loop/test\", strlen(\"message\"), \"message\", 0, false, NULL);\n}\n\n\nstatic void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\t(void)obj;\n\t(void)msg;\n\t(void)properties;\n\n\tmosquitto_disconnect(mosq);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"loop-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosquitto_connect_v5_callback_set(mosq, on_connect_v5);\n\tmosquitto_disconnect_v5_callback_set(mosq, on_disconnect_v5);\n\tmosquitto_subscribe_v5_callback_set(mosq, on_subscribe_v5);\n\tmosquitto_message_v5_callback_set(mosq, on_message_v5);\n\n\trc = mosquitto_connect_bind_v5(mosq, \"localhost\", port, 60, NULL, NULL);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, 300, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn 1;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-qos0-no-payload.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_publish(mosq, &sent_mid, \"pub/qos0/no-payload/test\", 0, NULL, 0, false);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\n\tif(mid == sent_mid){\n\t\tmosquitto_disconnect(mosq);\n\t\trun = 0;\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos0-test-np\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-publish-qos0.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int sent_mid = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_publish(mosq, &sent_mid, \"pub/qos0/test\", strlen(\"message\"), \"message\", 0, false);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\n\tif(mid == sent_mid){\n\t\tmosquitto_disconnect(mosq);\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos0-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\trc = mosquitto_loop_forever(mosq, -1, 1);\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn rc;\n}\n"
  },
  {
    "path": "test/lib/c/03-request-response-1.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n\n#define QOS 0\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe(mosq, NULL, \"response/topic\", QOS);\n\t}\n}\n\n\nstatic void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\tmosquitto_property *props = NULL;\n\n\t(void)obj;\n\t(void)mid;\n\tif(qos_count != 1 || granted_qos[0] != QOS){\n\t\tabort();\n\t}\n\n\tif(mosquitto_property_add_string(&props, MQTT_PROP_RESPONSE_TOPIC, \"response/topic\")){\n\t\tabort();\n\t}\n\tmosquitto_publish_v5(mosq, NULL, \"request/topic\", 6, \"action\", 0, 0, props);\n\tmosquitto_property_free_all(&props);\n}\n\n\nstatic void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(!strcmp(msg->payload, \"a response\")){\n\t\trun = 0;\n\t}else{\n\t\trun = 1;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint ver = PROTOCOL_VERSION_v5;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"request-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &ver);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe);\n\tmosquitto_message_callback_set(mosq, on_message);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-request-response-2.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe(mosq, NULL, \"request/topic\", 0);\n\t}\n}\n\n\nstatic void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *props)\n{\n\tconst mosquitto_property *p_resp, *p_corr = NULL;\n\tchar *resp_topic = NULL;\n\tint rc;\n\n\t(void)obj;\n\n\tif(!strcmp(msg->topic, \"request/topic\")){\n\t\tp_resp = mosquitto_property_read_string(props, MQTT_PROP_RESPONSE_TOPIC, &resp_topic, false);\n\t\tif(p_resp){\n\t\t\tp_corr = mosquitto_property_read_binary(props, MQTT_PROP_CORRELATION_DATA, NULL, NULL, false);\n\t\t\trc = mosquitto_publish_v5(mosq, NULL, resp_topic, strlen(\"a response\"), \"a response\", 0, false, p_corr);\n\t\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\t\tabort();\n\t\t\t}\n\t\t\tfree(resp_topic);\n\t\t}\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)mid;\n\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint ver = PROTOCOL_VERSION_v5;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"response-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &ver);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\tmosquitto_message_v5_callback_set(mosq, on_message_v5);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/03-request-response-correlation-1.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n\n#define QOS 0\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_subscribe(mosq, NULL, \"response/topic\", QOS);\n\t}\n}\n\n\nstatic void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\tmosquitto_property *props = NULL;\n\tint rc;\n\n\t(void)obj;\n\t(void)mid;\n\n\tif(qos_count != 1 || granted_qos[0] != QOS){\n\t\tabort();\n\t}\n\n\tif(mosquitto_property_add_string(&props, MQTT_PROP_RESPONSE_TOPIC, \"response/topic\")\n\t\t\t|| mosquitto_property_add_binary(&props, MQTT_PROP_CORRELATION_DATA, \"corridor\", 8)){\n\t\tabort();\n\t}\n\trc = mosquitto_publish_v5(mosq, NULL, \"request/topic\", 6, \"action\", QOS, 0, props);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tabort();\n\t}\n\tmosquitto_property_free_all(&props);\n}\n\n\nstatic void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(!strcmp(msg->payload, \"a response\")){\n\t\trun = 0;\n\t}else{\n\t\trun = 1;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint ver = PROTOCOL_VERSION_v5;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"request-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &ver);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe);\n\tmosquitto_message_callback_set(mosq, on_message);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/04-retain-qos0.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\n#define UNUSED(A) (void)(A)\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\tUNUSED(obj);\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_publish(mosq, NULL, \"retain/qos0/test\", strlen(\"retained message\"), \"retained message\", 0, true);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(mid);\n\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"retain-qos0-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/08-ssl-bad-cacert.c",
    "content": "#include <errno.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\n\nint main(int argc, char *argv[])\n{\n\tint rc = 1;\n\tstruct mosquitto *mosq;\n\n\t(void)argc;\n\t(void)argv;\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"08-ssl-bad-cacert\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tif(mosquitto_tls_set(mosq, \"this/file/doesnt/exist\", NULL, NULL, NULL, NULL) == MOSQ_ERR_INVAL){\n\t\trc = 0;\n\t}\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn rc;\n}\n"
  },
  {
    "path": "test/lib/c/08-ssl-connect-cert-auth-custom-ssl-ctx-default.c",
    "content": "#include <assert.h>\n#include <errno.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n#include <openssl/ssl.h>\n#include \"path_helper.h\"\n\nstatic int run = -1;\n\n\nvoid handle_sigint(int signal)\n{\n\t(void)signal;\n\n\trun = 0;\n}\n\n\nvoid on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nvoid on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tstruct mosquitto *mosq;\n\tSSL_CTX *ssl_ctx;\n\tassert(argc == 2);\n\tint port = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tOPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \\\n\t\t\t| OPENSSL_INIT_ADD_ALL_DIGESTS \\\n\t\t\t| OPENSSL_INIT_LOAD_CONFIG, NULL);\n\tssl_ctx = SSL_CTX_new(TLS_client_method());\n\n\tmosq = mosquitto_new(\"08-ssl-connect-crt-auth\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\n\tmosquitto_int_option(mosq, MOSQ_OPT_SSL_CTX_WITH_DEFAULTS, 1);\n\tmosquitto_void_option(mosq, MOSQ_OPT_SSL_CTX, ssl_ctx);\n\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tchar capath[4096];\n\tcat_sourcedir_with_relpath(capath, \"/../../ssl/certs\");\n\tchar certfile[4096];\n\tcat_sourcedir_with_relpath(certfile, \"/../../ssl/client.crt\");\n\tchar keyfile[4096];\n\tcat_sourcedir_with_relpath(keyfile, \"/../../ssl/client.key\");\n\n\tmosquitto_tls_set(mosq, cafile, capath, certfile, keyfile, NULL);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\tint rc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tsignal(SIGINT, handle_sigint);\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tSSL_CTX_free(ssl_ctx);\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/08-ssl-connect-cert-auth-custom-ssl-ctx.c",
    "content": "#include <assert.h>\n#include <errno.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n#include <openssl/ssl.h>\n#include \"path_helper.h\"\n\nstatic int run = -1;\n\n\nvoid handle_sigint(int signal)\n{\n\t(void)signal;\n\n\trun = 0;\n}\n\n\nvoid on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nvoid on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tstruct mosquitto *mosq;\n\tSSL_CTX *ssl_ctx;\n\tassert(argc == 2);\n\tint port = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tOPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \\\n\t\t\t| OPENSSL_INIT_ADD_ALL_DIGESTS \\\n\t\t\t| OPENSSL_INIT_LOAD_CONFIG, NULL);\n\tssl_ctx = SSL_CTX_new(TLS_client_method());\n\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tchar capath[4096];\n\tcat_sourcedir_with_relpath(capath, \"/../../ssl/certs\");\n\tchar certfile[4096];\n\tcat_sourcedir_with_relpath(certfile, \"/../../ssl/client.crt\");\n\tchar keyfile[4096];\n\tcat_sourcedir_with_relpath(keyfile, \"/../../ssl/client.key\");\n\n\tSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);\n\tSSL_CTX_use_certificate_chain_file(ssl_ctx, certfile);\n\tSSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile, SSL_FILETYPE_PEM);\n\tSSL_CTX_load_verify_locations(ssl_ctx, cafile, capath);\n\n\tmosq = mosquitto_new(\"08-ssl-connect-crt-auth\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_tls_set(mosq, \"../ssl/test-root-ca.crt\", \"../ssl/certs\", \"../ssl/client.crt\", \"../ssl/client.key\", NULL);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\tmosquitto_int_option(mosq, MOSQ_OPT_SSL_CTX_WITH_DEFAULTS, 0);\n\tmosquitto_void_option(mosq, MOSQ_OPT_SSL_CTX, ssl_ctx);\n\n\tint rc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc){\n\t\treturn rc;\n\t}\n\n\tsignal(SIGINT, handle_sigint);\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tSSL_CTX_free(ssl_ctx);\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/08-ssl-connect-cert-auth-enc.c",
    "content": "#include <errno.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\n#include \"path_helper.h\"\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nstatic int password_callback(char *buf, int size, int rwflag, void *userdata)\n{\n\t(void)rwflag;\n\t(void)userdata;\n\n\tstrncpy(buf, \"password\", (size_t)size);\n\tbuf[size-1] = '\\0';\n\n\treturn (int)strlen(buf);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"08-ssl-connect-crt-auth-enc\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tchar capath[4096];\n\tcat_sourcedir_with_relpath(capath, \"/../../ssl/certs\");\n\tchar certfile[4096];\n\tcat_sourcedir_with_relpath(certfile, \"/../../ssl/client-encrypted.crt\");\n\tchar keyfile[4096];\n\tcat_sourcedir_with_relpath(keyfile, \"/../../ssl/client-encrypted.key\");\n\tmosquitto_tls_set(mosq, cafile, capath, certfile, keyfile, password_callback);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/08-ssl-connect-cert-auth.c",
    "content": "#include \"path_helper.h\"\n#include <errno.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"08-ssl-connect-crt-auth\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tchar capath[4096];\n\tcat_sourcedir_with_relpath(capath, \"/../../ssl/certs\");\n\tchar certfile[4096];\n\tcat_sourcedir_with_relpath(certfile, \"/../../ssl/client.crt\");\n\tchar keyfile[4096];\n\tcat_sourcedir_with_relpath(keyfile, \"/../../ssl/client.key\");\n\tmosquitto_tls_set(mosq, cafile, capath, certfile, keyfile, NULL);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/08-ssl-connect-no-auth.c",
    "content": "#include \"path_helper.h\"\n#include <errno.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"08-ssl-connect-no-auth\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tmosquitto_tls_set(mosq, cafile, NULL, NULL, NULL, NULL);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/08-ssl-connect-san.c",
    "content": "#include \"path_helper.h\"\n#include <errno.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_disconnect(mosq);\n\t}\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\tchar *host;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\thost = argv[2];\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"08-ssl-connect-san\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tmosquitto_tls_set(mosq, cafile, NULL, NULL, NULL, NULL);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\n\trc = mosquitto_connect(mosq, host, port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/08-ssl-fake-cacert.c",
    "content": "#include <errno.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <mosquitto.h>\n\n#include \"path_helper.h\"\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\t(void)rc;\n\n\texit(1);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"08-ssl-connect-crt-auth\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-fake-root-ca.crt\");\n\tchar certfile[4096];\n\tcat_sourcedir_with_relpath(certfile, \"/../../ssl/client.crt\");\n\tchar keyfile[4096];\n\tcat_sourcedir_with_relpath(keyfile, \"/../../ssl/client.key\");\n\tmosquitto_tls_set(mosq, cafile, NULL, certfile, keyfile, NULL);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\trc = mosquitto_loop_forever(mosq, -1, 1);\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\tif(rc == MOSQ_ERR_ERRNO && errno == EPROTO){\n\t\treturn 0;\n\t}else{\n\t\treturn 1;\n\t}\n}\n\n"
  },
  {
    "path": "test/lib/c/09-util-topic-tokenise.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <mosquitto.h>\n\n\nstatic void print_error(const char *topic, char **topics, int topic_count)\n{\n\tint i;\n\tprintf(\"TOPIC: %s\\n\", topic);\n\tprintf(\"TOKENS: \");\n\tfor(i=0; i<topic_count; i++){\n\t\tprintf(\"%s\", topics[i]);\n\t\tif(i+1<topic_count){\n\t\t\tprintf(\"/\");\n\t\t}\n\t}\n\tprintf(\"\\n\");\n}\n\n\nint main(int argc, char *argv[])\n{\n\tchar **topics;\n\tint topic_count;\n\n\t(void)argc;\n\t(void)argv;\n\n\tif(mosquitto_sub_topic_tokenise(\"topic\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 1 || strcmp(topics[0], \"topic\")){\n\t\tprint_error(\"topic\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosquitto_sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosquitto_sub_topic_tokenise(\"a/deep/topic/hierarchy\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 4\n\t\t\t|| strcmp(topics[0], \"a\")\n\t\t\t|| strcmp(topics[1], \"deep\")\n\t\t\t|| strcmp(topics[2], \"topic\")\n\t\t\t|| strcmp(topics[3], \"hierarchy\")){\n\t\tprint_error(\"a/deep/topic/hierarchy\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosquitto_sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosquitto_sub_topic_tokenise(\"/a/deep/topic/hierarchy\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 5\n\t\t\t|| topics[0]\n\t\t\t|| strcmp(topics[1], \"a\")\n\t\t\t|| strcmp(topics[2], \"deep\")\n\t\t\t|| strcmp(topics[3], \"topic\")\n\t\t\t|| strcmp(topics[4], \"hierarchy\")){\n\t\tprint_error(\"/a/deep/topic/hierarchy\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosquitto_sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosquitto_sub_topic_tokenise(\"a/b/c\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 3\n\t\t\t|| strcmp(topics[0], \"a\")\n\t\t\t|| strcmp(topics[1], \"b\")\n\t\t\t|| strcmp(topics[2], \"c\")){\n\t\tprint_error(\"a/b/c\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosquitto_sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosquitto_sub_topic_tokenise(\"/a/b/c\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 4\n\t\t\t|| topics[0]\n\t\t\t|| strcmp(topics[1], \"a\")\n\t\t\t|| strcmp(topics[2], \"b\")\n\t\t\t|| strcmp(topics[3], \"c\")){\n\t\tprint_error(\"/a/b/c\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosquitto_sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosquitto_sub_topic_tokenise(\"a///hierarchy\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 4\n\t\t\t|| strcmp(topics[0], \"a\")\n\t\t\t|| topics[1]\n\t\t\t|| topics[2]\n\t\t\t|| strcmp(topics[3], \"hierarchy\")){\n\t\tprint_error(\"a///hierarchy\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosquitto_sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosquitto_sub_topic_tokenise(\"/a///hierarchy\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 5\n\t\t\t|| topics[0]\n\t\t\t|| strcmp(topics[1], \"a\")\n\t\t\t|| topics[2]\n\t\t\t|| topics[3]\n\t\t\t|| strcmp(topics[4], \"hierarchy\")){\n\t\tprint_error(\"/a///hierarchy\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosquitto_sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosquitto_sub_topic_tokenise(\"/a///hierarchy/\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 6\n\t\t\t|| topics[0]\n\t\t\t|| strcmp(topics[1], \"a\")\n\t\t\t|| topics[2]\n\t\t\t|| topics[3]\n\t\t\t|| strcmp(topics[4], \"hierarchy\")\n\t\t\t|| topics[3]){\n\t\tprint_error(\"/a///hierarchy/\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosquitto_sub_topic_tokens_free(&topics, topic_count);\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "test/lib/c/11-prop-oversize-packet.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc = mosquitto_subscribe(mosq, NULL, \"0123456789012345678901234567890\", 0);\n\t\tif(rc != MOSQ_ERR_OVERSIZE_PACKET){\n\t\t\tprintf(\"Fail on subscribe\\n\");\n\t\t\texit(1);\n\t\t}\n\n\t\trc = mosquitto_unsubscribe(mosq, NULL, \"0123456789012345678901234567890\");\n\t\tif(rc != MOSQ_ERR_OVERSIZE_PACKET){\n\t\t\tprintf(\"Fail on unsubscribe\\n\");\n\t\t\texit(1);\n\t\t}\n\n\t\trc = mosquitto_publish(mosq, &sent_mid, \"pub/test\", strlen(\"123456789012345678\"), \"123456789012345678\", 0, false);\n\t\tif(rc != MOSQ_ERR_OVERSIZE_PACKET){\n\t\t\tprintf(\"Fail on publish 1\\n\");\n\t\t\texit(1);\n\t\t}\n\t\trc = mosquitto_publish(mosq, &sent_mid, \"pub/test\", strlen(\"12345678901234567\"), \"12345678901234567\", 0, false);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tprintf(\"Fail on publish 2\\n\");\n\t\t\texit(1);\n\t\t}\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\n\tif(mid == sent_mid){\n\t\tmosquitto_disconnect(mosq);\n\t\trun = 0;\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"publish-qos0-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, -1, 1);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/11-prop-recv.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n\nstatic int run = -1;\nstatic int qos = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\t(void)mosq;\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nstatic void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\tint rc;\n\tchar *str;\n\n\t(void)obj;\n\n\tif(properties){\n\t\tif(mosquitto_property_read_string(properties, MQTT_PROP_CONTENT_TYPE, &str, false)){\n\t\t\trc = strcmp(str, \"plain/text\");\n\t\t\tfree(str);\n\n\t\t\tif(rc == 0){\n\t\t\t\tif(mosquitto_property_read_string(properties, MQTT_PROP_RESPONSE_TOPIC, &str, false)){\n\t\t\t\t\trc = strcmp(str, \"msg/123\");\n\t\t\t\t\tfree(str);\n\n\t\t\t\t\tif(rc == 0){\n\t\t\t\t\t\tif(msg->qos == qos){\n\t\t\t\t\t\t\tmosquitto_publish(mosq, NULL, \"ok\", 2, \"ok\", 0, 0);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/* No matching message, so quit with an error */\n\texit(1);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\tqos = atoi(argv[2]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"prop-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_message_v5_callback_set(mosq, on_message_v5);\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, -1, 1);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/11-prop-send-content-type.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\tint rc2;\n\tmosquitto_property *proplist = NULL;\n\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc2 = mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, \"application/json\");\n\t\tif(rc2 != MOSQ_ERR_SUCCESS){\n\t\t\tabort();\n\t\t}\n\t\tmosquitto_publish_v5(mosq, &sent_mid, \"prop/qos0\", strlen(\"message\"), \"message\", 0, false, proplist);\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\n\tif(mid == sent_mid){\n\t\tmosquitto_disconnect(mosq);\n\t\trun = 0;\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tint tmp;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"prop-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\ttmp = MQTT_PROTOCOL_V5;\n\tmosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &tmp);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, -1, 1);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/11-prop-send-payload-format.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <mosquitto.h>\n#include <mosquitto/mqtt_protocol.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\tint rc2;\n\tmosquitto_property *proplist = NULL;\n\n\t(void)obj;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc2 = mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n\t\tif(rc2 != MOSQ_ERR_SUCCESS){\n\t\t\tabort();\n\t\t}\n\t\tmosquitto_publish_v5(mosq, &sent_mid, \"prop/qos0\", strlen(\"message\"), \"message\", 0, false, proplist);\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\t(void)obj;\n\n\tif(mid == sent_mid){\n\t\tmosquitto_disconnect(mosq);\n\t\trun = 0;\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tint tmp;\n\tstruct mosquitto *mosq;\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"prop-test\", true, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\ttmp = MQTT_PROTOCOL_V5;\n\tmosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &tmp);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\trc = mosquitto_loop(mosq, -1, 1);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/c/CMakeLists.txt",
    "content": "set(BINARIES\n    01-con-discon-success\n    01-con-discon-success-v5\n    01-con-discon-will\n    01-con-discon-will-clear\n    01-con-discon-will-v5\n\t01-extended-auth-continue\n\t01-extended-auth-failure\n    01-keepalive-pingreq\n    01-no-clean-session\n    01-pre-connect-callback\n    01-server-keepalive-pingreq\n    01-unpwd-set\n    01-will-set\n    01-will-unpwd-set\n    02-subscribe-helper-callback-qos2\n    02-subscribe-helper-simple-qos2\n    02-subscribe-qos0\n    02-subscribe-qos1-async1\n    02-subscribe-qos1-async2\n    02-subscribe-qos1\n    02-subscribe-qos2\n    02-unsubscribe2-v5\n    02-unsubscribe\n    02-unsubscribe-multiple-v5\n    02-unsubscribe-v5\n    03-publish-b2c-qos1\n    03-publish-b2c-qos1-unexpected-puback\n    03-publish-b2c-qos2\n    03-publish-b2c-qos2-len\n    03-publish-b2c-qos2-unexpected-pubcomp\n    03-publish-b2c-qos2-unexpected-pubrel\n    03-publish-c2b-qos1-disconnect\n    03-publish-c2b-qos1-len\n    03-publish-c2b-qos1-receive-maximum\n    03-publish-c2b-qos2\n    03-publish-c2b-qos2-disconnect\n    03-publish-c2b-qos2-len\n    03-publish-c2b-qos2-maximum-qos-0\n    03-publish-c2b-qos2-maximum-qos-1\n    03-publish-c2b-qos2-pubrec-error\n    03-publish-c2b-qos2-receive-maximum\n\t03-publish-loop\n\t03-publish-loop-forever\n\t03-publish-loop-manual\n\t03-publish-loop-start\n    03-publish-qos0\n    03-publish-qos0-no-payload\n    03-request-response-1\n    03-request-response-2\n    03-request-response-correlation-1\n    04-retain-qos0\n    08-ssl-bad-cacert\n    08-ssl-connect-cert-auth\n    08-ssl-connect-cert-auth-custom-ssl-ctx\n    08-ssl-connect-cert-auth-custom-ssl-ctx-default\n    08-ssl-connect-cert-auth-enc\n    08-ssl-connect-no-auth\n    08-ssl-connect-san\n    08-ssl-fake-cacert\n    09-util-topic-tokenise\n    11-prop-oversize-packet\n    11-prop-recv\n    11-prop-send-content-type\n    11-prop-send-payload-format\n    fuzzish\n)\n\nforeach(BINARY ${BINARIES})\n    add_executable(${BINARY} ${BINARY}.c)\n    target_compile_definitions(${BINARY} PRIVATE TEST_SOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\")\n    target_include_directories(${BINARY} PRIVATE ${CMAKE_SOURCE_DIR}/test)\n    set_property(TARGET ${BINARY} PROPERTY SUFFIX .test)\n    target_link_libraries(${BINARY} PRIVATE common-options libmosquitto OpenSSL::SSL)\nendforeach()\n"
  },
  {
    "path": "test/lib/c/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all clean reallyclean\n\nLOCAL_CPPFLAGS+=-I${R}/test\nLOCAL_CFLAGS+=-Werror -ggdb\nLOCAL_LIBADD+=${LIBMOSQ}\n\nSRC = \\\n\t01-con-discon-success.c \\\n\t01-con-discon-success-v5.c \\\n\t01-con-discon-will.c \\\n\t01-con-discon-will-v5.c \\\n\t01-con-discon-will-clear.c \\\n\t01-extended-auth-continue.c \\\n\t01-extended-auth-failure.c \\\n\t01-keepalive-pingreq.c \\\n\t01-no-clean-session.c \\\n\t01-pre-connect-callback.c \\\n\t01-server-keepalive-pingreq.c \\\n\t01-unpwd-set.c \\\n\t01-will-set.c \\\n\t01-will-unpwd-set.c \\\n\t02-subscribe-helper-callback-qos2.c \\\n\t02-subscribe-helper-simple-qos2.c \\\n\t02-subscribe-qos0.c \\\n\t02-subscribe-qos1-async1.c \\\n\t02-subscribe-qos1-async2.c \\\n\t02-subscribe-qos1.c \\\n\t02-subscribe-qos2.c \\\n\t02-unsubscribe-multiple-v5.c \\\n\t02-unsubscribe-v5.c \\\n\t02-unsubscribe2-v5.c \\\n\t02-unsubscribe.c \\\n\t03-publish-b2c-qos1-unexpected-puback.c \\\n\t03-publish-b2c-qos1.c \\\n\t03-publish-b2c-qos2-len.c \\\n\t03-publish-b2c-qos2-unexpected-pubrel.c \\\n\t03-publish-b2c-qos2-unexpected-pubcomp.c \\\n\t03-publish-b2c-qos2.c \\\n\t03-publish-c2b-qos1-disconnect.c \\\n\t03-publish-c2b-qos1-len.c \\\n\t03-publish-c2b-qos1-receive-maximum.c \\\n\t03-publish-c2b-qos2-disconnect.c \\\n\t03-publish-c2b-qos2-len.c \\\n\t03-publish-c2b-qos2-maximum-qos-0.c \\\n\t03-publish-c2b-qos2-maximum-qos-1.c \\\n\t03-publish-c2b-qos2-pubrec-error.c \\\n\t03-publish-c2b-qos2-receive-maximum.c \\\n\t03-publish-c2b-qos2.c \\\n\t03-publish-loop.c \\\n\t03-publish-loop-forever.c \\\n\t03-publish-loop-manual.c \\\n\t03-publish-loop-start.c \\\n\t03-publish-qos0-no-payload.c \\\n\t03-publish-qos0.c \\\n\t03-request-response-1.c \\\n\t03-request-response-2.c \\\n\t03-request-response-correlation-1.c \\\n\t04-retain-qos0.c \\\n\t08-ssl-bad-cacert.c \\\n\t08-ssl-connect-cert-auth-enc.c \\\n\t08-ssl-connect-cert-auth.c \\\n\t08-ssl-connect-no-auth.c \\\n\t08-ssl-connect-san.c \\\n\t08-ssl-fake-cacert.c \\\n\t09-util-topic-tokenise.c \\\n\t11-prop-oversize-packet.c \\\n\t11-prop-recv.c \\\n\t11-prop-send-payload-format.c \\\n\t11-prop-send-content-type.c \\\n\tfuzzish.c\n\nifeq ($(WITH_TLS),yes)\nSRC += \\\n\t08-ssl-connect-cert-auth-custom-ssl-ctx.c \\\n\t08-ssl-connect-cert-auth-custom-ssl-ctx-default.c\nLOCAL_LIBADD+=-lssl -lcrypto\nendif\n\nTESTS = ${SRC:.c=.test}\n\nall : ${TESTS}\n\n${TESTS} : %.test: %.c ${R}/test/path_helper.h\n\t$(CC) $< -o $@ -D TEST_SOURCE_DIR='\"$(realpath .)\"' $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) $(LOCAL_LIBADD) $(LOCAL_LDFLAGS)\n\nreallyclean : clean\n\t-rm -f *.orig\n\nclean :\n\trm -f *.test\n"
  },
  {
    "path": "test/lib/c/fuzzish.c",
    "content": "#include <signal.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include <mosquitto.h>\n\n#define UNUSED(A) (void)(A)\n\nstatic int run = -1;\nstatic int proto_ver;\n\n#ifndef UNUSED\n#  define UNUSED(A) (void)(A)\n#endif\n\n\nstatic void signal_handler(int s)\n{\n\tUNUSED(s);\n\trun = 0;\n}\n\n\nstatic void prop_test(const mosquitto_property *props)\n{\n\tmosquitto_property *dest = NULL;\n\tint rc;\n\n\trc = mosquitto_property_copy_all(&dest, props);\n\tif(rc){\n\t\tprintf(\"bad prop_test: %s\\n\", mosquitto_strerror(rc));\n\t\texit(1);\n\t}\n\tmosquitto_property_free_all(&dest);\n}\n\n\nstatic void msg_test(const struct mosquitto_message *msg)\n{\n\tstruct mosquitto_message dest;\n\tint rc;\n\n\tmemset(&dest, 0, sizeof(dest));\n\trc = mosquitto_message_copy(&dest, msg);\n\tif(rc){\n\t\tprintf(\"bad msg_test: %s\\n\", mosquitto_strerror(rc));\n\t\texit(1);\n\t}\n\tmosquitto_message_free_contents(&dest);\n}\n\n\nstatic void on_pre_connect(struct mosquitto *mosq, void *obj)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n}\n\n\nstatic void on_connect(struct mosquitto *mosq, void *obj, int rc)\n{\n\tchar *command = obj;\n\tint mid;\n\tmosquitto_property *props = NULL;\n\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\n\tif(command){\n\t\tif(proto_ver == 5){\n\t\t\tif(!strncmp(command, \"subscribe\", strlen(\"subscribe\"))){\n\t\t\t\trc = mosquitto_property_add_varint(&props, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 268435455);\n\t\t\t\tif(rc){\n\t\t\t\t\tprintf(\"bad on_connect prop add: %s\\n\", mosquitto_strerror(rc));\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\trc = mosquitto_property_add_string_pair(&props, MQTT_PROP_USER_PROPERTY, \"key\", \"value\");\n\t\t\t\tif(rc){\n\t\t\t\t\tprintf(\"bad on_connect prop add: %s\\n\", mosquitto_strerror(rc));\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}else if(!strncmp(command, \"unsubscribe\", strlen(\"unsubscribe\"))){\n\t\t\t\trc = mosquitto_property_add_string_pair(&props, MQTT_PROP_USER_PROPERTY, \"key\", \"value\");\n\t\t\t\tif(rc){\n\t\t\t\t\tprintf(\"bad on_connect prop add: %s\\n\", mosquitto_strerror(rc));\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}else if(!strncmp(command, \"publish\", strlen(\"publish\"))){\n\t\t\t\trc = mosquitto_property_add_byte(&props, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n\t\t\t\tif(rc){\n\t\t\t\t\tprintf(\"bad on_connect prop add: %s\\n\", mosquitto_strerror(rc));\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\trc = mosquitto_property_add_int32(&props, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, UINT32_MAX);\n\t\t\t\tif(rc){\n\t\t\t\t\tprintf(\"bad on_connect prop add: %s\\n\", mosquitto_strerror(rc));\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\trc = mosquitto_property_add_int16(&props, MQTT_PROP_TOPIC_ALIAS, UINT16_MAX);\n\t\t\t\tif(rc){\n\t\t\t\t\tprintf(\"bad on_connect prop add: %s\\n\", mosquitto_strerror(rc));\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\trc = mosquitto_property_add_string(&props, MQTT_PROP_RESPONSE_TOPIC, \"response/topic\");\n\t\t\t\tif(rc){\n\t\t\t\t\tprintf(\"bad on_connect prop add: %s\\n\", mosquitto_strerror(rc));\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\trc = mosquitto_property_add_binary(&props, MQTT_PROP_CORRELATION_DATA, \"7deac5c5-8802-44ff-86ce-11479f337419\", strlen(\"7deac5c5-8802-44ff-86ce-11479f337419\"));\n\t\t\t\tif(rc){\n\t\t\t\t\tprintf(\"bad on_connect prop add: %s\\n\", mosquitto_strerror(rc));\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\trc = mosquitto_property_add_string(&props, MQTT_PROP_CONTENT_TYPE, \"text/plain\");\n\t\t\t\tif(rc){\n\t\t\t\t\tprintf(\"bad on_connect prop add: %s\\n\", mosquitto_strerror(rc));\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t\trc = mosquitto_property_add_string_pair(&props, MQTT_PROP_USER_PROPERTY, \"key\", \"value\");\n\t\t\t\tif(rc){\n\t\t\t\t\tprintf(\"bad on_connect prop add: %s\\n\", mosquitto_strerror(rc));\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif(!strcmp(command, \"subscribe-2\")){\n\t\t\trc = mosquitto_subscribe_v5(mosq, &mid, \"test/subscribe\", 2, 0, props);\n\t\t\tif(rc || mid != 1){\n\t\t\t\tprintf(\"bad on_connect subscribe-2 %d || %d\\n\", rc, mid);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else if(!strcmp(command, \"subscribe-1\")){\n\t\t\trc = mosquitto_subscribe_v5(mosq, &mid, \"test/subscribe\", 1, 0, props);\n\t\t\tif(rc || mid != 1){\n\t\t\t\tprintf(\"bad on_connect subscribe-1 %d || %d\\n\", rc, mid);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else if(!strcmp(command, \"subscribe-0\")){\n\t\t\trc = mosquitto_subscribe_v5(mosq, &mid, \"test/subscribe\", 0, 0, props);\n\t\t\tif(rc || mid != 1){\n\t\t\t\tprintf(\"bad on_connect subscribe-0 %d || %d\\n\", rc, mid);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else if(!strcmp(command, \"subscribe-multiple\")){\n\t\t\tchar *subs[] = {\"test/subscribe1\", \"test/subscribe2\"};\n\t\t\trc = mosquitto_subscribe_multiple(mosq, &mid, 2, subs, 2, 0, props);\n\t\t\tif(rc || mid != 1){\n\t\t\t\tprintf(\"bad on_connect subscribe-multiple %d || %d\\n\", rc, mid);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else if(!strcmp(command, \"unsubscribe\")){\n\t\t\trc = mosquitto_unsubscribe_v5(mosq, &mid, \"test/subscribe\", props);\n\t\t\tif(rc || mid != 1){\n\t\t\t\tprintf(\"bad on_connect unsubscribe %d || %d\\n\", rc, mid);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else if(!strcmp(command, \"unsubscribe-multiple\")){\n\t\t\tchar *subs[] = {\"test/subscribe1\", \"test/subscribe2\"};\n\t\t\trc = mosquitto_unsubscribe_multiple(mosq, &mid, 2, subs, props);\n\t\t\tif(rc || mid != 1){\n\t\t\t\tprintf(\"bad on_connect unsubscribe-multiple %d || %d\\n\", rc, mid);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else if(!strcmp(command, \"publish-2\")){\n\t\t\trc = mosquitto_publish_v5(mosq, &mid, \"test/publish\", strlen(\"message\"), \"message\", 2, 0, props);\n\t\t\tif(rc || mid != 1){\n\t\t\t\tprintf(\"bad on_connect publish-2 %d || %d\\n\", rc, mid);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else if(!strcmp(command, \"publish-1\")){\n\t\t\trc = mosquitto_publish_v5(mosq, &mid, \"test/publish\", strlen(\"message\"), \"message\", 1, 0, props);\n\t\t\tif(rc || mid != 1){\n\t\t\t\tprintf(\"bad on_connect publish-1 %d || %d\\n\", rc, mid);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else if(!strcmp(command, \"publish-0\")){\n\t\t\trc = mosquitto_publish_v5(mosq, &mid, \"test/publish\", strlen(\"message\"), \"message\", 0, 0, props);\n\t\t\tif(rc || mid != 1){\n\t\t\t\tprintf(\"bad on_connect publish-0 %d || %d\\n\", rc, mid);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}else{\n\t\t\tprintf(\"bad on_connect command '%s'\\n\", command);\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\tmosquitto_property_free_all(&props);\n\treturn;\n\nerror:\n\tmosquitto_property_free_all(&props);\n\texit(1);\n}\n\n\nstatic void on_connect_with_flags(struct mosquitto *mosq, void *obj, int rc, int flags)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(rc);\n\tUNUSED(flags);\n}\n\n\nstatic void on_connect_v5(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(rc);\n\tUNUSED(flags);\n\n\tprop_test(props);\n}\n\n\nstatic void on_disconnect(struct mosquitto *mosq, void *obj, int rc)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\n\trun = rc;\n}\n\n\nstatic void on_disconnect_v5(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\n\tprop_test(props);\n\n\trun = rc;\n}\n\n\nstatic void on_publish(struct mosquitto *mosq, void *obj, int mid)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(mid);\n}\n\n\nstatic void on_publish_v5(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(mid);\n\tUNUSED(reason_code);\n\n\tprop_test(props);\n}\n\n\nstatic void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\n\tmsg_test(msg);\n}\n\n\nstatic void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\n\tmsg_test(msg);\n\tprop_test(props);\n}\n\n\nstatic void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\n\tint tot = 0;\n\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(mid);\n\n\tfor(int i=0; i<qos_count; i++){\n\t\ttot += granted_qos[i];\n\t}\n\n\t(void)tot;\n}\n\n\nstatic void on_subscribe_v5(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\n\tint tot = 0;\n\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(mid);\n\n\tfor(int i=0; i<qos_count; i++){\n\t\ttot += granted_qos[i];\n\t}\n\tprop_test(props);\n\n\t(void)tot;\n}\n\n\nstatic void on_unsubscribe(struct mosquitto *mosq, void *obj, int mid)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(mid);\n}\n\n\nstatic void on_unsubscribe_v5(struct mosquitto *mosq, void *obj, int mid, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(mid);\n\n\tprop_test(props);\n}\n\n\nstatic void on_unsubscribe2_v5(struct mosquitto *mosq, void *obj, int mid, int reason_code_count, const int *reason_codes, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(mid);\n\n\tint sum = 0;\n\tprop_test(props);\n\tfor(int i=0; i<reason_code_count; i++){\n\t\tsum += reason_codes[i];\n\t}\n\tif(sum < 0){\n\t\t/* This is a \"fake\" condition to stop the above check being optimised out */\n\t\texit(1);\n\t}\n}\n\n\nstatic void on_log(struct mosquitto *mosq, void *obj, int level, const char *str)\n{\n\tUNUSED(mosq);\n\tUNUSED(obj);\n\tUNUSED(level);\n\n\tif(str == NULL){\n\t\tprintf(\"bad on_log\\n\");\n\t\texit(1);\n\t}\n\tsize_t i = strlen(str);\n\tif(i == SIZE_MAX){\n\t\tprintf(\"too large on_log\\n\");\n\t\texit(1);\n\t}\n}\n\n\nstatic void setup_signal_handler(void)\n{\n\tstruct sigaction act = { 0 };\n\n\tact.sa_handler = &signal_handler;\n\tif(sigaction(SIGTERM, &act, NULL) < 0){\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint rc;\n\tstruct mosquitto *mosq;\n\tint port;\n\tbool clean_start;\n\tchar *command = NULL;\n\n\tif(argc < 4){\n\t\treturn 1;\n\t}\n\tsetup_signal_handler();\n\n\tport = atoi(argv[1]);\n\tproto_ver = atoi(argv[2]);\n\tclean_start = strcasecmp(argv[3], \"false\");\n\tif(argc == 5){\n\t\tcommand = argv[4];\n\t}\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(\"fuzzish\", clean_start, NULL);\n\tif(mosq == NULL){\n\t\treturn 1;\n\t}\n\tmosquitto_user_data_set(mosq, command);\n\n\tmosquitto_pre_connect_callback_set(mosq, on_pre_connect);\n\n\tmosquitto_connect_callback_set(mosq, on_connect);\n\tmosquitto_connect_with_flags_callback_set(mosq, on_connect_with_flags);\n\tmosquitto_connect_v5_callback_set(mosq, on_connect_v5);\n\n\tmosquitto_disconnect_callback_set(mosq, on_disconnect);\n\tmosquitto_disconnect_v5_callback_set(mosq, on_disconnect_v5);\n\n\tmosquitto_publish_callback_set(mosq, on_publish);\n\tmosquitto_publish_v5_callback_set(mosq, on_publish_v5);\n\n\tmosquitto_message_callback_set(mosq, on_message);\n\tmosquitto_message_v5_callback_set(mosq, on_message_v5);\n\n\tmosquitto_subscribe_callback_set(mosq, on_subscribe);\n\tmosquitto_subscribe_v5_callback_set(mosq, on_subscribe_v5);\n\n\tmosquitto_unsubscribe_callback_set(mosq, on_unsubscribe);\n\tmosquitto_unsubscribe_v5_callback_set(mosq, on_unsubscribe_v5);\n\tmosquitto_unsubscribe2_v5_callback_set(mosq, on_unsubscribe2_v5);\n\n\tmosquitto_log_callback_set(mosq, on_log);\n\n\tmosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, proto_ver);\n\n\trc = mosquitto_connect(mosq, \"localhost\", port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tprintf(\"bad connect\\n\");\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosquitto_loop(mosq, -1, 1);\n\t}\n\n\tmosquitto_destroy(mosq);\n\n\tmosquitto_lib_cleanup();\n\treturn 0;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-con-discon-success-v5.cpp",
    "content": "#include <cassert>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect_v5(int rc, int flags, const mosquitto_property *props);\n\tvoid on_disconnect_v5(int rc, const mosquitto_property *props);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect_v5(int rc, int flags, const mosquitto_property *props)\n{\n\tassert(flags == 0);\n\tassert(props);\n\tassert(mosqpp::property_check_all(CMD_CONNACK, props) == MOSQ_ERR_SUCCESS);\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect_v5(int rc, const mosquitto_property *props)\n{\n\tassert(props == NULL);\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tmosquitto_property *props = NULL;\n\tint rc;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-con-discon-success-v5\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, 5);\n\n\tmosquitto_property_add_int32(&props, MQTT_PROP_MAXIMUM_PACKET_SIZE, 1000);\n\trc = mosq->connect_v5(\"localhost\", port, 60, NULL, props);\n\tmosquitto_property_free_all(&props);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-con-discon-success.cpp",
    "content": "#include <cstdio>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-con-discon-success\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-con-discon-will-clear.cpp",
    "content": "#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-con-discon-will\");\n\n\t/* Set twice, so it has to clear the old settings */\n\tmosq->will_set(\"will/topic\", strlen(\"will-payload\"), \"will-payload\", 1, true);\n\tmosq->will_clear();\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-con-discon-will-v5.cpp",
    "content": "#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-con-discon-will\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tabort();\n\t}\n\t/* Set twice, so it has to clear the old settings */\n\tmosq->will_set_v5(\"will/topic\", strlen(\"will-payload\"), \"will-payload\", 1, true, proplist);\n\tproplist = NULL;\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tabort();\n\t}\n\tmosq->will_set_v5(\"will/topic\", strlen(\"will-payload\"), \"will-payload\", 1, true, proplist);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-con-discon-will.cpp",
    "content": "#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-con-discon-will\");\n\n\t/* Set twice, so it has to clear the old settings */\n\tmosq->will_set(\"will/topic\", strlen(\"will-payload\"), \"will-payload\", 1, true);\n\tmosq->will_set(\"will/topic\", strlen(\"will-payload\"), \"will-payload\", 1, true);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-extended-auth-continue.cpp",
    "content": "#include <cassert>\n#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect_v5(int rc, int flags, const mosquitto_property *props);\n\tvoid on_disconnect_v5(int rc, const mosquitto_property *props);\n\tint on_ext_auth(const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *props);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect_v5(int rc, int flags, const mosquitto_property *props)\n{\n\tassert(flags == 0);\n\tassert(props);\n\tassert(mosqpp::property_check_all(CMD_CONNACK, props) == MOSQ_ERR_SUCCESS);\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect_v5(int rc, const mosquitto_property *props)\n{\n\tassert(props == NULL);\n\trun = rc;\n}\n\n\nint mosquittopp_test::on_ext_auth(const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *props)\n{\n\tassert(props != NULL);\n\n\tif(strcmp(auth_method, \"test-method\")){\n\t\trun = 1;\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\tif(auth_data_len == 0 || (!auth_data || strcmp((const char *)auth_data, \"test-request\"))){\n\t\trun = 1;\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n\treturn ext_auth_continue(auth_method, strlen(\"test-reply\"), \"test-reply\", NULL);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tmosquitto_property *props = NULL;\n\tint rc;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-extended-auth\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, 5);\n\n\tmosquitto_property_add_int32(&props, MQTT_PROP_MAXIMUM_PACKET_SIZE, 1000);\n\trc = mosq->connect_v5(\"localhost\", port, 60, NULL, props);\n\tmosquitto_property_free_all(&props);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-extended-auth-failure.cpp",
    "content": "#include <cassert>\n#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect_v5(int rc, int flags, const mosquitto_property *props);\n\tvoid on_disconnect_v5(int rc, const mosquitto_property *props);\n\tint on_ext_auth(const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *props);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect_v5(int rc, int flags, const mosquitto_property *props)\n{\n\tassert(flags == 0);\n\tassert(props);\n\tassert(mosqpp::property_check_all(CMD_CONNACK, props) == MOSQ_ERR_SUCCESS);\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect_v5(int rc, const mosquitto_property *props)\n{\n\tassert(props == NULL);\n\trun = rc;\n}\n\n\nint mosquittopp_test::on_ext_auth(const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *props)\n{\n\t(void)auth_method;\n\t(void)auth_data_len;\n\t(void)auth_data;\n\t(void)props;\n\treturn MOSQ_ERR_AUTH;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tmosquitto_property *props = NULL;\n\tint rc;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-extended-auth\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, 5);\n\n\tmosquitto_property_add_int32(&props, MQTT_PROP_MAXIMUM_PACKET_SIZE, 1000);\n\trc = mosq->connect_v5(\"localhost\", port, 60, NULL, props);\n\tmosquitto_property_free_all(&props);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-keepalive-pingreq.cpp",
    "content": "#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\tint rc;\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-keepalive-pingreq\");\n\n\tmosq->connect(\"localhost\", port, 5);\n\n\twhile(run == -1){\n\t\trc = mosq->loop();\n\t\tif(rc){\n\t\t\tbreak;\n\t\t}\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-no-clean-session.cpp",
    "content": "#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id, bool clean_session);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id, bool clean_session) : mosqpp::mosquittopp(id, clean_session)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-no-clean-session\", false);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-pre-connect-callback.cpp",
    "content": "#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_pre_connect();\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_pre_connect()\n{\n\tusername_pw_set(\"uname\", \";'[08gn=#\");\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-pre-connect\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-server-keepalive-pingreq.cpp",
    "content": "#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\tint rc;\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-server-keepalive-pingreq\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\trc = mosq->loop();\n\t\tif(rc){\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-unpwd-set.cpp",
    "content": "#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-unpwd-set\");\n\tmosq->username_pw_set(\"uname\", \";'[08gn=#\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-will-set.cpp",
    "content": "#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-will-set\");\n\tmosq->will_set(\"topic/on/unexpected/disconnect\", strlen(\"will message\"), \"will message\", 1, true);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/01-will-unpwd-set.cpp",
    "content": "#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"01-will-unpwd-set\");\n\tmosq->username_pw_set(\"oibvvwqw\", \"#'^2hg9a&nm38*us\");\n\tmosq->will_set(\"will-topic\", strlen(\"will message\"), \"will message\", 2, false);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/02-subscribe-helper-callback-qos2.cpp",
    "content": "#include <cassert>\n#include <cstdlib>\n#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\n#define QOS 2\nstatic int mydata = 1;\n\n\nint cb(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg)\n{\n\tassert(mosq);\n\tassert(userdata == &mydata);\n\tassert(msg);\n\tassert(!strcmp(msg->topic, \"qos2/test\"));\n\treturn 1;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tint port;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosqpp::subscribe_callback(\n\t\t\tcb, &mydata, \"qos2/test\", QOS, \"localhost\", port,\n\t\t\t\"subscribe-qos2-test\", 60, true, NULL, NULL, NULL, NULL);\n\n\tmosqpp::lib_cleanup();\n\treturn 0;\n}\n"
  },
  {
    "path": "test/lib/cpp/02-subscribe-helper-simple-qos2.cpp",
    "content": "#include <cstdlib>\n#include <mosquitto/libmosquittopp.h>\n\n#define QOS 2\n\n\nint main(int argc, char *argv[])\n{\n\tint port;\n\tstruct mosquitto_message *messages;\n\n\tif(argc < 2){\n\t\treturn 1;\n\t}\n\tport = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosqpp::subscribe_simple(&messages, 1,\n\t\t\ttrue, \"qos2/test\", QOS, \"localhost\", port,\n\t\t\t\"subscribe-qos2-test\", 60, true, NULL, NULL, NULL, NULL);\n\n\t/* FIXME - this should be in the wrapper */\n\tmosquitto_message_free(&messages);\n\n\tmosqpp::lib_cleanup();\n\treturn 0;\n}\n"
  },
  {
    "path": "test/lib/cpp/02-subscribe-qos0.cpp",
    "content": "#include <cassert>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_subscribe(int mid, int qos_count, const int *granted_qos);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe(NULL, \"qos0/test\", 0);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_subscribe(int mid, int qos_count, const int *granted_qos)\n{\n\tassert(mid == 1);\n\tassert(qos_count == 1);\n\tassert(granted_qos[0] == 0);\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"subscribe-qos0-test\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/02-subscribe-qos1-async1.cpp",
    "content": "#include <cassert>\n#include <cstdio>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\n/* mosquitto_connect_async() test, with mosquitto_loop_start() called before mosquitto_connect_async(). */\n\n#define QOS 1\n\nstatic int run = -1;\nstatic bool should_run = true;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_subscribe(int mid, int qos_count, const int *granted_qos);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe(NULL, \"qos1/test\", QOS);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_subscribe(int mid, int qos_count, const int *granted_qos)\n{\n\tassert(mid == 1);\n\tassert(qos_count == 1);\n\tassert(granted_qos[0] == QOS);\n\tshould_run = false;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tint rc;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"subscribe-qos1-test\");\n\n\trc = mosq->loop_start();\n\tif(rc){\n\t\tprintf(\"loop_start failed: %s\\n\", mosquitto_strerror(rc));\n\t\treturn rc;\n\t}\n\n\trc = mosq->connect_async(\"localhost\", port, 60);\n\tif(rc){\n\t\tprintf(\"connect_async failed: %s\\n\", mosquitto_strerror(rc));\n\t\treturn rc;\n\t}\n\n\t/* 50 millis to be system polite */\n\tstruct timespec tv = { 0, (long)50e6 };\n\twhile(should_run){\n\t\tnanosleep(&tv, NULL);\n\t}\n\n\tmosq->disconnect();\n\tmosq->loop_stop();\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/02-subscribe-qos1-async2.cpp",
    "content": "#include <cassert>\n#include <cstdio>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\n/* mosquitto_connect_async() test, with mosquitto_loop_start() called after mosquitto_connect_async(). */\n\n#define QOS 1\n\nstatic int run = -1;\nstatic bool should_run = true;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_subscribe(int mid, int qos_count, const int *granted_qos);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe(NULL, \"qos1/test\", QOS);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_subscribe(int mid, int qos_count, const int *granted_qos)\n{\n\tassert(mid == 1);\n\tassert(qos_count == 1);\n\tassert(granted_qos[0] == QOS);\n\tshould_run = false;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tint rc;\n\tstruct timespec tv = { 0, (long)100e6 };\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"subscribe-qos1-test\");\n\n\t/* Help with possible race condition on CI */\n\tnanosleep(&tv, NULL);\n\n\trc = mosq->connect_async(\"localhost\", port, 60, NULL);\n\tif(rc){\n\t\tprintf(\"connect_async failed: %s\\n\", mosquitto_strerror(rc));\n\t\treturn rc;\n\t}\n\n\trc = mosq->loop_start();\n\tif(rc){\n\t\tprintf(\"loop_start failed: %s\\n\", mosquitto_strerror(rc));\n\t\treturn rc;\n\t}\n\n\t/* 50 millis to be system polite */\n\ttv.tv_nsec = (long)50e6;\n\twhile(should_run){\n\t\tnanosleep(&tv, NULL);\n\t}\n\n\tmosq->disconnect();\n\tmosq->loop_stop();\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/02-subscribe-qos1.cpp",
    "content": "#include <cassert>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_subscribe(int mid, int qos_count, const int *granted_qos);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe(NULL, \"qos1/test\", 1);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_subscribe(int mid, int qos_count, const int *granted_qos)\n{\n\tassert(mid == 1);\n\tassert(qos_count == 1);\n\tassert(granted_qos[0] == 1);\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"subscribe-qos1-test\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/02-subscribe-qos2.cpp",
    "content": "#include <cassert>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_subscribe(int mid, int qos_count, const int *granted_qos);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe_v5(NULL, \"qos2/test\", 2, 2, NULL);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_subscribe(int mid, int qos_count, const int *granted_qos)\n{\n\tassert(mid == 1);\n\tassert(qos_count == 1);\n\tassert(granted_qos[0] == 2);\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"subscribe-qos2-test\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/02-unsubscribe-v5.cpp",
    "content": "#include <cassert>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_unsubscribe_v5(int mid, const mosquitto_property *props);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tint rc2;\n\tmosquitto_property *proplist = NULL;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc2 = mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, \"key\", \"value\");\n\t\tif(rc2 != MOSQ_ERR_SUCCESS){\n\t\t\tabort();\n\t\t}\n\t\tunsubscribe_v5(NULL, \"unsubscribe/test\", proplist);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_unsubscribe_v5(int mid, const mosquitto_property *props)\n{\n\tassert(mid == 1);\n\tassert(props == NULL);\n\n\tdisconnect_v5(0, NULL);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"unsubscribe-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/02-unsubscribe.cpp",
    "content": "#include <cassert>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_unsubscribe(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tunsubscribe(NULL, \"unsubscribe/test\");\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_unsubscribe(int mid)\n{\n\tassert(mid == 1);\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"unsubscribe-test\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/03-publish-b2c-qos1-unexpected-puback.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tint rc = 1;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos1-test\");\n\n\tmosq->connect(\"localhost\", port, 5);\n\n\twhile(1){\n\t\tif(mosq->loop(300, 1)){\n\t\t\trc = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn rc;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-b2c-qos1.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_message(const struct mosquitto_message *msg);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nvoid mosquittopp_test::on_message(const struct mosquitto_message *msg)\n{\n\tif(msg->mid != 123){\n\t\tprintf(\"Invalid mid (%d)\\n\", msg->mid);\n\t\texit(1);\n\t}\n\tif(msg->qos != 1){\n\t\tprintf(\"Invalid qos (%d)\\n\", msg->qos);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->topic, \"pub/qos1/receive\")){\n\t\tprintf(\"Invalid topic (%s)\\n\", msg->topic);\n\t\texit(1);\n\t}\n\tif(strcmp((char *)msg->payload, \"message\")){\n\t\tprintf(\"Invalid payload (%s)\\n\", (char *)msg->payload);\n\t\texit(1);\n\t}\n\tif(msg->payloadlen != 7){\n\t\tprintf(\"Invalid payloadlen (%d)\\n\", msg->payloadlen);\n\t\texit(1);\n\t}\n\tif(msg->retain != false){\n\t\tprintf(\"Invalid retain (%d)\\n\", msg->retain);\n\t\texit(1);\n\t}\n\n\texit(0);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tint rc = 1;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos1-test\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(1){\n\t\tif(mosq->loop()){\n\t\t\trc = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn rc;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-b2c-qos2-len.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_message(const struct mosquitto_message *msg);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_message(const struct mosquitto_message *msg)\n{\n\tif(msg->mid != 56){\n\t\tprintf(\"Invalid mid (%d)\\n\", msg->mid);\n\t\texit(1);\n\t}\n\tif(msg->qos != 2){\n\t\tprintf(\"Invalid qos (%d)\\n\", msg->qos);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->topic, \"len/qos2/test\")){\n\t\tprintf(\"Invalid topic (%s)\\n\", msg->topic);\n\t\texit(1);\n\t}\n\tif(strcmp((char *)msg->payload, \"message\")){\n\t\tprintf(\"Invalid payload (%s)\\n\", (char *)msg->payload);\n\t\texit(1);\n\t}\n\tif(msg->payloadlen != 7){\n\t\tprintf(\"Invalid payloadlen (%d)\\n\", msg->payloadlen);\n\t\texit(1);\n\t}\n\tif(msg->retain != false){\n\t\tprintf(\"Invalid retain (%d)\\n\", msg->retain);\n\t\texit(1);\n\t}\n\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop(100, 1);\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-b2c-qos2-unexpected-pubcomp.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\n\tmosq->connect(\"localhost\", port, 5);\n\n\twhile(run == -1){\n\t\tint rc = mosq->loop(300, 1);\n\t\tif(rc){\n\t\t\texit(0);\n\t\t}\n\t}\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/03-publish-b2c-qos2-unexpected-pubrel.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_message(const struct mosquitto_message *msg);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nvoid mosquittopp_test::on_message(const struct mosquitto_message *msg)\n{\n\tif(!strcmp(msg->topic, \"quit\")){\n\t\trun = 0;\n\t\treturn;\n\t}\n\tif(msg->mid != 13423){\n\t\tprintf(\"Invalid mid (%d)\\n\", msg->mid);\n\t\texit(1);\n\t}\n\tif(msg->qos != 2){\n\t\tprintf(\"Invalid qos (%d)\\n\", msg->qos);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->topic, \"pub/qos2/receive\")){\n\t\tprintf(\"Invalid topic (%s)\\n\", msg->topic);\n\t\texit(1);\n\t}\n\tif(strcmp((char *)msg->payload, \"message\")){\n\t\tprintf(\"Invalid payload (%s)\\n\", (char *)msg->payload);\n\t\texit(1);\n\t}\n\tif(msg->payloadlen != 7){\n\t\tprintf(\"Invalid payloadlen (%d)\\n\", msg->payloadlen);\n\t\texit(1);\n\t}\n\tif(msg->retain != false){\n\t\tprintf(\"Invalid retain (%d)\\n\", msg->retain);\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tint rc = mosq->loop(300, 1);\n\t\tif(rc){\n\t\t\texit(1);\n\t\t}\n\t}\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/03-publish-b2c-qos2.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_message(const struct mosquitto_message *msg);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nvoid mosquittopp_test::on_message(const struct mosquitto_message *msg)\n{\n\tif(msg->mid != 13423){\n\t\tprintf(\"Invalid mid (%d)\\n\", msg->mid);\n\t\texit(1);\n\t}\n\tif(msg->qos != 2){\n\t\tprintf(\"Invalid qos (%d)\\n\", msg->qos);\n\t\texit(1);\n\t}\n\tif(strcmp(msg->topic, \"pub/qos2/receive\")){\n\t\tprintf(\"Invalid topic (%s)\\n\", msg->topic);\n\t\texit(1);\n\t}\n\tif(strcmp((char *)msg->payload, \"message\")){\n\t\tprintf(\"Invalid payload (%s)\\n\", (char *)msg->payload);\n\t\texit(1);\n\t}\n\tif(msg->payloadlen != 7){\n\t\tprintf(\"Invalid payloadlen (%d)\\n\", msg->payloadlen);\n\t\texit(1);\n\t}\n\tif(msg->retain != false){\n\t\tprintf(\"Invalid retain (%d)\\n\", msg->retain);\n\t\texit(1);\n\t}\n\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\tmosq->message_retry_set(3);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-c2b-qos1-disconnect.cpp",
    "content": "#include <cassert>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\nstatic int first_connection = 1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tif(first_connection == 1){\n\t\t\tpublish(NULL, \"pub/qos1/test\", strlen(\"message\"), \"message\", 1, false);\n\t\t\tfirst_connection = 0;\n\t\t}\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\tif(rc){\n\t\treconnect();\n\t}else{\n\t\trun = 0;\n\t}\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tassert(mid == 1);\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos1-test\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-c2b-qos1-len.cpp",
    "content": "#include <cassert>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tpublish(NULL, \"pub/qos1/test\", strlen(\"message\"), \"message\", 1, false);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tassert(mid == 1);\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos1-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop(300, 1);\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-c2b-qos1-receive-maximum.cpp",
    "content": "#include <cassert>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_publish_v5(int mid, int reason_code, const mosquitto_property *properties);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n\tfor(int i=0; i<6; i++){\n\t\tpublish_v5(NULL, \"topic\", 5, \"12345\", 1, false, NULL);\n\t}\n}\n\n\nvoid mosquittopp_test::on_publish_v5(int mid, int reason_code, const mosquitto_property *properties)\n{\n\tassert(reason_code == 0);\n\tassert(properties == NULL);\n\tif(mid == 6){\n\t\tdisconnect();\n\t\trun = 0;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos1-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect_v5(\"localhost\", port, 60, NULL, NULL);\n\n\twhile(run == -1){\n\t\tmosq->loop(300, 1);\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-c2b-qos2-disconnect.cpp",
    "content": "#include <cassert>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\nstatic int first_connection = 1;\nstatic int sent_mid = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tif(first_connection == 1){\n\t\t\tpublish(&sent_mid, \"pub/qos2/test\", strlen(\"message\"), \"message\", 2, false);\n\t\t\tfirst_connection = 0;\n\t\t}\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\tif(rc){\n\t\treconnect();\n\t}else{\n\t\trun = 0;\n\t}\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tassert(mid == sent_mid);\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\tmosq->message_retry_set(3);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-c2b-qos2-len.cpp",
    "content": "#include <cassert>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tpublish(&sent_mid, \"pub/qos2/test\", strlen(\"message\"), \"message\", 2, false);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tassert(mid == sent_mid);\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-c2b-qos2-maximum-qos-0.cpp",
    "content": "#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc = publish(NULL, \"maximum/qos/qos2\", strlen(\"message\"), \"message\", 2, false);\n\t\tif(rc != MOSQ_ERR_QOS_NOT_SUPPORTED){\n\t\t\trun = 1;\n\t\t}\n\t\trc = publish(NULL, \"maximum/qos/qos1\", strlen(\"message\"), \"message\", 1, false);\n\t\tif(rc != MOSQ_ERR_QOS_NOT_SUPPORTED){\n\t\t\trun = 1;\n\t\t}\n\t\trc = publish(NULL, \"maximum/qos/qos0\", strlen(\"message\"), \"message\", 0, false);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\trun = 1;\n\t\t}\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tif(mid == 1){\n\t\tdisconnect();\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop(50, 1);\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-c2b-qos2-maximum-qos-1.cpp",
    "content": "#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc = publish(NULL, \"maximum/qos/qos2\", strlen(\"message\"), \"message\", 2, false);\n\t\tif(rc != MOSQ_ERR_QOS_NOT_SUPPORTED){\n\t\t\trun = 1;\n\t\t}\n\t\trc = publish(NULL, \"maximum/qos/qos1\", strlen(\"message\"), \"message\", 1, false);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\trun = 1;\n\t\t}\n\t\trc = publish(NULL, \"maximum/qos/qos0\", strlen(\"message\"), \"message\", 0, false);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\trun = 1;\n\t\t}\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tif(mid == 2){\n\t\tdisconnect();\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop(50, 1);\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-c2b-qos2-pubrec-error.cpp",
    "content": "#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tpublish_v5(NULL, \"topic\", strlen(\"rejected\"), \"rejected\", 2, false, NULL);\n\t\tpublish_v5(NULL, \"topic\", strlen(\"accepted\"), \"accepted\", 2, false, NULL);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tif(mid == 2){\n\t\trun = 0;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-c2b-qos2-receive-maximum.cpp",
    "content": "#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n\tfor(int i=0; i<5; i++){\n\t\tpublish_v5(NULL, \"topic\", 5, \"12345\", 2, false, NULL);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tif(mid == 5){\n\t\tdisconnect();\n\t\trun = 0;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-c2b-qos2.cpp",
    "content": "#include <cassert>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tpublish(&sent_mid, \"pub/qos2/test\", strlen(\"message\"), \"message\", 2, false);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tassert(mid == sent_mid);\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos2-test\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-loop-forever.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect_v5(int rc, int flags, const mosquitto_property *properties);\n\tvoid on_disconnect_v5(int rc, const mosquitto_property *properties);\n\tvoid on_subscribe_v5(int mid, int qos_count, const int *granted_qos, const mosquitto_property *props);\n\tvoid on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *properties);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect_v5(int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe_v5(NULL, \"loop/test\", 0, 0, NULL);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect_v5(int rc, const mosquitto_property *properties)\n{\n\t(void)properties;\n\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_subscribe_v5(int mid, int qos_count, const int *granted_qos, const mosquitto_property *props)\n{\n\t(void)mid;\n\t(void)qos_count;\n\t(void)granted_qos;\n\t(void)props;\n\n\tpublish_v5(NULL, \"loop/test\", strlen(\"message\"), \"message\", 0, false, NULL);\n}\n\n\nvoid mosquittopp_test::on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\t(void)msg;\n\t(void)properties;\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"loop-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect_v5(\"localhost\", port, 60, NULL, NULL);\n\n\tmosq->loop_forever();\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn 1;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-loop-manual.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <sys/select.h>\n#include <sys/types.h>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect_v5(int rc, int flags, const mosquitto_property *properties);\n\tvoid on_disconnect_v5(int rc, const mosquitto_property *properties);\n\tvoid on_subscribe_v5(int mid, int qos_count, const int *granted_qos, const mosquitto_property *props);\n\tvoid on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *properties);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect_v5(int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe_v5(NULL, \"loop/test\", 0, 0, NULL);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect_v5(int rc, const mosquitto_property *properties)\n{\n\t(void)properties;\n\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_subscribe_v5(int mid, int qos_count, const int *granted_qos, const mosquitto_property *props)\n{\n\t(void)mid;\n\t(void)qos_count;\n\t(void)granted_qos;\n\t(void)props;\n\n\tpublish_v5(NULL, \"loop/test\", strlen(\"message\"), \"message\", 0, false, NULL);\n}\n\n\nvoid mosquittopp_test::on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\t(void)msg;\n\t(void)properties;\n\tmosqpp::validate_utf8(msg->topic, (int)strlen(msg->topic));\n\tdisconnect();\n}\n\n\nvoid do_loop(mosquittopp_test *mosq)\n{\n\tint sock;\n\tstruct timeval tv;\n\tfd_set readfds, writefds;\n\n\tsock = mosq->socket();\n\tif(sock < 0){\n\t\texit(1);\n\t}\n\n\tFD_ZERO(&readfds);\n\tFD_ZERO(&writefds);\n\n\tFD_SET(sock, &readfds);\n\n\twhile(run == -1){\n\t\ttv.tv_sec = 0;\n\t\ttv.tv_usec = 100000;\n\n\t\tFD_SET(sock, &readfds);\n\t\tif(mosq->want_write()){\n\t\t\tFD_SET(sock, &writefds);\n\t\t}else{\n\t\t\tFD_CLR(sock, &writefds);\n\t\t}\n\n\t\tint fdcount = select(sock+1, &readfds, &writefds, NULL, &tv);\n\t\tif(fdcount < 0){\n\t\t\texit(1);\n\t\t}\n\n\t\tif(FD_ISSET(sock, &readfds)){\n\t\t\tmosq->loop_read();\n\t\t}\n\t\tif(FD_ISSET(sock, &writefds)){\n\t\t\tmosq->loop_write();\n\t\t}\n\t\tmosq->loop_misc();\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"loop-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect_v5(\"localhost\", port, 60, NULL, NULL);\n\n\tdo_loop(mosq);\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn 1;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-loop-start.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <ctime>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect_v5(int rc, int flags, const mosquitto_property *properties);\n\tvoid on_disconnect_v5(int rc, const mosquitto_property *properties);\n\tvoid on_subscribe_v5(int mid, int qos_count, const int *granted_qos, const mosquitto_property *props);\n\tvoid on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *properties);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect_v5(int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe_v5(NULL, \"loop/test\", 0, 0, NULL);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect_v5(int rc, const mosquitto_property *properties)\n{\n\t(void)properties;\n\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_subscribe_v5(int mid, int qos_count, const int *granted_qos, const mosquitto_property *props)\n{\n\t(void)mid;\n\t(void)qos_count;\n\t(void)granted_qos;\n\t(void)props;\n\n\tpublish_v5(NULL, \"loop/test\", strlen(\"message\"), \"message\", 0, false, NULL);\n}\n\n\nvoid mosquittopp_test::on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\t(void)msg;\n\t(void)properties;\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"loop-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect_v5(\"localhost\", port, 60, NULL, NULL);\n\n\tmosq->loop_start();\n\tstruct timespec tv = { 0, (long)50e6 };\n\twhile(run == -1){\n\t\tnanosleep(&tv, NULL);\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn 1;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-loop.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect_v5(int rc, int flags, const mosquitto_property *properties);\n\tvoid on_disconnect_v5(int rc, const mosquitto_property *properties);\n\tvoid on_subscribe_v5(int mid, int qos_count, const int *granted_qos, const mosquitto_property *props);\n\tvoid on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *properties);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect_v5(int rc, int flags, const mosquitto_property *properties)\n{\n\t(void)flags;\n\t(void)properties;\n\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe_v5(NULL, \"loop/test\", 0, 0, NULL);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect_v5(int rc, const mosquitto_property *properties)\n{\n\t(void)properties;\n\n\trun = rc;\n}\n\n\nvoid mosquittopp_test::on_subscribe_v5(int mid, int qos_count, const int *granted_qos, const mosquitto_property *props)\n{\n\t(void)mid;\n\t(void)qos_count;\n\t(void)granted_qos;\n\t(void)props;\n\n\tpublish_v5(NULL, \"loop/test\", strlen(\"message\"), \"message\", 0, false, NULL);\n}\n\n\nvoid mosquittopp_test::on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\t(void)msg;\n\t(void)properties;\n\tdisconnect();\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"loop-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect_v5(\"localhost\", port, 60, NULL, NULL);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn 1;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-publish-qos0-no-payload.cpp",
    "content": "#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tpublish(&sent_mid, \"pub/qos0/no-payload/test\", 0, NULL, 0, false);\n\t}\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tif(sent_mid == mid){\n\t\tdisconnect();\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos0-test-np\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/03-publish-qos0.cpp",
    "content": "#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int sent_mid = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tpublish(&sent_mid, \"pub/qos0/test\", strlen(\"message\"), \"message\", 0, false);\n\t}\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tif(sent_mid == mid){\n\t\tdisconnect();\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tint rc;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos0-test\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\trc = mosq->loop_forever();\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn rc;\n}\n"
  },
  {
    "path": "test/lib/cpp/03-request-response-1.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\n#define QOS 0\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_message(const struct mosquitto_message *msg);\n\tvoid on_subscribe(int mid, int qos_count, const int *granted_qos);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe(NULL, \"response/topic\", QOS);\n\t}\n}\n\n\nvoid mosquittopp_test::on_subscribe(int mid, int qos_count, const int *granted_qos)\n{\n\tmosquitto_property *props = NULL;\n\n\t(void)mid;\n\tif(qos_count != 1 || granted_qos[0] != QOS){\n\t\tabort();\n\t}\n\n\tif(mosquitto_property_add_string(&props, MQTT_PROP_RESPONSE_TOPIC, \"response/topic\")){\n\t\tabort();\n\t}\n\tpublish_v5(NULL, \"request/topic\", 6, \"action\", 0, 0, props);\n\tmosquitto_property_free_all(&props);\n}\n\n\nvoid mosquittopp_test::on_message(const struct mosquitto_message *msg)\n{\n\tif(!strcmp((char *)msg->payload, \"a response\")){\n\t\trun = 0;\n\t}else{\n\t\trun = 1;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"request-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, 5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn 1;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-request-response-2.cpp",
    "content": "#include <cassert>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\n#define QOS 0\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *props);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe(NULL, \"request/topic\", QOS);\n\t}\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tassert(mid == sent_mid);\n\trun = 0;\n}\n\n\nvoid mosquittopp_test::on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *props)\n{\n\tconst mosquitto_property *p_resp, *p_corr = NULL;\n\tchar *resp_topic = NULL;\n\tint rc;\n\n\tif(!strcmp(msg->topic, \"request/topic\")){\n\t\tp_resp = mosquitto_property_read_string(props, MQTT_PROP_RESPONSE_TOPIC, &resp_topic, false);\n\t\tif(p_resp){\n\t\t\tp_corr = mosquitto_property_read_binary(props, MQTT_PROP_CORRELATION_DATA, NULL, NULL, false);\n\t\t\trc = publish_v5(&sent_mid, resp_topic, strlen(\"a response\"), \"a response\", 0, false, p_corr);\n\t\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\t\tabort();\n\t\t\t}\n\t\t\tfree(resp_topic);\n\t\t}\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"response-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, 5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn 1;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/03-request-response-correlation-1.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\n#define QOS 0\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_message(const struct mosquitto_message *msg);\n\tvoid on_subscribe(int mid, int qos_count, const int *granted_qos);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tsubscribe(NULL, \"response/topic\", QOS);\n\t}\n}\n\n\nvoid mosquittopp_test::on_subscribe(int mid, int qos_count, const int *granted_qos)\n{\n\tmosquitto_property *props = NULL;\n\tint rc;\n\n\t(void)mid;\n\n\tif(qos_count != 1 || granted_qos[0] != QOS){\n\t\tabort();\n\t}\n\n\tif(mosquitto_property_add_string(&props, MQTT_PROP_RESPONSE_TOPIC, \"response/topic\")\n\t\t\t|| mosquitto_property_add_binary(&props, MQTT_PROP_CORRELATION_DATA, \"corridor\", 8)){\n\t\tabort();\n\t}\n\trc = publish_v5(NULL, \"request/topic\", 6, \"action\", QOS, 0, props);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tabort();\n\t}\n\tmosquitto_property_free_all(&props);\n}\n\n\nvoid mosquittopp_test::on_message(const struct mosquitto_message *msg)\n{\n\tif(!strcmp((char *)msg->payload, \"a response\")){\n\t\trun = 0;\n\t}else{\n\t\trun = 1;\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"request-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, 5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn 1;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/04-retain-qos0.cpp",
    "content": "#include <cassert>\n#include <cstring>\n\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_publish(int mid);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tpublish(&sent_mid, \"retain/qos0/test\", strlen(\"retained message\"), \"retained message\", 0, true);\n\t}\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tassert(mid == sent_mid);\n\trun = 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"retain-qos0-test\");\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/08-ssl-bad-cacert.cpp",
    "content": "#include <mosquitto/libmosquittopp.h>\n#include \"path_helper.h\"\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tint rc = 1;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\t(void)argv;\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"08-ssl-bad-cacert\");\n\n\tif(mosq->tls_set(\"this/file/doesnt/exist\") == MOSQ_ERR_INVAL){\n\t\trc = 0;\n\t}\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn rc;\n}\n"
  },
  {
    "path": "test/lib/cpp/08-ssl-connect-cert-auth-custom-ssl-ctx-default.cpp",
    "content": "#include <signal.h>\n#include <mosquitto/libmosquittopp.h>\n#include <openssl/ssl.h>\n#include \"path_helper.h\"\n\nstatic int run = -1;\n\n\nvoid handle_sigint(int signal)\n{\n\t(void)signal;\n\trun = 0;\n}\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tSSL_CTX *ssl_ctx;\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tOPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \\\n\t\t\t| OPENSSL_INIT_ADD_ALL_DIGESTS \\\n\t\t\t| OPENSSL_INIT_LOAD_CONFIG, NULL);\n\tssl_ctx = SSL_CTX_new(TLS_client_method());\n\n\tmosq = new mosquittopp_test(\"08-ssl-connect-crt-auth\");\n\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tchar capath[4096];\n\tcat_sourcedir_with_relpath(capath, \"/../../ssl/certs\");\n\tchar certfile[4096];\n\tcat_sourcedir_with_relpath(certfile, \"/../../ssl/client.crt\");\n\tchar keyfile[4096];\n\tcat_sourcedir_with_relpath(keyfile, \"/../../ssl/client.key\");\n\n\tmosq->tls_set(cafile, NULL, certfile, keyfile);\n\n\tmosq->int_option(MOSQ_OPT_SSL_CTX_WITH_DEFAULTS, 1);\n\tmosq->void_option(MOSQ_OPT_SSL_CTX, ssl_ctx);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\tsignal(SIGINT, handle_sigint);\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tSSL_CTX_free(ssl_ctx);\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/08-ssl-connect-cert-auth-custom-ssl-ctx.cpp",
    "content": "#include <signal.h>\n#include <mosquitto/libmosquittopp.h>\n#include <openssl/ssl.h>\n#include \"path_helper.h\"\n\n\nstatic int run = -1;\n\n\nvoid handle_sigint(int signal)\n{\n\t(void)signal;\n\trun = 0;\n}\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tSSL_CTX *ssl_ctx;\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tOPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \\\n\t\t\t| OPENSSL_INIT_ADD_ALL_DIGESTS \\\n\t\t\t| OPENSSL_INIT_LOAD_CONFIG, NULL);\n\tssl_ctx = SSL_CTX_new(TLS_client_method());\n\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tchar capath[4096];\n\tcat_sourcedir_with_relpath(capath, \"/../../ssl/certs\");\n\tchar certfile[4096];\n\tcat_sourcedir_with_relpath(certfile, \"/../../ssl/client.crt\");\n\tchar keyfile[4096];\n\tcat_sourcedir_with_relpath(keyfile, \"/../../ssl/client.key\");\n\n\tSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);\n\tSSL_CTX_use_certificate_chain_file(ssl_ctx, certfile);\n\tSSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile, SSL_FILETYPE_PEM);\n\tSSL_CTX_load_verify_locations(ssl_ctx, cafile, capath);\n\n\tmosq = new mosquittopp_test(\"08-ssl-connect-crt-auth\");\n\n\tmosq->tls_set(cafile, NULL, certfile, keyfile);\n\n\tmosq->int_option(MOSQ_OPT_SSL_CTX_WITH_DEFAULTS, 0);\n\tmosq->void_option(MOSQ_OPT_SSL_CTX, ssl_ctx);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\tsignal(SIGINT, handle_sigint);\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tSSL_CTX_free(ssl_ctx);\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/08-ssl-connect-cert-auth-enc.cpp",
    "content": "#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n#include \"path_helper.h\"\n\nstatic int run = -1;\n\n\nstatic int password_callback(char *buf, int size, int rwflag, void *userdata)\n{\n\t(void)rwflag;\n\t(void)userdata;\n\n\tstrncpy(buf, \"password\", (size_t )size);\n\tbuf[size-1] = '\\0';\n\n\treturn (int)strlen(buf);\n}\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"08-ssl-connect-crt-auth-enc\");\n\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tchar capath[4096];\n\tcat_sourcedir_with_relpath(capath, \"/../../ssl/certs\");\n\tchar certfile[4096];\n\tcat_sourcedir_with_relpath(certfile, \"/../../ssl/client.crt\");\n\tchar keyfile[4096];\n\tcat_sourcedir_with_relpath(keyfile, \"/../../ssl/client.key\");\n\n\tmosq->tls_set(cafile, NULL, certfile, keyfile, password_callback);\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/08-ssl-connect-cert-auth.cpp",
    "content": "#include <mosquitto/libmosquittopp.h>\n#include \"path_helper.h\"\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"08-ssl-connect-crt-auth\");\n\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tchar capath[4096];\n\tcat_sourcedir_with_relpath(capath, \"/../../ssl/certs\");\n\tchar certfile[4096];\n\tcat_sourcedir_with_relpath(certfile, \"/../../ssl/client.crt\");\n\tchar keyfile[4096];\n\tcat_sourcedir_with_relpath(keyfile, \"/../../ssl/client.key\");\n\tmosq->tls_set(cafile, capath, certfile, keyfile);\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/08-ssl-connect-no-auth.cpp",
    "content": "#include <mosquitto/libmosquittopp.h>\n#include \"path_helper.h\"\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"08-ssl-connect-no-auth\");\n\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/all-ca.crt\");\n\tmosq->tls_set(cafile);\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/08-ssl-connect-san.cpp",
    "content": "#include <cassert>\n#include \"path_helper.h\"\n#include <mosquitto/libmosquittopp.h>\n#include \"path_helper.h\"\n\nstatic int run = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_disconnect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tdisconnect();\n\t}\n}\n\n\nvoid mosquittopp_test::on_disconnect(int rc)\n{\n\trun = rc;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tint rc;\n\tchar cafile[4096];\n\tassert(argc == 3);\n\tint port = atoi(argv[1]);\n\tchar *host = argv[2];\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"08-ssl-connect-san\");\n\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-root-ca.crt\");\n\tmosq->tls_set(cafile);\n\trc = mosq->connect(host, port, 60);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn rc;\n\t}\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/08-ssl-fake-cacert.cpp",
    "content": "#include <errno.h>\n#include <mosquitto/libmosquittopp.h>\n#include \"path_helper.h\"\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\t(void)rc;\n\texit(1);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tint rc;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"08-ssl-fake-cacert\");\n\n\tchar cafile[4096];\n\tcat_sourcedir_with_relpath(cafile, \"/../../ssl/test-fake-root-ca.crt\");\n\tchar capath[4096];\n\tcat_sourcedir_with_relpath(capath, \"/../../ssl/certs\");\n\tchar certfile[4096];\n\tcat_sourcedir_with_relpath(certfile, \"/../../ssl/client.crt\");\n\tchar keyfile[4096];\n\tcat_sourcedir_with_relpath(keyfile, \"/../../ssl/client.key\");\n\n\tmosq->tls_set(cafile, NULL, certfile, keyfile);\n\tmosq->connect(\"localhost\", port, 60);\n\n\trc = mosq->loop_forever();\n\tdelete mosq;\n\tmosqpp::lib_cleanup();\n\tif(rc == MOSQ_ERR_ERRNO && errno == EPROTO){\n\t\treturn 0;\n\t}else{\n\t\treturn 1;\n\t}\n}\n"
  },
  {
    "path": "test/lib/cpp/09-util-topic-tokenise.cpp",
    "content": "#include <cstdio>\n#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\n\nvoid print_error(const char *topic, char **topics, int topic_count)\n{\n\tint i;\n\tprintf(\"TOPIC: %s\\n\", topic);\n\tprintf(\"TOKENS: \");\n\tfor(i=0; i<topic_count; i++){\n\t\tprintf(\"%s\", topics[i]);\n\t\tif(i+1<topic_count){\n\t\t\tprintf(\"/\");\n\t\t}\n\t}\n\tprintf(\"\\n\");\n}\n\n\nint main(int argc, char *argv[])\n{\n\tchar **topics;\n\tint topic_count;\n\n\t(void)argc;\n\t(void)argv;\n\n\tif(mosqpp::sub_topic_tokenise(\"topic\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 1 || strcmp(topics[0], \"topic\")){\n\t\tprint_error(\"topic\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosqpp::sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosqpp::sub_topic_tokenise(\"a/deep/topic/hierarchy\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 4\n\t\t\t|| strcmp(topics[0], \"a\")\n\t\t\t|| strcmp(topics[1], \"deep\")\n\t\t\t|| strcmp(topics[2], \"topic\")\n\t\t\t|| strcmp(topics[3], \"hierarchy\")){\n\t\tprint_error(\"a/deep/topic/hierarchy\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosqpp::sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosqpp::sub_topic_tokenise(\"/a/deep/topic/hierarchy\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 5\n\t\t\t|| topics[0]\n\t\t\t|| strcmp(topics[1], \"a\")\n\t\t\t|| strcmp(topics[2], \"deep\")\n\t\t\t|| strcmp(topics[3], \"topic\")\n\t\t\t|| strcmp(topics[4], \"hierarchy\")){\n\t\tprint_error(\"/a/deep/topic/hierarchy\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosqpp::sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosqpp::sub_topic_tokenise(\"a/b/c\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 3\n\t\t\t|| strcmp(topics[0], \"a\")\n\t\t\t|| strcmp(topics[1], \"b\")\n\t\t\t|| strcmp(topics[2], \"c\")){\n\t\tprint_error(\"a/b/c\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosqpp::sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosqpp::sub_topic_tokenise(\"/a/b/c\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 4\n\t\t\t|| topics[0]\n\t\t\t|| strcmp(topics[1], \"a\")\n\t\t\t|| strcmp(topics[2], \"b\")\n\t\t\t|| strcmp(topics[3], \"c\")){\n\t\tprint_error(\"/a/b/c\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosqpp::sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosqpp::sub_topic_tokenise(\"a///hierarchy\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 4\n\t\t\t|| strcmp(topics[0], \"a\")\n\t\t\t|| topics[1]\n\t\t\t|| topics[2]\n\t\t\t|| strcmp(topics[3], \"hierarchy\")){\n\t\tprint_error(\"a///hierarchy\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosqpp::sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosqpp::sub_topic_tokenise(\"/a///hierarchy\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 5\n\t\t\t|| topics[0]\n\t\t\t|| strcmp(topics[1], \"a\")\n\t\t\t|| topics[2]\n\t\t\t|| topics[3]\n\t\t\t|| strcmp(topics[4], \"hierarchy\")){\n\t\tprint_error(\"/a///hierarchy\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosqpp::sub_topic_tokens_free(&topics, topic_count);\n\n\tif(mosqpp::sub_topic_tokenise(\"/a///hierarchy/\", &topics, &topic_count)){\n\t\tprintf(\"Out of memory.\\n\");\n\t\treturn 1;\n\t}\n\tif(topic_count != 6\n\t\t\t|| topics[0]\n\t\t\t|| strcmp(topics[1], \"a\")\n\t\t\t|| topics[2]\n\t\t\t|| topics[3]\n\t\t\t|| strcmp(topics[4], \"hierarchy\")\n\t\t\t|| topics[3]){\n\t\tprint_error(\"/a///hierarchy/\", topics, topic_count);\n\t\treturn 1;\n\t}\n\tmosqpp::sub_topic_tokens_free(&topics, topic_count);\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "test/lib/cpp/11-prop-oversize-packet.cpp",
    "content": "#include <cstdio>\n#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_publish(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\trc = subscribe(NULL, \"0123456789012345678901234567890\", 0);\n\t\tif(rc != MOSQ_ERR_OVERSIZE_PACKET){\n\t\t\tprintf(\"Fail on subscribe\\n\");\n\t\t\texit(1);\n\t\t}\n\n\t\trc = unsubscribe(NULL, \"0123456789012345678901234567890\");\n\t\tif(rc != MOSQ_ERR_OVERSIZE_PACKET){\n\t\t\tprintf(\"Fail on unsubscribe\\n\");\n\t\t\texit(1);\n\t\t}\n\n\t\trc = publish(&sent_mid, \"pub/test\", strlen(\"123456789012345678\"), \"123456789012345678\", 0, false);\n\t\tif(rc != MOSQ_ERR_OVERSIZE_PACKET){\n\t\t\tprintf(\"Fail on publish 1\\n\");\n\t\t\texit(1);\n\t\t}\n\t\trc = publish(&sent_mid, \"pub/test\", strlen(\"12345678901234567\"), \"12345678901234567\", 0, false);\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\tprintf(\"Fail on publish 2\\n\");\n\t\t\texit(1);\n\t\t}\n\t}\n\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tif(mid == sent_mid){\n\t\tdisconnect();\n\t\trun = 0;\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"publish-qos0-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/11-prop-recv.cpp",
    "content": "#include <cassert>\n#include <cstdio>\n#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\nstatic int qos = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *properties);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}\n}\n\n\nvoid mosquittopp_test::on_message_v5(const struct mosquitto_message *msg, const mosquitto_property *properties)\n{\n\tint rc;\n\tchar *str;\n\n\tif(properties){\n\t\tif(mosquitto_property_read_string(properties, MQTT_PROP_CONTENT_TYPE, &str, false)){\n\t\t\trc = strcmp(str, \"plain/text\");\n\t\t\tfree(str);\n\n\t\t\tif(rc == 0){\n\t\t\t\tif(mosquitto_property_read_string(properties, MQTT_PROP_RESPONSE_TOPIC, &str, false)){\n\t\t\t\t\trc = strcmp(str, \"msg/123\");\n\t\t\t\t\tfree(str);\n\n\t\t\t\t\tif(rc == 0){\n\t\t\t\t\t\tif(msg->qos == qos){\n\t\t\t\t\t\t\tpublish(NULL, \"ok\", 2, \"ok\", 0, 0);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/* No matching message, so quit with an error */\n\texit(1);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\tint rc;\n\n\tassert(argc == 3);\n\tint port = atoi(argv[1]);\n\tqos = atoi(argv[2]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"prop-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\trc = mosq->loop();\n\t\tif(rc != MOSQ_ERR_SUCCESS){\n\t\t\treturn rc;\n\t\t}\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/11-prop-send-content-type.cpp",
    "content": "#include <cstdio>\n#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_publish(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_property *proplist = NULL;\n\t\tint rc2 = mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, \"application/json\");\n\t\tif(rc2 != MOSQ_ERR_SUCCESS){\n\t\t\tabort();\n\t\t}\n\t\tpublish_v5(&sent_mid, \"prop/qos0\", strlen(\"message\"), \"message\", 0, false, proplist);\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tif(mid == sent_mid){\n\t\tdisconnect();\n\t\trun = 0;\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"prop-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60, NULL);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/11-prop-send-payload-format.cpp",
    "content": "#include <cstdio>\n#include <cstring>\n#include <mosquitto/libmosquittopp.h>\n\nstatic int run = -1;\nstatic int sent_mid = -1;\n\nclass mosquittopp_test : public mosqpp::mosquittopp\n{\npublic:\n\tmosquittopp_test(const char *id);\n\n\tvoid on_connect(int rc);\n\tvoid on_publish(int rc);\n};\n\nmosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id)\n{\n}\n\n\nvoid mosquittopp_test::on_connect(int rc)\n{\n\tif(rc){\n\t\texit(1);\n\t}else{\n\t\tmosquitto_property *proplist = NULL;\n\t\tint rc2 = mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n\t\tif(rc2 != MOSQ_ERR_SUCCESS){\n\t\t\tabort();\n\t\t}\n\t\tpublish_v5(&sent_mid, \"prop/qos0\", strlen(\"message\"), \"message\", 0, false, proplist);\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n\n}\n\n\nvoid mosquittopp_test::on_publish(int mid)\n{\n\tif(mid == sent_mid){\n\t\tdisconnect();\n\t\trun = 0;\n\t}else{\n\t\texit(1);\n\t}\n}\n\n\nint main(int argc, char *argv[])\n{\n\tmosquittopp_test *mosq;\n\n\tif(argc != 2){\n\t\treturn 1;\n\t}\n\tint port = atoi(argv[1]);\n\n\tmosqpp::lib_init();\n\n\tmosq = new mosquittopp_test(\"prop-test\");\n\tmosq->int_option(MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);\n\n\tmosq->connect(\"localhost\", port, 60);\n\n\twhile(run == -1){\n\t\tmosq->loop();\n\t}\n\tdelete mosq;\n\n\tmosqpp::lib_cleanup();\n\n\treturn run;\n}\n"
  },
  {
    "path": "test/lib/cpp/CMakeLists.txt",
    "content": "set(BINARIES\n    01-con-discon-success\n    01-con-discon-success-v5\n    01-con-discon-will-clear\n    01-con-discon-will\n    01-con-discon-will-v5\n    01-extended-auth-continue\n    01-extended-auth-failure\n    01-keepalive-pingreq\n    01-no-clean-session\n    01-pre-connect-callback\n    01-server-keepalive-pingreq\n    01-unpwd-set\n    01-will-set\n    01-will-unpwd-set\n    02-subscribe-helper-callback-qos2\n    02-subscribe-helper-simple-qos2\n    02-subscribe-qos0\n    02-subscribe-qos1-async1\n    02-subscribe-qos1-async2\n    02-subscribe-qos1\n    02-subscribe-qos2\n    02-unsubscribe\n    02-unsubscribe-v5\n    03-publish-b2c-qos1\n    03-publish-b2c-qos1-unexpected-puback\n    03-publish-b2c-qos2\n    03-publish-b2c-qos2-len\n    03-publish-b2c-qos2-unexpected-pubcomp\n    03-publish-b2c-qos2-unexpected-pubrel\n    03-publish-c2b-qos1-disconnect\n    03-publish-c2b-qos1-len\n    03-publish-c2b-qos1-receive-maximum\n    03-publish-c2b-qos2\n    03-publish-c2b-qos2-disconnect\n    03-publish-c2b-qos2-len\n    03-publish-c2b-qos2-maximum-qos-0\n    03-publish-c2b-qos2-maximum-qos-1\n    03-publish-c2b-qos2-pubrec-error\n    03-publish-c2b-qos2-receive-maximum\n    03-publish-loop\n\t03-publish-loop-forever\n\t03-publish-loop-manual\n\t03-publish-loop-start\n    03-publish-qos0\n    03-publish-qos0-no-payload\n    03-request-response-1\n    03-request-response-2\n    03-request-response-correlation-1\n    04-retain-qos0\n    08-ssl-bad-cacert\n    08-ssl-connect-cert-auth\n    08-ssl-connect-cert-auth-custom-ssl-ctx\n    08-ssl-connect-cert-auth-custom-ssl-ctx-default\n    08-ssl-connect-cert-auth-enc\n    08-ssl-connect-no-auth\n    08-ssl-connect-san\n    08-ssl-fake-cacert\n    09-util-topic-tokenise\n    11-prop-oversize-packet\n    11-prop-recv\n    11-prop-send-content-type\n    11-prop-send-payload-format\n)\n\nforeach(BINARY ${BINARIES})\n    set(TARGET \"${BINARY}-cpp\")\n    add_executable(${TARGET} ${BINARY}.cpp)\n    target_compile_definitions(${TARGET} PRIVATE TEST_SOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\")\n\ttarget_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/test ${CMAKE_SOURCE_DIR}/include)\n    set_property(TARGET ${TARGET} PROPERTY SUFFIX .test)\n    target_link_libraries(${TARGET} PRIVATE common-options mosquittopp OpenSSL::SSL)\n\tset_target_properties(${TARGET} PROPERTIES OUTPUT_NAME ${BINARY})\nendforeach()\n"
  },
  {
    "path": "test/lib/cpp/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all test clean reallyclean\n\nLOCAL_CXXFLAGS+=-I${R}/include -I${R}/test -I${R}/lib/cpp -DDEBUG -Werror -W\nLIBS=${LIBMOSQ} ${R}/lib/cpp/libmosquittopp.so.1\n\nSRC = \\\n\t01-con-discon-success.cpp \\\n\t01-con-discon-success-v5.cpp \\\n\t01-con-discon-will-clear.cpp \\\n\t01-con-discon-will-v5.cpp \\\n\t01-con-discon-will.cpp \\\n\t01-extended-auth-continue.cpp \\\n\t01-extended-auth-failure.cpp \\\n\t01-keepalive-pingreq.cpp \\\n\t01-no-clean-session.cpp \\\n\t01-pre-connect-callback.cpp \\\n\t01-server-keepalive-pingreq.cpp \\\n\t01-unpwd-set.cpp \\\n\t01-will-set.cpp \\\n\t01-will-unpwd-set.cpp \\\n\t02-subscribe-helper-callback-qos2.cpp \\\n\t02-subscribe-helper-simple-qos2.cpp \\\n\t02-subscribe-qos0.cpp \\\n\t02-subscribe-qos1-async1.cpp \\\n\t02-subscribe-qos1-async2.cpp \\\n\t02-subscribe-qos1.cpp \\\n\t02-subscribe-qos2.cpp \\\n\t02-unsubscribe.cpp \\\n\t02-unsubscribe-v5.cpp \\\n\t03-publish-b2c-qos1.cpp \\\n\t03-publish-b2c-qos1-unexpected-puback.cpp \\\n\t03-publish-b2c-qos2.cpp \\\n\t03-publish-b2c-qos2-len.cpp \\\n\t03-publish-b2c-qos2-unexpected-pubcomp.cpp \\\n\t03-publish-b2c-qos2-unexpected-pubrel.cpp \\\n\t03-publish-c2b-qos1-disconnect.cpp \\\n\t03-publish-c2b-qos1-len.cpp \\\n\t03-publish-c2b-qos1-receive-maximum.cpp \\\n\t03-publish-c2b-qos2-disconnect.cpp \\\n\t03-publish-c2b-qos2.cpp \\\n\t03-publish-c2b-qos2-len.cpp \\\n\t03-publish-c2b-qos2-maximum-qos-0.cpp \\\n\t03-publish-c2b-qos2-maximum-qos-1.cpp \\\n\t03-publish-c2b-qos2-pubrec-error.cpp \\\n\t03-publish-c2b-qos2-receive-maximum.cpp \\\n\t03-publish-loop.cpp \\\n\t03-publish-loop-forever.cpp \\\n\t03-publish-loop-manual.cpp \\\n\t03-publish-loop-start.cpp \\\n\t03-publish-qos0-no-payload.cpp \\\n\t03-publish-qos0.cpp \\\n\t03-request-response-1.cpp \\\n\t03-request-response-2.cpp \\\n\t03-request-response-correlation-1.cpp \\\n\t04-retain-qos0.cpp \\\n\t08-ssl-bad-cacert.cpp \\\n\t08-ssl-connect-cert-auth-enc.cpp \\\n\t08-ssl-connect-cert-auth.cpp \\\n\t08-ssl-connect-cert-auth-custom-ssl-ctx.cpp \\\n\t08-ssl-connect-cert-auth-custom-ssl-ctx-default.cpp \\\n\t08-ssl-connect-no-auth.cpp \\\n\t08-ssl-connect-san.cpp \\\n\t08-ssl-fake-cacert.cpp \\\n\t09-util-topic-tokenise.cpp \\\n\t11-prop-oversize-packet.cpp \\\n\t11-prop-send-content-type.cpp \\\n\t11-prop-send-payload-format.cpp \\\n\t11-prop-recv.cpp\n\nLIBS += -lssl -lcrypto\n\nTESTS = ${SRC:.cpp=.test}\n\nall : ${TESTS}\n\n${TESTS} : %.test: %.cpp ${R}/test/path_helper.h\n\t$(CXX) -DTEST_SOURCE_DIR='\"$(realpath .)\"' $< -o $@ $(LOCAL_CXXFLAGS) $(LIBS) $(LOCAL_LDFLAGS)\n\nreallyclean : clean\n\t-rm -f *.orig\n\nclean :\n\trm -f *.test\n"
  },
  {
    "path": "test/lib/data/AUTH.json",
    "content": "[\n\t{\n\t\t\"comment\": \"AUTH TESTS ARE INCOMPLETE\",\n\t\t\"group\": \"v3.1.1 AUTH\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"F0 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"F0 r0\"}]},\n\t\t\t{ \"name\": \"F0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"F0 r268435456\"}]},\n\t\t\t{ \"name\": \"F0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F0 r1 00\"}]},\n\t\t\t{ \"name\": \"F1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F1 r0\"}]},\n\t\t\t{ \"name\": \"F2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F2 r0\"}]},\n\t\t\t{ \"name\": \"F4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F4 r0\"}]},\n\t\t\t{ \"name\": \"F8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F8 r0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   AUTH\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"F0 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"F0 r0\"}]},\n\t\t\t{ \"name\": \"F0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"F0 r268435456\"}]},\n\t\t\t{ \"name\": \"F0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F0 r1 00\"}]},\n\t\t\t{ \"name\": \"F1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F1 r0\"}]},\n\t\t\t{ \"name\": \"F2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F2 r0\"}]},\n\t\t\t{ \"name\": \"F4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F4 r0\"}]},\n\t\t\t{ \"name\": \"F8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"F8 r0\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/CONNACK.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 CONNACK\",\n\t\t\"connack\": false,\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"20 [MQTT-3.1.0-1]\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 00 00\"}]},\n\t\t\t{ \"name\": \"20 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"20 r268435456\"}]},\n\t\t\t{ \"name\": \"20 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 00 00\"}]},\n\t\t\t{ \"name\": \"20 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r1 00\"}]},\n\t\t\t{ \"name\": \"20 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r0\"}]},\n\t\t\t{ \"name\": \"20\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 00 00\"}]},\n\t\t\t{ \"name\": \"21\", \"msgs\": [{\"type\":\"send\", \"payload\":\"21 02 00 00\"}]},\n\t\t\t{ \"name\": \"22\", \"msgs\": [{\"type\":\"send\", \"payload\":\"22 02 00 00\"}]},\n\t\t\t{ \"name\": \"24\", \"msgs\": [{\"type\":\"send\", \"payload\":\"24 02 00 00\"}]},\n\t\t\t{ \"name\": \"28\", \"msgs\": [{\"type\":\"send\", \"payload\":\"28 02 00 00\"}]},\n\t\t\t{ \"name\": \"issue 2163 v3\", \"ver\":3, \"msgs\": [{\"type\":\"send\", \"payload\":\"29 02 00 01\"}]},\n\t\t\t{ \"name\": \"issue 2163 v4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"29 02 00 01\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x01\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 01 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x02\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 02 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x04\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 04 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x08\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 08 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x10\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 10 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x20\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 20 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x40\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 40 00\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x80\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 80 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNACK\",\n\t\t\"comment\": \"CMD RL FLAG RC PROPLEN PROPS\",\n\t\t\"ver\":5,\n\t\t\"connack\": false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"20 [MQTT-3.1.0-1]\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 00 v0\"}]},\n\t\t\t{ \"name\": \"20 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"20 r268435456\"}]},\n\t\t\t{ \"name\": \"20 with properties\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 21 H10\"}]},\n\t\t\t{ \"name\": \"20 long\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 00 00\"}]},\n\t\t\t{ \"name\": \"20 short 2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r2 00 00\"}]},\n\t\t\t{ \"name\": \"20 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r1 00\"}]},\n\t\t\t{ \"name\": \"20 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r0\"}]},\n\t\t\t{ \"name\": \"21\", \"msgs\": [{\"type\":\"send\", \"payload\":\"21 r3 00 00 v0\"}]},\n\t\t\t{ \"name\": \"22\", \"msgs\": [{\"type\":\"send\", \"payload\":\"22 r3 00 00 v0\"}]},\n\t\t\t{ \"name\": \"24\", \"msgs\": [{\"type\":\"send\", \"payload\":\"24 r3 00 00 v0\"}]},\n\t\t\t{ \"name\": \"28\", \"msgs\": [{\"type\":\"send\", \"payload\":\"28 r3 00 00 v0\"}]},\n\t\t\t{ \"name\": \"issue 2163 v5\", \"msgs\": [{\"type\":\"send\", \"payload\":\"29 r2 00 01\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x01\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 01 00 v0\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x02\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 02 00 v0\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x04\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 04 00 v0\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x08\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 08 00 v0\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x10\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 10 00 v0\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x20\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 20 00 v0\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x40\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 40 00 v0\"}]},\n\t\t\t{ \"name\": \"20 CAF=0x80\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 80 00 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x01 (invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 01 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x80 (unspecified error)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 80 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x81 (malformed packet)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 81 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x82 (protocol error)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 82 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x83 (implementation specific error)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 83 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x84 (unsupported protocol version)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 84 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x85 (client identifier not valid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 85 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x86 (bad user name or password)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 86 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x87 (not authorised)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 87 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x88 (server unavailable)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 88 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x89 (server busy)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 89 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x8A (banned)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 8A v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x8C (bad authentication method)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 8C v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x90 (topic name invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 90 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x95 (packet too large)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 95 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x97 (quota exceeded)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 97 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x99 (payload format invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 99 v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x9A (retain not supported)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 9A v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x9B (qos not supported)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 9B v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x9C (use another server)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 9C v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x9D (server moved)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 9D v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0x9F (connection rate exceeded)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 9F v0\"}]},\n\t\t\t{ \"name\": \"20 RC=0xFF (invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r3 00 FF v0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNACK ALLOWED PROPERTIES VALID\",\n\t\t\"comment\": \"CMD RL FLAG RC PROPLEN PROPS\",\n\t\t\"ver\":5,\n\t\t\"connack\": false,\n\t\t\"expect_disconnect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"20 with session-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 11 L1\"}]},\n\t\t\t{ \"name\": \"20 with receive-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 21 H5\"}]},\n\t\t\t{ \"name\": \"20 with maximum-qos (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 24 i0\"}]},\n\t\t\t{ \"name\": \"20 with retain-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 25 i0\"}]},\n\t\t\t{ \"name\": \"20 with maximum-packet-size (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 27 L1\"}]},\n\t\t\t{ \"name\": \"20 with maximum-packet-size (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 27 L1\"}]},\n\t\t\t{ \"name\": \"20 with topic-alias-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 22 H5\"}]},\n\t\t\t{ \"name\": \"20 with reason-string property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 1F s1 'p'\"}]},\n\t\t\t{ \"name\": \"20 with reason-string property empty\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 1F s0\"}]},\n\t\t\t{ \"name\": \"20 with wildcard-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 28 i0\"}]},\n\t\t\t{ \"name\": \"20 with subscription-identifier-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 29 i0\"}]},\n\t\t\t{ \"name\": \"20 with server-keep-alive (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 13 H5\"}]},\n\t\t\t{ \"name\": \"20 with shared-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 2A i0\"}]},\n\t\t\t{ \"name\": \"20 with response-information (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 1A s1 'p'\"}]},\n\t\t\t{ \"name\": \"20 with server-reference (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 1C s1 'p'\"}]},\n\t\t\t{ \"name\": \"20 with authentication-method (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 15 s1 'p'\"}]},\n\t\t\t{ \"name\": \"20 with authentication-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 16 s1 'p'\"}]},\n\t\t\t{ \"name\": \"20 with user-property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r10 00 00 07 26 s1 'p' s1 'q'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNACK ALLOWED PROPERTIES INVALID\",\n\t\t\"comment\": \"CMD RL FLAG RC PROPLEN PROPS\",\n\t\t\"ver\":5,\n\t\t\"connack\": false,\n\t\t\"expect_disconnect\":true,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"20 with session-expiry-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 11\"}]},\n\t\t\t{ \"name\": \"20 with receive-maximum (two byte integer) 0 value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 21 H0\"}]},\n\t\t\t{ \"name\": \"20 with receive-maximum (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 21\"}]},\n\t\t\t{ \"name\": \"20 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 12 s1 'p'\"}]},\n\t\t\t{ \"name\": \"20 with maximum-qos (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 24\"}]},\n\t\t\t{ \"name\": \"20 with maximum-qos (byte) 2 value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 24 i2\"}]},\n\t\t\t{ \"name\": \"20 with retain-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 25\"}]},\n\t\t\t{ \"name\": \"20 with maximum-packet-size (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 27\"}]},\n\t\t\t{ \"name\": \"20 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 12\"}]},\n\t\t\t{ \"name\": \"20 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 22\"}]},\n\t\t\t{ \"name\": \"20 with reason-string property missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 01 1F\"}]},\n\t\t\t{ \"name\": \"20 with wildcard-subscription-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 28\"}]},\n\t\t\t{ \"name\": \"20 with subscription-identifier-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 29\"}]},\n\t\t\t{ \"name\": \"20 with server-keep-alive (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 13\"}]},\n\t\t\t{ \"name\": \"20 with shared-subscription-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 2A\"}]},\n\t\t\t{ \"name\": \"20 with response-information (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 01 1A\"}]},\n\t\t\t{ \"name\": \"20 with server-reference (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 1C\"}]},\n\t\t\t{ \"name\": \"20 with authentication-method (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 15\"}]},\n\t\t\t{ \"name\": \"20 with authentication-data (binary data) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 16\"}]},\n\t\t\t{ \"name\": \"20 with user-property missing value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 04 26 s1 'p'\"}]},\n\t\t\t{ \"name\": \"20 with user-property missing key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 26\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNACK DISALLOWED PROPERTIES\",\n\t\t\"comment\": \"CMD RL FLAG RC PROPLEN PROPS\",\n\t\t\"ver\":5,\n\t\t\"connack\": false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"20 with payload-format-indicator (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 01 i0\"}]},\n\t\t\t{ \"name\": \"20 with request-problem-information (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 17 i0\"}]},\n\t\t\t{ \"name\": \"20 with payload-format-indicator (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 01\"}]},\n\t\t\t{ \"name\": \"20 with request-problem-information (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 17\"}]},\n\t\t\t{ \"name\": \"20 with message-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 02 L1\"}]},\n\t\t\t{ \"name\": \"20 with will-delay-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 18 L1\"}]},\n\t\t\t{ \"name\": \"20 with message-expiry-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 02\"}]},\n\t\t\t{ \"name\": \"20 with will-delay-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 18\"}]},\n\t\t\t{ \"name\": \"20 with content-type (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 03 s1 'p'\"}]},\n\t\t\t{ \"name\": \"20 with response-topic (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 08 s1 'p'\"}]},\n\t\t\t{ \"name\": \"20 with content-type (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 03\"}]},\n\t\t\t{ \"name\": \"20 with response-topic (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 08\"}]},\n\t\t\t{ \"name\": \"20 with correlation-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 09 s1 'p'\"}]},\n\t\t\t{ \"name\": \"20 with correlation-data (binary data) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 09\"}]},\n\t\t\t{ \"name\": \"20 with subscription-identifier (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0B i1\"}]},\n\t\t\t{ \"name\": \"20 with subscription-identifier (variable byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 04 00 00 v1 0B\"}]},\n\t\t\t{ \"name\": \"20 with topic-alias (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 23 H5\"}]},\n\t\t\t{ \"name\": \"20 with topic-alias (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r4 00 00 v1 23\"}]},\n\t\t\t{ \"name\": \"20 with invalid-property 0x00 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 00 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x04 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 04 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x05 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 05 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x06 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 06 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x07 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 07 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x0A (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0A i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x0C (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0C i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x0D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0D i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x0E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0E i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x0F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 0F i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x10 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 10 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x14 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 14 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x1B (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 1B i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x1D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 1D i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x1E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 1E i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x20 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 20 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r5 00 00 v2 7F i1\"}]},\n\t\t\t{ \"name\": \"20 with invalid-property 0x8000 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 8000 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x8001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 8001 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0xFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r6 00 00 v3 FF7F i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 808001 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r7 00 00 v4 FFFF7F i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0x80808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 80808001 i1\"}]},\n\t\t\t{ \"name\": \"20 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"20 r8 00 00 v5 FFFFFF7F i1\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/CONNECT.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1   CONNECT\",\n\t\t\"ver\":3,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"10 ok \", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 03 01 k10 s1 'p'\", \"comment\":\"minimal valid CONNECT\"}]},\n\t\t\t{ \"name\": \"10 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r268435456\"}]},\n\t\t\t{ \"name\": \"14 ok \", \"msgs\":[{\"type\":\"send\", \"payload\":\"14 r15 s6 'MQIsdp' 03 01 k10 s1 'p'\", \"comment\":\"CONNECT with QoS=1\"}]},\n\t\t\t{ \"name\": \"10 proto ver 2\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 02 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}]},\n\t\t\t{ \"name\": \"10 proto ver 6\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 06 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}]},\n\t\t\t{ \"name\": \"10 empty client ID\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r14 s6 'MQIsdp' 03 02 k10 0000\", \"comment\":\"CONNECT clean session true, no client id\"}]},\n\t\t\t{ \"name\": \"10 ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 03 02 k10 s1 'p'\", \"comment\":\"CONNECT clean session true, no client id\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v3.1   CONNECT instead of CONNACK\",\n\t\t\"ver\":3,\n\t\t\"connack\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"10 ok \", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 03 01 k10 s1 'p'\", \"comment\":\"minimal valid CONNECT\"}]},\n\t\t\t{ \"name\": \"10 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r268435456\"}]},\n\t\t\t{ \"name\": \"14 ok \", \"msgs\":[{\"type\":\"send\", \"payload\":\"14 r15 s6 'MQIsdp' 03 01 k10 s1 'p'\", \"comment\":\"CONNECT with QoS=1\"}]},\n\t\t\t{ \"name\": \"10 proto ver 2\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 02 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}]},\n\t\t\t{ \"name\": \"10 proto ver 6\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 06 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}]},\n\t\t\t{ \"name\": \"10 empty client ID\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r14 s6 'MQIsdp' 03 02 k10 s0\", \"comment\":\"CONNECT clean session true, no client id\"}]},\n\t\t\t{ \"name\": \"10 ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s6 'MQIsdp' 03 02 k10 s1 'p'\", \"comment\":\"CONNECT clean session true, no client id\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v3.1.1 CONNECT\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"10 ok \", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 02 k10 s1 'p'\", \"comment\":\"minimal valid CONNECT\"}]},\n\t\t\t{ \"name\": \"10 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r268435456\"}]},\n\t\t\t{ \"name\": \"10 missing client ID\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r8 s4 'MQTT' 04 02 000A\"}]},\n\t\t\t{ \"name\": \"10 empty client ID\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r12 s4 'MQTT' 04 02 k10 0000\", \"comment\":\"CONNECT clean session true, no client id\"}]},\n\t\t\t{ \"name\": \"10 empty client ID clean false [MQTT-3.1.3-7]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r12 s4 'MQTT' 04 02 k10 s0\", \"comment\":\"CONNECT clean session false, no client id\"}]},\n\t\t\t{ \"name\": \"10 proto ver 2 [MQTT-3.1.2-2]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 02 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}]},\n\t\t\t{ \"name\": \"10 proto ver 6 [MQTT-3.1.2-2]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 06 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}]},\n\t\t\t{ \"name\": \"10 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 FFFFFFFF7F s4 'MQTT' 05 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}]},\n\t\t\t{ \"name\": \"11\", \"msgs\":[{\"type\":\"send\", \"payload\":\"11 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"12\", \"msgs\":[{\"type\":\"send\", \"payload\":\"12 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"14\", \"msgs\":[{\"type\":\"send\", \"payload\":\"14 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"18\", \"msgs\":[{\"type\":\"send\", \"payload\":\"18 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 short proto\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r12 s3 'MQT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 zero proto\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r9 s0 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 long proto\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r14 s5 'MQTTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-1]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTU' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-3] \", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 01 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 0 Will QoS 1 [MQTT-3.1.2-11]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 0A k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 0 Will retain 1 [MQTT-3.1.2-11]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 12 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 1 no Will topic no Will message [MQTT-3.1.2-9]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 06 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 1 no Will topic [MQTT-3.1.2-9]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 06 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 1 ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 06 k10 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 1 Will Qos 3 [MQTT-3.1.2-14]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 1E k10 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F700000 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746FEDA080 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F700170 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F701F70 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F707F70 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746FC29F70 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746FEDBFBF s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Client ID with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F700000\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F700170\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-18]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 02 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-19]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 82 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x0000\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F700000\"}]},\n\t\t\t{ \"name\": \"10 Username with 0xD800\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x0001\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F700170\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x001F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x007F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x009F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0xFFFF\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"10 Username zero length ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 04 82 k10 s1 'p' s0\"}]},\n\t\t\t{ \"name\": \"10 Username flag 1 Password flag 1 ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 C2 k10 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-20]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 82 k10 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-21]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 C2 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-22]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 42 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Password with 0x0000\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 C2 k10 s1 'p' s1 'p' s5 746F700000\"}]},\n\t\t\t{ \"name\": \"NanoMQ CWE-119\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r7 s4 'MQTT' 04 C2 k60 s11 746573742D707974686F6E s5 61646d696E s8 70617373776F7264\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v3.1.1 CONNECT instead of CONNACK\",\n\t\t\"ver\":4,\n\t\t\"connack\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"10 ok \", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 02 k10 s1 'p'\", \"comment\":\"minimal valid CONNECT\"}]},\n\t\t\t{ \"name\": \"10 missing client ID\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r8 s4 'MQTT' 04 02 k10\"}]},\n\t\t\t{ \"name\": \"10 empty client ID\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r12 s4 'MQTT' 04 02 k10 s0\", \"comment\":\"CONNECT clean session true, no client id\"}]},\n\t\t\t{ \"name\": \"10 empty client ID clean false [MQTT-3.1.3-7]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r12 s4 'MQTT' 04 02 k10 s0\", \"comment\":\"CONNECT clean session false, no client id\"}]},\n\t\t\t{ \"name\": \"10 proto ver 2 [MQTT-3.1.2-2]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 02 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}]},\n\t\t\t{ \"name\": \"10 proto ver 6 [MQTT-3.1.2-2]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 06 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}]},\n\t\t\t{ \"name\": \"10 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 FFFFFFFF7F s4 'MQTT' 05 02 k10 s1 'p'\", \"comment\":\"CONNECT\"}]},\n\t\t\t{ \"name\": \"11\", \"msgs\":[{\"type\":\"send\", \"payload\":\"11 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"12\", \"msgs\":[{\"type\":\"send\", \"payload\":\"12 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"14\", \"msgs\":[{\"type\":\"send\", \"payload\":\"14 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"18\", \"msgs\":[{\"type\":\"send\", \"payload\":\"18 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 short proto\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r12 s3 'MQT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 zero proto\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r9 s0 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 long proto\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r14 s5 'MQTTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-1]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 02 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-3] \", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 01 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 0 Will QoS 1 [MQTT-3.1.2-11]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 0A k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 0 Will retain 1 [MQTT-3.1.2-11]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 12 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 1 no Will topic no Will message [MQTT-3.1.2-9]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 06 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 1 no Will topic [MQTT-3.1.2-9]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 06 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 1 ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 06 k10 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 1 Will Qos 3 [MQTT-3.1.2-14]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 1E k10 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F700000 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746FEDA080 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F700170 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F701F70 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746F707F70 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746FC29F70 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 06 k10 s1 'p' s5 746FEDBFBF s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Client ID with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F700000\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F700170\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 04 02 k10 s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-18]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 02 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-19]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r13 s4 'MQTT' 04 82 k10 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x0000\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F700000\"}]},\n\t\t\t{ \"name\": \"10 Username with 0xD800\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x0001\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F700170\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x001F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x007F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x009F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0xFFFF\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 04 82 k10 s1 'p' s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"10 Username zero length ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 04 82 k10 s1 'p' 0000\"}]},\n\t\t\t{ \"name\": \"10 Username flag 1 Password flag 1 ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 C2 k10 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-20]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 04 82 k10 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-21]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 C2 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-22]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 04 42 k10 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Password with 0x0000\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 04 C2 k10 s1 'p' s1 'p' s5 746F700000\"}]},\n\t\t\t{ \"name\": \"NanoMQ CWE-119\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r7 s4 'MQTT' 04 C2 003C 000B 746573742D707974686F6E s5 61646d696E s8 70617373776F7264\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNECT\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"10 ok \", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r14 s4 'MQTT' 05 02 k10 00 s1 'p'\", \"comment\":\"minimal valid CONNECT\"}]},\n\t\t\t{ \"name\": \"10 Username flag 1 ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 82 k10 00 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Client ID with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746F700000\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746F700170\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-16]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 00 0001 71 0001 71\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-17]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r14 s4 'MQTT' 05 82 k10 00 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x0000\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746F700000\"}]},\n\t\t\t{ \"name\": \"10 Username with 0xD800\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x0001\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746F700170\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x001F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x007F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x009F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0xFFFF\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-18]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 82 k10 00 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-19]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 C2 k10 00 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 1 ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 06 k10 00 s1 'p' 00 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"tiny max packet\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 2700000002 s1 'p'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNECT instead of CONNACK\",\n\t\t\"ver\":5,\n\t\t\"connack\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"10 ok \", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r14 s4 'MQTT' 05 02 k10 00 s1 'p'\", \"comment\":\"minimal valid CONNECT\"}]},\n\t\t\t{ \"name\": \"10 Username flag 1 ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 82 k10 00 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Client ID with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746F700000\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746F700170\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"10 Client ID with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 00 s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-16]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 00 0001 71 0001 71\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-17]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r14 s4 'MQTT' 05 82 k10 00 s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x0000\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746F700000\"}]},\n\t\t\t{ \"name\": \"10 Username with 0xD800\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x0001\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746F700170\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x001F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x007F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0x009F\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"10 Username with 0xFFFF\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 82 k10 00 s1 'p' s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-18]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 82 k10 00 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 [MQTT-3.1.2-19]\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 C2 k10 00 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"10 Will flag 1 ok\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 06 k10 00 s1 'p' 00 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"tiny max packet\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 2700000002 s1 'p'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNECT EXTENDED AUTH\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"unsupported authentication method\", \"msgs\":[\n\t\t\t\t{\"type\":\"send\", \"payload\":\"10 r35 s4 'MQTT' 05 02 k10 15 15000B756E737570706F7274656416000474657374 s1 'p'\", \"comment\":\"auth-method:unsupported, auth-data:test\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNECT ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 11 L1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*session-expiry-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 02 k10 0A 11 L1 11 L1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 11 s1 'p'\"}]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 21 0101 s1 'p'\"}]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer) 0 value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 21 H0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*receive-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 21 0101 21 0101 s1 'p'\"}]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 21 s1 'p'\"}]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 27 10000001 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*maximum-packet-size (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 02 k10 0A 27 10000001 27 10000001 s1 'p'\"}]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 27 s1 'p'\"}]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer) 0 value\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 27 L0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer) FFFFFFFF value\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 27 FFFFFFFF s1 'p'\"}]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 22 0101 s1 'p'\"}]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer) 0 value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 22 H0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*topic-alias-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 22 0101 22 0101 s1 'p'\"}]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 22 s1 'p'\"}]},\n\t\t\t{ \"name\": \"request-response-information (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 19 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*request-response-information (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 19 01 19 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"request-response-information (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 19 s1 'p'\"}]},\n\t\t\t{ \"name\": \"request-response-information (byte) 2 value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 19 02 s1 'p'\"}]},\n\t\t\t{ \"name\": \"request-problem-information (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 17 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*request-problem-information (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 17 01 17 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"request-problem-information (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 17 s1 'p'\"}]},\n\t\t\t{ \"name\": \"request-problem-information (byte) 2 value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 17 02 s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r21 s4 'MQTT' 05 02 k10 07 26 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*user-property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r28 s4 'MQTT' 05 02 k10 0E 26 s1 'p' s1 'p' 26 s1 'p' s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property missing value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 26 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property missing key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 26 s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property empty key\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 26 s0 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property empty value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 26 s1 'p' s0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property empty key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 26 s0 s0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 15 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*authentication-method (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 02 k10 08 15 s1 'p' 15 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"authentication-data (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 15 s1 'p' 16 s1 'p'\"}]},\n\t\t\t{ \"name\": \"authentication-data (UTF-8 string) no authentication-method\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 16 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*authentication-data (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 02 k10 0C 15 s1 'p' 16 s1 'p' 16 s1 'p' s1 'p'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   CONNECT DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"connect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"payload-format-indicator (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 01 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"payload-format-indicator (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"maximum-qos (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 24 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"maximum-qos (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 24 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"retain-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 25 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"retain-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 25 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 28 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 28 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"subscription-identifier-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 29 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"subscription-identifier-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 29 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"shared-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 2A 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"shared-subscription-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 2A 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"invalid-property 0x00 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 00 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x04 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 04 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x05 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 05 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x06 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 06 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x07 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 07 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x0A (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0A 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x0C (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0C 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x0D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0D 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x0E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0E 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x0F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0F 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x10 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 10 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x14 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 14 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x1B (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 1B 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x1D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 1D 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x1E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 1E 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x20 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 20 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 7F 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"invalid-property 0x8000 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 8000 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x8001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 8001 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0xFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 FF7F 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 808001 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0xFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 FFFF7F 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x80808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 80808001 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 FFFFFF7F 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"unknown-property 0x8080808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 8080808001 01 s1 'p'\"}]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 02 10000001 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*message-expiry-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 02 k10 0A 02 10000001 02 10000001 s1 'p'\"}]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 02 s1 'p'\"}]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer) 0 value\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 02 L0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer) FFFFFFFF value\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 02 FFFFFFFF s1 'p'\"}]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 18 10000001 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*will-delay-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 02 k10 0A 18 10000001 18 10000001 s1 'p'\"}]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 18 s1 'p'\"}]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer) 0 value\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 18 L0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer) FFFFFFFF value\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r19 s4 'MQTT' 05 02 k10 05 18 FFFFFFFF s1 'p'\"}]},\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 13 0001 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*server-keep-alive (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 13 0001 13 0001 s1 'p'\"}]},\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 13 s1 'p'\"}]},\n\t\t\t{ \"name\": \"topic-alias (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 23 0001 s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*topic-alias (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r20 s4 'MQTT' 05 02 k10 06 23 0001 23 0001 s1 'p'\"}]},\n\t\t\t{ \"name\": \"topic-alias (two byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 23 s1 'p'\"}]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 03 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 03 s1 'p'\"}]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 03 s0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 08 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 08 s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 08 s0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 12 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 12 s1 'p'\"}]},\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 12 s0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 1A s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 1A s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 1A s0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"correlation-data (binary)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r18 s4 'MQTT' 05 02 k10 04 09 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"correlation-data (binary) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 09 s1 'p'\"}]},\n\t\t\t{ \"name\": \"correlation-data (binary) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r17 s4 'MQTT' 05 02 k10 03 09 s0 s1 'p'\"}]},\n\t\t\t{\"name\": \"subscription-identifier (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r16 s4 'MQTT' 05 02 k10 02 0B 01 s1 'p'\"}]},\n\t\t\t{\"name\": \"subscription-identifier (variable byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"10 r15 s4 'MQTT' 05 02 k10 01 0B s1 'p'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   WILL ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"connect\":false,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"payload-format-indicator (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 00 s1 'p'  02 01 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"payload-format-indicator (byte) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*payload-format-indicator (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 01 01 01 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 06 k10 00 s1 'p'  05 02 L1  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*message-expiry-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r31 s4 'MQTT' 05 06 k10 00 s1 'p'  0A 02 L1 02 L1  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 02  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 06 k10 00 s1 'p'  05 18 L1  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r31 s4 'MQTT' 05 06 k10 00 s1 'p'  0A 18 L1 18 L1  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 18  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 03 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*content-type (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 00 s1 'p'  08 03 s1 'p' 03 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 03  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 03 s0  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 08 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*response-topic (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 00 s1 'p'  08 08 s1 'p' 08 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 08  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 08 s0  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"correlation-data (binary)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 09 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*correlation-data (binary)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 00 s1 'p'  08 09 s1 'p' 09 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"correlation-data (binary) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 09  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"correlation-data (binary) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 09 s0  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r28 s4 'MQTT' 05 06 k10 00 s1 'p'  07 26 s1 'p' s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*user-property\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r35 s4 'MQTT' 05 06 k10 00 s1 'p'  0E 26 s1 'p' s1 'p' 26 s1 'p' s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property missing value\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 26 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property missing key,value\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 26  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property empty key\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 00 s1 'p'  06 26 s0 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property empty value\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 00 s1 'p'  06 26 s1 'p' s0  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"user-property empty key,value\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 06 k10 00 s1 'p'  05 26 s0 s0  s1 'p' s1 'p'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   WILL DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"request-problem-information (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 00 s1 'p'  02 17 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"request-problem-information (byte) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 17  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*request-problem-information (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 17 01 17 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"request-response-information (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 00 s1 'p'  02 19 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"request-response-information (byte) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 19  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*request-response-information (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 19 01 19 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"maximum-qos (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 00 s1 'p'  02 24 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"maximum-qos (byte) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 24  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*maximum-qos (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 24 01 24 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"retain-available (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 00 s1 'p'  02 25 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"retain-available (byte) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 25  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*retain-available (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 25 01 25 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 00 s1 'p'  02 28 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 28  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*wildcard-subscription-available (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 28 01 28 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"subscription-identifier-available (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 00 s1 'p'  02 29 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"subscription-identifier-available (byte) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 29  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*subscription-identifier-available (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 29 01 29 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"shared-subscription-available (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 00 s1 'p'  02 2A 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"shared-subscription-available (byte) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 2A  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*shared-subscription-available (byte)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 2A 01 2A 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 13 0001  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 13  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*server-keep-alive (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 00 s1 'p'  06 13 0001 13 0001  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 21 0001  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 21  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*receive-maximum (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 00 s1 'p'  06 21 0001 21 0001  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 22 0001  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 22  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*topic-alias-maximum (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 00 s1 'p'  06 22 0001 22 0001  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"topic-alias (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 23 0001  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"topic-alias (two byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 23  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*topic-alias (two byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r27 s4 'MQTT' 05 06 k10 00 s1 'p'  06 23 0001 23 0001  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 06 k10 00 s1 'p'  05 11 L1  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 11  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*session-expiry-interval (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r31 s4 'MQTT' 05 06 k10 00 s1 'p'  0A 11 L1 11 L1  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r26 s4 'MQTT' 05 06 k10 00 s1 'p'  05 27 L1  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 27  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*maximum-packet-size (four byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r31 s4 'MQTT' 05 06 k10 00 s1 'p'  0A 27 L1 27 L1  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 12 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*assigned-client-identifier (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 00 s1 'p'  08 12 s1 'p' 12 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 12  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 12 s0  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 15 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*authentication-method (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 00 s1 'p'  08 15 s1 'p' 15 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 15  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 15 s0  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 1A s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*response-information (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 00 s1 'p'  08 1A s1 'p' 1A s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 1A  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 1A s0  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"server-reference (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 1C s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*server-reference (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 00 s1 'p'  08 1C s1 'p' 1C s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"server-reference (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 1C  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"server-reference (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 1C s0  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"reason-string (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 1F s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*reason-string (UTF-8 string)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 00 s1 'p'  08 1F s1 'p' 1F s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"reason-string (UTF-8 string) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 1F  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"reason-string (UTF-8 string) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 1F s0  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"authentication-data (binary)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r25 s4 'MQTT' 05 06 k10 00 s1 'p'  04 16 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"2*authentication-data (binary)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r29 s4 'MQTT' 05 06 k10 00 s1 'p'  08 16 s1 'p' 16 s1 'p'  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"authentication-data (binary) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 16  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"authentication-data (binary) empty\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r24 s4 'MQTT' 05 06 k10 00 s1 'p'  03 16 s0  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"subscription-identifier (variable byte integer)\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r23 s4 'MQTT' 05 06 k10 00 s1 'p'  02 0B 01  s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"subscription-identifier (variable byte integer) missing\", \"msgs\":[{\"type\":\"send\", \"payload\":\"10 r22 s4 'MQTT' 05 06 k10 00 s1 'p'  01 0B  s1 'p' s1 'p'\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/DISCONNECT.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 DISCONNECT\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"E0 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r0\"}]},\n\t\t\t{ \"name\": \"E0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"E0 r268435456\"}]},\n\t\t\t{ \"name\": \"E0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 00\"}]},\n\t\t\t{ \"name\": \"E0 valid\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r0\"}]},\n\t\t\t{ \"name\": \"E1 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E1 r0\"}]},\n\t\t\t{ \"name\": \"E2 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E2 r0\"}]},\n\t\t\t{ \"name\": \"E4 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E4 r0\"}]},\n\t\t\t{ \"name\": \"E8 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E8 r0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   DISCONNECT\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"E0 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r0\"}]},\n\t\t\t{ \"name\": \"E0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"E0 r268435456\"}]},\n\t\t\t{ \"name\": \"E0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 00\"}]},\n\t\t\t{ \"name\": \"E0 valid\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r0\"}]},\n\t\t\t{ \"name\": \"E1 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E1 r0\"}]},\n\t\t\t{ \"name\": \"E2 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E2 r0\"}]},\n\t\t\t{ \"name\": \"E4 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E4 r0\"}]},\n\t\t\t{ \"name\": \"E8 [MQTT-3.14.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E8 r0\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x00 (normal disconnection)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 00\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x01 (qos 1 - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 01\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x04 (disconnect with will)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 04\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x05 (invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 05\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x80 (unspecified error)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 80\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x81 (malformed packet)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 81\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x82 (protocol error)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 82\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x83 (implementation specific error)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 83\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x87 (not authorised - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 87\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x89 (server busy - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 89\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x8B (server shutting down - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 8B\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x8D (keep alive timeout - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 8D\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x8E (session taken over - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 8E\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x8F (topic filter invalid - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 8F\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x90 (topic name invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 90\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x93 (receive maximum exceeded)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 93\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x94 (topic alias invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 94\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x95 (packet too large)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 95\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x96 (message rate too high)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 96\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x97 (quota exceeded)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 97\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x98 (administrative action)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 98\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x99 (payload format invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 99\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9A (retain not supported - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9A\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9B (qos not supported - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9B\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9C (use another server - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9C\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9D (server moved - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9D\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9E (shared subs not supported - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9E\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x9F (connection rate exceeded - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 9F\"}]},\n\t\t\t{ \"name\": \"E0 RC=0xA0 (maximum connect time - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 A0\"}]},\n\t\t\t{ \"name\": \"E0 RC=0xA1 (subscription ids not supported - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 A1\"}]},\n\t\t\t{ \"name\": \"E0 RC=0xA2 (wildcard subs not supported - invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r1 A2\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x82 PL=0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r2 82 00\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=1 P=0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 01 00\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=1 P=0x11\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 01 11\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=2 P=0x11\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 02 1100\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=3 P=0x11\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r5 00 03 110000\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=4 P=0x11\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 04 11000000\"}]},\n\t\t\t{ \"name\": \"E0 RC=0x00 PL=5 P=0x11\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r7 00 05 1100000000\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   DISCONNECT ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"E0 with reason-string property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 04 1F s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with 2*reason-string property (invalid)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r10 00 v8 1F s1 'p' 1F s1 'q'\"}]},\n\t\t\t{ \"name\": \"E0 with reason-string property missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 1F\"}]},\n\t\t\t{ \"name\": \"E0 with reason-string property empty\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r5 00 v3 1F s0\"}]},\n\t\t\t{ \"name\": \"E0 with user-property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r9 00 v7 26 s1 'p' s1 'q'\"}]},\n\t\t\t{ \"name\": \"E0 with user-property missing value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 26 s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with user-property missing key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 26\"}]},\n\t\t\t{ \"name\": \"E0 with user-property empty key\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r8 00 v6 26 s0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with user-property empty key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r8 00 v6 26 s1 'p' s0\"}]},\n\t\t\t{ \"name\": \"E0 with user-property empty key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r7 00 v5 26 s0 s0\"}]},\n\t\t\t{ \"name\": \"E0 with session-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r7 00 v5 11 L0\"}]},\n\t\t\t{ \"name\": \"E0 with 2*session-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r12 00 v10 11 L0 11 L0\"}]},\n\t\t\t{ \"name\": \"E0 with session-expiry-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 11\"}]},\n\t\t\t{ \"name\": \"E0 with server-reference (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 1C s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with 2*server-reference (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r10 00 v8 1C s1 'p' 1C s1 'q'\"}]},\n\t\t\t{ \"name\": \"E0 with server-reference (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 1C\"}]},\n\t\t\t{ \"name\": \"E0 with server-reference (UTF-8 string) empty\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r5 00 v3 1C s0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   DISCONNECT DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"E0 with payload-format-indicator (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 01 i0\"}]},\n\t\t\t{ \"name\": \"E0 with request-problem-information (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 17 i0\"}]},\n\t\t\t{ \"name\": \"E0 with maximum-qos (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 24 i0\"}]},\n\t\t\t{ \"name\": \"E0 with retain-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 25 i0\"}]},\n\t\t\t{ \"name\": \"E0 with wildcard-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 28 i0\"}]},\n\t\t\t{ \"name\": \"E0 with subscription-identifier-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 29 i0\"}]},\n\t\t\t{ \"name\": \"E0 with shared-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 2A i0\"}]},\n\t\t\t{ \"name\": \"E0 with payload-format-indicator (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 01\"}]},\n\t\t\t{ \"name\": \"E0 with request-problem-information (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 17\"}]},\n\t\t\t{ \"name\": \"E0 with maximum-qos (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 24\"}]},\n\t\t\t{ \"name\": \"E0 with retain-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 25\"}]},\n\t\t\t{ \"name\": \"E0 with wildcard-subscription-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 28\"}]},\n\t\t\t{ \"name\": \"E0 with subscription-identifier-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 29\"}]},\n\t\t\t{ \"name\": \"E0 with shared-subscription-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 2A\"}]},\n\t\t\t{ \"name\": \"E0 with message-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r7 00 v5 02 L1\"}]},\n\t\t\t{ \"name\": \"E0 with will-delay-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r7 00 v5 18 L1\"}]},\n\t\t\t{ \"name\": \"E0 with maximum-packet-size (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r7 00 v5 27 L1\"}]},\n\t\t\t{ \"name\": \"E0 with message-expiry-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 02\"}]},\n\t\t\t{ \"name\": \"E0 with will-delay-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 18\"}]},\n\t\t\t{ \"name\": \"E0 with maximum-packet-size (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 27\"}]},\n\t\t\t{ \"name\": \"E0 with content-type (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 03 s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with response-topic (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 08 s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 12 s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with authentication-method (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 15 s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with response-information (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 1A s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with content-type (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 03\"}]},\n\t\t\t{ \"name\": \"E0 with response-topic (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 08\"}]},\n\t\t\t{ \"name\": \"E0 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 12\"}]},\n\t\t\t{ \"name\": \"E0 with authentication-method (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 15\"}]},\n\t\t\t{ \"name\": \"E0 with response-information (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 1A\"}]},\n\t\t\t{ \"name\": \"E0 with correlation-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 09 s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with authentication-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 16 s1 'p'\"}]},\n\t\t\t{ \"name\": \"E0 with correlation-data (binary data) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 09\"}]},\n\t\t\t{ \"name\": \"E0 with authentication-data (binary data) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 16\"}]},\n\t\t\t{ \"name\": \"E0 with subscription-identifier (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 0B v1\"}]},\n\t\t\t{ \"name\": \"E0 with subscription-identifier (variable byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 0B\"}]},\n\t\t\t{ \"name\": \"E0 with server-keep-alive (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r5 00 03 13 H5\"}]},\n\t\t\t{ \"name\": \"E0 with receive-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r5 00 v3 21 H5\"}]},\n\t\t\t{ \"name\": \"E0 with topic-alias-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r5 00 v3 22 H5\"}]},\n\t\t\t{ \"name\": \"E0 with topic-alias (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r5 00 v3 23 H5\"}]},\n\t\t\t{ \"name\": \"E0 with server-keep-alive (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 13\"}]},\n\t\t\t{ \"name\": \"E0 with receive-maximum (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 21\"}]},\n\t\t\t{ \"name\": \"E0 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 22\"}]},\n\t\t\t{ \"name\": \"E0 with topic-alias (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r3 00 v1 23\"}]},\n\t\t\t{ \"name\": \"E0 with invalid-property 0x00 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 00 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x04 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 04 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x05 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 05 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x06 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 06 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x07 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 07 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x0A (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 0A i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x0C (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 0C i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x0D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 0D i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x0E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 0E i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x0F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 0F i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x10 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 10 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x14 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 14 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x1B (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 1B i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x1D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 1D i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x1E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 1E i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x20 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 20 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r4 00 v2 7F i1\"}]},\n\t\t\t{ \"name\": \"E0 with invalid-property 0x8000 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r5 00 v3 8000 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x8001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r5 00 v3 8001 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0xFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r5 00 v3 FF7F i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 808001 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r6 00 v4 FFFF7F i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0x80808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r7 00 v5 80808001 i1\"}]},\n\t\t\t{ \"name\": \"E0 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"E0 r7 00 v5 FFFFFF7F i1\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/FORBIDDEN.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 FORBIDDEN\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"00 first packet\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"00 r0\"}]},\n\t\t\t{ \"name\": \"00 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"00 r268435456\"}]},\n\t\t\t{ \"name\": \"01 first packet\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"01 r0\"}]},\n\t\t\t{ \"name\": \"02 first packet\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"02 r0\"}]},\n\t\t\t{ \"name\": \"04 first packet\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"04 r0\"}]},\n\t\t\t{ \"name\": \"08 first packet\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"08 r0\"}]},\n\t\t\t{ \"name\": \"00 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"00 r1 00\"}]},\n\t\t\t{ \"name\": \"00\", \"msgs\": [{\"type\":\"send\", \"payload\":\"00 r0\"}]},\n\t\t\t{ \"name\": \"01\", \"msgs\": [{\"type\":\"send\", \"payload\":\"01 r0\"}]},\n\t\t\t{ \"name\": \"02\", \"msgs\": [{\"type\":\"send\", \"payload\":\"02 r0\"}]},\n\t\t\t{ \"name\": \"04\", \"msgs\": [{\"type\":\"send\", \"payload\":\"04 r0\"}]},\n\t\t\t{ \"name\": \"08\", \"msgs\": [{\"type\":\"send\", \"payload\":\"08 r0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   FORBIDDEN\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"00 first packet\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"00 r0\"}]},\n\t\t\t{ \"name\": \"00 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"00 r268435456\"}]},\n\t\t\t{ \"name\": \"01 first packet\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"01 r0\"}]},\n\t\t\t{ \"name\": \"02 first packet\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"02 r0\"}]},\n\t\t\t{ \"name\": \"04 first packet\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"04 r0\"}]},\n\t\t\t{ \"name\": \"08 first packet\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"08 r0\"}]},\n\t\t\t{ \"name\": \"00 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"00 r1 00\"}]},\n\t\t\t{ \"name\": \"00\", \"msgs\": [{\"type\":\"send\", \"payload\":\"00 r0\"}]},\n\t\t\t{ \"name\": \"01\", \"msgs\": [{\"type\":\"send\", \"payload\":\"01 r0\"}]},\n\t\t\t{ \"name\": \"02\", \"msgs\": [{\"type\":\"send\", \"payload\":\"02 r0\"}]},\n\t\t\t{ \"name\": \"04\", \"msgs\": [{\"type\":\"send\", \"payload\":\"04 r0\"}]},\n\t\t\t{ \"name\": \"08\", \"msgs\": [{\"type\":\"send\", \"payload\":\"08 r0\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/PINGREQ.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PINGREQ\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"C0 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 r0\"}]},\n\t\t\t{ \"name\": \"C0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"C0 r268435456\"}]},\n\t\t\t{ \"name\": \"C0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 r1 00\"}]},\n\t\t\t{ \"name\": \"C0 valid\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 00\"}]},\n\t\t\t{ \"name\": \"C1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C1 r0\"}]},\n\t\t\t{ \"name\": \"C2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C2 r0\"}]},\n\t\t\t{ \"name\": \"C4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C4 r0\"}]},\n\t\t\t{ \"name\": \"C8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C8 r0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PINGREQ\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"C0 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 r0\"}]},\n\t\t\t{ \"name\": \"C0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"C0 r268435456\"}]},\n\t\t\t{ \"name\": \"C0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 r1 00\"}]},\n\t\t\t{ \"name\": \"C0 valid\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C0 r0\"}]},\n\t\t\t{ \"name\": \"C1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C1 r0\"}]},\n\t\t\t{ \"name\": \"C2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C2 r0\"}]},\n\t\t\t{ \"name\": \"C4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C4 r0\"}]},\n\t\t\t{ \"name\": \"C8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"C8 r0\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/PINGRESP.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PINGRESP\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"D0 [MQTT-3.1.0-1]\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r0\"}]},\n\t\t\t{ \"name\": \"D0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"D0 r268435456\"}]},\n\t\t\t{ \"name\": \"D0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r1 00\"}]},\n\t\t\t{ \"name\": \"D0\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r0\"}]},\n\t\t\t{ \"name\": \"D1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D1 r0\"}]},\n\t\t\t{ \"name\": \"D2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D2 r0\"}]},\n\t\t\t{ \"name\": \"D4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D4 r0\"}]},\n\t\t\t{ \"name\": \"D8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D8 r0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PINGRESP\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"D0 [MQTT-3.1.0-1]\", \"connack\": false, \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r0\"}]},\n\t\t\t{ \"name\": \"D0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"D0 r268435456\"}]},\n\t\t\t{ \"name\": \"D0 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r1 00\"}]},\n\t\t\t{ \"name\": \"D0\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"D0 r0\"}]},\n\t\t\t{ \"name\": \"D1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D1 r0\"}]},\n\t\t\t{ \"name\": \"D2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D2 r0\"}]},\n\t\t\t{ \"name\": \"D4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D4 r0\"}]},\n\t\t\t{ \"name\": \"D8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"D8 r0\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/PUBACK.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PUBACK unsolicited\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"40 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m1\"}]},\n\t\t\t{ \"name\": \"40 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"40 r268435456\"}]},\n\t\t\t{ \"name\": \"40 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"40 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m0\"}]},\n\t\t\t{ \"name\": \"40 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r0\"}]},\n\t\t\t{ \"name\": \"40 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r1 01\"}]},\n\t\t\t{ \"name\": \"40\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m1\"}]},\n\t\t\t{ \"name\": \"41\", \"msgs\": [{\"type\":\"send\", \"payload\":\"41 r2 m1\"}]},\n\t\t\t{ \"name\": \"42\", \"msgs\": [{\"type\":\"send\", \"payload\":\"42 r2 m1\"}]},\n\t\t\t{ \"name\": \"44\", \"msgs\": [{\"type\":\"send\", \"payload\":\"44 r2 m1\"}]},\n\t\t\t{ \"name\": \"48\", \"msgs\": [{\"type\":\"send\", \"payload\":\"48 r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v3.1.1 PUBACK\",\n\t\t\"ver\":4,\n\t\t\"command\":\"publish-1\",\n\t\t\"group_msgs\": [\n\t\t\t{\"type\":\"recv\", \"payload\":\"32 r23 s12 'test/publish' m1 'message'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"40 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"40 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"40 r268435456\"}]},\n\t\t\t{ \"name\": \"40 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m0\"}]},\n\t\t\t{ \"name\": \"40 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r0\"}]},\n\t\t\t{ \"name\": \"40 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r1 01\"}]},\n\t\t\t{ \"name\": \"41\", \"msgs\": [{\"type\":\"send\", \"payload\":\"41 r2 m1\"}]},\n\t\t\t{ \"name\": \"42\", \"msgs\": [{\"type\":\"send\", \"payload\":\"42 r2 m1\"}]},\n\t\t\t{ \"name\": \"44\", \"msgs\": [{\"type\":\"send\", \"payload\":\"44 r2 m1\"}]},\n\t\t\t{ \"name\": \"48\", \"msgs\": [{\"type\":\"send\", \"payload\":\"48 r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBACK unsolicited\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"40 [MQTT-3.1.0-1] (no reason code)\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m1\"}]},\n\t\t\t{ \"name\": \"40 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"40 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r5 m1 00 00 00\"}]},\n\t\t\t{ \"name\": \"40 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r3 m0 00\"}]},\n\t\t\t{ \"name\": \"40 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r0\"}]},\n\t\t\t{ \"name\": \"40 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r1 01\"}]},\n\t\t\t{ \"name\": \"40 len=2\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r2 m1\"}]},\n\t\t\t{ \"name\": \"40 len=3\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"40 len=3 fail\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r3 m1 80\"}]},\n\t\t\t{ \"name\": \"40 len=4 ok\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r4 m1 00 00\"}]},\n\t\t\t{ \"name\": \"40 len=4 rc=fail\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r4 m1 80 00\"}]},\n\t\t\t{ \"name\": \"40 len=4 rc=unknown\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r4 m1 FF 00\"}]},\n\t\t\t{ \"name\": \"40 len=4 short\", \"msgs\": [{\"type\":\"send\", \"payload\":\"40 r4 m1 00 01\"}]},\n\t\t\t{ \"name\": \"41 \", \"msgs\": [{\"type\":\"send\", \"payload\":\"41 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"42\", \"msgs\": [{\"type\":\"send\", \"payload\":\"42 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"44\", \"msgs\": [{\"type\":\"send\", \"payload\":\"44 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"48\", \"msgs\": [{\"type\":\"send\", \"payload\":\"48 r3 m1 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBACK\",\n\t\t\"ver\":5,\n\t\t\"command\": \"publish-1\",\n\t\t\"group_msgs\": [\n\t\t\t{\"type\":\"recv\", \"payload\":\"32 r116 s12 'test/publish' m1 v92 01 i1 02 ffffffff 23 ffff 08 s14 'response/topic' 09 H36 '7deac5c5-8802-44ff-86ce-11479f337419' 03 s10 'text/plain' 26 s3 'key' s5 'value' 'message'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"40 long\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 mid 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r3 m0 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 short 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 short 1\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r1 01\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 len=2\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 len=3\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r3 m1 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 len=3 fail\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r3 m1 80\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 len=4 ok\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r4 m1 00 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 len=4 rc=fail\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r4 m1 80 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 len=4 rc=unknown\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r4 m1 FF 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 len=4 short\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r4 m1 00 01\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"41\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"41 r3 m1 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"42\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"42 r3 m1 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"44\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"44 r3 m1 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"48\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"48 r3 m1 00\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBACK ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"expect_disconnect\":false,\n\t\t\"command\":\"publish-1\",\n\t\t\"group_msgs\": [\n\t\t\t{\"type\":\"recv\", \"payload\":\"32 r116 s12 'test/publish' m1 v92 01 i1 02 ffffffff 23 ffff 08 s14 'response/topic' 09 H36 '7deac5c5-8802-44ff-86ce-11479f337419' 03 s10 'text/plain' 26 s3 'key' s5 'value' 'message'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"40 with reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 1F s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with 2*reason-string property\", \"expect_disconnect\":true, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r12 m1 00 v8 1F s1 'p' 1F s1 'q'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with reason-string property missing\", \"expect_disconnect\":true, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 1F\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with reason-string property incomplete string\", \"expect_disconnect\":true, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 1F 00\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with reason-string property empty string\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 1F s0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r11 m1 00 v7 26 s1 'p' s1 'q'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with 2*user-property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r18 m1 00 v14 26 s1 'p' s1 'q' 26 s1 'p' s1 'q'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property missing value\", \"expect_disconnect\":true, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 26 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property missing key,value\", \"expect_disconnect\":true, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 26\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property empty key\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r10 m1 00 v6 26 s0 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property empty value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r10 m1 00 v6 26 s1 'p' s0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with user-property empty key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 26 s0 s0\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBACK DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"command\":\"publish-1\",\n\t\t\"group_msgs\": [\n\t\t\t{\"type\":\"recv\", \"payload\":\"32 r116 s12 'test/publish' m1 v92 01 i1 02 ffffffff 23 ffff 08 s14 'response/topic' 09 H36 '7deac5c5-8802-44ff-86ce-11479f337419' 03 s10 'text/plain' 26 s3 'key' s5 'value' 'message'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"40 with payload-format-indicator (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 01 i0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 17 i0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 24 i0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 25 i0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 28 i0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 29 i0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 2A i0\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 01\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 17\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 24\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 25\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 28\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 29\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 2A\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with message-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 02 L1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 11 L1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 18 L1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 27 L1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 02\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 11\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 18\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 27\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 03 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 08 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 12 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 15 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 1A s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 1C s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 03\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 08\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 12\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 15\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 1A\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 1C\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 09 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 16 s1 'p'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 09\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 16\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with subscription-identifier (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0B v1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 0B\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 13 H5\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 21 H5\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 22 H5\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 23 H5\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 13\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 21\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 22\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r5 m1 00 v1 23\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 00 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 04 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 05 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 06 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 07 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0A i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0C i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0D i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0E i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 0F i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 10 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 14 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 1B i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 1D i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 1E i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 20 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r6 m1 00 v2 7F i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 8000 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 8001 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r7 m1 00 v3 FF7F i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 808001 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r8 m1 00 v4 FFFF7F i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 80808001 i1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"40 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"40 r9 m1 00 v5 FFFFFF7F i1\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/PUBLISH.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PUBLISH\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"30 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 'topic' 'payload'\"}]},\n\t\t\t{ \"name\": \"30 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"30 r268435456\"}]},\n\t\t\t{ \"name\": \"30\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 s5 'topic' 'payload'\"}]},\n\t\t\t{ \"name\": \"31 retain 1\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"31 r14 s5 'topic' 'payload'\"}]},\n\t\t\t{ \"name\": \"31 retain 1 zero length\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"31 r7 s5 'topic'\"}]},\n\t\t\t{ \"name\": \"30 topic 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r9 s0 'payload'\"}]},\n\t\t\t{ \"name\": \"38 QoS 0 Dup 1\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"38 r14 s5 'topic' 'payload'\"}]},\n\t\t\t{ \"name\": \"36 QoS 3 (no mid) [MQTT-3.3.1-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"36 r14 s5 'topic' 'payload'\"}]},\n\t\t\t{ \"name\": \"36 QoS 3 (with mid) [MQTT-3.3.1-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"36 r16 s5 'topic' m1234 'payload'\"}]},\n\t\t\t{ \"name\": \"32 QoS 1 Mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"32 r16 s5 'topic' m0 'payload'\"}]},\n\t\t\t{ \"name\": \"34 QoS 2 Mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"34 r16 s5 'topic' m0 'payload'\"}]},\n\t\t\t{ \"name\": \"32 QoS 1 Dup 0\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r16 s5 'topic' m1234 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"3A QoS 1 Dup 1\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"3A r16 s5 'topic' m1234 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"34 QoS 2 Dup 0\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"34 r16 s5 'topic' m1234 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r2 m1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"3C QoS 2 Dup 1\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"3C r16 s5 'topic' m1234 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r2 m1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 m5 746F700000 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 m5 746FEDA080 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 m5 746F700170 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 m5 746F701F70 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 m5 746F707F70 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 m5 746FC29F70 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 m5 746FEDBFBF 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with U+2A6D4 (section 1.5.3.1)\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 m5 41F0AA9B94 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with + [MQTT-3.3.2-2]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 m5 2B6F706963 'payload'\"}]},\n\t\t\t{ \"name\": \"30 topic with # [MQTT-3.3.2-2]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r14 m5 236F706963 'payload'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBLISH\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"30 [MQTT-3.1.0-1]\", \"connect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r15 s5 'topic' 00 'payload'\"}]},\n\t\t\t{ \"name\": \"30 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"30 r268435456\"}]},\n\t\t\t{ \"name\": \"30\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"30 r15 s5 'topic' 00 'payload'\"}]},\n\t\t\t{ \"name\": \"31 retain 1\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"31 r15 s5 'topic' 00 'payload'\"}]},\n\t\t\t{ \"name\": \"31 retain 1 zero length\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"31 r8 s5 'topic' 00\"}]},\n\t\t\t{ \"name\": \"30 topic 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r10 m0 00 'payload'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"36 QoS 3 (no mid) [MQTT-3.3.1-4]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"36 r15 s5 'topic' 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"36 QoS 3 (with mid) [MQTT-3.3.1-4]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"36 r17 s5 'topic' m1234 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"32 QoS 1 Mid 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r17 s5 'topic' m0 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"34 QoS 2 Mid 0\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"34 r17 s5 'topic' m0 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"32 QoS 1 Dup 0\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r17 s5 'topic' m1234 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"3A QoS 1 Dup 1\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"3A r17 s5 'topic' m1234 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"34 QoS 2 Dup 0\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"34 r17 s5 'topic' m1234 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r2 m1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"3C QoS 2 Dup 1\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"3C r17 s5 'topic' m1234 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"50 r2 m1234\"},\n\t\t\t\t{\"type\":\"send\", \"payload\":\"62 r2 m1234\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"70 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with 0x0000\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746F700000 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+D800\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746FEDA080 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+0001\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746F700170 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+001F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746F701F70 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+007F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746F707F70 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+009F\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746FC29F70 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+FFFF\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 746FEDBFBF 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with U+2A6D4 (section 1.5.3.1)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 41F0AA9B94 00 'payload'\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with + [MQTT-3.3.2-2]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 '+opic' 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"30 topic with # [MQTT-3.3.2-2]\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r15 s5 '#opic' 00 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBLISH ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"payload-format-indicator=0 (byte)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 01 i0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"payload-format-indicator=1 (byte)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 01 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"payload-format-indicator=2 (byte, invalid)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 01 i2 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*payload-format-indicator=1 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 01 i1 01 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"payload-format-indicator (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 01 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"message-expiry-interval=0 (four byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 02 L0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"message-expiry-interval=1 (four byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 02 L1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*message-expiry-interval=1 (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r27 s5 'topic' m1234 v10 02 L1 02 L1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"message-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 02 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"topic alias > max topic alias\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"30 r18 s5 'topic' v3 23 H11 'payload'\", \"comment\":\"PUBLISH with topic alias 11 (server has set max topic alias=10)\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 94\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 23 H1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 94\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*topic-alias (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 23 H1 23 H1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*topic-alias different (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 23 H1 23 H2 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 23 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"response-topic (UTF-8 string)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 08 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string, with wildcard)\", \"ver\":5, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 08 s1 '#' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*response-topic (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r25 s5 'topic' m1234 v8 08 s1 'p' 08 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 08 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-topic (UTF-8 string) empty\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 08 s0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"correlation-data (binary data)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 09 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*correlation-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r25 s5 'topic' m1234 v8 09 s1 'p' 09 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"correlation-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 09 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"correlation-data (binary data) empty\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 09 H0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r24 s5 'topic' m1234 v7 26 s1 'p' s1 'q' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r31 s5 'topic' m1234 v14 26 s1 'p' s1 'q' 26 s1 'p' s1 'q' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property missing value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 26 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property missing key,value\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 26 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty key\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 26 s0 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 26 s1 'p' s0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 26 s0 s0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"subscription-identifier=0 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0B v0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x7F (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0B 7F 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x8000 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 0B 8000 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x8001 (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 0B 8001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0xFF7F (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 0B FF7F 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x808001 (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 0B 808001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0xFFFF7F (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 0B FFFF7F 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x80808001 (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 0B 80808001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0xFFFFFF7F (variable byte integer)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 0B FFFFFF7F 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier=0x8080808001 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 0B 8080808001 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*subscription-identifier=1 (variable byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 0B v1 0B v1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"subscription-identifier (variable byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 0B 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"content-type (UTF-8 string)\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 03 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"2*content-type (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r25 s5 'topic' m1234 v8 03 s1 'p' 03 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 03 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"content-type (UTF-8 string) empty\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 03 s0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"40 r2 m1234\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBLISH DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"reason-string property\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 1F s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"request-problem-information (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 17 i0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-qos (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 24 i0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"retain-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 25 i0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 28 i0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 29 i0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"shared-subscription-available (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 2A i0 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"request-problem-information (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 17 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-qos (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 24 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"retain-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 25 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"wildcard-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 28 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"subscription-identifier-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 29 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"shared-subscription-available (byte) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 2A 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 11 L1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 18 L1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 27 L1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"session-expiry-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v4 11 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"will-delay-interval (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v4 18 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"maximum-packet-size (four byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v4 27 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 12 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 15 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 1A s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"server-reference (UTF-8 string)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 1C s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 12 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"authentication-method (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 15 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"response-information (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 1A 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"server-reference (UTF-8 string) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 1C 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"authentication-data (binary data)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 16 s1 'p' 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"authentication-data (binary data) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 16 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 13 H5 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 21 H5 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 22 H5 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 82\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"server-keep-alive (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 13 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"receive-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 21 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"topic-alias-maximum (two byte integer) missing\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r18 s5 'topic' m1234 v1 22 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\n\t\t\t{ \"name\": \"invalid-property 0x00 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 00 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x04 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 04 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x05 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 05 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x06 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 06 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x07 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 07 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0A (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0A i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0C (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0C i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0D i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0E i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x0F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 0F i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x10 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 10 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x14 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 14 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x1B (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 1B i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x1D (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 1D i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x1E (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 1E i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x20 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 20 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r19 s5 'topic' m1234 v2 7F i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"invalid-property 0x8000 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 8000 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x8001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 8001 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0xFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r20 s5 'topic' m1234 v3 FF7F i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 808001 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0xFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r21 s5 'topic' m1234 v4 FFFF7F i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x80808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 80808001 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r22 s5 'topic' m1234 v5 FFFFFF7F i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"unknown-property 0x8080808001 (byte)\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"32 r23 s5 'topic' m1234 v6 8080808001 i1 'payload'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"E0 r1 81\"}\n\t\t\t]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/PUBREC.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 PUBREC unsolicited\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r2 m1\"}]},\n\t\t\t{ \"name\": \"50 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"50 r268435456\"}]},\n\t\t\t{ \"name\": \"50\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"50 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r2 m0\"}]},\n\t\t\t{ \"name\": \"50 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r0\"}]},\n\t\t\t{ \"name\": \"50 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r1 01\"}]},\n\t\t\t{ \"name\": \"51\", \"msgs\": [{\"type\":\"send\", \"payload\":\"51 r2 m1\"}]},\n\t\t\t{ \"name\": \"52\", \"msgs\": [{\"type\":\"send\", \"payload\":\"52 r2 m1\"}]},\n\t\t\t{ \"name\": \"54\", \"msgs\": [{\"type\":\"send\", \"payload\":\"54 r2 m1\"}]},\n\t\t\t{ \"name\": \"58\", \"msgs\": [{\"type\":\"send\", \"payload\":\"58 r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v3.1.1 PUBREC\",\n\t\t\"ver\":4,\n\t\t\"command\":\"publish-2\",\n\t\t\"group_msgs\":[\n\t\t\t{\"type\":\"recv\", \"payload\":\"34 r23 s12 'test/publish' m1 'message'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"50 r268435456\"}]},\n\t\t\t{ \"name\": \"50 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"50 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r2 m0\"}]},\n\t\t\t{ \"name\": \"50 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r0\"}]},\n\t\t\t{ \"name\": \"50 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r1 01\"}]},\n\t\t\t{ \"name\": \"51\", \"msgs\": [{\"type\":\"send\", \"payload\":\"51 r2 m1\"}]},\n\t\t\t{ \"name\": \"52\", \"msgs\": [{\"type\":\"send\", \"payload\":\"52 r2 m1\"}]},\n\t\t\t{ \"name\": \"54\", \"msgs\": [{\"type\":\"send\", \"payload\":\"54 r2 m1\"}]},\n\t\t\t{ \"name\": \"58\", \"msgs\": [{\"type\":\"send\", \"payload\":\"58 r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREC unsolicited\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50 [MQTT-3.1.0-1] (no reason code)\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r2 m1\"}]},\n\t\t\t{ \"name\": \"50 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"50 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 00 00\"}]},\n\t\t\t{ \"name\": \"50 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r3 m0 00\"}]},\n\t\t\t{ \"name\": \"50 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r0\"}]},\n\t\t\t{ \"name\": \"50 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r1 01\"}]},\n\t\t\t{ \"name\": \"50 len=2\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 len=3\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 len=3 fail\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r3 m1 80\"}]},\n\t\t\t{ \"name\": \"50 len=4 ok\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r4 m1 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 len=4 rc=fail\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r4 m1 80 00\"}]},\n\t\t\t{ \"name\": \"50 len=4 rc=unknown\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r4 m1 FF 00\"}]},\n\t\t\t{ \"name\": \"50 len=4 short\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r4 m1 00 01\"}]},\n\t\t\t{ \"name\": \"51\", \"msgs\": [{\"type\":\"send\", \"payload\":\"51 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"52\", \"msgs\": [{\"type\":\"send\", \"payload\":\"52 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"54\", \"msgs\": [{\"type\":\"send\", \"payload\":\"54 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"58\", \"msgs\": [{\"type\":\"send\", \"payload\":\"58 r3 m1 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREC valid\",\n\t\t\"ver\":5,\n\t\t\"expect_disconnect\":false,\n\t\t\"command\":\"publish-2\",\n\t\t\"group_msgs\":[\n\t\t\t{\"type\":\"recv\", \"payload\":\"34 r116 s12 'test/publish' m1 v92 01 i1 02 ffffffff 23 ffff 08 s14 'response/topic' 09 H36 '7deac5c5-8802-44ff-86ce-11479f337419' 03 s10 'text/plain' 26 s3 'key' s5 'value' 'message'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50 len=2\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r2 m1\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 len=3\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r3 m1 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 len=3 fail\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r3 m1 80\"}]},\n\t\t\t{ \"name\": \"50 len=4 ok\", \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r4 m1 00 00\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 len=4 rc=fail\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r4 m1 80 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREC invalid\",\n\t\t\"ver\":5,\n\t\t\"command\":\"publish-2\",\n\t\t\"expect_disconnect\":true,\n\t\t\"group_msgs\":[\n\t\t\t{\"type\":\"recv\", \"payload\":\"34 r116 s12 'test/publish' m1 v92 01 i1 02 ffffffff 23 ffff 08 s14 'response/topic' 09 H36 '7deac5c5-8802-44ff-86ce-11479f337419' 03 s10 'text/plain' 26 s3 'key' s5 'value' 'message'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 00 00\"}]},\n\t\t\t{ \"name\": \"50 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r3 m0 00\"}]},\n\t\t\t{ \"name\": \"50 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r0\"}]},\n\t\t\t{ \"name\": \"50 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r1 01\"}]},\n\t\t\t{ \"name\": \"50 len=4 rc=unknown\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r4 m1 FF 00\"}]},\n\t\t\t{ \"name\": \"50 len=4 short\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r4 m1 00 01\"}]},\n\t\t\t{ \"name\": \"51 \", \"msgs\": [{\"type\":\"send\", \"payload\":\"51 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"52\", \"msgs\": [{\"type\":\"send\", \"payload\":\"52 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"54\", \"msgs\": [{\"type\":\"send\", \"payload\":\"54 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"58\", \"msgs\": [{\"type\":\"send\", \"payload\":\"58 r3 m1 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREC ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"command\":\"publish-2\",\n\t\t\"group_msgs\":[\n\t\t\t{\"type\":\"recv\", \"payload\":\"34 r116 s12 'test/publish' m1 v92 01 i1 02 ffffffff 23 ffff 08 s14 'response/topic' 09 H36 '7deac5c5-8802-44ff-86ce-11479f337419' 03 s10 'text/plain' 26 s3 'key' s5 'value' 'message'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50 with reason-string property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 1F s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with 2*reason-string property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r12 m1 00 v8 1F s1 'p' 1F s1 'q'\"}]},\n\t\t\t{ \"name\": \"50 with reason-string property missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 1F\"}]},\n\t\t\t{ \"name\": \"50 with reason-string property empty\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 1F s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r11 m1 00 v7 26 s1 'p' s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with 2*user-property\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 12 m1 00 v14 26 s1 'p' s1 'q' 26 s1 'p' s1 'q'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 02 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with user-property missing value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 26 s1 'p'\"}]},\n\t\t\t{ \"name\": \"50 with user-property missing key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 26\"}]},\n\t\t\t{ \"name\": \"50 with user-property empty key\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r10 m1 00 v6 26 s0 s1 'p'\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with user-property empty value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r10 m1 00 v6 26 s1 'p' s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]},\n\t\t\t{ \"name\": \"50 with user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [\n\t\t\t\t{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 26 s0 s0\"},\n\t\t\t\t{\"type\":\"recv\", \"payload\":\"62 r2 m1\"}\n\t\t\t]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   PUBREC DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"50 with payload-format-indicator (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 01 i0\"}]},\n\t\t\t{ \"name\": \"50 with request-problem-information (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 17 i0\"}]},\n\t\t\t{ \"name\": \"50 with maximum-qos (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 24 i0\"}]},\n\t\t\t{ \"name\": \"50 with retain-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 25 i0\"}]},\n\t\t\t{ \"name\": \"50 with wildcard-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 28 i0\"}]},\n\t\t\t{ \"name\": \"50 with subscription-identifier-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 29 i0\"}]},\n\t\t\t{ \"name\": \"50 with shared-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 2A i0\"}]},\n\t\t\t{ \"name\": \"50 with payload-format-indicator (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 01\"}]},\n\t\t\t{ \"name\": \"50 with request-problem-information (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 17\"}]},\n\t\t\t{ \"name\": \"50 with maximum-qos (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 24\"}]},\n\t\t\t{ \"name\": \"50 with retain-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 25\"}]},\n\t\t\t{ \"name\": \"50 with wildcard-subscription-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 28\"}]},\n\t\t\t{ \"name\": \"50 with subscription-identifier-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 29\"}]},\n\t\t\t{ \"name\": \"50 with shared-subscription-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 2A\"}]},\n\t\t\t{ \"name\": \"50 with message-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r9 m1 00 05 02 L1\"}]},\n\t\t\t{ \"name\": \"50 with session-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r9 m1 00 05 11 L1\"}]},\n\t\t\t{ \"name\": \"50 with will-delay-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 18 L1\"}]},\n\t\t\t{ \"name\": \"50 with maximum-packet-size (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 27 L1\"}]},\n\t\t\t{ \"name\": \"50 with message-expiry-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 02\"}]},\n\t\t\t{ \"name\": \"50 with session-expiry-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 11\"}]},\n\t\t\t{ \"name\": \"50 with will-delay-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 18\"}]},\n\t\t\t{ \"name\": \"50 with maximum-packet-size (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 27\"}]},\n\t\t\t{ \"name\": \"50 with content-type (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 03 s1 'p'\"}]},\n\t\t\t{ \"name\": \"50 with response-topic (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 08 s1 'p'\"}]},\n\t\t\t{ \"name\": \"50 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 12 s1 'p'\"}]},\n\t\t\t{ \"name\": \"50 with authentication-method (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 15 s1 'p'\"}]},\n\t\t\t{ \"name\": \"50 with response-information (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 1A s1 'p'\"}]},\n\t\t\t{ \"name\": \"50 with server-reference (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 1C s1 'p'\"}]},\n\t\t\t{ \"name\": \"50 with content-type (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 03\"}]},\n\t\t\t{ \"name\": \"50 with response-topic (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 08\"}]},\n\t\t\t{ \"name\": \"50 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 12\"}]},\n\t\t\t{ \"name\": \"50 with authentication-method (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 15\"}]},\n\t\t\t{ \"name\": \"50 with response-information (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 1A\"}]},\n\t\t\t{ \"name\": \"50 with server-reference (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 1C\"}]},\n\t\t\t{ \"name\": \"50 with correlation-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 09 s1 'p'\"}]},\n\t\t\t{ \"name\": \"50 with authentication-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 16 s1 'p'\"}]},\n\t\t\t{ \"name\": \"50 with correlation-data (binary data) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 09\"}]},\n\t\t\t{ \"name\": \"50 with authentication-data (binary data) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 16\"}]},\n\t\t\t{ \"name\": \"50 with subscription-identifier (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0B v1\"}]},\n\t\t\t{ \"name\": \"50 with subscription-identifier (variable byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 0B\"}]},\n\t\t\t{ \"name\": \"50 with server-keep-alive (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 13 H5\"}]},\n\t\t\t{ \"name\": \"50 with receive-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 21 H5\"}]},\n\t\t\t{ \"name\": \"50 with topic-alias-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 22 H5\"}]},\n\t\t\t{ \"name\": \"50 with topic-alias (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 23 H5\"}]},\n\t\t\t{ \"name\": \"50 with server-keep-alive (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 13\"}]},\n\t\t\t{ \"name\": \"50 with receive-maximum (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 21\"}]},\n\t\t\t{ \"name\": \"50 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 22\"}]},\n\t\t\t{ \"name\": \"50 with topic-alias (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r5 m1 00 v1 23\"}]},\n\t\t\t{ \"name\": \"50 with invalid-property 0x00 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 00 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x04 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 04 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x05 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 05 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x06 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 06 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x07 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 07 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x0A (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0A i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x0C (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0C i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x0D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0D i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x0E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0E i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x0F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 0F i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x10 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 10 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x14 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 14 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x1B (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 1B i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x1D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 1D i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x1E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 1E i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x20 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 20 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r6 m1 00 v2 7F i1\"}]},\n\t\t\t{ \"name\": \"50 with invalid-property 0x8000 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 8000 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x8001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 8001 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0xFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r7 m1 00 v3 FF7F i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 808001 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r8 m1 00 v4 FFFF7F i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0x80808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 80808001 i1\"}]},\n\t\t\t{ \"name\": \"50 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"50 r9 m1 00 v5 FFFFFF7F i1\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/SUBSCRIBE.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 SUBSCRIBE\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"82 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"82 r268435456\"}]},\n\t\t\t{ \"name\": \"80\", \"msgs\": [{\"type\":\"send\", \"payload\":\"80 r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"83 [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"83 r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"84 [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"84 r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"86 [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"86 r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"8A [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"8A r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 QoS 3 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 03\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x04 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 04\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x08 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 08\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x10 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 10\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x20 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 20\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x40 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 40\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x80 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 80\"}]},\n\t\t\t{ \"name\": \"82 topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746F700000 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746FEDA080 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746F700170 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746F701F70 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746F707F70 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746FC29F70 00\"} ] },\n\t\t\t{ \"name\": \"82 topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s5 746FEDBFBF 00\"} ] },\n\t\t\t{ \"name\": \"82 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 s1 'p' 00 00\"}]},\n\t\t\t{ \"name\": \"82 short 5 [MQTT-3.8.3-3]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 05 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"82 short 4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r4 m1234 0000\"}]},\n\t\t\t{ \"name\": \"82 short 3\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r3 m1234 00\"}]},\n\t\t\t{ \"name\": \"82 short 2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r2 1234\"}]},\n\t\t\t{ \"name\": \"82 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r1 12\"}]},\n\t\t\t{ \"name\": \"82 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r0\"}]},\n\t\t\t{ \"name\": \"82 single topic len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r5 m1234 s0 00\"}]},\n\t\t\t{ \"name\": \"82 multiple topic 1 len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1234 s0 00 s1 'q' 00\"}]},\n\t\t\t{ \"name\": \"82 multiple topic 2 len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1234 s1 'q' 00 s0 00\"}]},\n\t\t\t{ \"name\": \"82 multiple topic 1,2 len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r8 m1234 s0 00 s0 00\"}]},\n\t\t\t{ \"name\": \"82 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 0 [MQTT-3.8.4-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 1 [MQTT-3.8.4-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 01\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 2 [MQTT-3.8.4-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 s1 'p' 02\"}]},\n\t\t\t{ \"name\": \"82 multiple ok [MQTT-3.8.4-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 s1 'p' 00 s1 'q' 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   SUBSCRIBE\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"82 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"82 07 m1234 v0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"82 r268435456\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 0 [MQTT-3.8.4-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 07 m1234 v0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 1 [MQTT-3.8.4-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 07 m1234 v0 s1 'p' 01\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 2 [MQTT-3.8.4-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 07 m1234 v0 s1 'p' 02\"}]},\n\t\t\t{ \"name\": \"80\", \"msgs\": [{\"type\":\"send\", \"payload\":\"80 r7 m1234 v0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"83 [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"83 r7 m1234 v0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"84 [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"84 r7 m1234 v0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"86 [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"86 r7 m1234 v0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"8A [MQTT-3.8.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"8A r7 m1234 v0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 QoS 3 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 03\"}]},\n\t\t\t{ \"name\": \"82 QoS 0 no local 0x04\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 04\"}]},\n\t\t\t{ \"name\": \"82 QoS 0 retain as published 0x08\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 08\"}]},\n\t\t\t{ \"name\": \"82 QoS 0 retain handling=1 0x10\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 10\"}]},\n\t\t\t{ \"name\": \"82 QoS 0 retain handling=2 0x20\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 20\"}]},\n\t\t\t{ \"name\": \"82 QoS 0 retain handling=3 0x30 [MQTT-3-8.3-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 30\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x40 [MQTT-3-8.3-5]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 40\"}]},\n\t\t\t{ \"name\": \"82 QoS 0x80 [MQTT-3-8.3-5]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 80\"}]},\n\t\t\t{ \"name\": \"82 topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746F700000 'payload' 00\"}]},\n\t\t\t{ \"name\": \"82 topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746FEDA080 'payload' 00\"}]},\n\t\t\t{ \"name\": \"82 topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746F700170 'payload' 00\"}]},\n\t\t\t{ \"name\": \"82 topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746F701F70 'payload' 00\"}]},\n\t\t\t{ \"name\": \"82 topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746F707F70 'payload' 00\"}]},\n\t\t\t{ \"name\": \"82 topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746FC29F70 'payload' 00\"}]},\n\t\t\t{ \"name\": \"82 topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r18 m1234 v0 s5 746FEDBFBF 'payload' 00\"}]},\n\t\t\t{ \"name\": \"82 long\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r8 m1234 v0 s1 'p' 00 00\"}]},\n\t\t\t{ \"name\": \"82 short 5 [MQTT-3.8.3-3]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 06 m1234 v0 0001 70\"}]},\n\t\t\t{ \"name\": \"82 short 5\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r5 m1234 v0 0000\"}]},\n\t\t\t{ \"name\": \"82 short 4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r4 m1234 v0 00\"}]},\n\t\t\t{ \"name\": \"82 short 3\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r3 m1234 v0\"}]},\n\t\t\t{ \"name\": \"82 short 2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r2 m1234\"}]},\n\t\t\t{ \"name\": \"82 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r1 12\"}]},\n\t\t\t{ \"name\": \"82 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r0\"}]},\n\t\t\t{ \"name\": \"82 single topic len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r6 m1234 v0 0000 00\"}]},\n\t\t\t{ \"name\": \"82 multiple topic 1 len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 v0 0000 00 s1 'q' 00\"}]},\n\t\t\t{ \"name\": \"82 multiple topic 2 len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1234 v0 s1 'q' 00 0000 00\"}]},\n\t\t\t{ \"name\": \"82 multiple topic 1,2 len 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1234 v0 0000 00 0000 00\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 0 [MQTT-3.8.4-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 1 [MQTT-3.8.4-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 01\"}]},\n\t\t\t{ \"name\": \"82 single ok QoS 2 [MQTT-3.8.4-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m1234 v0 s1 'p' 02\"}]},\n\t\t\t{ \"name\": \"82 multiple ok [MQTT-3.8.4-4]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1234 v0 s1 'p' 00 s1 'q' 00\"}]},\n\t\t\t{ \"name\": \"82 mid 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r7 m0 v0 s1 'p' 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   SUBSCRIBE ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"82 with user-property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r14 m1 v7 26 s1 'p' s1 'q' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with 2*user-property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r21 m1 v14 26 s1 'p' s1 'q' 26 s1 'p' s1 'q' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with user-property missing value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 26 s1 'p' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with user-property missing key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r8 m1 v1 26 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with user-property empty key\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r13 m1 v6 26 0000 s1 'p' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with user-property empty value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r13 m1 v6 26 s1 'p' s0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with user-property empty key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 26 s0 s0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier missing (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r8 m1 v1 0B s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0 (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0B v0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=1 (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0B v8 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with 2*subscription-identifier=1 (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 0B v1 0B i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x7F (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0B 7F s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x8000 (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 0B 8000 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x8001 (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 0B 8001 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0xFF7F (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 0B FF7F s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x808001 (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 0B 808001 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0xFFFF7F (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 0B FFFF7F s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x80808001 (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 0B 80808001 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0xFFFFFF7F (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 0B FFFFFF7F s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier=0x8080808001 (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r13 m1 v6 0B 8080808001 s1 'p' 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   SUBSCRIBE DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"82 with payload-format-indicator (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 01 i0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with request-problem-information (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 17 i0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with maximum-qos (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 24 i0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with retain-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 25 i0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with wildcard-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 28 i0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with subscription-identifier-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 29 i0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with shared-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 02 2A i0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with message-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 02 L1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with session-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 11 L1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with will-delay-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 18 L1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with maximum-packet-size (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 27 L1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with content-type (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 03 s1 'p' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with response-topic (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 08 s1 'p' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 12 s1 'p' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with authentication-method (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 15 s1 'p' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with response-information (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 1A s1 'p' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with server-reference (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 1C s1 'p' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with correlation-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 09 s1 'p' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with authentication-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 16 s1 'p' s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with server-keep-alive (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 13 H5 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with receive-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 21 H5 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with topic-alias-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 22 H5 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with topic-alias (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 23 H5 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with invalid-property 0x00 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 m1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x04 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 04 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x05 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 05 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x06 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 06 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x07 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 07 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x0A (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0A i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x0C (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0C i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x0D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0D i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x0E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0E i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x0F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 0F i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x10 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 10 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x14 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 14 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x1B (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 1B i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x1D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 1D i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x1E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 1E i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x20 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 20 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r9 m1 v2 7F i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with invalid-property 0x8000 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 8000 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x8001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 8001 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0xFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r10 m1 v3 FF7F i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 808001 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r11 m1 v4 FFFF7F i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0x80808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 80808001 i1 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"82 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"82 r12 m1 v5 FFFFFF7F i1 s1 'p' 00\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/UNSUBACK.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 UNSUBACK unsolicited\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"B0 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r2 m1\"}]},\n\t\t\t{ \"name\": \"B0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"B0 r268435456\"}]},\n\t\t\t{ \"name\": \"B0 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r0\"}]},\n\t\t\t{ \"name\": \"B0 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r1 01\"}]},\n\t\t\t{ \"name\": \"B0\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r2 m1\"}]},\n\t\t\t{ \"name\": \"B1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B1 r2 m1\"}]},\n\t\t\t{ \"name\": \"B2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B2 r2 m1\"}]},\n\t\t\t{ \"name\": \"B4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B4 r2 m1\"}]},\n\t\t\t{ \"name\": \"B8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B8 r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v3.1.1 UNSUBACK\",\n\t\t\"ver\":4,\n\t\t\"command\":\"unsubscribe\",\n\t\t\"group_msgs\":[\n\t\t\t{\"type\":\"recv\", \"payload\":\"A2 r18 m1 s14 'test/subscribe'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"B0 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"B0 r268435456\"}]},\n\t\t\t{ \"name\": \"B0 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r0\"}]},\n\t\t\t{ \"name\": \"B0 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r1 01\"}]},\n\t\t\t{ \"name\": \"B0\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r2 m1\"}]},\n\t\t\t{ \"name\": \"B1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B1 r2 m1\"}]},\n\t\t\t{ \"name\": \"B2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B2 r2 m1\"}]},\n\t\t\t{ \"name\": \"B4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B4 r2 m1\"}]},\n\t\t\t{ \"name\": \"B8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B8 r2 m1\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBACK unsolicited\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"B0 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B0 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r0\"}]},\n\t\t\t{ \"name\": \"B0 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r1 01\"}]},\n\t\t\t{ \"name\": \"B0 short 2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r2 m1\"}]},\n\t\t\t{ \"name\": \"B0\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B1 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B2 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B4 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B8 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B0 with property\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r7 m1 04 1F s1 'p'\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x11 no sub\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r4 m1 00 11\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x80 unspecified error\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r4 m1 00 80\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x83 implementation specific error\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r4 m1 00 83\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x87 not authorised\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r4 m1 00 87\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x8F topic filter invalid\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r4 m1 00 8F\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x91 packet identifier in use\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r4 m1 00 91\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0xFF unknown\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r4 m1 00 FF\"}]}\n\t\t]\n\t},\n\t{\n\n\n\t\t\"group\": \"v5.0   UNSUBACK\",\n\t\t\"ver\":5,\n\t\t\"command\":\"unsubscribe\",\n\t\t\"group_msgs\":[\n\t\t\t{\"type\":\"recv\", \"payload\":\"A2 r32 m1 v13 26 s3 'key' s5 'value' s14 'test/subscribe'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"B0 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r0\"}]},\n\t\t\t{ \"name\": \"B0 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r1 01\"}]},\n\t\t\t{ \"name\": \"B0 short 2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r2 m1\"}]},\n\t\t\t{ \"name\": \"B0\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B1 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B2 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B4 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B8\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B8 r3 m1 00\"}]},\n\t\t\t{ \"name\": \"B0 with property\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 v7 m1 v4 1F s1 'p'\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x11 no sub\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 v4 m1 v0 11\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x80 unspecified error\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 v4 m1 v0 80\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x83 implementation specific error\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 v4 m1 v0 83\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x87 not authorised\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 v4 m1 v0 87\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x8F topic filter invalid\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 v4 m1 v0 8F\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0x91 packet identifier in use\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 v4 m1 v0 91\"}]},\n\t\t\t{ \"name\": \"B0 reason code 0xFF unknown\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 v4 m1 v0 FF\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBACK ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"command\":\"unsubscribe\",\n\t\t\"group_msgs\":[\n\t\t\t{\"type\":\"recv\", \"payload\":\"A2 r32 m1 v13 26 s3 'key' s5 'value' s14 'test/subscribe'\"}\n\t\t],\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"B0 with reason-string property\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 1F s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with reason-string property missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 1F 00\"}]},\n\t\t\t{ \"name\": \"B0 with user-property\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r11 m1 v7 26 s1 'p' s1 'q' 00\"}]},\n\t\t\t{ \"name\": \"B0 with user-property missing value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 26 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with user-property missing key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 26 00\"}]},\n\t\t\t{ \"name\": \"B0 with user-property empty key\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r10 m1 v6 26 s0 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with user-property empty value\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r10 m1 v6 26 s1 'p' s0 00\"}]},\n\t\t\t{ \"name\": \"B0 with user-property empty key,value\", \"expect_disconnect\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 26 s0 s0 00\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBACK DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"group_msgs\":[\n\t\t\t{\"type\":\"recv\", \"payload\":\"A2 r32 m1 v13 26 s3 'key' s5 'value' s14 'test/subscribe'\"}\n\t\t],\n\t\t\"command\":\"unsubscribe\",\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"B0 with payload-format-indicator (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 01 i0 00\"}]},\n\t\t\t{ \"name\": \"B0 with request-problem-information (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 17 i0 00\"}]},\n\t\t\t{ \"name\": \"B0 with maximum-qos (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 24 i0 00\"}]},\n\t\t\t{ \"name\": \"B0 with retain-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 25 i0 00\"}]},\n\t\t\t{ \"name\": \"B0 with wildcard-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 28 i0 00\"}]},\n\t\t\t{ \"name\": \"B0 with subscription-identifier-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 29 i0 00\"}]},\n\t\t\t{ \"name\": \"B0 with shared-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 2A i0 00\"}]},\n\t\t\t{ \"name\": \"B0 with payload-format-indicator (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 01 00\"}]},\n\t\t\t{ \"name\": \"B0 with request-problem-information (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 17 00\"}]},\n\t\t\t{ \"name\": \"B0 with maximum-qos (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 24 00\"}]},\n\t\t\t{ \"name\": \"B0 with retain-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 25 00\"}]},\n\t\t\t{ \"name\": \"B0 with wildcard-subscription-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 28 00\"}]},\n\t\t\t{ \"name\": \"B0 with subscription-identifier-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 29 00\"}]},\n\t\t\t{ \"name\": \"B0 with shared-subscription-available (byte) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 2A 00\"}]},\n\t\t\t{ \"name\": \"B0 with message-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 02 L1 00\"}]},\n\t\t\t{ \"name\": \"B0 with session-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 11 L1 00\"}]},\n\t\t\t{ \"name\": \"B0 with will-delay-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 18 L1 00\"}]},\n\t\t\t{ \"name\": \"B0 with maximum-packet-size (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 27 L1 00\"}]},\n\t\t\t{ \"name\": \"B0 with message-expiry-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 02 00\"}]},\n\t\t\t{ \"name\": \"B0 with session-expiry-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 11 00\"}]},\n\t\t\t{ \"name\": \"B0 with will-delay-interval (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 18 00\"}]},\n\t\t\t{ \"name\": \"B0 with maximum-packet-size (four byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 27 00\"}]},\n\t\t\t{ \"name\": \"B0 with content-type (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 03 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with response-topic (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 08 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 12 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with authentication-method (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 15 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with response-information (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 1A s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with server-reference (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 1C s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with content-type (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 03 00\"}]},\n\t\t\t{ \"name\": \"B0 with response-topic (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 08 00\"}]},\n\t\t\t{ \"name\": \"B0 with assigned-client-identifier (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 12 00\"}]},\n\t\t\t{ \"name\": \"B0 with authentication-method (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 15 00\"}]},\n\t\t\t{ \"name\": \"B0 with response-information (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 1A 00\"}]},\n\t\t\t{ \"name\": \"B0 with server-reference (UTF-8 string) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 1C 00\"}]},\n\t\t\t{ \"name\": \"B0 with correlation-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 09 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with authentication-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 16 s1 'p' 00\"}]},\n\t\t\t{ \"name\": \"B0 with correlation-data (binary data) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 09 00\"}]},\n\t\t\t{ \"name\": \"B0 with authentication-data (binary data) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 16 00\"}]},\n\t\t\t{ \"name\": \"B0 with subscription-identifier (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v2 0B01\"}]},\n\t\t\t{ \"name\": \"B0 with subscription-identifier (variable byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r4 m1 v1 0B\"}]},\n\t\t\t{ \"name\": \"B0 with server-keep-alive (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 13 H5 00\"}]},\n\t\t\t{ \"name\": \"B0 with receive-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 21 H5 00\"}]},\n\t\t\t{ \"name\": \"B0 with topic-alias-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 22 H5 00\"}]},\n\t\t\t{ \"name\": \"B0 with topic-alias (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 23 H5 00\"}]},\n\t\t\t{ \"name\": \"B0 with server-keep-alive (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 13 00\"}]},\n\t\t\t{ \"name\": \"B0 with receive-maximum (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 21 00\"}]},\n\t\t\t{ \"name\": \"B0 with topic-alias-maximum (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 22 00\"}]},\n\t\t\t{ \"name\": \"B0 with topic-alias (two byte integer) missing\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r5 m1 v1 23 00\"}]},\n\t\t\t{ \"name\": \"B0 with invalid-property 0x00 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 00 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x04 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 04 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x05 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 05 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x06 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 06 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x07 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 07 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x0A (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 0A i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x0C (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 0C i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x0D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 0D i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x0E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 0E i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x0F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 0F i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x10 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 10 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x14 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 14 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x1B (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 1B i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x1D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 1D i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x1E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 1E i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x20 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 20 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r6 m1 v2 7F i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with invalid-property 0x8000 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 8000 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x8001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 8001 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0xFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r7 m1 v3 FF7F i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 808001 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r8 m1 v4 FFFF7F i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0x80808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 80808001 i1 00\"}]},\n\t\t\t{ \"name\": \"B0 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"B0 r9 m1 v5 FFFFFF7F i1 00\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/data/UNSUBSCRIBE.json",
    "content": "[\n\t{\n\t\t\"group\": \"v3.1.1 UNSUBSCRIBE\",\n\t\t\"ver\":4,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"A2 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"A2 r268435456\"}]},\n\t\t\t{ \"name\": \"A2 (no subscribe) [MQTT-3.10.4-5]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 multiple [MQTT-3.10.4-6]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1234 s1 'p' s1 'q'\"}]},\n\t\t\t{ \"name\": \"A2 multiple zero 1st\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r7 m1234 s0 s1 'q'\"}]},\n\t\t\t{ \"name\": \"A2 multiple zero 2nd\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r7 m1234 s1 'q' s0\"}]},\n\t\t\t{ \"name\": \"A2 short 4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r4 m1234 0001\"}]},\n\t\t\t{ \"name\": \"A2 short 3\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r3 m1234 01\"}]},\n\t\t\t{ \"name\": \"A2 short 2 [MQTT-3.10.3-2]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r2 m1234\"}]},\n\t\t\t{ \"name\": \"A2 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r1 12\"}]},\n\t\t\t{ \"name\": \"A2 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r0\"}]},\n\t\t\t{ \"name\": \"A0 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A0 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A3 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A3 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A4 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A4 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A6 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A6 r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"AA [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"AA r5 m1234 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746F700000\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746F700170\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"A2 zero mid\", \"msgs\": [ {\"type\":\"send\", \"payload\":\"A2 r8 m0 s1 'p' s1 'q'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBSCRIBE\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"A2 [MQTT-3.1.0-1]\", \"connack\":false, \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r6 m1234 00 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 remaining length 5 bytes\", \"msgs\":[{\"type\":\"send\", \"payload\":\"A2 r268435456\"}]},\n\t\t\t{ \"name\": \"A2 [MQTT-3.10.4-5]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r6 m1234 00 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 multiple zero 1st\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1234 00 s0 s1 'q'\"}]},\n\t\t\t{ \"name\": \"A2 multiple zero 2nd\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1234 00 s1 'q' s0\"}]},\n\t\t\t{ \"name\": \"A2 short 5\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r5 m1234 00 0001\"}]},\n\t\t\t{ \"name\": \"A2 short 4\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r4 m1234 00 01\"}]},\n\t\t\t{ \"name\": \"A2 short 3 [MQTT-3.10.3-2]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r3 m1234 00\"}]},\n\t\t\t{ \"name\": \"A2 short 2\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r1 m1234\"}]},\n\t\t\t{ \"name\": \"A2 short 1\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r1 12\"}]},\n\t\t\t{ \"name\": \"A2 short 0\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r0\"}]},\n\t\t\t{ \"name\": \"A0 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A0 r6 m1234 00 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A3 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A3 r6 m1234 00 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A4 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A4 r6 m1234 00 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A6 [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A6 r6 m1234 00 s1 'p'\"}]},\n\t\t\t{ \"name\": \"AA [MQTT-3.10.1-1]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"AA r6 m1234 00 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 topic with 0x0000\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1234 00 s5 746F700000\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+D800\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1234 00 s5 746FEDA080\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+0001\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1234 00 s5 746F700170\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+001F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1234 00 s5 746F701F70\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+007F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1234 00 s5 746F707F70\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+009F\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1234 00 s5 746FC29F70\"}]},\n\t\t\t{ \"name\": \"A2 topic with U+FFFF\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1234 00 s5 746FEDBFBF\"}]},\n\t\t\t{ \"name\": \"A2 multiple [MQTT-3.10.4-6]\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1234 00 s1 'p' s1 'q'\"}]},\n\t\t\t{ \"name\": \"A2 zero mid\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r6 m0 00 s1 'p'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBSCRIBE ALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"A2 with user-property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r13 m1 v7 26 s1 'p' s1 'q' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with 2*user-property\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r20 m1 v14 26 s1 'p' s1 'q' 26 s1 'p' s1 'q' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with user-property missing value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 26 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with user-property missing key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r7 m1 v1 26 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with user-property empty key\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r12 m1 v6 26 s0 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with user-property empty value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r12 m1 v6 26 s1 'p' s0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with user-property empty key,value\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 26 s0 s0 s1 'p'\"}]}\n\t\t]\n\t},\n\t{\n\t\t\"group\": \"v5.0   UNSUBSCRIBE DISALLOWED PROPERTIES\",\n\t\t\"ver\":5,\n\t\t\"tests\": [\n\t\t\t{ \"name\": \"A2 with payload-format-indicator (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 01 i0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with request-problem-information (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 17 i0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with maximum-qos (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 24 i0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with retain-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 25 i0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with wildcard-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 28 i0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with subscription-identifier-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 29 i0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with shared-subscription-available (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 2A i0 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with message-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 02 L1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with session-expiry-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 11 L1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with will-delay-interval (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 18 L1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with maximum-packet-size (four byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 27 L1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with content-type (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 03 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with response-topic (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 08 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with assigned-client-identifier (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 12 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with authentication-method (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 15 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with response-information (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 1A s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with server-reference (UTF-8 string)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 1C s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with correlation-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 09 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with authentication-data (binary data)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 16 s1 'p' s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with subscription-identifier (variable byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0B v1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with server-keep-alive (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 13 H5 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with receive-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 21 H5 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with topic-alias-maximum (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 22 H5 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with topic-alias (two byte integer)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 23 H5 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with invalid-property 0x00 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 00 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x04 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 04 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x05 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 05 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x06 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 06 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x07 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 07 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x0A (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0A i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x0C (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0C i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x0D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0D i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x0E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0E i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x0F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 0F i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x10 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 10 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x14 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 14 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x1B (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 1B i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x1D (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 1D i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x1E (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 1E i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x20 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 20 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r8 m1 v2 7F i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with invalid-property 0x8000 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 8000 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x8001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 8001 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0xFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r9 m1 v3 FF7F i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"r2 r10 m1 v4 808001 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0xFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r10 m1 v4 FFFF7F i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0x80808001 (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 80808001 i1 s1 'p'\"}]},\n\t\t\t{ \"name\": \"A2 with unknown-property 0xFFFFFF7F (byte)\", \"msgs\": [{\"type\":\"send\", \"payload\":\"A2 r11 m1 v5 FFFFFF7F i1 s1 'p'\"}]}\n\t\t]\n\t}\n]\n"
  },
  {
    "path": "test/lib/mosq_test_helper.py",
    "content": "import inspect, os, sys\n\n# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder\ncmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],\"..\")))\nif cmd_subfolder not in sys.path:\n    sys.path.insert(0, cmd_subfolder)\n\nimport mosq_test\nimport mqtt5_props\n\nimport socket\nimport ssl\nimport struct\nimport subprocess\nimport time\n\n\nfrom pathlib import Path\n\nsource_dir = Path(__file__).resolve().parent\nssl_dir = source_dir.parent / \"ssl\"\n"
  },
  {
    "path": "test/lib/msg_sequence_test.py",
    "content": "#!/usr/bin/env python3\n\n# Test whether a valid CONNECT results in the correct CONNACK packet.\n\nimport atexit\nfrom mosq_test_helper import *\nimport importlib\nfrom os import walk\nimport socket\nimport json\nfrom collections import deque\nimport mosq_test\n\nsend = 1\nrecv = 2\ndisconnected_check = 3\nconnected_check = 4\npublish = 5\n\nvg_index = 1\nvg_logfiles = []\n\n@atexit.register\ndef test_cleanup():\n    global vg_logfiles\n\n    for f in vg_logfiles:\n        try:\n            if os.stat(f).st_size == 0:\n                os.remove(f)\n        except OSError:\n            pass\n\nclass SingleMsg(object):\n    __slots__ = 'action', 'message', 'comment'\n    def __init__(self, action, message, comment=''):\n        self.action = action\n        self.message = message\n        self.comment = comment\n\nclass MsgSequence(object):\n    __slots__ = 'name', 'port', 'proto_ver', 'msgs', 'expect_disconnect', 'sock', 'client', 'clean_start', 'command'\n\n    def __init__(self, name, port, default_connect=True, default_connack=True, proto_ver=4, clean_start=True, expect_disconnect=True, command=None):\n        self.name = name\n        self.msgs = deque()\n        self.expect_disconnect = expect_disconnect\n        self.port = port\n        self.proto_ver = proto_ver\n        self.clean_start = clean_start\n        self.command = command\n        self.sock = -1\n        self.client = None\n        if default_connect:\n            self.add_recv(mosq_test.gen_connect(\"fuzzish\", proto_ver=proto_ver), \"default connect\")\n        if default_connack:\n            properties = mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n            self.add_send(mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=properties, property_helper=False), \"default connack\")\n\n    def add_msg(self, message):\n        try:\n            c = message[\"comment\"]\n        except KeyError:\n            c = \"\"\n        if message[\"type\"] == \"send\":\n            self.add_send(parse_message(message[\"payload\"]), c)\n        elif message[\"type\"] == \"recv\":\n            self.add_recv(parse_message(message[\"payload\"]), c)\n        elif message[\"type\"] == \"publish\":\n            self.add_publish(message, c)\n\n    def add_send(self, message, comment=\"\"):\n        self._add(send, message, comment)\n\n    def add_recv(self, message, comment):\n        self._add(recv, message, comment)\n\n    def add_publish(self, message, comment):\n        self._add(publish, message, comment)\n\n    def add_connected_check(self):\n        self._add(connected_check, b\"\")\n\n    def add_disconnected_check(self):\n        self._add(disconnected_check, b\"\")\n\n    def run_client(self, server_sock, port):\n        global vg_index\n        global vg_logfiles\n\n        env = mosq_test.env_add_ld_library_path()\n        cmd = [\n                mosq_test.get_build_root() + '/test/lib/c/fuzzish.test',\n                str(port), str(self.proto_ver), str(self.clean_start)\n                ]\n        if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n            logfile = 'seq.'+str(vg_index)+'.vglog'\n            cmd = ['/snap/bin/valgrind', '-q', '--trace-children=yes', '--leak-check=full', '--show-leak-kinds=all', '--log-file='+logfile] + cmd\n            vg_logfiles.append(logfile)\n            vg_index += 1\n\n        if self.command is not None:\n            cmd.append(self.command)\n        self.client = subprocess.Popen(cmd, stderr=subprocess.PIPE, env=env)\n        (self.sock, _) = server_sock.accept()\n\n    def kill_client(self):\n        self.sock.close()\n        self.client.terminate()\n        self.client.wait()\n        if self.client.returncode != 0:\n            raise RuntimeError\n\n    def _add(self, action, message, comment=\"\"):\n        msg = SingleMsg(action, message, comment)\n        self.msgs.append(msg)\n\n    def _connected_check(self):\n        if not self._puback_check():\n            raise ValueError(\"connection failed\")\n\n    def _send_message(self, msg):\n        self.sock.send(msg.message)\n\n    def _publish_message(self, msg):\n        sock = mosq_test.client_connect_only(hostname=\"localhost\", port=1888, timeout=2)\n        sock.send(mosq_test.gen_connect(\"helper\"))\n        mosq_test.expect_packet(sock, \"connack\", mosq_test.gen_connack(rc=0))\n\n        m = msg.message\n        if m['qos'] == 0:\n            sock.send(mosq_test.gen_publish(topic=m['topic'], payload=m['payload']))\n        elif m['qos'] == 1:\n            sock.send(mosq_test.gen_publish(mid=1, qos=1, topic=m['topic'], payload=m['payload']))\n            mosq_test.expect_packet(sock, \"helper puback\", mosq_test.gen_puback(mid=1))\n        elif m['qos'] == 2:\n            sock.send(mosq_test.gen_publish(mid=1, qos=2, topic=m['topic'], payload=m['payload']))\n            mosq_test.expect_packet(sock, \"helper pubrec\", mosq_test.gen_pubrec(mid=1))\n            sock.send(mosq_test.gen_pubrel(mid=1))\n            mosq_test.expect_packet(sock, \"helper pubcomp\", mosq_test.gen_pubcomp(mid=1))\n        sock.close()\n\n    def _recv_message(self, msg):\n        data = self.sock.recv(len(msg.message))\n        if data != msg.message:\n            raise ValueError(\"Receive message %s | %s | %s\" % (msg.comment, data, msg.message))\n\n\n    def _puback_check(self):\n        publish_packet = mosq_test.gen_publish(mid=65535, qos=1, topic=\"alive check\", payload=\"payload\", proto_ver=self.proto_ver)\n        puback_packet = mosq_test.gen_puback(mid=65535, proto_ver=self.proto_ver)\n        self.sock.send(publish_packet)\n        packet = self.sock.recv(len(puback_packet))\n        return packet == puback_packet\n\n\n    def _disconnected_check(self):\n        try:\n            if self._puback_check() and self.expect_disconnect:\n                raise ValueError(\"Still connected\")\n        except ConnectionResetError:\n            if self.expect_disconnect:\n                pass\n            else:\n                raise\n\n    def _process_message(self, msg):\n        if msg.action == send:\n            self._send_message(msg)\n        elif msg.action == recv:\n            self._recv_message(msg)\n        elif msg.action == publish:\n            self._publish_message(msg)\n        elif msg.action == disconnected_check:\n            self._disconnected_check()\n        elif msg.action == connected_check:\n            self._connected_check()\n\n    def process_next(self):\n        msg = self.msgs.popleft()\n        self._process_message(msg)\n\n    def process_all(self):\n        while len(self.msgs):\n            self.process_next()\n        if self.expect_disconnect:\n            self._disconnected_check()\n        else:\n            self._connected_check()\n\n\ndef parse_message(message):\n    b = bytes()\n    parts = message.split(\" \")\n    for i in range(0, len(parts)):\n        if len(parts[i]) == 0:\n            continue\n        elif parts[i][0] in ['i']:\n            # General 8-bit unsigned decimal\n            b += int(parts[i][1:]).to_bytes(length=1, byteorder='big', signed=False)\n        elif parts[i][0] in ['H', 'k', 'm', 's']:\n            # General 16-bit unsigned decimal\n            # Or 'k' keepalive specific\n            # Or 'm' mid specific\n            # Or 's' string specific\n            b += int(parts[i][1:]).to_bytes(length=2, byteorder='big', signed=False)\n        elif parts[i][0] == \"L\":\n            # 32-bit unsigned decimal\n            b += int(parts[i][1:]).to_bytes(length=4, byteorder='big', signed=False)\n        elif parts[i][0] == \"'\":\n            s = parts[i][1:]\n            while s[-1] != \"'\" and i < len(parts)-1:\n                i += 1\n                s += \" \" + parts[i]\n            if s[-1] != \"'\":\n                raise ValueError(f\"message {message} has incomplete string type\")\n            b += bytes(s[0:-1].encode('utf-8'))\n        elif parts[i][0] in ['v', 'r']:\n            # General variable length integer\n            # Or 'r' remaining length\n            v = int(parts[i][1:])\n\n            # This allows non-compliant values >=2^28\n            while True:\n                byte = v % 128\n                v = v // 128\n\n                if v > 0:\n                    byte = byte | 0x80\n                b += byte.to_bytes(length=1, byteorder='big', signed=False)\n                if v == 0:\n                    break\n        else:\n            # hex\n            try:\n                b += bytes.fromhex(parts[i])\n            except ValueError:\n                raise ValueError(f\"message {message} has invalid hex bytes\")\n\n    return b\n\n\ndef do_test(hostname, port):\n    data_path=Path(__file__).resolve().parent/\"data\"\n    rc = 0\n    sequences = []\n    for (_, _, filenames) in walk(data_path):\n        sequences.extend(filenames)\n        break\n\n    total = 0\n    succeeded = 0\n    test = None\n\n    server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    server_sock.settimeout(10)\n    server_sock.bind(('', port))\n    server_sock.listen(5)\n\n    for seq in sorted(sequences):\n        if seq[-5:] != \".json\":\n            continue\n\n        with open(data_path/seq, \"r\") as f:\n            test_file = json.load(f)\n\n        for g in test_file:\n            group_name = g[\"group\"]\n            try:\n                disabled = g[\"disable\"]\n                if disabled:\n                    continue\n            except KeyError:\n                pass\n            try:\n                g_command = g[\"command\"]\n            except KeyError:\n                g_command = None\n            try:\n                g_proto_ver = g[\"ver\"]\n            except KeyError:\n                g_proto_ver = 4\n            try:\n                g_clean_start = g[\"clean_start\"]\n            except KeyError:\n                g_clean_start = True\n            try:\n                g_connect = g[\"connect\"]\n            except KeyError:\n                g_connect = True\n            try:\n                g_connack = g[\"connack\"]\n            except KeyError:\n                g_connack = True\n            try:\n                g_expect_disconnect = g[\"expect_disconnect\"]\n            except KeyError:\n                g_expect_disconnect = True\n\n            try:\n                group_msgs = g[\"group_msgs\"]\n            except KeyError:\n                group_msgs = None\n\n            tests = g[\"tests\"]\n\n            for t in tests:\n                tname = group_name + \" \" + t[\"name\"]\n                try:\n                    command = t[\"command\"]\n                except KeyError:\n                    command = g_command\n                try:\n                    proto_ver = t[\"ver\"]\n                except KeyError:\n                    proto_ver = g_proto_ver\n                try:\n                    clean_start = t[\"clean_start\"]\n                except KeyError:\n                    clean_start = g_clean_start\n                try:\n                    connect = t[\"connect\"]\n                except KeyError:\n                    connect = g_connect\n                try:\n                    connack = t[\"connack\"]\n                except KeyError:\n                    connack = g_connack\n                try:\n                    expect_disconnect = t[\"expect_disconnect\"]\n                except KeyError:\n                    expect_disconnect = g_expect_disconnect\n\n                this_test = MsgSequence(tname, port,\n                        proto_ver=proto_ver,\n                        clean_start=clean_start,\n                        expect_disconnect=expect_disconnect,\n                        default_connect=connect,\n                        default_connack=connack,\n                        command=command)\n\n                if group_msgs is not None:\n                    for m in group_msgs:\n                        this_test.add_msg(m)\n\n                for m in t[\"msgs\"]:\n                    this_test.add_msg(m)\n\n                this_test.run_client(server_sock, port)\n\n                total += 1\n                try:\n                    this_test.process_all()\n                    this_test.kill_client()\n                    this_test = None\n                    #print(\"\\033[32m\" + tname + \"\\033[0m\")\n                    succeeded += 1\n                except (ValueError, ConnectionResetError, socket.timeout, mosq_test.TestError, RuntimeError) as e:\n                    print(\"\\033[31m\" + tname + \" failed: \" + str(e) + \"\\033[0m\")\n                    rc = 1\n                finally:\n                    if this_test is not None:\n                        try:\n                            this_test.kill_client()\n                        except RuntimeError:\n                            pass\n\n    print(\"%d tests total\\n%d tests succeeded\" % (total, succeeded))\n    return rc\n\nhostname = \"localhost\"\nport = mosq_test.get_port()\n\nrc = do_test(hostname=hostname, port=port)\nexit(rc)\n"
  },
  {
    "path": "test/lib/test.py",
    "content": "#!/usr/bin/env python3\n\nimport sys\nsys.path.insert(0, \"..\")\nimport ptest\n\ntests = [\n    (1, './msg_sequence_test.py'),\n    (1, './01-con-discon-success-v5.py'),\n    (1, './01-con-discon-success.py'),\n    (1, './01-con-discon-will-clear.py'),\n    (1, './01-con-discon-will-v5.py'),\n    (1, './01-con-discon-will.py'),\n    (1, './01-extended-auth-continue.py'),\n    (1, './01-extended-auth-failure.py'),\n    (1, './01-keepalive-pingreq.py'),\n    (1, './01-no-clean-session.py'),\n    (1, './01-server-keepalive-pingreq.py'),\n    (1, './01-unpwd-set.py'),\n    (1, './01-will-set.py'),\n    (1, './01-will-unpwd-set.py'),\n\n    (1, './02-subscribe-qos0.py'),\n    (1, './02-subscribe-qos1.py'),\n    (1, './02-subscribe-qos2.py'),\n    (1, './02-unsubscribe-multiple-v5.py'),\n    (1, './02-unsubscribe-v5.py'),\n    (1, './02-unsubscribe.py'),\n\n    (1, './03-publish-b2c-qos1-unexpected-puback.py'),\n    (1, './03-publish-b2c-qos1.py'),\n    (1, './03-publish-b2c-qos2-len.py'),\n    (1, './03-publish-b2c-qos2-unexpected-pubcomp.py'),\n    (1, './03-publish-b2c-qos2-unexpected-pubrel.py'),\n    (1, './03-publish-b2c-qos2.py'),\n    (1, './03-publish-c2b-qos1-disconnect.py'),\n    (1, './03-publish-c2b-qos1-len.py'),\n    (1, './03-publish-c2b-qos1-receive-maximum.py'),\n    (1, './03-publish-c2b-qos2-disconnect.py'),\n    (1, './03-publish-c2b-qos2-len.py'),\n    (1, './03-publish-c2b-qos2-maximum-qos-0.py'),\n    (1, './03-publish-c2b-qos2-maximum-qos-1.py'),\n    (1, './03-publish-c2b-qos2-pubrec-error.py'),\n    (1, './03-publish-c2b-qos2-receive-maximum-1.py'),\n    (1, './03-publish-c2b-qos2-receive-maximum-2.py'),\n    (1, './03-publish-c2b-qos2.py'),\n    (1, './03-publish-qos0-no-payload.py'),\n    (1, './03-publish-qos0.py'),\n    (1, './03-request-response-correlation.py'),\n    (1, './03-request-response.py'),\n\n    (1, './04-retain-qos0.py'),\n\n    (1, './08-ssl-bad-cacert.py'),\n    (1, './08-ssl-connect-cert-auth-enc.py'),\n    (1, './08-ssl-connect-cert-auth.py'),\n    (1, './08-ssl-connect-no-auth.py'),\n    (1, './08-ssl-connect-san.py'),\n\n    (1, './09-util-topic-tokenise.py'),\n\n    (1, './11-prop-oversize-packet.py'),\n    (1, './11-prop-send-content-type.py'),\n    (1, './11-prop-send-payload-format.py'),\n    ]\n\n\nif __name__ == \"__main__\":\n    test = ptest.PTest()\n    if len(sys.argv) == 2 and sys.argv[1] == \"--rerun-failed\":\n        test.run_failed_tests()\n    else:\n        test.run_tests(tests)\n"
  },
  {
    "path": "test/mock/CMakeLists.txt",
    "content": "add_subdirectory(libcommon)\nadd_subdirectory(lib)\nadd_subdirectory(apps)\n\nif(EDITLINE_FOUND)\n\tadd_library(editline_mock OBJECT editline_mock.cpp)\n\ttarget_include_directories(editline_mock\n\t\tPUBLIC\n\t\t\t${mosquitto_SOURCE_DIR}/test/mock\n\t\t\t${LINEEDITING_INCLUDE_DIRS}\n\t)\n\ttarget_link_libraries(editline_mock PRIVATE GTest::gmock)\nendif()\n\nadd_library(pthread_mock OBJECT pthread_mock.cpp)\ntarget_include_directories(pthread_mock\n    PUBLIC\n        ${mosquitto_SOURCE_DIR}/test/mock\n)\ntarget_link_libraries(pthread_mock PRIVATE GTest::gmock)\n"
  },
  {
    "path": "test/mock/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\n.PHONY: all check test-compile test clean\n\nLOCAL_CPPFLAGS+= \\\n\t-DWITH_THREADING \\\n\t-DWITH_TLS \\\n\t-I../ \\\n\t-I${R}/include \\\n\t-I${R} \\\n\t-I${R}/lib \\\n\t-I${R}/test/mock\n\nLOCAL_CXXFLAGS+=-std=c++20 -Wall -ggdb -D TEST_SOURCE_DIR='\"$(realpath .)\"'\n\nMOCKS = \\\n\tpthread_mock.o\n\nifeq ($(WITH_EDITLINE),yes)\nMOCKS+=editline_mock.o\nendif\n\nall : test-compile\ntest-compile : ${MOCKS}\n\t$(MAKE) -C apps/mosquitto_ctrl test-compile\n\t$(MAKE) -C lib test-compile\n\ncheck : test\ntest :\n\n${MOCKS} : %.o: %.cpp\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_CXXFLAGS) -c $< -o $@\n\n\nclean :\n\t-rm -rf *.o *.gcda *.gcno coverage.info out/\n\ncoverage :\n\tlcov --capture --directory . --output-file coverage.info\n\tgenhtml coverage.info --output-directory out\n\ninstall:\nuninstall:\n"
  },
  {
    "path": "test/mock/apps/CMakeLists.txt",
    "content": "add_subdirectory(mosquitto_ctrl)\n"
  },
  {
    "path": "test/mock/apps/mosquitto_ctrl/CMakeLists.txt",
    "content": "if(WITH_CTRL_SHELL AND EDITLINE_FOUND)\n\tadd_library(ctrl_shell_mock OBJECT\n\t\tctrl_shell_mock.cpp\n\t\tctrl_shell_mock.hpp\n\t)\n\ttarget_compile_definitions(ctrl_shell_mock PRIVATE\n\t\tWITH_CTRL_SHELL\n\t\tWITH_EDITLINE\n\t)\n\n\ttarget_include_directories(ctrl_shell_mock\n\t\tPUBLIC\n\t\t\t${mosquitto_SOURCE_DIR}\n\t\t\t${mosquitto_SOURCE_DIR}/include\n\t\t\t${mosquitto_SOURCE_DIR}/apps/mosquitto_ctrl\n\t\t\t${mosquitto_SOURCE_DIR}/test/mock\n\t\t\t${mosquitto_SOURCE_DIR}/test/mock/apps/mosquitto_ctrl\n\t)\n\ttarget_link_libraries(ctrl_shell_mock PRIVATE cJSON GTest::gmock)\nendif()\n"
  },
  {
    "path": "test/mock/apps/mosquitto_ctrl/Makefile",
    "content": "R=../../../..\ninclude ${R}/config.mk\n\n.PHONY: all check test-compile test clean\n\nLOCAL_CPPFLAGS+= \\\n\t-DWITH_CTRL_SHELL \\\n\t-DWITH_EDITLINE \\\n\t-I../ \\\n\t-I${R} \\\n\t-I${R}/apps/mosquitto_ctrl \\\n\t-I${R}/include \\\n\t-I${R}/lib \\\n\t-I${R}/test/mock\n\nLOCAL_CXXFLAGS+=-std=c++20 -Wall -ggdb -D TEST_SOURCE_DIR='\"$(realpath .)\"'\n\nMOCK_OBJS = \\\n\tctrl_shell_mock.o\n\nifeq ($(WITH_EDITLINE),yes)\nall : test-compile\ntest-compile: ${MOCK_OBJS}\ncheck : test\nelse\nall :\ntest-compile:\ncheck :\nendif\n\n\n# MOCKS\n\n${MOCK_OBJS} : %.o: %.cpp ctrl_shell_mock.hpp\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_CXXFLAGS) -c $< -o $@\n\nclean :\n\t-rm -rf *.o *.gcda *.gcno\n\ninstall:\n\nuninstall:\n"
  },
  {
    "path": "test/mock/apps/mosquitto_ctrl/ctrl_shell_mock.cpp",
    "content": "#include \"ctrl_shell_mock.hpp\"\n\nCtrlShellMock::CtrlShellMock()\n{\n}\nCtrlShellMock::~CtrlShellMock()\n{\n}\n\n\nvoid ctrl_shell__output(const char *s)\n{\n\treturn CtrlShellMock::get_mock().ctrl_shell__output(s);\n}\n\n\nchar *ctrl_shell_fgets(char *s, int size, FILE *stream)\n{\n\treturn CtrlShellMock::get_mock().ctrl_shell_fgets(s, size, stream);\n}\n"
  },
  {
    "path": "test/mock/apps/mosquitto_ctrl/ctrl_shell_mock.hpp",
    "content": "#pragma once\n\n#include <gmock/gmock.h>\n#include <editline/readline.h>\n\n#include \"mosquitto_ctrl.h\"\n#include \"ctrl_shell_internal.h\"\n\n#include \"c_function_mock.hpp\"\n\nclass CtrlShellMock : public CFunctionMock<CtrlShellMock> {\n\tpublic:\n\t\tCtrlShellMock();\n\t\tvirtual ~CtrlShellMock();\n\n\t\tMOCK_METHOD(void, ctrl_shell__output, (const char *s));\n\t\tMOCK_METHOD(char *, ctrl_shell_fgets, (char *s, int size, FILE *stream));\n};\n"
  },
  {
    "path": "test/mock/c_function_mock.hpp",
    "content": "#pragma once\n\n#include <dlfcn.h>\n\n#include <functional>\n#include <stdexcept>\n\ntemplate <typename MockClass>\nclass CFunctionMock {\n  static CFunctionMock<MockClass>*& get_obj() {\n    static CFunctionMock<MockClass>* mock_obj = nullptr;\n    return mock_obj;\n  }\n\n public:\n  CFunctionMock() {\n    if (get_obj() != nullptr) {\n      throw std::logic_error(\"only one instance of MockClass allowed\");\n    }\n    get_obj() = this;\n  }\n  CFunctionMock(const CFunctionMock&) = delete;\n  CFunctionMock(CFunctionMock&&) = delete;\n  CFunctionMock& operator=(const CFunctionMock&) = delete;\n  CFunctionMock& operator=(CFunctionMock&&) = delete;\n  virtual ~CFunctionMock() { get_obj() = nullptr; }\n\n  static bool mock_exists() { return get_obj() != nullptr; }\n\n  static MockClass& get_mock() {\n    auto* instance = dynamic_cast<MockClass*>(get_obj());\n    if (instance == nullptr) {\n      throw std::logic_error(std::string(\"mock \") + typeid(MockClass).name() +\n                             \" is not initialized\");\n    }\n    return *instance;\n  }\n\n  template <class ReturnType, bool Void = std::is_void<ReturnType>::value, typename... Args>\n  static std::function<ReturnType(Args...)> optional_mocked_fn(\n      ReturnType (MockClass::*mock_fn)(Args...), const std::string& function_name) {\n    return std::function<ReturnType(Args...)>{\n        OptionalMockFnWrapper<ReturnType, std::is_void<ReturnType>::value, Args...>(mock_fn,\n                                                                                    function_name)};\n  }\n\n  template <class ReturnType, bool Void = std::is_void<ReturnType>::value, typename... Args>\n  static std::function<ReturnType(Args...)> original_fn(ReturnType (MockClass::*mock_fn)(Args...),\n                                                        const std::string& function_name) {\n    return std::function<ReturnType(Args...)>{\n        OriginalFnWrapper<ReturnType, std::is_void<ReturnType>::value, Args...>(mock_fn,\n                                                                                function_name)};\n  }\n\n private:\n  template <class ReturnType, bool Void = std::is_void<ReturnType>::value, typename... Args>\n  class OriginalFnWrapper {\n   public:\n    OriginalFnWrapper(ReturnType (MockClass::* /* mock_fn */)(Args...),\n                      const std::string& function_name)\n        : orginial_fn{\n              reinterpret_cast<ReturnType (*)(Args...)>(dlsym(RTLD_NEXT, function_name.c_str()))} {}\n    ReturnType operator()(Args... args) { return (*orginial_fn)(args...); }\n\n   private:\n    ReturnType (*orginial_fn)(Args...);\n  };\n\n  template <class ReturnType, bool Void = std::is_void<ReturnType>::value, typename... Args>\n  class OptionalMockFnWrapper : public OriginalFnWrapper<ReturnType, Void, Args...> {\n   public:\n    using base_class = OriginalFnWrapper<ReturnType, Void, Args...>;\n    OptionalMockFnWrapper(ReturnType (MockClass::*mock_fn)(Args...),\n                          const std::string& function_name)\n        : base_class{mock_fn, function_name}, mock_fn_(mock_fn) {}\n    ReturnType operator()(Args... args) {\n      if (get_obj() != nullptr) {\n        return (get_mock().*mock_fn_)(args...);\n      }\n      return base_class::operator()(args...);\n    }\n\n   private:\n    ReturnType (MockClass::*mock_fn_)(Args...);\n  };\n\n  template <class ReturnType, typename... Args>\n  class OriginalFnWrapper<ReturnType, true, Args...> {\n   public:\n    OriginalFnWrapper(void (MockClass::* /* mock_fn */)(Args...), const std::string& function_name)\n        : orginial_fn{\n              reinterpret_cast<void (*)(Args...)>(dlsym(RTLD_NEXT, function_name.c_str()))} {}\n    void operator()(Args... args) { (*orginial_fn)(args...); }\n\n   private:\n    void (*orginial_fn)(Args...);\n  };\n\n  template <class ReturnType, typename... Args>\n  class OptionalMockFnWrapper<ReturnType, true, Args...>\n      : public OriginalFnWrapper<ReturnType, true, Args...> {\n   public:\n    using base_class = OriginalFnWrapper<ReturnType, true, Args...>;\n    OptionalMockFnWrapper(void (MockClass::*mock_fn)(Args...), const std::string& function_name)\n        : base_class(mock_fn, function_name), mock_fn_(mock_fn) {}\n\n    void operator()(Args... args) {\n      if (get_obj() != nullptr) {\n        (get_mock().*mock_fn_)(args...);\n      } else {\n        base_class::operator()(args...);\n      }\n    }\n\n   private:\n    void (MockClass::*mock_fn_)(Args...);\n  };\n};\n"
  },
  {
    "path": "test/mock/editline_mock.cpp",
    "content": "#include \"editline_mock.hpp\"\n\nchar *rl_line_buffer = nullptr;\nconst char *rl_readline_name = nullptr;\nrl_compentry_func_t *rl_completion_entry_function = nullptr;\nint rl_attempted_completion_over = 0;\nrl_completion_func_t *rl_attempted_completion_function = nullptr;\n\nEditLineMock::EditLineMock()\n{\n}\nEditLineMock::~EditLineMock()\n{\n}\n\n\nvoid EditLineMock::reset()\n{\n\tfree(rl_line_buffer);\n\trl_line_buffer = nullptr;\n\trl_readline_name = nullptr;\n\trl_completion_entry_function = nullptr;\n\trl_attempted_completion_over = 9;\n\trl_attempted_completion_function = nullptr;\n}\n\n\nint add_history(const char *s)\n{\n\treturn EditLineMock::get_mock().add_history(s);\n}\n\n\nvoid clear_history(void)\n{\n\tEditLineMock::get_mock().clear_history();\n}\n\n\nvoid rl_resize_terminal(void)\n{\n\tEditLineMock::get_mock().rl_resize_terminal();\n}\n\n\nchar *readline(const char *s)\n{\n\treturn EditLineMock::get_mock().readline(s);\n}\n\n\nchar **rl_completion_matches(const char *s, rl_compentry_func_t *f)\n{\n\treturn EditLineMock::get_mock().rl_completion_matches(s, f);\n}\n\n\nint rl_complete(int a, int b)\n{\n\treturn EditLineMock::get_mock().rl_complete(a, b);\n}\n\n\nint rl_bind_key(int a, rl_command_func_t *f)\n{\n\treturn EditLineMock::get_mock().rl_bind_key(a, f);\n}\n"
  },
  {
    "path": "test/mock/editline_mock.hpp",
    "content": "#pragma once\n\n#include <gmock/gmock.h>\n#include <editline/readline.h>\n\n#include \"c_function_mock.hpp\"\n\nclass EditLineMock : public CFunctionMock<EditLineMock> {\n\tpublic:\n\t\tEditLineMock();\n\t\tvirtual ~EditLineMock();\n\t\tvoid reset();\n\n\t\tMOCK_METHOD(int, add_history, (const char *s));\n\t\tMOCK_METHOD(void, clear_history, ());\n\t\tMOCK_METHOD(void, rl_resize_terminal, ());\n\t\tMOCK_METHOD(char *, readline, (const char *s));\n\t\tMOCK_METHOD(char **, rl_completion_matches, (const char *s, rl_compentry_func_t *f));\n\t\tMOCK_METHOD(int, rl_complete, (int a, int b));\n\t\tMOCK_METHOD(int, rl_bind_key, (int a, rl_command_func_t *f));\n};\n"
  },
  {
    "path": "test/mock/lib/CMakeLists.txt",
    "content": "add_library(libmosquitto_mock OBJECT\n\tactions_publish_mock.cpp\n\tactions_subscribe_mock.cpp\n\tactions_unsubscribe_mock.cpp\n\tcallbacks_mock.cpp\n\tconnect_mock.cpp\n\textended_auth_mock.cpp\n\thelpers_mock.cpp\n\tlibmosquitto_mock.cpp\n\tlibmosquitto_mock.hpp\n\tloop_mock.cpp\n\tmessages_mosq_mock.cpp\n\tnet_mosq_mock.cpp\n\toptions_mock.cpp\n\tsocks_mosq_mock.cpp\n\tsrv_mosq_mock.cpp\n\tthread_mosq_mock.cpp\n)\n\ntarget_include_directories(libmosquitto_mock\n    PUBLIC\n        ${mosquitto_SOURCE_DIR}\n        ${mosquitto_SOURCE_DIR}/include\n        ${mosquitto_SOURCE_DIR}/lib\n        ${mosquitto_SOURCE_DIR}/test/mock\n        ${mosquitto_SOURCE_DIR}/test/mock/lib\n)\ntarget_link_libraries(libmosquitto_mock PRIVATE cJSON GTest::gmock)\n"
  },
  {
    "path": "test/mock/lib/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all check test-compile test clean\n\nLOCAL_CPPFLAGS+= \\\n\t-I../ \\\n\t-I${R}/include \\\n\t-I${R} \\\n\t-I${R}/lib \\\n\t-I${R}/test/mock\n\nLOCAL_CXXFLAGS+=-std=c++20 -Wall -ggdb -D TEST_SOURCE_DIR='\"$(realpath .)\"'\n\nMOCK_OBJS = \\\n\tactions_publish_mock.o \\\n\tactions_subscribe_mock.o \\\n\tactions_unsubscribe_mock.o \\\n\tcallbacks_mock.o \\\n\tconnect_mock.o \\\n\textended_auth_mock.o \\\n\thelpers_mock.o \\\n\tlibmosquitto_mock.o \\\n\tloop_mock.o \\\n\tmessages_mosq_mock.o \\\n\tnet_mosq_mock.o \\\n\toptions_mock.o \\\n\tsocks_mosq_mock.o \\\n\tsrv_mosq_mock.o \\\n\tthread_mosq_mock.o\n\nall : test-compile\n\ntest-compile: ${MOCK_OBJS}\ncheck : test\n\n# MOCKS\n\n${MOCK_OBJS} : %.o: %.cpp libmosquitto_mock.hpp\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_CXXFLAGS) -c $< -o $@\n\nclean :\n\t-rm -rf *.o *.gcda *.gcno\n\ninstall:\n\nuninstall:\n"
  },
  {
    "path": "test/mock/lib/actions_publish_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic,\n\t\tint payloadlen, const void *payload, int qos, bool retain)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_publish(mosq, mid, topic, payloadlen, static_cast<const char *>(payload), qos, retain);\n}\n\n\nint mosquitto_publish_v5(struct mosquitto *mosq, int *mid, const char *topic,\n\t\tint payloadlen, const void *payload, int qos, bool retain, const mosquitto_property *properties)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_publish_v5(mosq, mid, topic, payloadlen, static_cast<const char *>(payload), qos, retain, properties);\n}\n"
  },
  {
    "path": "test/mock/lib/actions_subscribe_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_subscribe(mosq, mid, sub, qos);\n}\n\n\nint mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos,\n\t\tint options, const mosquitto_property *properties)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_subscribe_v5(mosq, mid, sub, qos, options, properties);\n}\n\n\nint mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count,\n\t\tchar *const *const sub, int qos, int options, const mosquitto_property *properties)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_subscribe_multiple(mosq, mid, sub_count, sub, qos, options, properties);\n}\n"
  },
  {
    "path": "test/mock/lib/actions_unsubscribe_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_unsubscribe(mosq, mid, sub);\n}\n\n\nint mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub,\n\t\tconst mosquitto_property *properties)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_unsubscribe_v5(mosq, mid, sub, properties);\n}\n\n\nint mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count,\n\t\tchar *const *const sub, const mosquitto_property *properties)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_unsubscribe_multiple(mosq,\n\t\t\tmid, sub_count, sub, properties);\n}\n"
  },
  {
    "path": "test/mock/lib/callbacks_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nvoid mosquitto_connect_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_connect on_connect)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_connect_callback_set(mosq, on_connect);\n}\n\n\nvoid mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_connect_with_flags on_connect)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_connect_with_flags_callback_set(mosq, on_connect);\n}\n\n\nvoid mosquitto_connect_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_connect_v5 on_connect)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_connect_v5_callback_set(mosq, on_connect);\n}\n\n\nvoid mosquitto_pre_connect_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_pre_connect on_pre_connect)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_pre_connect_callback_set(mosq, on_pre_connect);\n}\n\n\nvoid mosquitto_disconnect_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_disconnect on_disconnect)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_disconnect_callback_set(mosq, on_disconnect);\n}\n\n\nvoid mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_disconnect_v5 on_disconnect)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_disconnect_v5_callback_set(mosq, on_disconnect);\n}\n\n\nvoid mosquitto_publish_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_publish on_publish)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_publish_callback_set(mosq, on_publish);\n}\n\n\nvoid mosquitto_publish_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_publish_v5 on_publish)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_publish_v5_callback_set(mosq, on_publish);\n}\n\n\nvoid mosquitto_message_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_message on_message)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_message_callback_set(mosq, on_message);\n}\n\n\nvoid mosquitto_message_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_message_v5 on_message)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_message_v5_callback_set(mosq, on_message);\n}\n\n\nvoid mosquitto_subscribe_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_subscribe on_subscribe)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_subscribe_callback_set(mosq, on_subscribe);\n}\n\n\nvoid mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_subscribe_v5 on_subscribe)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_subscribe_v5_callback_set(mosq, on_subscribe);\n}\n\n\nvoid mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_unsubscribe on_unsubscribe)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_unsubscribe_callback_set(mosq, on_unsubscribe);\n}\n\n\nvoid mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_unsubscribe_v5 on_unsubscribe)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_unsubscribe_v5_callback_set(mosq, on_unsubscribe);\n}\n\n\nvoid mosquitto_unsubscribe2_v5_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_unsubscribe2_v5 on_unsubscribe)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_unsubscribe2_v5_callback_set(mosq, on_unsubscribe);\n}\n\n\nvoid mosquitto_log_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_log on_log)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_log_callback_set(mosq, on_log);\n}\n\n\nvoid mosquitto_ext_auth_callback_set(struct mosquitto *mosq, LIBMOSQ_CB_ext_auth on_ext_auth)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_ext_auth_callback_set(mosq, on_ext_auth);\n}\n"
  },
  {
    "path": "test/mock/lib/connect_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_connect(struct mosquitto *mosq, const char *host,\n\t\tint port, int keepalive)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_connect(mosq, host,\n\t\t\tport, keepalive);\n}\n\n\nint mosquitto_connect_bind(struct mosquitto *mosq, const char *host,\n\t\tint port, int keepalive, const char *bind_address)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_connect_bind(mosq, host,\n\t\t\tport, keepalive, bind_address);\n}\n\n\nint mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host,\n\t\tint port, int keepalive, const char *bind_address,\n\t\tconst mosquitto_property *properties)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_connect_bind_v5(mosq, host,\n\t\t\tport, keepalive, bind_address, properties);\n}\n\n\nint mosquitto_connect_async(struct mosquitto *mosq, const char *host,\n\t\tint port, int keepalive)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_connect_async(mosq, host,\n\t\t\tport, keepalive);\n}\n\n\nint mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host,\n\t\tint port, int keepalive, const char *bind_address)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_connect_bind_async(mosq, host,\n\t\t\tport, keepalive, bind_address);\n}\n\n\nint mosquitto_reconnect_async(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_reconnect_async(mosq);\n}\n\n\nint mosquitto_reconnect(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_reconnect(mosq);\n}\n\n\nint mosquitto_disconnect(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_disconnect(mosq);\n}\n\n\nint mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code,\n\t\tconst mosquitto_property *properties)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_disconnect_v5(mosq,\n\t\t\treason_code, properties);\n}\n"
  },
  {
    "path": "test/mock/lib/extended_auth_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_ext_auth_continue(struct mosquitto *context,\n\t\tconst char *auth_method, uint16_t auth_data_len, const void *auth_data,\n\t\tconst mosquitto_property *input_props)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_ext_auth_continue(context,\n\t\t\tauth_method, auth_data_len, auth_data, input_props);\n}\n"
  },
  {
    "path": "test/mock/lib/helpers_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_subscribe_simple(struct mosquitto_message **messages,\n\t\tint msg_count, bool want_retained, const char *topic, int qos,\n\t\tconst char *host, int port, const char *clientid, int keepalive,\n\t\tbool clean_session, const char *username, const char *password,\n\t\tconst struct libmosquitto_will *will, const struct libmosquitto_tls *tls)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_subscribe_simple(messages,\n\t\t\tmsg_count, want_retained, topic, qos, host, port, clientid,\n\t\t\tkeepalive, clean_session, username, password, will, tls);\n}\n\n\nint mosquitto_subscribe_callback(int (*callback)(struct mosquitto *,\n\t\tvoid *, const struct mosquitto_message *), void *userdata,\n\t\tconst char *topic, int qos, const char *host, int port,\n\t\tconst char *clientid, int keepalive, bool clean_session,\n\t\tconst char *username, const char *password, const struct libmosquitto_will *will,\n\t\tconst struct libmosquitto_tls *tls)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_subscribe_callback(\n\t\t\tcallback, userdata, topic, qos, host, port, clientid,\n\t\t\tkeepalive, clean_session, username, password, will, tls);\n\n}\n"
  },
  {
    "path": "test/mock/lib/libmosquitto_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\nLibMosquittoMock::LibMosquittoMock()\n{\n};\nLibMosquittoMock::~LibMosquittoMock()\n{\n};\n\n\nint mosquitto_lib_version(int *major, int *minor, int *revision)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_lib_version(\n\t\t\tmajor, minor, revision);\n}\n\n\nint mosquitto_lib_init()\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_lib_init();\n}\n\n\nint mosquitto_lib_cleanup()\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_lib_cleanup();\n}\n\nstruct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_new(id, clean_start, userdata);\n}\n\n\nint mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_start,\n\t\tvoid *userdata)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_reinitialise(mosq, id, clean_start, userdata);\n}\n\n\nvoid mosquitto_destroy(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_destroy(mosq);\n}\n\n\nint mosquitto_socket(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_socket(mosq);\n}\n\n\nbool mosquitto_want_write(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_want_write(mosq);\n}\n"
  },
  {
    "path": "test/mock/lib/libmosquitto_mock.hpp",
    "content": "#pragma once\n\n#include <gmock/gmock.h>\n#include <mosquitto_internal.h>\n\n#include \"c_function_mock.hpp\"\n\nclass LibMosquittoMock : public CFunctionMock<LibMosquittoMock> {\n\tpublic:\n\t\tLibMosquittoMock();\n\t\tvirtual ~LibMosquittoMock();\n\n\t\t/* libmosquitto.c */\n\t\tMOCK_METHOD(int, mosquitto_lib_version, (int *major, int *minor, int *revision));\n\t\tMOCK_METHOD(int, mosquitto_lib_init, ());\n\t\tMOCK_METHOD(int, mosquitto_lib_cleanup, ());\n\t\tMOCK_METHOD(struct mosquitto *, mosquitto_new, (const char *id, bool clean_start, void *userdata));\n\t\tMOCK_METHOD(int, mosquitto_reinitialise, (struct mosquitto *mosq, const char *id, bool clean_start,\n\t\t\tvoid *userdata));\n\t\tMOCK_METHOD(void, mosquitto_destroy, (struct mosquitto *mosq));\n\t\tMOCK_METHOD(int, mosquitto_socket, (struct mosquitto *mosq));\n\t\tMOCK_METHOD(bool, mosquitto_want_write, (struct mosquitto *mosq));\n\n\t\t/* actions_publish.c */\n\t\tMOCK_METHOD(int, mosquitto_publish, (struct mosquitto *mosq, int *mid, const char *topic,\n\t\t\tint payloadlen, const /*void*/char *payload, int qos, bool retain));\n\t\tMOCK_METHOD(int, mosquitto_publish_v5, (struct mosquitto *mosq, int *mid, const char *topic,\n\t\t\tint payloadlen, const /*void*/char *payload, int qos, bool retain, const mosquitto_property *properties));\n\n\t\t/* actions_subscribe.c */\n\t\tMOCK_METHOD(int, mosquitto_subscribe, (struct mosquitto *mosq, int *mid, const char *sub, int qos));\n\t\tMOCK_METHOD(int, mosquitto_subscribe_v5, (struct mosquitto *mosq, int *mid, const char *sub, int qos,\n\t\t\tint options, const mosquitto_property *properties));\n\t\tMOCK_METHOD(int, mosquitto_subscribe_multiple, (struct mosquitto *mosq, int *mid, int sub_count,\n\t\t\tchar *const *const sub, int qos, int options, const mosquitto_property *properties));\n\n\t\t/* actions_unsubscribe.c */\n\t\tMOCK_METHOD(int, mosquitto_unsubscribe, (struct mosquitto *mosq, int *mid, const char *sub));\n\t\tMOCK_METHOD(int, mosquitto_unsubscribe_v5, (struct mosquitto *mosq, int *mid, const char *sub,\n\t\t\tconst mosquitto_property *properties));\n\t\tMOCK_METHOD(int, mosquitto_unsubscribe_multiple, (struct mosquitto *mosq, int *mid, int sub_count,\n\t\t\tchar *const *const sub, const mosquitto_property *properties));\n\n\t\t/* callbacks.c */\n\t\tMOCK_METHOD(void, mosquitto_connect_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_connect on_connect));\n\t\tMOCK_METHOD(void, mosquitto_connect_with_flags_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_connect_with_flags on_connect));\n\t\tMOCK_METHOD(void, mosquitto_connect_v5_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_connect_v5 on_connect));\n\t\tMOCK_METHOD(void, mosquitto_pre_connect_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_pre_connect on_pre_connect));\n\t\tMOCK_METHOD(void, mosquitto_disconnect_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_disconnect on_disconnect));\n\t\tMOCK_METHOD(void, mosquitto_disconnect_v5_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_disconnect_v5 on_disconnect));\n\t\tMOCK_METHOD(void, mosquitto_publish_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_publish on_publish));\n\t\tMOCK_METHOD(void, mosquitto_publish_v5_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_publish_v5 on_publish));\n\t\tMOCK_METHOD(void, mosquitto_message_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_message on_message));\n\t\tMOCK_METHOD(void, mosquitto_message_v5_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_message_v5 on_message));\n\t\tMOCK_METHOD(void, mosquitto_subscribe_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_subscribe on_subscribe));\n\t\tMOCK_METHOD(void, mosquitto_subscribe_v5_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_subscribe_v5 on_subscribe));\n\t\tMOCK_METHOD(void, mosquitto_unsubscribe_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_unsubscribe on_unsubscribe));\n\t\tMOCK_METHOD(void, mosquitto_unsubscribe_v5_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_unsubscribe_v5 on_unsubscribe));\n\t\tMOCK_METHOD(void, mosquitto_unsubscribe2_v5_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_unsubscribe2_v5 on_unsubscribe));\n\t\tMOCK_METHOD(void, mosquitto_log_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_log on_log));\n\t\tMOCK_METHOD(void, mosquitto_ext_auth_callback_set, (struct mosquitto *mosq,\n\t\t\tLIBMOSQ_CB_ext_auth on_ext_auth));\n\n\t\t/* connect.c */\n\t\tMOCK_METHOD(int, mosquitto_connect, (struct mosquitto *mosq, const char *host,\n\t\t\tint port, int keepalive));\n\t\tMOCK_METHOD(int, mosquitto_connect_bind, (struct mosquitto *mosq, const char *host,\n\t\t\tint port, int keepalive, const char *bind_address));\n\t\tMOCK_METHOD(int, mosquitto_connect_bind_v5, (struct mosquitto *mosq, const char *host,\n\t\t\tint port, int keepalive, const char *bind_address, const mosquitto_property *properties));\n\t\tMOCK_METHOD(int, mosquitto_connect_async, (struct mosquitto *mosq, const char *host,\n\t\t\tint port, int keepalive));\n\t\tMOCK_METHOD(int, mosquitto_connect_bind_async, (struct mosquitto *mosq, const char *host,\n\t\t\tint port, int keepalive, const char *bind_address));\n\t\tMOCK_METHOD(int, mosquitto_reconnect_async, (struct mosquitto *mosq));\n\t\tMOCK_METHOD(int, mosquitto_reconnect, (struct mosquitto *mosq));\n\t\tMOCK_METHOD(int, mosquitto_disconnect, (struct mosquitto *mosq));\n\t\tMOCK_METHOD(int, mosquitto_disconnect_v5, (struct mosquitto *mosq, int reason_code,\n\t\t\tconst mosquitto_property *properties));\n\n\t\t/* extended_auth.c */\n\t\tMOCK_METHOD(int, mosquitto_ext_auth_continue, (struct mosquitto *context,\n\t\t\tconst char *auth_method, uint16_t auth_data_len, const void *auth_data,\n\t\t\tconst mosquitto_property *input_props));\n\n\t\t/* helpers.c */\n\t\tMOCK_METHOD(int, mosquitto_subscribe_simple, (struct mosquitto_message **messages,\n\t\t\tint msg_count, bool want_retained, const char *topic, int qos,\n\t\t\tconst char *host, int port, const char *clientid, int keepalive,\n\t\t\tbool clean_session, const char *username, const char *password,\n\t\t\tconst struct libmosquitto_will *will, const struct libmosquitto_tls *tls));\n\t\tMOCK_METHOD(int, mosquitto_subscribe_callback, (int (*callback)(struct mosquitto *,\n\t\t\tvoid *, const struct mosquitto_message *), void *userdata,\n\t\t\tconst char *topic, int qos, const char *host, int port,\n\t\t\tconst char *clientid, int keepalive, bool clean_session,\n\t\t\tconst char *username, const char *password, const struct libmosquitto_will *will,\n\t\t\tconst struct libmosquitto_tls *tls));\n\n\t\t/* loop.c */\n\t\tMOCK_METHOD(int, mosquitto_loop, (struct mosquitto *mosq, int timeout, int max_packets));\n\t\tMOCK_METHOD(int, mosquitto_loop_forever, (struct mosquitto *mosq, int timeout, int max_packets));\n\t\tMOCK_METHOD(int, mosquitto_loop_misc, (struct mosquitto *mosq));\n\t\tMOCK_METHOD(int, mosquitto_loop_read, (struct mosquitto *mosq, int max_packets));\n\t\tMOCK_METHOD(int, mosquitto_loop_write, (struct mosquitto *mosq, int max_packets));\n\n\t\t/* messages_mosq.c */\n\t\tMOCK_METHOD(int, mosquitto_message_copy, (struct mosquitto_message *dst,\n\t\t\tconst struct mosquitto_message *src));\n\t\tMOCK_METHOD(void, mosquitto_message_free, (struct mosquitto_message **message));\n\t\tMOCK_METHOD(void, mosquitto_message_free_contents, (struct mosquitto_message *message));\n\t\tMOCK_METHOD(void, mosquitto_message_retry_set, (struct mosquitto *mosq,\n\t\t\tunsigned int message_retry));\n\n\t\t/* net_mosq.c */\n\t\tMOCK_METHOD(void *, mosquitto_ssl_get, (struct mosquitto *mosq));\n\n\t\t/* options.c */\n\t\tMOCK_METHOD(int, mosquitto_will_set, (struct mosquitto *mosq, const char *topic,\n\t\t\tint payloadlen, const void *payload, int qos, bool retain));\n\t\tMOCK_METHOD(int, mosquitto_will_set_v5, (struct mosquitto *mosq, const char *topic,\n\t\t\tint payloadlen, const void *payload, int qos, bool retain,\n\t\t\tmosquitto_property *properties));\n\t\tMOCK_METHOD(int, mosquitto_will_clear, (struct mosquitto *mosq));\n\t\tMOCK_METHOD(int, mosquitto_username_pw_set, (struct mosquitto *mosq,\n\t\t\tconst char *username, const char *password));\n\t\tMOCK_METHOD(int, mosquitto_reconnect_delay_set, (struct mosquitto *mosq,\n\t\t\tunsigned int reconnect_delay, unsigned int reconnect_delay_max,\n\t\t\tbool reconnect_exponential_backoff));\n\t\tMOCK_METHOD(int, mosquitto_tls_set, (struct mosquitto *mosq, const char *cafile,\n\t\t\tconst char *capath, const char *certfile, const char *keyfile,\n\t\t\tint (*pw_callback)(char *buf, int size, int rwflag, void *userdata)));\n\t\tMOCK_METHOD(int, mosquitto_tls_opts_set, (struct mosquitto *mosq, int cert_reqs,\n\t\t\tconst char *tls_version, const char *ciphers));\n\t\tMOCK_METHOD(int, mosquitto_tls_insecure_set, (struct mosquitto *mosq, bool value));\n\t\tMOCK_METHOD(int, mosquitto_string_option, (struct mosquitto *mosq,\n\t\t\tenum mosq_opt_t option, const char *value));\n\t\tMOCK_METHOD(int, mosquitto_tls_psk_set, (struct mosquitto *mosq, const char *psk,\n\t\t\tconst char *identity, const char *ciphers));\n\t\tMOCK_METHOD(int, mosquitto_opts_set, (struct mosquitto *mosq, enum mosq_opt_t option,\n\t\t\tvoid *value));\n\t\tMOCK_METHOD(int, mosquitto_int_option, (struct mosquitto *mosq, enum mosq_opt_t option,\n\t\t\tint value));\n\t\tMOCK_METHOD(int, mosquitto_void_option, (struct mosquitto *mosq, enum mosq_opt_t option,\n\t\t\tvoid *value));\n\t\tMOCK_METHOD(void, mosquitto_user_data_set, (struct mosquitto *mosq, void *userdata));\n\t\tMOCK_METHOD(void *, mosquitto_userdata, (struct mosquitto *mosq));\n\n\t\t/* socks_mosq.c */\n\t\tMOCK_METHOD(int,  mosquitto_socks5_set, (struct mosquitto *mosq, const char *host,\n\t\t\tint port, const char *username, const char *password));\n\n\t\t/* srv_mosq.c */\n\t\tMOCK_METHOD(int, mosquitto_connect_srv, (struct mosquitto *mosq, const char *host,\n\t\t\tint keepalive, const char *bind_address));\n\n\t\t/* thread_mosq.c */\n\t\tMOCK_METHOD(int, mosquitto_loop_start, (struct mosquitto *mosq));\n\t\tMOCK_METHOD(int, mosquitto_loop_stop, (struct mosquitto *mosq, bool force));\n\t\tMOCK_METHOD(int, mosquitto_threaded_set, (struct mosquitto *mosq, bool threaded));\n};\n"
  },
  {
    "path": "test/mock/lib/loop_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_loop(mosq, timeout, max_packets);\n}\n\n\nint mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_loop_forever(mosq, timeout, max_packets);\n}\n\n\nint mosquitto_loop_misc(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_loop_misc(mosq);\n}\n\n\nint mosquitto_loop_read(struct mosquitto *mosq, int max_packets)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_loop_read(mosq, max_packets);\n}\n\n\nint mosquitto_loop_write(struct mosquitto *mosq, int max_packets)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_loop_write(mosq, max_packets);\n}\n"
  },
  {
    "path": "test/mock/lib/messages_mosq_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_message_copy(struct mosquitto_message *dst,\n\t\tconst struct mosquitto_message *src)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_message_copy(dst, src);\n}\n\n\nvoid mosquitto_message_free(struct mosquitto_message **message)\n{\n\tLibMosquittoMock::get_mock().mosquitto_message_free(message);\n}\n\n\nvoid mosquitto_message_free_contents(struct mosquitto_message *message)\n{\n\tLibMosquittoMock::get_mock().mosquitto_message_free_contents(message);\n}\n"
  },
  {
    "path": "test/mock/lib/net_mosq_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nvoid *mosquitto_ssl_get(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_ssl_get(mosq);\n}\n"
  },
  {
    "path": "test/mock/lib/options_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_will_set(struct mosquitto *mosq, const char *topic,\n\t\tint payloadlen, const void *payload, int qos, bool retain)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_will_set(mosq,\n\t\t\ttopic, payloadlen, payload, qos, retain);\n}\n\n\nint mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic,\n\t\tint payloadlen, const void *payload, int qos, bool retain,\n\t\tmosquitto_property *properties)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_will_set_v5(mosq,\n\t\t\ttopic, payloadlen, payload, qos, retain, properties);\n}\n\n\nint mosquitto_will_clear(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_will_clear(mosq);\n}\n\n\nint mosquitto_username_pw_set(struct mosquitto *mosq,\n\t\tconst char *username, const char *password)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_username_pw_set(mosq,\n\t\t\tusername, password);\n}\n\n\nint mosquitto_reconnect_delay_set(struct mosquitto *mosq,\n\t\tunsigned int reconnect_delay, unsigned int reconnect_delay_max,\n\t\tbool reconnect_exponential_backoff)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_reconnect_delay_set(mosq,\n\t\t\treconnect_delay, reconnect_delay_max, reconnect_exponential_backoff);\n}\n\n\nint mosquitto_tls_set(struct mosquitto *mosq, const char *cafile,\n\t\tconst char *capath, const char *certfile, const char *keyfile,\n\t\tint (*pw_callback)(char *buf, int size, int rwflag, void *userdata))\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_tls_set(mosq,\n\t\t\tcafile, capath, certfile, keyfile, pw_callback);\n}\n\n\nint mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs,\n\t\tconst char *tls_version, const char *ciphers)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_tls_opts_set(mosq,\n\t\t\tcert_reqs, tls_version, ciphers);\n}\n\n\nint mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_tls_insecure_set(mosq,\n\t\t\tvalue);\n}\n\n\nint mosquitto_string_option(struct mosquitto *mosq,\n\t\tenum mosq_opt_t option, const char *value)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_string_option(mosq,\n\t\t\toption, value);\n}\n\n\nint mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk,\n\t\tconst char *identity, const char *ciphers)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_tls_psk_set(mosq,\n\t\t\tpsk, identity, ciphers);\n}\n\n\nint mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option,\n\t\tvoid *value)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_opts_set(mosq,\n\t\t\toption, value);\n}\n\n\nint mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option,\n\t\tint value)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_int_option(mosq,\n\t\t\toption, value);\n}\n\n\nint mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option,\n\t\tvoid *value)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_void_option(mosq,\n\t\t\toption, value);\n}\n\n\nvoid mosquitto_user_data_set(struct mosquitto *mosq, void *userdata)\n{\n\tLibMosquittoMock::get_mock().mosquitto_user_data_set(mosq, userdata);\n}\n\n\nvoid *mosquitto_userdata(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_userdata(mosq);\n}\n"
  },
  {
    "path": "test/mock/lib/socks_mosq_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_socks5_set(struct mosquitto *mosq, const char *host,\n\t\tint port, const char *username, const char *password)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_socks5_set(mosq,\n\t\t\thost, port, username, password);\n}\n"
  },
  {
    "path": "test/mock/lib/srv_mosq_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_connect_srv(struct mosquitto *mosq, const char *host,\n\t\tint keepalive, const char *bind_address)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_connect_srv(mosq,\n\t\t\thost, keepalive, bind_address);\n}\n"
  },
  {
    "path": "test/mock/lib/thread_mosq_mock.cpp",
    "content": "#include \"libmosquitto_mock.hpp\"\n\n\nint mosquitto_loop_start(struct mosquitto *mosq)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_loop_start(mosq);\n}\n\n\nint mosquitto_loop_stop(struct mosquitto *mosq, bool force)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_loop_stop(mosq, force);\n}\n\n\nint mosquitto_threaded_set(struct mosquitto *mosq, bool threaded)\n{\n\treturn LibMosquittoMock::get_mock().mosquitto_threaded_set(mosq, threaded);\n}\n"
  },
  {
    "path": "test/mock/libcommon/CMakeLists.txt",
    "content": "add_library(libmosquitto_common_mock OBJECT\n\tlibmosquitto_common_mock.hpp\n\tlibmosquitto_common_mock.cpp\n\tbase64_common_mock.cpp\n\tfile_common_mock.cpp\n\tmemory_common_mock.cpp\n\tmqtt_common_mock.cpp\n\tpassword_common_mock.cpp\n\tproperty_common_mock.cpp\n\trandom_common_mock.cpp\n\tstrings_common_mock.cpp\n\ttime_common_mock.cpp\n\ttopic_common_mock.cpp\n\tutf8_common_mock.cpp\n)\n\ntarget_include_directories(libmosquitto_common_mock\n    PUBLIC\n        ${mosquitto_SOURCE_DIR}\n        ${mosquitto_SOURCE_DIR}/include\n        ${mosquitto_SOURCE_DIR}/lib\n        ${mosquitto_SOURCE_DIR}/test/mock\n        ${mosquitto_SOURCE_DIR}/test/mock/lib\n)\ntarget_link_libraries(libmosquitto_common_mock PRIVATE cJSON GTest::gmock)\n"
  },
  {
    "path": "test/mock/libcommon/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all check test-compile test clean\n\nLOCAL_CPPFLAGS+= \\\n\t-I../ \\\n\t-I${R}/include \\\n\t-I${R} \\\n\t-I${R}/lib \\\n\t-I${R}/test/mock\n\nLOCAL_CXXFLAGS+=-std=c++20 -Wall -ggdb -D TEST_SOURCE_DIR='\"$(realpath .)\"'\n\nMOCK_OBJS = \\\n\tbase64_common_mock.o \\\n\tfile_common_mock.o \\\n\tlibmosquitto_common_mock.o \\\n\tmemory_common_mock.o \\\n\tmqtt_common_mock.o \\\n\tpassword_common_mock.o \\\n\tproperty_common_mock.o \\\n\trandom_common_mock.o \\\n\tstrings_common_mock.o \\\n\ttime_common_mock.o \\\n\ttopic_common_mock.o \\\n\tutf8_common_mock.o\n\nall : test-compile\n\ntest-compile: ${MOCK_OBJS}\ncheck : test\n\n# MOCKS\n\n${MOCK_OBJS} : %.o: %.cpp libmosquitto_common_mock.hpp\n\t$(CROSS_COMPILE)$(CXX) $(LOCAL_CPPFLAGS) $(LOCAL_CXXFLAGS) -c $< -o $@\n\nclean :\n\t-rm -rf *.o *.gcda *.gcno\n\ninstall:\n\nuninstall:\n"
  },
  {
    "path": "test/mock/libcommon/base64_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nint mosquitto_base64_encode(const unsigned char *in, size_t in_len, char **encoded)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_base64_encode(\n\t\t\tin, in_len, encoded);\n}\n\n\nint mosquitto_base64_decode(const char *in, unsigned char **decoded, unsigned int *decoded_len)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_base64_decode(\n\t\t\tin, decoded, decoded_len);\n}\n"
  },
  {
    "path": "test/mock/libcommon/cjson_common.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\ncJSON *mosquitto_properties_to_json(const mosquitto_property *properties)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_properties_to_json(\n\t\t\tproperties);\n}\n"
  },
  {
    "path": "test/mock/libcommon/file_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nFILE *mosquitto_fopen(const char *path, const char *mode, bool restrict_read)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_fopen(\n\t\t\tpath, mode, restrict_read);\n}\n\n\nchar *mosquitto_trimblanks(char *str)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_trimblanks(str);\n}\n\n\nchar *mosquitto_fgets(char **buf, int *buflen, FILE *stream)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_fgets(buf, buflen, stream);\n}\n\n\nint mosquitto_write_file(const char *target_path, bool restrict_read, int (*write_fn)(FILE *fptr, void *user_data), void *user_data, void (*log_fn)(const char *msg))\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_write_file(\n\t\t\ttarget_path, restrict_read, write_fn, user_data, log_fn);\n}\n\n\nint mosquitto_read_file(const char *file, char **buf, size_t *buflen)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_read_file(\n\t\t\tfile, buf, buflen);\n}\n"
  },
  {
    "path": "test/mock/libcommon/libmosquitto_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\nLibMosquittoCommonMock::LibMosquittoCommonMock()\n{\n};\nLibMosquittoCommonMock::~LibMosquittoCommonMock()\n{\n};\n"
  },
  {
    "path": "test/mock/libcommon/libmosquitto_common_mock.hpp",
    "content": "#pragma once\n\n#include <gmock/gmock.h>\n#include <mosquitto.h>\n\n#include \"c_function_mock.hpp\"\n\nclass LibMosquittoCommonMock : public CFunctionMock<LibMosquittoCommonMock> {\n\tpublic:\n\t\tLibMosquittoCommonMock();\n\t\tvirtual ~LibMosquittoCommonMock();\n\n\t\t/* base64_common.c */\n\t\tMOCK_METHOD(int, mosquitto_base64_encode, (const unsigned char *in, size_t in_len, char **encoded));\n\t\tMOCK_METHOD(int, mosquitto_base64_decode, (const char *in, unsigned char **decoded, unsigned int *decoded_len));\n\n\t\t/* cjson_common.c */\n\t\tMOCK_METHOD(cJSON *, mosquitto_properties_to_json, (const mosquitto_property *properties));\n\n\t\t/* file_common.c */\n\t\tMOCK_METHOD(FILE *, mosquitto_fopen, (const char *path, const char *mode, bool restrict_read));\n\t\tMOCK_METHOD(char *, mosquitto_trimblanks, (char *str));\n\t\tMOCK_METHOD(char *, mosquitto_fgets, (char **buf, int *buflen, FILE *stream));\n\t\tMOCK_METHOD(int, mosquitto_write_file, (const char* target_path, bool restrict_read,\n\t\t\t\t\tint (*write_fn)(FILE* fptr, void* user_data), void* user_data, void (*log_fn)(const char* msg)));\n\t\tMOCK_METHOD(int, mosquitto_read_file, (const char *file, char **buf, size_t *buflen));\n\n\t\t/* memory_common.c */\n\t\tMOCK_METHOD(void, mosquitto_memory_set_limit, (size_t lim));\n\t\tMOCK_METHOD(unsigned long, mosquitto_memory_used, ());\n\t\tMOCK_METHOD(unsigned long, mosquitto_max_memory_used, ());\n\t\tMOCK_METHOD(void *, mosquitto_malloc, (size_t size));\n\t\tMOCK_METHOD(void *, mosquitto_realloc, (void *ptr, size_t size));\n\t\tMOCK_METHOD(void, mosquitto_free, (void *mem));\n\t\tMOCK_METHOD(void *, mosquitto_calloc, (size_t nmemb, size_t size));\n\t\tMOCK_METHOD(char *, mosquitto_strdup, (const char *s));\n\t\tMOCK_METHOD(char *, mosquitto_strndup, (const char *s, size_t n));\n\n\t\t/* mqtt_common.c */\n\t\tMOCK_METHOD(unsigned int, mosquitto_varint_bytes, (uint32_t word));\n\n\t\t/* password_common.c */\n\t\tMOCK_METHOD(int, mosquitto_pw_new, (struct mosquitto_pw **pw, enum mosquitto_pwhash_type hashtype));\n\t\tMOCK_METHOD(int, mosquitto_pw_hash_encoded, (struct mosquitto_pw *pw, const char *password));\n\t\tMOCK_METHOD(int, mosquitto_pw_verify, (struct mosquitto_pw *pw, const char *password));\n\t\tMOCK_METHOD(void, mosquitto_pw_set_valid, (struct mosquitto_pw *pw, bool valid));\n\t\tMOCK_METHOD(bool, mosquitto_pw_is_valid, (struct mosquitto_pw *pw));\n\t\tMOCK_METHOD(int, mosquitto_pw_decode, (struct mosquitto_pw *pw, const char *password));\n\t\tMOCK_METHOD(const char *, mosquitto_pw_get_encoded, (struct mosquitto_pw *pw));\n\t\tMOCK_METHOD(int, mosquitto_pw_set_param, (struct mosquitto_pw *pw, int param, int value));\n\t\tMOCK_METHOD(void, mosquitto_pw_cleanup, (struct mosquitto_pw *pw));\n\n\t\t/* property_common.c */\n\t\tMOCK_METHOD(void, mosquitto_property_free, (mosquitto_property **property));\n\t\tMOCK_METHOD(void, mosquitto_property_free_all, (mosquitto_property **property));\n\t\tMOCK_METHOD(unsigned int, mosquitto_property_get_length, (const mosquitto_property *property));\n\t\tMOCK_METHOD(unsigned int, mosquitto_property_get_length_all, (const mosquitto_property *property));\n\t\tMOCK_METHOD(int, mosquitto_property_check_command, (int command, int identifier));\n\t\tMOCK_METHOD(const char *, mosquitto_property_identifier_to_string, (int identifier));\n\t\tMOCK_METHOD(int, mosquitto_string_to_property_info, (const char *propname, int *identifier, int *type));\n\t\tMOCK_METHOD(int, mosquitto_property_add_byte, (mosquitto_property **proplist, int identifier, uint8_t value));\n\t\tMOCK_METHOD(int, mosquitto_property_add_int16, (mosquitto_property **proplist, int identifier, uint16_t value));\n\t\tMOCK_METHOD(int, mosquitto_property_add_int32, (mosquitto_property **proplist, int identifier, uint32_t value));\n\t\tMOCK_METHOD(int, mosquitto_property_add_varint, (mosquitto_property **proplist, int identifier, uint32_t value));\n\t\tMOCK_METHOD(int, mosquitto_property_add_binary, (mosquitto_property **proplist, int identifier, const void *value, uint16_t len));\n\t\tMOCK_METHOD(int, mosquitto_property_add_string, (mosquitto_property **proplist, int identifier, const char *value));\n\t\tMOCK_METHOD(int, mosquitto_property_add_string_pair, (mosquitto_property **proplist, int identifier, const char *name, const char *value));\n\t\tMOCK_METHOD(int, mosquitto_property_check_all, (int command, const mosquitto_property *properties));\n\t\tMOCK_METHOD(int, mosquitto_property_identifier, (const mosquitto_property *property));\n\t\tMOCK_METHOD(int, mosquitto_property_type, (const mosquitto_property *property));\n\t\tMOCK_METHOD(mosquitto_property *, mosquitto_property_next, (const mosquitto_property *proplist));\n\t\tMOCK_METHOD(const mosquitto_property *, mosquitto_property_read_byte, (const mosquitto_property *proplist, int identifier, uint8_t *value, bool skip_first));\n\t\tMOCK_METHOD(const mosquitto_property *, mosquitto_property_read_int16, (const mosquitto_property *proplist, int identifier, uint16_t *value, bool skip_first));\n\t\tMOCK_METHOD(const mosquitto_property *, mosquitto_property_read_int32, (const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first));\n\t\tMOCK_METHOD(const mosquitto_property *, mosquitto_property_read_varint, (const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first));\n\t\tMOCK_METHOD(const mosquitto_property *, mosquitto_property_read_binary, (const mosquitto_property *proplist, int identifier, void **value, uint16_t *len, bool skip_first));\n\t\tMOCK_METHOD(const mosquitto_property *, mosquitto_property_read_string, (const mosquitto_property *proplist, int identifier, char **value, bool skip_first));\n\t\tMOCK_METHOD(const mosquitto_property *, mosquitto_property_read_string_pair, (const mosquitto_property *proplist, int identifier, char **name, char **value, bool skip_first));\n\t\tMOCK_METHOD(int, mosquitto_property_remove, (mosquitto_property **proplist, const mosquitto_property *property));\n\t\tMOCK_METHOD(int, mosquitto_property_copy_all, (mosquitto_property **dest, const mosquitto_property *src));\n\t\tMOCK_METHOD(uint8_t, mosquitto_property_byte_value, (const mosquitto_property *property));\n\t\tMOCK_METHOD(uint16_t, mosquitto_property_int16_value, (const mosquitto_property *property));\n\t\tMOCK_METHOD(uint32_t, mosquitto_property_int32_value, (const mosquitto_property *property));\n\t\tMOCK_METHOD(uint32_t, mosquitto_property_varint_value, (const mosquitto_property *property));\n\t\tMOCK_METHOD(const void *, mosquitto_property_binary_value, (const mosquitto_property *property));\n\t\tMOCK_METHOD(uint16_t, mosquitto_property_binary_value_length, (const mosquitto_property *property));\n\t\tMOCK_METHOD(const char *, mosquitto_property_string_value, (const mosquitto_property *property));\n\t\tMOCK_METHOD(uint16_t, mosquitto_property_string_value_length, (const mosquitto_property *property));\n\t\tMOCK_METHOD(const char *, mosquitto_property_string_name, (const mosquitto_property *property));\n\t\tMOCK_METHOD(uint16_t, mosquitto_property_string_name_length, (const mosquitto_property *property));\n\t\tMOCK_METHOD(unsigned int, mosquitto_property_get_remaining_length, (const mosquitto_property *props));\n\n\t\t/* random_common.c */\n\t\tMOCK_METHOD(int, mosquitto_getrandom, (void *bytes, int count));\n\n\t\t/* strings_common.c */\n\t\tMOCK_METHOD(const char *, mosquitto_strerror, (int mosq_errno));\n\t\tMOCK_METHOD(const char *, mosquitto_connack_string, (int connack_code));\n\t\tMOCK_METHOD(const char *, mosquitto_reason_string, (int reason_code));\n\t\tMOCK_METHOD(int, mosquitto_string_to_command, (const char *str, int *cmd));\n\n\t\t/* time_common.c */\n\t\tMOCK_METHOD(void, mosquitto_time_init, ());\n\t\tMOCK_METHOD(time_t, mosquitto_time, ());\n\t\tMOCK_METHOD(void, mosquitto_time_ns, (time_t *s, long *ns));\n\t\tMOCK_METHOD(long, mosquitto_time_cmp, (time_t t1_s, long t1_ns, time_t t2_s, long t2_ns));\n\n\t\t/* topic_common.c */\n\t\tMOCK_METHOD(int, mosquitto_pub_topic_check, (const char *str));\n\t\tMOCK_METHOD(int, mosquitto_pub_topic_check2, (const char *str, size_t len));\n\t\tMOCK_METHOD(int, mosquitto_sub_topic_check, (const char *str));\n\t\tMOCK_METHOD(int, mosquitto_sub_topic_check2, (const char *str, size_t len));\n\t\tMOCK_METHOD(int, mosquitto_sub_matches_acl, (const char *acl, const char *sub, bool *result));\n\t\tMOCK_METHOD(int, mosquitto_sub_matches_acl_with_pattern, (const char *acl, const char *sub, const char *clientid, const char *username, bool *result));\n\t\tMOCK_METHOD(int, mosquitto_topic_matches_sub, (const char *sub, const char *topic, bool *result));\n\t\tMOCK_METHOD(int, mosquitto_topic_matches_sub_with_pattern, (const char *sub, const char *topic, const char *clientid, const char *username, bool *result));\n\t\tMOCK_METHOD(int, mosquitto_topic_matches_sub2, (const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result));\n\t\tMOCK_METHOD(int, mosquitto_sub_topic_tokenise, (const char *subtopic, char ***topics, int *count));\n\t\tMOCK_METHOD(int, mosquitto_sub_topic_tokens_free, (char ***topics, int count));\n\n\t\t/* utf8_common.c */\n\t\tMOCK_METHOD(int, mosquitto_validate_utf8, (const char *str, int len));\n\n};\n"
  },
  {
    "path": "test/mock/libcommon/memory_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nvoid mosquitto_memory_set_limit(size_t lim)\n{\n\tLibMosquittoCommonMock::get_mock().mosquitto_memory_set_limit(lim);\n}\n\n\nunsigned long mosquitto_memory_used(void)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_memory_used();\n}\n\n\nunsigned long mosquitto_max_memory_used(void)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_max_memory_used();\n}\n\n\nvoid *mosquitto_malloc(size_t size)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_malloc(size);\n}\n\n\nvoid *mosquitto_realloc(void *ptr, size_t size)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_realloc(ptr, size);\n}\n\n\nvoid mosquitto_free(void *mem)\n{\n\tLibMosquittoCommonMock::get_mock().mosquitto_free(mem);\n}\n\n\nvoid *mosquitto_calloc(size_t nmemb, size_t size)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_calloc(nmemb, size);\n}\n\n\nchar *mosquitto_strdup(const char *s)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_strdup(s);\n}\n\n\nchar *mosquitto_strndup(const char *s, size_t n)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_strndup(s, n);\n}\n"
  },
  {
    "path": "test/mock/libcommon/mqtt_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nunsigned int mosquitto_varint_bytes(uint32_t word)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_varint_bytes(word);\n}\n"
  },
  {
    "path": "test/mock/libcommon/password_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nint mosquitto_pw_new(struct mosquitto_pw **pw, enum mosquitto_pwhash_type hashtype)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_pw_new(\n\t\t\tpw, hashtype);\n}\n\n\nint mosquitto_pw_hash_encoded(struct mosquitto_pw *pw, const char *password)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_pw_hash_encoded(\n\t\t\tpw, password);\n}\n\n\nint mosquitto_pw_verify(struct mosquitto_pw *pw, const char *password)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_pw_verify(\n\t\t\tpw, password);\n}\n\n\nvoid mosquitto_pw_set_valid(struct mosquitto_pw *pw, bool valid)\n{\n\tLibMosquittoCommonMock::get_mock().mosquitto_pw_set_valid(\n\t\t\tpw, valid);\n}\n\n\nbool mosquitto_pw_is_valid(struct mosquitto_pw *pw)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_pw_is_valid(pw);\n}\n\n\nint mosquitto_pw_decode(struct mosquitto_pw *pw, const char *password)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_pw_decode(\n\t\t\tpw, password);\n}\n\n\nconst char *mosquitto_pw_get_encoded(struct mosquitto_pw *pw)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_pw_get_encoded(pw);\n}\n\n\nint mosquitto_pw_set_param(struct mosquitto_pw *pw, int param, int value)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_pw_set_param(\n\t\t\tpw, param, value);\n}\n\n\nvoid mosquitto_pw_cleanup(struct mosquitto_pw *pw)\n{\n\tLibMosquittoCommonMock::get_mock().mosquitto_pw_cleanup(pw);\n}\n"
  },
  {
    "path": "test/mock/libcommon/property_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nvoid mosquitto_property_free(mosquitto_property **property)\n{\n\tLibMosquittoCommonMock::get_mock().mosquitto_property_free(\n\t\t\tproperty);\n}\n\n\nvoid mosquitto_property_free_all(mosquitto_property **property)\n{\n\tLibMosquittoCommonMock::get_mock().mosquitto_property_free_all(\n\t\t\tproperty);\n}\n\n\nunsigned int mosquitto_property_get_length(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_get_length(\n\t\t\tproperty);\n}\n\n\nunsigned int mosquitto_property_get_length_all(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_get_length_all(\n\t\t\tproperty);\n}\n\n\nint mosquitto_property_check_command(int command, int identifier)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_check_command(\n\t\t\tcommand, identifier);\n}\n\n\nconst char *mosquitto_property_identifier_to_string(int identifier)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_identifier_to_string(\n\t\t\tidentifier);\n}\n\n\nint mosquitto_string_to_property_info(const char *propname, int *identifier, int *type)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_string_to_property_info(\n\t\t\tpropname, identifier, type);\n}\n\n\nint mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_add_byte(\n\t\t\tproplist, identifier, value);\n}\n\n\nint mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_add_int16(\n\t\t\tproplist, identifier, value);\n}\n\n\nint mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_add_int32(\n\t\t\tproplist, identifier, value);\n}\n\n\nint mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_add_varint(\n\t\t\tproplist, identifier, value);\n}\n\n\nint mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_add_binary(\n\t\t\tproplist, identifier, value, len);\n}\n\n\nint mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_add_string(\n\t\t\tproplist, identifier, value);\n}\n\n\nint mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_add_string_pair(\n\t\t\tproplist, identifier, name, value);\n}\n\n\nint mosquitto_property_check_all(int command, const mosquitto_property *properties)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_check_all(\n\t\t\tcommand, properties);\n}\n\n\nint mosquitto_property_identifier(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_identifier(\n\t\t\tproperty);\n}\n\n\nint mosquitto_property_type(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_type(\n\t\t\tproperty);\n}\n\n\nmosquitto_property *mosquitto_property_next(const mosquitto_property *proplist)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_next(\n\t\t\tproplist);\n}\n\n\nconst mosquitto_property *mosquitto_property_read_byte(const mosquitto_property *proplist, int identifier, uint8_t *value, bool skip_first)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_read_byte(\n\t\t\tproplist, identifier, value, skip_first);\n}\n\n\nconst mosquitto_property *mosquitto_property_read_int16(const mosquitto_property *proplist, int identifier, uint16_t *value, bool skip_first)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_read_int16(\n\t\t\tproplist, identifier, value, skip_first);\n}\n\n\nconst mosquitto_property *mosquitto_property_read_int32(const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_read_int32(\n\t\t\tproplist, identifier, value, skip_first);\n}\n\n\nconst mosquitto_property *mosquitto_property_read_varint(const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_read_varint(\n\t\t\tproplist, identifier, value, skip_first);\n}\n\n\nconst mosquitto_property *mosquitto_property_read_binary(const mosquitto_property *proplist, int identifier, void **value, uint16_t *len, bool skip_first)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_read_binary(\n\t\t\tproplist, identifier, value, len, skip_first);\n}\n\n\nconst mosquitto_property *mosquitto_property_read_string(const mosquitto_property *proplist, int identifier, char **value, bool skip_first)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_read_string(\n\t\t\tproplist, identifier, value, skip_first);\n}\n\n\nconst mosquitto_property *mosquitto_property_read_string_pair(const mosquitto_property *proplist, int identifier, char **name, char **value, bool skip_first)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_read_string_pair(\n\t\t\tproplist, identifier, name, value, skip_first);\n}\n\n\nint mosquitto_property_remove(mosquitto_property **proplist, const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_remove(\n\t\t\tproplist, property);\n}\n\n\nint mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_copy_all(\n\t\t\tdest, src);\n}\n\n\nuint8_t mosquitto_property_byte_value(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_byte_value(\n\t\t\tproperty);\n}\n\n\nuint16_t mosquitto_property_int16_value(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_int16_value(\n\t\t\tproperty);\n}\n\n\nuint32_t mosquitto_property_int32_value(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_int32_value(\n\t\t\tproperty);\n}\n\n\nuint32_t mosquitto_property_varint_value(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_varint_value(\n\t\t\tproperty);\n}\n\n\nconst void *mosquitto_property_binary_value(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_binary_value(\n\t\t\tproperty);\n}\n\n\nuint16_t mosquitto_property_binary_value_length(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_binary_value_length(\n\t\t\tproperty);\n}\n\n\nconst char *mosquitto_property_string_value(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_string_value(\n\t\t\tproperty);\n}\n\n\nuint16_t mosquitto_property_string_value_length(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_string_value_length(\n\t\t\tproperty);\n}\n\n\nconst char *mosquitto_property_string_name(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_string_name(\n\t\t\tproperty);\n}\n\n\nuint16_t mosquitto_property_string_name_length(const mosquitto_property *property)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_string_name_length(\n\t\t\tproperty);\n}\n\n\nunsigned int mosquitto_property_get_remaining_length(const mosquitto_property *props)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_property_get_remaining_length(\n\t\t\tprops);\n}\n"
  },
  {
    "path": "test/mock/libcommon/random_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nint mosquitto_getrandom(void *bytes, int count)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_getrandom(\n\t\t\tbytes, count);\n}\n"
  },
  {
    "path": "test/mock/libcommon/strings_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nconst char *mosquitto_strerror(int mosq_errno)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_strerror(\n\t\t\tmosq_errno);\n}\n\n\nconst char *mosquitto_connack_string(int connack_code)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_connack_string(\n\t\t\tconnack_code);\n}\n\n\nconst char *mosquitto_reason_string(int reason_code)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_reason_string(\n\t\t\treason_code);\n}\n\n\nint mosquitto_string_to_command(const char *str, int *cmd)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_string_to_command(\n\t\t\tstr, cmd);\n}\n"
  },
  {
    "path": "test/mock/libcommon/time_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nvoid mosquitto_time_init(void)\n{\n\tLibMosquittoCommonMock::get_mock().mosquitto_time_init();\n}\n\n\ntime_t mosquitto_time(void)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_time();\n}\n\n\nvoid mosquitto_time_ns(time_t *s, long *ns)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_time_ns(\n\t\t\ts, ns);\n}\n\n\nlong mosquitto_time_cmp(time_t t1_s, long t1_ns, time_t t2_s, long t2_ns)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_time_cmp(\n\t\t\tt1_s, t1_ns, t2_s, t2_ns);\n}\n"
  },
  {
    "path": "test/mock/libcommon/topic_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nint mosquitto_pub_topic_check(const char *str)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_pub_topic_check(\n\t\t\tstr);\n}\n\n\nint mosquitto_pub_topic_check2(const char *str, size_t len)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_pub_topic_check2(\n\t\t\tstr, len);\n}\n\n\nint mosquitto_sub_topic_check(const char *str)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_sub_topic_check(\n\t\t\tstr);\n}\n\n\nint mosquitto_sub_topic_check2(const char *str, size_t len)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_sub_topic_check2(\n\t\t\tstr, len);\n}\n\n\nint mosquitto_sub_matches_acl(const char *acl, const char *sub, bool *result)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_sub_matches_acl(\n\t\t\tacl, sub, result);\n}\n\n\nint mosquitto_sub_matches_acl_with_pattern(const char *acl, const char *sub, const char *clientid, const char *username, bool *result)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_sub_matches_acl_with_pattern(\n\t\t\tacl, sub, clientid, username, result);\n}\n\n\nint mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_topic_matches_sub(\n\t\t\tsub, topic, result);\n}\n\n\nint mosquitto_topic_matches_sub_with_pattern(const char *sub, const char *topic, const char *clientid, const char *username, bool *result)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_topic_matches_sub_with_pattern(\n\t\t\tsub, topic, clientid, username, result);\n}\n\n\nint mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_topic_matches_sub2(\n\t\t\tsub, sublen, topic, topiclen, result);\n}\n\n\nint mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_sub_topic_tokenise(\n\t\t\tsubtopic, topics, count);\n}\n\n\nint mosquitto_sub_topic_tokens_free(char ***topics, int count)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_sub_topic_tokens_free(\n\t\t\ttopics, count);\n}\n"
  },
  {
    "path": "test/mock/libcommon/utf8_common_mock.cpp",
    "content": "#include \"libmosquitto_common_mock.hpp\"\n\n\nint mosquitto_validate_utf8(const char *str, int len)\n{\n\treturn LibMosquittoCommonMock::get_mock().mosquitto_validate_utf8(\n\t\t\tstr, len);\n}\n"
  },
  {
    "path": "test/mock/pthread_mock.cpp",
    "content": "#include \"pthread_mock.hpp\"\n\nPThreadMock::PThreadMock()\n{\n}\nPThreadMock::~PThreadMock()\n{\n}\n\n\nint pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)\n{\n\treturn PThreadMock::get_mock().pthread_cond_timedwait(cond, mutex, abstime);\n}\n"
  },
  {
    "path": "test/mock/pthread_mock.hpp",
    "content": "#pragma once\n\n#include <gmock/gmock.h>\n#include <pthread.h>\n\n#include \"c_function_mock.hpp\"\n\nclass PThreadMock : public CFunctionMock<PThreadMock> {\n\tpublic:\n\t\tPThreadMock();\n\t\tvirtual ~PThreadMock();\n\n\t\tMOCK_METHOD(int, pthread_cond_timedwait, (pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime));\n};\n"
  },
  {
    "path": "test/mosq_test.py",
    "content": "import atexit\nimport base64\nimport errno\nimport hashlib\nimport os\nimport socket\nimport subprocess\nimport struct\nimport sys\nimport time\nimport uuid\n\nimport traceback\n\nimport mqtt5_props\n\nimport __main__\n\nfrom pathlib import Path\nvg_index = 1\nvg_logfiles = []\n\nclass TestError(Exception):\n    def __init__(self, message=\"Mismatched packets\"):\n        self.message = message\n\ndef get_build_root():\n    result = os.getenv(\"BUILD_ROOT\")\n    if result is None:\n        result = str(Path(__file__).resolve().parents[1])\n    return result\n\ndef env_add_ld_library_path(env=None):\n    p = \":\".join([\n        get_build_root() + '/libcommon',\n        get_build_root() + '/lib',\n        get_build_root() + '/lib/cpp',\n        os.getenv(\"LD_LIBRARY_PATH\", \"\")\n    ])\n\n    if env is None:\n        env = {\n            'LD_LIBRARY_PATH': p,\n            'DYLIB_LIBRARY_PATH': p,\n        }\n    else:\n        for v in ['LD_LIBRARY_PATH', 'DYLIB_LIBRARY_PATH']:\n            try:\n                val = env[v]\n                env[v] = \":\".join([val, p])\n            except KeyError:\n                env[v] = p\n\n    return env\n\ndef listen_sock(port):\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    sock.settimeout(10)\n    sock.bind(('', port))\n    sock.listen(5)\n    return sock\n\ndef start_broker(filename, cmd=None, port=0, use_conf=False, expect_fail=False, expect_fail_log=None, nolog=False, checkhost=\"localhost\", env=None, check_port=True, cmd_args=None, timeout=0.1):\n    global vg_index\n    global vg_logfiles\n\n    if use_conf == True:\n        cmd = [get_build_root() + '/src/mosquitto', '-v', '-c', filename.replace('.py', '.conf')]\n\n        if port == 0:\n            port = 1888\n        else:\n            cmd += ['-p', str(port)]\n    else:\n        if cmd is None and port != 0:\n            cmd = [get_build_root() + '/src/mosquitto', '-v', '-p', str(port)]\n        elif cmd is None and port == 0:\n            cmd = [get_build_root() + '/src/mosquitto', '-v', '-c', filename.replace('.py', '.conf')]\n\n    if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n        logfile = filename+'.'+str(vg_index)+'.vglog'\n        if os.environ.get('MOSQ_USE_VALGRIND') == 'callgrind':\n            cmd = ['valgrind', '-q', '--tool=callgrind', '--log-file='+logfile] + cmd\n        elif os.environ.get('MOSQ_USE_VALGRIND') == 'massif':\n            cmd = ['valgrind', '-q', '--tool=massif', '--log-file='+logfile] + cmd\n        elif os.environ.get('MOSQ_USE_VALGRIND') == 'failgrind':\n            cmd = ['fg-helper'] + cmd\n        else:\n            cmd = ['valgrind', '-q', '--gen-suppressions=all', '--suppressions=test.supp', '--track-fds=yes', '--trace-children=yes', '--leak-check=full', '--show-leak-kinds=all', '--log-file='+logfile] + cmd\n        vg_logfiles.append(logfile)\n        vg_index += 1\n        timeout = 1\n\n    if cmd_args:\n        cmd.extend(cmd_args)\n\n    #print(port)\n    #print(cmd)\n    if nolog:\n        stderr = subprocess.DEVNULL\n    else:\n        stderr = subprocess.PIPE\n\n    broker = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=stderr, env=env)\n\n    if expect_fail:\n        try:\n            broker.wait(timeout*10)\n            if expect_fail_log is not None:\n                (_, stde) = broker.communicate()\n                if expect_fail_log not in stde.decode('utf-8'):\n                    print(f\"{expect_fail_log} not found in log.\")\n                    print(stde.decode('utf-8'))\n                    raise ValueError()\n        except subprocess.TimeoutExpired:\n            _, errs = terminate_broker(broker)\n            print(f\"Broker did not fail to start:\\n{errs.decode('utf-8')}\")\n            raise\n        return broker\n\n    if check_port == False:\n        return broker\n\n    assert port != 0\n\n    for i in range(0, 20):\n        time.sleep(timeout)\n        c = None\n        try:\n            c = socket.create_connection((checkhost, port))\n        except socket.error as err:\n            if err.errno != errno.ECONNREFUSED:\n                raise\n\n        if c is not None:\n            c.close()\n            return broker\n\n    if expect_fail == False:\n        outs, errs = broker.communicate(timeout=timeout)\n        print(\"FAIL: unable to start broker: %s\" % errs)\n        raise IOError\n    else:\n        return broker\n\ndef start_client(filename, cmd, env=None):\n    if cmd is None:\n        raise ValueError\n    env = env_add_ld_library_path(env)\n    if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n        cmd = ['valgrind', '-q', '--log-file='+filename+'.vglog'] + cmd\n\n    return subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\n\ndef wait_for_subprocess(client,timeout=10,terminate_timeout=2):\n    rc=0\n    try:\n        client.wait(timeout)\n    except subprocess.TimeoutExpired:\n        rc=1\n        client.terminate()\n        try:\n            client.wait(terminate_timeout)\n        except subprocess.TimeoutExpired:\n            rc=2\n            client.kill()\n            try:\n                client.wait(terminate_timeout)\n            except subprocess.TimeoutExpired:\n                rc=3\n                pass\n    return rc\n\n\ndef terminate_broker(broker):\n    broker.terminate()\n    (_, stde) = broker.communicate()\n    if wait_for_subprocess(broker):\n        print(\"broker not terminated\")\n        return (1, stde)\n    else:\n        return (0, stde)\n\n\ndef pub_helper(port, proto_ver=4):\n    connect_packet = gen_connect(\"pub-helper\", proto_ver=proto_ver)\n    connack_packet = gen_connack(rc=0, proto_ver=proto_ver)\n\n    sock = do_client_connect(connect_packet, connack_packet, port=port, connack_error=\"pub helper connack\")\n    return sock\n\n\ndef sub_helper(port, topic='#', qos=0, proto_ver=4):\n    connect_packet = gen_connect(\"sub-helper\", proto_ver=proto_ver)\n    connack_packet = gen_connack(rc=0, proto_ver=proto_ver)\n\n    mid = 1\n    subscribe_packet = gen_subscribe(mid=mid, topic=topic, qos=qos, proto_ver=proto_ver)\n    suback_packet = gen_suback(mid=mid, qos=qos, proto_ver=proto_ver)\n    sock = do_client_connect(connect_packet, connack_packet, port=port)\n    do_send_receive(sock, subscribe_packet, suback_packet, \"sub helper suback\")\n    return sock\n\n\ndef expect_packet(sock, name, expected):\n    if len(expected) > 0:\n        rlen = len(expected)\n    else:\n        rlen = 1\n\n    packet_recvd = b\"\"\n    try:\n        while len(packet_recvd) < rlen:\n            data = sock.recv(rlen-len(packet_recvd))\n            if len(data) == 0:\n                try:\n                    s = f\"when reading {name} from {sock.getpeername()}\"\n                except OSError:\n                    s = f\"when reading {name} from {sock}\"\n                raise BrokenPipeError(s)\n            packet_recvd += data\n    except socket.timeout:\n        pass\n\n    if packet_matches(name, packet_recvd, expected):\n        return True\n    else:\n        raise TestError\n\n\ndef packet_matches(name, recvd, expected):\n    if recvd != expected:\n        print(\"FAIL: Received incorrect \"+name+\".\")\n        try:\n            print(\"Received: \"+to_string(recvd))\n        except struct.error:\n            print(\"Received (not decoded, len=%d): %s\" % (len(recvd), recvd))\n        try:\n            print(\"Expected: \"+to_string(expected))\n        except struct.error:\n            print(\"Expected (not decoded, len=%d): %s\" % (len(expected), expected))\n        traceback.print_stack(file=sys.stdout)\n\n        return False\n    else:\n        return True\n\n\ndef receive_unordered(sock, recv1_packet, recv2_packet, error_string):\n    expected1 = recv1_packet + recv2_packet\n    expected2 = recv2_packet + recv1_packet\n    recvd = b''\n    while len(recvd) < len(expected1):\n        r = sock.recv(1)\n        if len(r) == 0:\n            raise ValueError(error_string)\n        recvd += r\n\n    if recvd == expected1 or recvd == expected2:\n        return\n    else:\n        packet_matches(error_string, recvd, expected2)\n        raise ValueError(error_string)\n\n\ndef do_send(sock, send_packet):\n    size = len(send_packet)\n    total_sent = 0\n    while total_sent < size:\n        sent = sock.send(send_packet[total_sent:])\n        if sent == 0:\n            raise RuntimeError(\"socket connection broken\")\n        total_sent += sent\n\ndef do_send_receive(sock, send_packet, receive_packet, error_string=\"send receive error\"):\n    do_send(sock, send_packet)\n\n    if expect_packet(sock, error_string, receive_packet):\n        return sock\n    else:\n        sock.close()\n        raise ValueError\n\n\n# Useful for mocking a client receiving (with ack) a qos1 publish\ndef do_receive_send(sock, receive_packet, send_packet, error_string=\"receive send error\"):\n    if expect_packet(sock, error_string, receive_packet):\n        do_send(sock, send_packet)\n        return sock\n    else:\n        sock.close()\n        raise ValueError\n\n\ndef client_connect_only(hostname=\"localhost\", port=1888, timeout=10, protocol=\"mqtt\"):\n    if protocol == \"websockets\":\n        addr = (hostname, port)\n        sock = socket.create_connection(addr, timeout=timeout)\n        sock.settimeout(timeout)\n        sock = WebsocketWrapper(sock, hostname, port, False, \"/mqtt\", None)\n        #sock.setblocking(0)\n    else:\n        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        sock.settimeout(timeout)\n        sock.connect((hostname, port))\n    return sock\n\ndef client_connect_only_unix(path, timeout=10):\n    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n    sock.settimeout(timeout)\n    sock.connect(path)\n    return sock\n\ndef do_client_connect(connect_packet, connack_packet, hostname=\"localhost\", port=1888, timeout=10, connack_error=\"connack\", protocol=\"mqtt\"):\n    sock = client_connect_only(hostname, port, timeout, protocol)\n\n    return do_send_receive(sock, connect_packet, connack_packet, connack_error)\n\n\ndef do_client_connect_unix(connect_packet, connack_packet, path, timeout=10, connack_error=\"connack\"):\n    sock = client_connect_only_unix(path, timeout)\n\n    return do_send_receive(sock, connect_packet, connack_packet, connack_error)\n\n\ndef remaining_length(packet):\n    l = min(5, len(packet))\n    all_bytes = struct.unpack(\"!\"+\"B\"*l, packet[:l])\n    mult = 1\n    rl = 0\n    for i in range(1,l-1):\n        byte = all_bytes[i]\n\n        rl += (byte & 127) * mult\n        mult *= 128\n        if byte & 128 == 0:\n            packet = packet[i+1:]\n            break\n\n    return (packet, rl)\n\n\ndef to_hex_string(packet):\n    if len(packet) == 0:\n        return \"\"\n\n    s = \"\"\n    while len(packet) > 0:\n        packet0 = struct.unpack(\"!B\", packet[0])\n        s = s+hex(packet0[0]) + \" \"\n        packet = packet[1:]\n\n    return s\n\n\ndef to_string(packet):\n    if len(packet) == 0:\n        return \"\"\n\n    packet0 = struct.unpack(\"!B%ds\" % (len(packet)-1), bytes(packet))\n    packet0 = packet0[0]\n    cmd = packet0 & 0xF0\n    if cmd == 0x00:\n        # Reserved\n        return \"0x00\"\n    elif cmd == 0x10:\n        # CONNECT\n        (packet, rl) = remaining_length(packet)\n        pack_format = \"!H\" + str(len(packet)-2) + 's'\n        (slen, packet) = struct.unpack(pack_format, packet)\n        pack_format = \"!\" + str(slen)+'sBBH' + str(len(packet)-slen-4) + 's'\n        (protocol, proto_ver, flags, keepalive, packet) = struct.unpack(pack_format, packet)\n        s = \"CONNECT, proto=\"+str(protocol)+str(proto_ver)+\", keepalive=\"+str(keepalive)\n        if flags&2:\n            s = s+\", clean-session\"\n        else:\n            s = s+\", durable\"\n\n        pack_format = \"!H\" + str(len(packet)-2) + 's'\n        (slen, packet) = struct.unpack(pack_format, packet)\n        pack_format = \"!\" + str(slen)+'s' + str(len(packet)-slen) + 's'\n        (client_id, packet) = struct.unpack(pack_format, packet)\n        s = s+\", id=\"+str(client_id)\n\n        if flags&4:\n            pack_format = \"!H\" + str(len(packet)-2) + 's'\n            (slen, packet) = struct.unpack(pack_format, packet)\n            pack_format = \"!\" + str(slen)+'s' + str(len(packet)-slen) + 's'\n            (will_topic, packet) = struct.unpack(pack_format, packet)\n            s = s+\", will-topic=\"+str(will_topic)\n\n            pack_format = \"!H\" + str(len(packet)-2) + 's'\n            (slen, packet) = struct.unpack(pack_format, packet)\n            pack_format = \"!\" + str(slen)+'s' + str(len(packet)-slen) + 's'\n            (will_message, packet) = struct.unpack(pack_format, packet)\n            s = s+\", will-message=\"+will_message\n\n            s = s+\", will-qos=\"+str((flags&24)>>3)\n            s = s+\", will-retain=\"+str((flags&32)>>5)\n\n        if flags&128:\n            pack_format = \"!H\" + str(len(packet)-2) + 's'\n            (slen, packet) = struct.unpack(pack_format, packet)\n            pack_format = \"!\" + str(slen)+'s' + str(len(packet)-slen) + 's'\n            (username, packet) = struct.unpack(pack_format, packet)\n            s = s+\", username=\"+str(username)\n\n        if flags&64:\n            pack_format = \"!H\" + str(len(packet)-2) + 's'\n            (slen, packet) = struct.unpack(pack_format, packet)\n            pack_format = \"!\" + str(slen)+'s' + str(len(packet)-slen) + 's'\n            (password, packet) = struct.unpack(pack_format, packet)\n            s = s+\", password=\"+str(password)\n\n        if flags&1:\n            s = s+\", reserved=1\"\n\n        return s\n    elif cmd == 0x20:\n        # CONNACK\n        if len(packet) >= 4:\n            (cmd, rl, flags, reason_code) = struct.unpack('!BBBB', packet[0:4])\n            s=f\"CONNACK, rl={rl}, res/flags={flags}, rc={reason_code}\"\n            if len(packet) > 4:\n                s = s+ f\", properties={mqtt5_props.print_properties(packet[4:])}\"\n            return s\n        else:\n            return \"CONNACK, (not decoded)\"\n\n    elif cmd == 0x30:\n        # PUBLISH\n        dup = (packet0 & 0x08)>>3\n        qos = (packet0 & 0x06)>>1\n        retain = (packet0 & 0x01)\n        (packet, rl) = remaining_length(packet)\n        pack_format = \"!H\" + str(len(packet)-2) + 's'\n        (tlen, packet) = struct.unpack(pack_format, packet)\n        pack_format = \"!\" + str(tlen)+'s' + str(len(packet)-tlen) + 's'\n        (topic, packet) = struct.unpack(pack_format, packet)\n        s = \"PUBLISH, rl=\"+str(rl)+\", topic=\"+str(topic)+\", qos=\"+str(qos)+\", retain=\"+str(retain)+\", dup=\"+str(dup)\n        if qos > 0:\n            pack_format = \"!H\" + str(len(packet)-2) + 's'\n            (mid, packet) = struct.unpack(pack_format, packet)\n            s = s + \", mid=\"+str(mid)\n\n        s = s + \", payload=\"+str(packet)\n        return s\n    elif cmd == 0x40:\n        # PUBACK\n        if len(packet) == 5:\n            (cmd, rl, mid, reason_code) = struct.unpack('!BBHB', packet)\n            return \"PUBACK, rl=\"+str(rl)+\", mid=\"+str(mid)+\", reason_code=\"+str(reason_code)\n        else:\n            (cmd, rl, mid) = struct.unpack('!BBH', packet)\n            return \"PUBACK, rl=\"+str(rl)+\", mid=\"+str(mid)\n    elif cmd == 0x50:\n        # PUBREC\n        if len(packet) == 5:\n            (cmd, rl, mid, reason_code) = struct.unpack('!BBHB', packet)\n            return \"PUBREC, rl=\"+str(rl)+\", mid=\"+str(mid)+\", reason_code=\"+str(reason_code)\n        else:\n            (cmd, rl, mid) = struct.unpack('!BBH', packet)\n            return \"PUBREC, rl=\"+str(rl)+\", mid=\"+str(mid)\n    elif cmd == 0x60:\n        # PUBREL\n        dup = (packet0 & 0x08)>>3\n        (cmd, rl, mid) = struct.unpack('!BBH', packet)\n        return \"PUBREL, rl=\"+str(rl)+\", mid=\"+str(mid)+\", dup=\"+str(dup)\n    elif cmd == 0x70:\n        # PUBCOMP\n        (cmd, rl, mid) = struct.unpack('!BBH', packet)\n        return \"PUBCOMP, rl=\"+str(rl)+\", mid=\"+str(mid)\n    elif cmd == 0x80:\n        # SUBSCRIBE\n        (packet, rl) = remaining_length(packet)\n        pack_format = \"!H\" + str(len(packet)-2) + 's'\n        (mid, packet) = struct.unpack(pack_format, packet)\n        s = \"SUBSCRIBE, rl=\"+str(rl)+\", mid=\"+str(mid)\n        topic_index = 0\n        while len(packet) > 0:\n            pack_format = \"!H\" + str(len(packet)-2) + 's'\n            (tlen, packet) = struct.unpack(pack_format, packet)\n            pack_format = \"!\" + str(tlen)+'sB' + str(len(packet)-tlen-1) + 's'\n            (topic, qos, packet) = struct.unpack(pack_format, packet)\n            s = s + \", topic\"+str(topic_index)+\"=\"+str(topic)+\",\"+str(qos)\n        return s\n    elif cmd == 0x90:\n        # SUBACK\n        (packet, rl) = remaining_length(packet)\n        pack_format = \"!H\" + str(len(packet)-2) + 's'\n        (mid, packet) = struct.unpack(pack_format, packet)\n        pack_format = \"!\" + \"B\"*len(packet)\n        granted_qos = struct.unpack(pack_format, packet)\n\n        s = \"SUBACK, rl=\"+str(rl)+\", mid=\"+str(mid)+\", granted_qos=\"+str(granted_qos[0])\n        for i in range(1, len(granted_qos)-1):\n            s = s+\", \"+str(granted_qos[i])\n        return s\n    elif cmd == 0xA0:\n        # UNSUBSCRIBE\n        (packet, rl) = remaining_length(packet)\n        pack_format = \"!H\" + str(len(packet)-2) + 's'\n        (mid, packet) = struct.unpack(pack_format, packet)\n        s = \"UNSUBSCRIBE, rl=\"+str(rl)+\", mid=\"+str(mid)\n        topic_index = 0\n        while len(packet) > 0:\n            pack_format = \"!H\" + str(len(packet)-2) + 's'\n            (tlen, packet) = struct.unpack(pack_format, packet)\n            pack_format = \"!\" + str(tlen)+'s' + str(len(packet)-tlen) + 's'\n            (topic, packet) = struct.unpack(pack_format, packet)\n            s = s + \", topic\"+str(topic_index)+\"=\"+str(topic)\n        return s\n    elif cmd == 0xB0:\n        # UNSUBACK\n        (cmd, rl, mid) = struct.unpack('!BBH', packet)\n        return \"UNSUBACK, rl=\"+str(rl)+\", mid=\"+str(mid)\n    elif cmd == 0xC0:\n        # PINGREQ\n        (cmd, rl) = struct.unpack('!BB', packet)\n        return \"PINGREQ, rl=\"+str(rl)\n    elif cmd == 0xD0:\n        # PINGRESP\n        (cmd, rl) = struct.unpack('!BB', packet)\n        return \"PINGRESP, rl=\"+str(rl)\n    elif cmd == 0xE0:\n        # DISCONNECT\n        if len(packet) == 3:\n            (cmd, rl, reason_code) = struct.unpack('!BBB', packet)\n            return \"DISCONNECT, rl=\"+str(rl)+\", reason_code=\"+str(reason_code)\n        else:\n            (cmd, rl) = struct.unpack('!BB', packet)\n            return \"DISCONNECT, rl=\"+str(rl)\n    elif cmd == 0xF0:\n        # AUTH\n        (cmd, rl) = struct.unpack('!BB', packet)\n        return \"AUTH, rl=\"+str(rl)\n\n\ndef read_varint(sock, rl):\n    varint = 0\n    multiplier = 1\n    while True:\n        byte = sock.recv(1)\n        byte, = struct.unpack(\"!B\", byte)\n        varint += (byte & 127)*multiplier\n        multiplier *= 128\n        rl -= 1\n        if byte & 128 == 0x00:\n            return (varint, rl)\n\n\ndef mqtt_read_string(sock, rl):\n    slen = sock.recv(2)\n    slen, = struct.unpack(\"!H\", slen)\n    payload = sock.recv(slen)\n    payload, = struct.unpack(\"!%ds\" % (slen), payload)\n    rl -= (2 + slen)\n    return (payload, rl)\n\n\ndef read_publish(sock, proto_ver=4):\n    cmd, = struct.unpack(\"!B\", sock.recv(1))\n    if cmd & 0xF0 != 0x30:\n        raise ValueError\n\n    qos = (cmd & 0x06) >> 1\n    rl, t = read_varint(sock, 0)\n    topic, rl = mqtt_read_string(sock, rl)\n\n    if qos > 0:\n        sock.recv(2)\n        rl -= 1\n\n    if proto_ver == 5:\n        proplen, rl = read_varint(sock, rl)\n        sock.recv(proplen)\n        rl -= proplen\n\n    payload = sock.recv(rl).decode('utf-8')\n    return payload\n\n\ndef gen_fixed_hdr(command, remaining_length):\n    return struct.pack(\"B\", command) + pack_remaining_length(remaining_length)\n\ndef gen_variable_hdr(mid=None):\n    if mid is not None:\n        return struct.pack(\"!H\", mid)\n    else:\n        return b\"\"\n\n\ndef gen_connect(client_id, clean_session=True, keepalive=60, username=None, password=None, will_topic=None, will_qos=0, will_retain=False, will_payload=b\"\", proto_ver=4, connect_reserved=False, properties=b\"\", will_properties=b\"\", session_expiry=-1):\n    if (proto_ver&0x7F) == 3 or proto_ver == 0:\n        remaining_length = 12\n    elif (proto_ver&0x7F) == 4 or proto_ver == 5:\n        remaining_length = 10\n    else:\n        raise ValueError\n\n    if client_id != None:\n        client_id = client_id.encode(\"utf-8\")\n        remaining_length = remaining_length + 2+len(client_id)\n    else:\n        remaining_length = remaining_length + 2\n\n    connect_flags = 0\n\n    if connect_reserved:\n        connect_flags = connect_flags | 0x01\n\n    if clean_session:\n        connect_flags = connect_flags | 0x02\n\n    if proto_ver == 5:\n        if properties == b\"\":\n            properties += mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n\n        if session_expiry != -1:\n            properties += mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, session_expiry)\n\n        properties = mqtt5_props.prop_finalise(properties)\n        remaining_length += len(properties)\n\n    if will_topic != None:\n        will_topic = will_topic.encode(\"utf-8\")\n        remaining_length = remaining_length + 2+len(will_topic) + 2+len(will_payload)\n        connect_flags = connect_flags | 0x04 | ((will_qos&0x03) << 3)\n        if will_retain:\n            connect_flags = connect_flags | 32\n        if proto_ver == 5:\n            will_properties = mqtt5_props.prop_finalise(will_properties)\n            remaining_length += len(will_properties)\n\n    if username != None:\n        username = username.encode(\"utf-8\")\n        remaining_length = remaining_length + 2+len(username)\n        connect_flags = connect_flags | 0x80\n        if password != None:\n            password = password.encode(\"utf-8\")\n            connect_flags = connect_flags | 0x40\n            remaining_length = remaining_length + 2+len(password)\n\n    rl = pack_remaining_length(remaining_length)\n    packet = struct.pack(\"!B\"+str(len(rl))+\"s\", 0x10, rl)\n    if (proto_ver&0x7F) == 3 or proto_ver == 0:\n        packet = packet + struct.pack(\"!H6sBBH\", len(b\"MQIsdp\"), b\"MQIsdp\", proto_ver, connect_flags, keepalive)\n    elif (proto_ver&0x7F) == 4 or proto_ver == 5:\n        packet = packet + struct.pack(\"!H4sBBH\", len(b\"MQTT\"), b\"MQTT\", proto_ver, connect_flags, keepalive)\n\n    if proto_ver == 5:\n        packet += properties\n\n    if client_id != None:\n        packet = packet + struct.pack(\"!H\"+str(len(client_id))+\"s\", len(client_id), bytes(client_id))\n    else:\n        packet = packet + struct.pack(\"!H\", 0)\n\n    if will_topic != None:\n        packet += will_properties\n        packet = packet + struct.pack(\"!H\"+str(len(will_topic))+\"s\", len(will_topic), will_topic)\n        if len(will_payload) > 0:\n            packet = packet + struct.pack(\"!H\"+str(len(will_payload))+\"s\", len(will_payload), will_payload)\n        else:\n            packet = packet + struct.pack(\"!H\", 0)\n\n    if username != None:\n        packet = packet + struct.pack(\"!H\"+str(len(username))+\"s\", len(username), username)\n        if password != None:\n            packet = packet + struct.pack(\"!H\"+str(len(password))+\"s\", len(password), password)\n    return packet\n\ndef gen_connack(flags=0, rc=0, proto_ver=4, properties=b\"\", property_helper=True):\n    if proto_ver == 5:\n        if property_helper == True:\n            if properties is not None:\n                properties = mqtt5_props.gen_uint16_prop(mqtt5_props.TOPIC_ALIAS_MAXIMUM, 10) \\\n                    + properties \\\n                    + mqtt5_props.gen_uint32_prop(mqtt5_props.MAXIMUM_PACKET_SIZE, 2000000) \\\n                    + mqtt5_props.gen_uint16_prop(mqtt5_props.RECEIVE_MAXIMUM, 20)\n            else:\n                properties = b\"\"\n        properties = mqtt5_props.prop_finalise(properties)\n\n        packet = struct.pack('!BBBB', 32, 2+len(properties), flags, rc) + properties\n    else:\n        packet = struct.pack('!BBBB', 32, 2, flags, rc);\n\n    return packet\n\ndef gen_publish(topic, qos, payload=None, retain=False, dup=False, mid=0, proto_ver=4, properties=b\"\"):\n    topic = topic.encode(\"utf-8\")\n    rl = 2+len(topic)\n    pack_format = \"H\"+str(len(topic))+\"s\"\n    if qos > 0:\n        rl = rl + 2\n        pack_format = pack_format + \"H\"\n\n    if proto_ver == 5:\n        properties = mqtt5_props.prop_finalise(properties)\n        rl += len(properties)\n        # This will break if len(properties) > 127\n        pack_format = pack_format + \"%ds\"%(len(properties))\n\n    if payload != None:\n        if isinstance(payload, bytes) == False:\n            payload = payload.encode(\"utf-8\")\n        rl = rl + len(payload)\n        pack_format = pack_format + str(len(payload))+\"s\"\n    else:\n        payload = b\"\"\n        pack_format = pack_format + \"0s\"\n\n    rlpacked = pack_remaining_length(rl)\n    cmd = 48 | (qos<<1)\n    if retain:\n        cmd = cmd + 1\n    if dup:\n        cmd = cmd + 8\n\n    if proto_ver == 5:\n        if qos > 0:\n            return struct.pack(\"!B\" + str(len(rlpacked))+\"s\" + pack_format, cmd, rlpacked, len(topic), topic, mid, properties, payload)\n        else:\n            return struct.pack(\"!B\" + str(len(rlpacked))+\"s\" + pack_format, cmd, rlpacked, len(topic), topic, properties, payload)\n    else:\n        if qos > 0:\n            return struct.pack(\"!B\" + str(len(rlpacked))+\"s\" + pack_format, cmd, rlpacked, len(topic), topic, mid, payload)\n        else:\n            return struct.pack(\"!B\" + str(len(rlpacked))+\"s\" + pack_format, cmd, rlpacked, len(topic), topic, payload)\n\ndef _gen_command_with_mid(cmd, mid, proto_ver=4, reason_code=-1, properties=None):\n    if proto_ver == 5 and (reason_code != -1 or properties is not None):\n        if reason_code == -1:\n            reason_code = 0\n\n        if properties is None:\n            return struct.pack('!BBHB', cmd, 3, mid, reason_code)\n        elif properties == \"\":\n            return struct.pack('!BBHBB', cmd, 4, mid, reason_code, 0)\n        else:\n            properties = mqtt5_props.prop_finalise(properties)\n            pack_format = \"!BBHB\"+str(len(properties))+\"s\"\n            return struct.pack(pack_format, cmd, 2+1+len(properties), mid, reason_code, properties)\n    else:\n        return struct.pack('!BBH', cmd, 2, mid)\n\ndef gen_puback(mid, proto_ver=4, reason_code=-1, properties=None):\n    return _gen_command_with_mid(64, mid, proto_ver, reason_code, properties)\n\ndef gen_pubrec(mid, proto_ver=4, reason_code=-1, properties=None):\n    return _gen_command_with_mid(80, mid, proto_ver, reason_code, properties)\n\ndef gen_pubrel(mid, dup=False, proto_ver=4, reason_code=-1, properties=None):\n    if dup:\n        cmd = 96+8+2\n    else:\n        cmd = 96+2\n    return _gen_command_with_mid(cmd, mid, proto_ver, reason_code, properties)\n\ndef gen_pubcomp(mid, proto_ver=4, reason_code=-1, properties=None):\n    return _gen_command_with_mid(112, mid, proto_ver, reason_code, properties)\n\n\ndef gen_subscribe(mid, topic, qos, cmd=130, proto_ver=4, properties=b\"\"):\n    topic = topic.encode(\"utf-8\")\n    packet = struct.pack(\"!B\", cmd)\n    if proto_ver == 5:\n        if properties == b\"\":\n            packet += pack_remaining_length(2+1+2+len(topic)+1)\n            pack_format = \"!HBH\"+str(len(topic))+\"sB\"\n            return packet + struct.pack(pack_format, mid, 0, len(topic), topic, qos)\n        else:\n            properties = mqtt5_props.prop_finalise(properties)\n            packet += pack_remaining_length(2+1+2+len(topic)+len(properties))\n            pack_format = \"!H\"+str(len(properties))+\"s\"+\"H\"+str(len(topic))+\"sB\"\n            return packet + struct.pack(pack_format, mid, properties, len(topic), topic, qos)\n    else:\n        packet += pack_remaining_length(2+2+len(topic)+1)\n        pack_format = \"!HH\"+str(len(topic))+\"sB\"\n        return packet + struct.pack(pack_format, mid, len(topic), topic, qos)\n\n\ndef gen_suback(mid, qos, proto_ver=4):\n    if proto_ver == 5:\n        return struct.pack('!BBHBB', 144, 2+1+1, mid, 0, qos)\n    else:\n        return struct.pack('!BBHB', 144, 2+1, mid, qos)\n\ndef gen_unsubscribe(mid, topic, cmd=162, proto_ver=4, properties=b\"\"):\n    topic = topic.encode(\"utf-8\")\n    if proto_ver == 5:\n        if properties == b\"\":\n            pack_format = \"!BBHBH\"+str(len(topic))+\"s\"\n            return struct.pack(pack_format, cmd, 2+2+len(topic)+1, mid, 0, len(topic), topic)\n        else:\n            properties = mqtt5_props.prop_finalise(properties)\n            packet = struct.pack(\"!B\", cmd)\n            l = 2+2+len(topic)+len(properties)\n            packet += pack_remaining_length(l)\n            pack_format = \"!H\"+str(len(properties))+\"sH\"+str(len(topic))+\"s\"\n            packet += struct.pack(pack_format, mid, properties, len(topic), topic)\n            return packet\n    else:\n        pack_format = \"!BBHH\"+str(len(topic))+\"s\"\n        return struct.pack(pack_format, cmd, 2+2+len(topic), mid, len(topic), topic)\n\ndef gen_unsubscribe_multiple(mid, topics, proto_ver=4):\n    packet = b\"\"\n    remaining_length = 0\n    for t in topics:\n        t = t.encode(\"utf-8\")\n        remaining_length += 2+len(t)\n        packet += struct.pack(\"!H\"+str(len(t))+\"s\", len(t), t)\n\n    if proto_ver == 5:\n        remaining_length += 2+1\n\n        return struct.pack(\"!BBHB\", 162, remaining_length, mid, 0) + packet\n    else:\n        remaining_length += 2\n\n        return struct.pack(\"!BBH\", 162, remaining_length, mid) + packet\n\ndef gen_unsuback(mid, reason_code=0, proto_ver=4):\n    if proto_ver == 5:\n        if isinstance(reason_code, list):\n            reason_code_count = len(reason_code)\n            p = struct.pack('!BBHB', 176, 3+reason_code_count, mid, 0)\n            for r in reason_code:\n                p += struct.pack('B', r)\n            return p\n        else:\n            return struct.pack('!BBHBB', 176, 4, mid, 0, reason_code)\n    else:\n        return struct.pack('!BBH', 176, 2, mid)\n\ndef gen_pingreq():\n    return struct.pack('!BB', 192, 0)\n\ndef gen_pingresp():\n    return struct.pack('!BB', 208, 0)\n\n\ndef _gen_short(cmd, reason_code=-1, proto_ver=5, properties=None):\n    if proto_ver == 5 and (reason_code != -1 or properties is not None):\n        if reason_code == -1:\n             reason_code = 0\n\n        if properties is None:\n            return struct.pack('!BBB', cmd, 1, reason_code)\n        elif properties == \"\":\n            return struct.pack('!BBBB', cmd, 2, reason_code, 0)\n        else:\n            properties = mqtt5_props.prop_finalise(properties)\n            return struct.pack(\"!BBB\", cmd, 1+len(properties), reason_code) + properties\n    else:\n        return struct.pack('!BB', cmd, 0)\n\ndef gen_disconnect(reason_code=-1, proto_ver=4, properties=None):\n    return _gen_short(0xE0, reason_code, proto_ver, properties)\n\ndef gen_auth(reason_code=-1, properties=None):\n    return _gen_short(0xF0, reason_code, 5, properties)\n\n\ndef pack_remaining_length(remaining_length):\n    s = b\"\"\n    while True:\n        byte = remaining_length % 128\n        remaining_length = remaining_length // 128\n        # If there are more digits to encode, set the top bit of this digit\n        if remaining_length > 0:\n            byte = byte | 0x80\n\n        s = s + struct.pack(\"!B\", byte)\n        if remaining_length == 0:\n            return s\n\n\ndef get_port(count=1):\n    if count == 1:\n        if len(sys.argv) == 2:\n            return int(sys.argv[1])\n        else:\n            return 1888\n    else:\n        if len(sys.argv) >= 1+count:\n            p = ()\n            for i in range(0, count):\n                p = p + (int(sys.argv[1+i]),)\n            return p\n        else:\n            return tuple(range(1888, 1888+count))\n\n\ndef do_ping(sock, error_string=\"pingresp\"):\n     do_send_receive(sock, gen_pingreq(), gen_pingresp(), error_string)\n\ndef client_test(client_cmd, client_args, callback, cb_data):\n    port = get_port()\n\n    rc = 1\n\n    sock = listen_sock(port)\n\n    args = [get_build_root() + \"/test/lib/\" + client_cmd, str(port)]\n    if client_args is not None:\n        args = args + client_args\n\n    client = start_client(filename=client_cmd.replace('/', '-'), cmd=args)\n\n    try:\n        (conn, address) = sock.accept()\n        conn.settimeout(10)\n\n        callback(conn, cb_data)\n        rc = 0\n\n        conn.close()\n    except TestError as err:\n        print(err)\n    except Exception as err:\n        print(err)\n        raise\n    finally:\n        client_rc = wait_for_subprocess(client)\n        if client_rc:\n            print(\"test client not finished\")\n            rc=1\n        sock.close()\n        if rc:\n            (o, e) = client.communicate()\n            print(o)\n            print(e)\n            print(f\"Fail: {client_cmd} rc={rc}, client_rc={client_rc}\")\n            exit(rc)\n\n\ndef get_non_loopback_ip():\n    # https://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib\n    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    s.settimeout(0)\n    try:\n        # doesn't even have to be reachable\n        s.connect(('10.254.254.254', 1))\n        IP = s.getsockname()[0]\n    except Exception:\n        # Explicitly not 127.0.0.1 - we want something that doesn't match a\n        # certificate SAN\n        IP = '127.0.0.2'\n    finally:\n        s.close()\n    return IP\n\n\n# =============================================\n# Websockets wrapper\n# =============================================\nclass WebsocketConnectionError(ValueError):\n    pass\n\nclass WebsocketWrapper(object):\n    OPCODE_CONTINUATION = 0x0\n    OPCODE_TEXT = 0x1\n    OPCODE_BINARY = 0x2\n    OPCODE_CONNCLOSE = 0x8\n    OPCODE_PING = 0x9\n    OPCODE_PONG = 0xa\n\n    def __init__(self, socket, host, port, is_ssl, path, extra_headers):\n\n        self.connected = False\n\n        self._ssl = is_ssl\n        self._host = host\n        self._port = port\n        self._socket = socket\n        self._path = path\n\n        self._sendbuffer = bytearray()\n        self._readbuffer = bytearray()\n\n        self._requested_size = 0\n        self._payload_head = 0\n        self._readbuffer_head = 0\n\n        self._do_handshake(extra_headers)\n\n    def __del__(self):\n\n        self._sendbuffer = None\n        self._readbuffer = None\n\n    def _do_handshake(self, extra_headers):\n\n        sec_websocket_key = uuid.uuid4().bytes\n        sec_websocket_key = base64.b64encode(sec_websocket_key)\n\n        websocket_headers = {\n            \"Host\": \"{self._host:s}:{self._port:d}\".format(self=self),\n            \"Upgrade\": \"websocket\",\n            \"Connection\": \"Upgrade\",\n            \"Origin\": \"https://{self._host:s}:{self._port:d}\".format(self=self),\n            \"Sec-WebSocket-Key\": sec_websocket_key.decode(\"utf8\"),\n            \"Sec-Websocket-Version\": \"13\",\n            \"Sec-Websocket-Protocol\": \"mqtt\",\n        }\n\n        # This is checked in ws_set_options so it will either be None, a\n        # dictionary, or a callable\n        if isinstance(extra_headers, dict):\n            websocket_headers.update(extra_headers)\n        elif callable(extra_headers):\n            websocket_headers = extra_headers(websocket_headers)\n\n        header = \"\\r\\n\".join([\n            \"GET {self._path} HTTP/1.1\".format(self=self),\n            \"\\r\\n\".join(\"{}: {}\".format(i, j)\n                        for i, j in websocket_headers.items()),\n            \"\\r\\n\",\n        ]).encode(\"utf8\")\n\n        self._socket.send(header)\n\n        has_secret = False\n        has_upgrade = False\n\n        while True:\n            # read HTTP response header as lines\n            byte = self._socket.recv(1)\n\n            self._readbuffer.extend(byte)\n\n            # line end\n            if byte == b\"\\n\":\n                if len(self._readbuffer) > 2:\n                    # check upgrade\n                    if b\"connection\" in str(self._readbuffer).lower().encode('utf-8'):\n                        if b\"upgrade\" not in str(self._readbuffer).lower().encode('utf-8'):\n                            raise WebsocketConnectionError(\n                                \"WebSocket handshake error, connection not upgraded\")\n                        else:\n                            has_upgrade = True\n\n                    # check key hash\n                    if b\"sec-websocket-accept\" in str(self._readbuffer).lower().encode('utf-8'):\n                        GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n\n                        server_hash = self._readbuffer.decode(\n                            'utf-8').split(\": \", 1)[1]\n                        server_hash = server_hash.strip().encode('utf-8')\n\n                        client_hash = sec_websocket_key.decode('utf-8') + GUID\n                        client_hash = hashlib.sha1(client_hash.encode('utf-8'))\n                        client_hash = base64.b64encode(client_hash.digest())\n\n                        if server_hash != client_hash:\n                            raise WebsocketConnectionError(\n                                \"WebSocket handshake error, invalid secret key\")\n                        else:\n                            has_secret = True\n                else:\n                    # ending linebreak\n                    break\n\n                # reset linebuffer\n                self._readbuffer = bytearray()\n\n            # connection reset\n            elif not byte:\n                raise WebsocketConnectionError(\"WebSocket handshake error\")\n\n        if not has_upgrade or not has_secret:\n            raise WebsocketConnectionError(\"WebSocket handshake error\")\n\n        self._readbuffer = bytearray()\n        self.connected = True\n\n    def _create_frame(self, opcode, data, do_masking=1):\n\n        header = bytearray()\n        length = len(data)\n\n        mask_key = bytearray(os.urandom(4))\n        mask_flag = do_masking\n\n        # 1 << 7 is the final flag, we don't send continuated data\n        header.append(1 << 7 | opcode)\n\n        if length < 126:\n            header.append(mask_flag << 7 | length)\n\n        elif length < 65536:\n            header.append(mask_flag << 7 | 126)\n            header += struct.pack(\"!H\", length)\n\n        elif length < 0x8000000000000001:\n            header.append(mask_flag << 7 | 127)\n            header += struct.pack(\"!Q\", length)\n\n        else:\n            raise ValueError(\"Maximum payload size is 2^63\")\n\n        if mask_flag == 1:\n            for index in range(length):\n                data[index] ^= mask_key[index % 4]\n            data = mask_key + data\n\n        return header + data\n\n    def _buffered_read(self, length):\n\n        # try to recv and store needed bytes\n        wanted_bytes = length - (len(self._readbuffer) - self._readbuffer_head)\n        if wanted_bytes > 0:\n\n            data = self._socket.recv(wanted_bytes)\n\n            if not data:\n                raise ConnectionAbortedError\n            else:\n                self._readbuffer.extend(data)\n\n            if len(data) < wanted_bytes:\n                print(f\"{len(data)} {wanted_bytes}\")\n                raise BlockingIOError\n\n        self._readbuffer_head += length\n        return self._readbuffer[self._readbuffer_head - length:self._readbuffer_head]\n\n    def _recv_impl(self, length):\n\n        # try to decode websocket payload part from data\n        try:\n\n            self._readbuffer_head = 0\n\n            result = None\n\n            chunk_startindex = self._payload_head\n            chunk_endindex = self._payload_head + length\n\n            header1 = self._buffered_read(1)\n            header2 = self._buffered_read(1)\n\n            opcode = (header1[0] & 0x0f)\n            maskbit = (header2[0] & 0x80) == 0x80\n            lengthbits = (header2[0] & 0x7f)\n            payload_length = lengthbits\n            mask_key = None\n\n            # read length\n            if lengthbits == 0x7e:\n\n                value = self._buffered_read(2)\n                payload_length, = struct.unpack(\"!H\", value)\n\n            elif lengthbits == 0x7f:\n\n                value = self._buffered_read(8)\n                payload_length, = struct.unpack(\"!Q\", value)\n\n            # read mask\n            if maskbit:\n                mask_key = self._buffered_read(4)\n\n            # if frame payload is shorter than the requested data, read only the possible part\n            readindex = chunk_endindex\n            if payload_length < readindex:\n                readindex = payload_length\n\n            if readindex > 0:\n                # get payload chunk\n                payload = self._buffered_read(readindex)\n\n                # unmask only the needed part\n                if maskbit:\n                    for index in range(chunk_startindex, readindex):\n                        payload[index] ^= mask_key[index % 4]\n\n                result = payload[chunk_startindex:readindex]\n                self._payload_head = readindex\n            else:\n                payload = bytearray()\n\n            # check if full frame arrived and reset readbuffer and payloadhead if needed\n            if readindex == payload_length:\n                self._readbuffer = bytearray()\n                self._payload_head = 0\n\n                # respond to non-binary opcodes, their arrival is not guaranteed beacause of non-blocking sockets\n                if opcode == WebsocketWrapper.OPCODE_CONNCLOSE:\n                    frame = self._create_frame(\n                        WebsocketWrapper.OPCODE_CONNCLOSE, payload, 0)\n                    self._socket.send(frame)\n\n                if opcode == WebsocketWrapper.OPCODE_PING:\n                    frame = self._create_frame(\n                        WebsocketWrapper.OPCODE_PONG, payload, 0)\n                    self._socket.send(frame)\n\n            # This isn't *proper* handling of continuation frames, but given\n            # that we only support binary frames, it is *probably* good enough.\n            if (opcode == WebsocketWrapper.OPCODE_BINARY or opcode == WebsocketWrapper.OPCODE_CONTINUATION) \\\n                    and payload_length > 0:\n                return result\n            else:\n                #raise BlockingIOError\n                return b\"\"\n\n        except ConnectionError:\n            self.connected = False\n            return b''\n\n    def _send_impl(self, data):\n\n        # if previous frame was sent successfully\n        if len(self._sendbuffer) == 0:\n            # create websocket frame\n            frame = self._create_frame(\n                WebsocketWrapper.OPCODE_BINARY, bytearray(data))\n            self._sendbuffer.extend(frame)\n            self._requested_size = len(data)\n\n        # try to write out as much as possible\n        length = self._socket.send(self._sendbuffer)\n\n        self._sendbuffer = self._sendbuffer[length:]\n\n        if len(self._sendbuffer) == 0:\n            # buffer sent out completely, return with payload's size\n            return self._requested_size\n        else:\n            # couldn't send whole data, request the same data again with 0 as sent length\n            return 0\n\n    def recv(self, length):\n        return self._recv_impl(length)\n\n    def read(self, length):\n        return self._recv_impl(length)\n\n    def send(self, data):\n        return self._send_impl(data)\n\n    def write(self, data):\n        return self._send_impl(data)\n\n    def close(self):\n        self._socket.close()\n\n    def fileno(self):\n        return self._socket.fileno()\n\n    def pending(self):\n        # Fix for bug #131: a SSL socket may still have data available\n        # for reading without select() being aware of it.\n        if self._ssl:\n            return self._socket.pending()\n        else:\n            # normal socket rely only on select()\n            return 0\n\n    def setblocking(self, flag):\n        self._socket.setblocking(flag)\n\n\n@atexit.register\ndef test_cleanup():\n    global vg_logfiles\n\n    if os.environ.get('MOSQ_USE_VALGRIND') is not None:\n        for f in vg_logfiles:\n            try:\n                if os.stat(f).st_size == 0:\n                    os.remove(f)\n            except OSError:\n                pass\n"
  },
  {
    "path": "test/mqtt5_opts.py",
    "content": "MQTT_SUB_OPT_NO_LOCAL = 0x04\nMQTT_SUB_OPT_RETAIN_AS_PUBLISHED = 0x08\nMQTT_SUB_OPT_SEND_RETAIN_ALWAYS = 0x00\nMQTT_SUB_OPT_SEND_RETAIN_NEW = 0x10\nMQTT_SUB_OPT_SEND_RETAIN_NEVER = 0x20\n"
  },
  {
    "path": "test/mqtt5_props.py",
    "content": "import struct\n\nPAYLOAD_FORMAT_INDICATOR = 1\nMESSAGE_EXPIRY_INTERVAL = 2\nCONTENT_TYPE = 3\nRESPONSE_TOPIC = 8\nCORRELATION_DATA = 9\nSUBSCRIPTION_IDENTIFIER = 11\nSESSION_EXPIRY_INTERVAL = 17\nASSIGNED_CLIENT_IDENTIFIER = 18\nSERVER_KEEP_ALIVE = 19\nAUTHENTICATION_METHOD = 21\nAUTHENTICATION_DATA = 22\nREQUEST_PROBLEM_INFO = 23\nWILL_DELAY_INTERVAL = 24\nREQUEST_RESPONSE_INFO = 25\nRESPONSE_INFO = 26\nSERVER_REFERENCE = 28\nREASON_STRING = 31\nRECEIVE_MAXIMUM = 33\nTOPIC_ALIAS_MAXIMUM = 34\nTOPIC_ALIAS = 35\nMAXIMUM_QOS = 36\nRETAIN_AVAILABLE = 37\nUSER_PROPERTY = 38\nMAXIMUM_PACKET_SIZE = 39\nWILDCARD_SUB_AVAILABLE = 40\nSUBSCRIPTION_ID_AVAILABLE = 41\nSHARED_SUB_AVAILABLE = 42\n\ndef gen_byte_prop(identifier, byte):\n    prop = struct.pack('BB', identifier, byte)\n    return prop\n\ndef gen_uint16_prop(identifier, word):\n    prop = struct.pack('!BH', identifier, word)\n    return prop\n\ndef gen_uint32_prop(identifier, word):\n    prop = struct.pack('!BI', identifier, word)\n    return prop\n\ndef gen_string_prop(identifier, s):\n    s = s.encode(\"utf-8\")\n    prop = struct.pack('!BH%ds'%(len(s)), identifier, len(s), s)\n    return prop\n\ndef gen_string_pair_prop(identifier, s1, s2):\n    s1 = s1.encode(\"utf-8\")\n    s2 = s2.encode(\"utf-8\")\n    prop = struct.pack('!BH%dsH%ds'%(len(s1), len(s2)), identifier, len(s1), s1, len(s2), s2)\n    return prop\n\ndef gen_varint_prop(identifier, val):\n    v = pack_varint(val)\n    return struct.pack(\"!B\"+str(len(v))+\"s\", identifier, v)\n\ndef pack_varint(varint):\n    s = b\"\"\n    while True:\n        byte = varint % 128\n        varint = varint // 128\n        # If there are more digits to encode, set the top bit of this digit\n        if varint > 0:\n            byte = byte | 0x80\n\n        s = s + struct.pack(\"!B\", byte)\n        if varint == 0:\n            return s\n\n\ndef prop_finalise(props):\n    return pack_varint(len(props)) + props\n\n\ndef gen_properties(properties_dict: dict) -> bytes:\n    props = b\"\"\n    if properties_dict is None:\n        return props\n    for prop in properties_dict:\n        id = prop.get(\"identifier\")\n        value = prop.get(\"value\")\n        if id in (\n            PAYLOAD_FORMAT_INDICATOR,\n            REQUEST_PROBLEM_INFO,\n            REQUEST_RESPONSE_INFO,\n            MAXIMUM_QOS,\n            RETAIN_AVAILABLE,\n            WILDCARD_SUB_AVAILABLE,\n            SUBSCRIPTION_ID_AVAILABLE,\n            SHARED_SUB_AVAILABLE,\n        ):\n            props += gen_byte_prop(id, value)\n        elif id in (\n            MESSAGE_EXPIRY_INTERVAL,\n            SESSION_EXPIRY_INTERVAL,\n            WILL_DELAY_INTERVAL,\n            MAXIMUM_PACKET_SIZE,\n        ):\n            props += gen_uint32_prop(id, value)\n        elif id in (\n            CONTENT_TYPE,\n            RESPONSE_TOPIC,\n            CORRELATION_DATA,\n            ASSIGNED_CLIENT_IDENTIFIER,\n            AUTHENTICATION_METHOD,\n            AUTHENTICATION_DATA,\n            RESPONSE_INFO,\n            SERVER_REFERENCE,\n            REASON_STRING,\n        ):\n            props += gen_string_prop(id, value)\n        elif id == SUBSCRIPTION_IDENTIFIER:\n            props += gen_varint_prop(id, value)\n        elif id in (\n            SERVER_KEEP_ALIVE,\n            RECEIVE_MAXIMUM,\n            TOPIC_ALIAS_MAXIMUM,\n            TOPIC_ALIAS,\n        ):\n            props += gen_uint16_prop(id, value)\n        elif id == USER_PROPERTY:\n            props += gen_string_pair_prop(id, prop[\"name\"], value)\n    return props\n\n\ndef unpack_varint(b: bytes):\n    def decode_len(b: bytes):\n        for i in range(len(b)):\n            if b[i] & 0x80 == 0:\n                return i + 1\n        return 0\n\n    var_len = decode_len(b)\n    variant = 0\n    for i in range(var_len - 1, -1, -1):\n        variant = 0x80 * variant + (struct.unpack(\"!B\", b[i : i + 1])[0] & 0x7F)\n    return variant, var_len\n\n\ndef unpack_string(b: bytes):\n    str_len = struct.unpack(\"!B\", b[0:1])[0]\n    str_value = struct.unpack(f\"!{str_len}s\", b[1 : str_len + 1])[0]\n    return str_value, str_len + 1\n\n\ndef unpack_property(b: bytes):\n    id = struct.unpack(\"!B\", b[0:1])[0]\n    if id == PAYLOAD_FORMAT_INDICATOR:\n        return \"PAYLOAD_FORMAT_INDICATOR\", struct.unpack(\"!B\", b[1:2])[0], 2\n    elif id == PAYLOAD_FORMAT_INDICATOR:\n        return \"PAYLOAD_FORMAT_INDICATOR\", struct.unpack(\"!B\", b[1:2])[0], 2\n    elif id == REQUEST_PROBLEM_INFO:\n        return \"REQUEST_PROBLEM_INFO\", struct.unpack(\"!B\", b[1:2])[0], 2\n    elif id == REQUEST_RESPONSE_INFO:\n        return \"REQUEST_RESPONSE_INFO\", struct.unpack(\"!B\", b[1:2])[0], 2\n    elif id == MAXIMUM_QOS:\n        return \"MAXIMUM_QOS\", struct.unpack(\"!B\", b[1:2])[0], 2\n    elif id == RETAIN_AVAILABLE:\n        return \"RETAIN_AVAILABLE\", struct.unpack(\"!B\", b[1:2])[0], 2\n    elif id == WILDCARD_SUB_AVAILABLE:\n        return \"WILDCARD_SUB_AVAILABLE\", struct.unpack(\"!B\", b[1:2])[0], 2\n    elif id == SUBSCRIPTION_ID_AVAILABLE:\n        return \"SUBSCRIPTION_ID_AVAILABLE\", struct.unpack(\"!B\", b[1:2])[0], 2\n    elif id == SHARED_SUB_AVAILABLE:\n        return \"SHARED_SUB_AVAILABLE\", struct.unpack(\"!B\", b[1:2])[0], 2\n    elif id == SERVER_KEEP_ALIVE:\n        return \"SERVER_KEEP_ALIVE\", struct.unpack(\"!H\", b[1:3])[0], 3\n    elif id == RECEIVE_MAXIMUM:\n        return \"RECEIVE_MAXIMUM\", struct.unpack(\"!H\", b[1:3])[0], 3\n    elif id == TOPIC_ALIAS_MAXIMUM:\n        return \"TOPIC_ALIAS_MAXIMUM\", struct.unpack(\"!H\", b[1:3])[0], 3\n    elif id == TOPIC_ALIAS:\n        return \"TOPIC_ALIAS\", struct.unpack(\"!H\", b[1:3])[0], 3\n    elif id == MESSAGE_EXPIRY_INTERVAL:\n        return \"MESSAGE_EXPIRY_INTERVAL\", struct.unpack(\"!B\", b[1:5])[0], 5\n    elif id == SESSION_EXPIRY_INTERVAL:\n        return \"SESSION_EXPIRY_INTERVAL\", struct.unpack(\"!B\", b[1:5])[0], 5\n    elif id == WILL_DELAY_INTERVAL:\n        return \"WILL_DELAY_INTERVAL\", struct.unpack(\"!B\", b[1:5])[0], 5\n    elif id == MAXIMUM_PACKET_SIZE:\n        return \"MAXIMUM_PACKET_SIZE\", struct.unpack(\"!B\", b[1:5])[0], 5\n    elif id == CONTENT_TYPE:\n        value, pack_len = unpack_string(b[1:])\n        return \"CONTENT_TYPE\", value, pack_len + 1\n    elif id == RESPONSE_TOPIC:\n        value, pack_len = unpack_string(b[1:])\n        return \"RESPONSE_TOPIC\", value, pack_len + 1\n    elif id == CORRELATION_DATA:\n        value, pack_len = unpack_string(b[1:])\n        return \"CORRELATION_DATA\", value, pack_len + 1\n    elif id == ASSIGNED_CLIENT_IDENTIFIER:\n        value, pack_len = unpack_string(b[1:])\n        return \"ASSIGNED_CLIENT_IDENTIFIER\", value, pack_len + 1\n    elif id == AUTHENTICATION_METHOD:\n        value, pack_len = unpack_string(b[1:])\n        return \"AUTHENTICATION_METHOD\", value, pack_len + 1\n    elif id == AUTHENTICATION_DATA:\n        value, pack_len = unpack_string(b[1:])\n        return \"AUTHENTICATION_DATA\", value, pack_len + 1\n    elif id == RESPONSE_INFO:\n        value, pack_len = unpack_string(b[1:])\n        return \"RESPONSE_INFO\", value, pack_len + 1\n    elif id == SERVER_REFERENCE:\n        value, pack_len = unpack_string(b[1:])\n        return \"SERVER_REFERENCE\", value, pack_len + 1\n    elif id == REASON_STRING:\n        value, pack_len = unpack_string(b[1:])\n        return \"REASON_STRING\", value, pack_len + 1\n    elif id == SUBSCRIPTION_IDENTIFIER:\n        value, pack_len = unpack_varint(b[1:])\n        return \"SUBSCRIPTION_IDENTIFIER\", value, pack_len + 1\n    elif id == USER_PROPERTY:\n        name, pack_name_len = unpack_string(b[1:])\n        value, pack_value_len = unpack_varint(b[1 + pack_name_len :])\n        return (\n            f\"USER_PROPERTY:{name}\",\n            value,\n            1 + pack_name_len + pack_value_len + pack_len,\n        )\n    else:\n        return f\"<Unknown property ID={id}>\", f\"not decoded (len<={len(b)-1})\", len(b)\n\n\ndef print_properties(b: bytes):\n    _, offset = unpack_varint(b)\n    props = []\n    while offset < len(b):\n        try:\n            key, value, prop_len = unpack_property(b[offset:])\n            offset += prop_len\n            props.append(f\"{key}:{value}\")\n        except struct.error:\n            props.append(f\"decode error at offset {offset}: {b}\")\n            break\n    return f\"[{','.join(props)}]\"\n"
  },
  {
    "path": "test/mqtt5_rc.py",
    "content": "SUCCESS = 0\nNORMAL_DISCONNECTION = 0\nGRANTED_QOS0 = 0\nGRANTED_QOS1 = 1\nGRANTED_QOS2 = 2\nDISCONNECT_WITH_WILL_MSG = 4\nNO_MATCHING_SUBSCRIBERS = 16\nNO_SUBSCRIPTION_EXISTED = 17\nCONTINUE_AUTHENTICATION = 24\nREAUTHENTICATE = 25\n\nUNSPECIFIED = 128\nMALFORMED_PACKET = 129\nPROTOCOL_ERROR = 130\nIMPLEMENTATION_SPECIFIC = 131\nUNSUPPORTED_PROTOCOL_VERSION = 132\nCLIENTID_NOT_VALID = 133\nBAD_USERNAME_OR_PASSWORD = 134\nNOT_AUTHORIZED = 135\nSERVER_UNAVAILABLE = 136\nSERVER_BUSY = 137\nBANNED = 138\nSERVER_SHUTTING_DOWN = 139\nBAD_AUTHENTICATION_METHOD = 140\nKEEP_ALIVE_TIMEOUT = 141\nSESSION_TAKEN_OVER = 142\nTOPIC_FILTER_INVALID = 143\nTOPIC_NAME_INVALID = 144\nPACKET_ID_IN_USE = 145\nPACKET_ID_NOT_FOUND = 146\nRECEIVE_MAXIMUM_EXCEEDED = 147\nTOPIC_ALIAS_INVALID = 148\nPACKET_TOO_LARGE = 149\nMESSAGE_RATE_TOO_HIGH = 150\nQUOTA_EXCEEDED = 151\nADMINISTRATIVE_ACTION = 152\nPAYLOAD_FORMAT_INVALID = 153\nRETAIN_NOT_SUPPORTED = 154\nQOS_NOT_SUPPORTED = 155\nUSE_ANOTHER_SERVER = 156\nSERVER_MOVED = 157\nSHARED_SUBS_NOT_SUPPORTED = 158\nCONNECTION_RATE_EXCEEDED = 159\nMAXIMUM_CONNECT_TIME = 160\nSUBSCRIPTION_IDS_NOT_SUPPORTED = 161\nWILDCARD_SUBS_NOT_SUPPORTED = 162\n"
  },
  {
    "path": "test/old/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\nCC=cc\nLOCAL_CFLAGS=-I${R}/src -I${R}/include -I. -I${R} -Wall -ggdb -DDEBUG -DWITH_CLIENT\nLOCAL_LDFLAGS=\nSOVERSION=1\n\n.PHONY: all test clean\n\nall : msgsps_pub msgsps_sub\n\nmsgsps_pub : msgsps_pub.o\n\t${CC} $^ -o $@ ${LIBMOSQ}\n\nmsgsps_pub.o : msgsps_pub.c msgsps_common.h\n\t${CC} $(LOCAL_CFLAGS) -c $< -o $@\n\nmsgsps_sub : msgsps_sub.o\n\t${CC} $^ -o $@ ${LIBMOSQ}\n\nmsgsps_sub.o : msgsps_sub.c msgsps_common.h\n\t${CC} $(LOCAL_CFLAGS) -c $< -o $@\n\nclean :\n\t-rm -f *.o msgsps_pub msgsps_sub\n"
  },
  {
    "path": "test/old/msgsps_common.h",
    "content": "#ifndef MSGSPS_COMMON_H\n#define MSGSPS_COMMON_H\n\n#define HOST \"127.0.0.1\"\n#define PORT 1883\n\n#define PUB_QOS 1\n#define SUB_QOS 1\n#define MESSAGE_SIZE 1024L\n\n#endif\n\n"
  },
  {
    "path": "test/old/msgsps_pub.c",
    "content": "/* This provides a crude manner of testing the performance of a broker in messages/s. */\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <mosquitto.h>\n#include <stdatomic.h>\n\n#include <msgsps_common.h>\n\nstatic atomic_int message_count = 0;\n\n\nvoid my_publish_callback(struct mosquitto *mosq, void *obj, int mid)\n{\n\tmessage_count++;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tstruct mosquitto *mosq;\n\tint i;\n\tuint8_t buf[MESSAGE_SIZE];\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(NULL, true, NULL);\n\tmosquitto_publish_callback_set(mosq, my_publish_callback);\n\tmosquitto_connect(mosq, HOST, PORT, 600);\n\tmosquitto_loop_start(mosq);\n\n\ti=0;\n\twhile(1){\n\t\tmosquitto_publish(mosq, NULL, \"perf/test\", sizeof(buf), buf, PUB_QOS, false);\n\t\tusleep(100);\n\t\ti++;\n\t\tif(i == 10000){\n\t\t\t/* Crude \"messages per second\" count */\n\t\t\ti = message_count;\n\t\t\tmessage_count = 0;\n\t\t\tprintf(\"%d\\n\", i);\n\t\t\ti = 0;\n\t\t}\n\t}\n\tmosquitto_loop_stop(mosq, false);\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/old/msgsps_sub.c",
    "content": "/* This provides a crude manner of testing the performance of a broker in messages/s. */\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <sys/time.h>\n#include <unistd.h>\n#include <mosquitto.h>\n#include <stdatomic.h>\n\n#include <msgsps_common.h>\n\nstatic atomic_int message_count = 0;\n\n\nvoid my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)\n{\n\tmessage_count++;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tstruct mosquitto *mosq;\n\tint c;\n\n\tmosquitto_lib_init();\n\n\tmosq = mosquitto_new(NULL, true, NULL);\n\tmosquitto_message_callback_set(mosq, my_message_callback);\n\n\tmosquitto_connect(mosq, HOST, PORT, 600);\n\tmosquitto_subscribe(mosq, NULL, \"perf/test\", SUB_QOS);\n\n\tmosquitto_loop_start(mosq);\n\twhile(1){\n\t\tsleep(1);\n\t\tc = message_count;\n\t\tmessage_count = 0;\n\t\tprintf(\"%d\\n\", c);\n\n\t}\n\n\tmosquitto_destroy(mosq);\n\tmosquitto_lib_cleanup();\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/path_helper.h",
    "content": "#ifndef PATH_HELPER_H\n#define PATH_HELPER_H\n\n#include <libgen.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <errno.h>\n\n\n/* returns <current source directory>/<relpath> written to <dest>  */\nvoid cat_sourcedir_with_relpath(char *dest, const char *relpath)\n{\n\tstrcpy(dest, TEST_SOURCE_DIR);\n\tstrcat(dest, relpath);\n}\n\n#endif\n"
  },
  {
    "path": "test/ptest.py",
    "content": "#!/usr/bin/env python3\n\nimport json\nimport os\nfrom pathlib import Path\nimport subprocess\nimport time\nimport sys\n\nCOLOUR_PASS = 34\nCOLOUR_FAIL = 124\n\nclass PTestCase():\n    def __init__(self, path, ports, cmd, args=None):\n        self.path = path\n        self.ports = ports\n        self.cmd = str(cmd)\n        self.attempts = 0\n        if args is not None:\n            self.args = [self.cmd] + args\n        else:\n            self.args = [self.cmd]\n        self.start_time = 0\n        self.proc = None\n        self.mosq_port = None\n        self.runtime = 0\n\n    def start(self):\n        self.run_args = self.args.copy()\n        for p in self.mosq_port:\n            self.run_args.append(str(p))\n\n        self.proc = subprocess.Popen(self.run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.path)\n        self.start_time = time.time()\n\n    def print_result(self, attempt, col):\n        cmd = \" \".join(self.run_args)\n        if col == COLOUR_PASS:\n            stat = \"✓\"\n        elif col == COLOUR_FAIL:\n            stat = \"✗\"\n        else:\n            stat = attempt\n\n        if sys.stdout.isatty():\n            ansi_col = f\"\\033[38:5:{col}m\"\n            ansi_reset = \"\\033[0m\"\n        else:\n            ansi_col = \"\"\n            ansi_reset = \"\"\n        print(f\"{self.runtime:0.3f}s : {stat} : {ansi_col}{self.path}/{cmd}{ansi_reset}\")\n\n    def print_timed_out(self, col):\n        cmd = \" \".join(self.run_args)\n        if sys.stdout.isatty():\n            ansi_col = f\"\\033[38:5:{col}m\"\n            ansi_reset = \"\\033[0m\"\n        else:\n            ansi_col = \"\"\n            ansi_reset = \"\"\n        print(f\"{self.runtime:0.3f}s : ⏳ : {ansi_col}{self.path}/{cmd}{ansi_reset}\")\n\n    def print_log(self):\n        (stdo, stde) = self.proc.communicate()\n        print(stdo.decode('utf-8'))\n        print(stde.decode('utf-8'))\n\n\nclass PTest():\n    def __init__(self, minport=1888, max_running=20):\n        self.minport = minport\n        self.max_running = 20\n        self.tests = []\n\n    def add_tests(self, test_list, path=\".\", label=\"\"):\n        for testdef in test_list:\n            try:\n                if isinstance(testdef[2], (list,)):\n                    args = testdef[2]\n                else:\n                    args = [testdef[2]]\n            except IndexError:\n                args = None\n            self.tests.append(PTestCase(path, testdef[0], testdef[1], args))\n\n    def _next_test(self, ports):\n        if len(self.tests) == 0 or len(ports) == 0:\n            return\n\n        test = self.tests.pop()\n        test.mosq_port = []\n\n        if len(ports) < test.ports:\n            self.tests.insert(0, test)\n            return None\n        else:\n            for i in range(0, test.ports):\n                proc_port = ports.pop()\n                test.mosq_port.append(proc_port)\n\n            test.start()\n            return test\n\n    def run_tests(self, test_list):\n        self.add_tests(test_list)\n        self.run()\n\n    def load_failed_tests(self):\n        with open(\"failed-tests.json\", \"rt\") as f:\n            return json.loads(f.read())\n\n    def run_failed_tests(self):\n        test_list = self.load_failed_tests()\n        self.add_tests(test_list)\n        self.run()\n\n    def run(self):\n        ports = list(range(self.minport, self.minport+self.max_running+1))\n        start_time = time.time()\n        passed = 0\n        retried = 0\n        failed = 0\n\n        failed_tests = []\n        failed_tests_output = []\n        running_tests = []\n        retry_tests = []\n        while len(self.tests) > 0 or len(running_tests) > 0 or len(retry_tests) > 0:\n            if len(running_tests) <= self.max_running:\n                t = self._next_test(ports)\n                if t is None:\n                    time.sleep(0.1)\n                else:\n                    running_tests.append(t)\n\n            if len(running_tests) == 0 and len(self.tests) == 0 and len(retry_tests) > 0:\n                # Only retry tests after everything else has finished to reduce load\n                self.tests = retry_tests\n                retry_tests = []\n\n            for t in running_tests:\n                t.proc.poll()\n                t.runtime = time.time() - t.start_time\n                if t.proc.returncode is not None:\n                    running_tests.remove(t)\n\n                    for portret in t.mosq_port:\n                        ports.append(portret)\n                    t.proc.terminate()\n                    t.proc.wait()\n\n                    if t.proc.returncode != 0 and t.attempts < 5:\n                        t.print_result(t.attempts+1, 226-6*t.attempts)\n                        retried += 1\n                        t.attempts += 1\n                        t.proc = None\n                        t.mosq_port = None\n                        retry_tests.append(t)\n                        continue\n\n                    if t.proc.returncode != 0:\n                        t.print_result(0, COLOUR_FAIL)\n                        failed = failed + 1\n                        failed_tests.append(t.cmd)\n                        failed_tests_output.append([t.ports]+t.args)\n                        print(f\"{t.cmd}:\")\n                        t.print_log()\n                    else:\n                        passed = passed + 1\n                        t.print_result(0, COLOUR_PASS)\n                elif t.runtime > 180: # 3 minutes max\n                    t.print_timed_out(226-6*t.attempts)\n                    t.proc.terminate()\n                    t.proc.wait()\n                    t.print_log()\n\n        print(\"Passed: %d\\nRetried: %d\\nFailed: %d\\nTotal: %d\\nTotal time: %0.2f\" % (passed, retried, failed, passed+failed, time.time()-start_time))\n        if failed > 0:\n            print(\"Failing tests:\")\n            failed_tests.sort()\n            for f in failed_tests:\n                print(f)\n            with open(\"failed-tests.json\", \"wt\") as out:\n                out.write(json.dumps(failed_tests_output))\n            sys.exit(1)\n        else:\n            try:\n                os.remove(\"failed-tests.json\")\n            except FileNotFoundError:\n                pass\n"
  },
  {
    "path": "test/random/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\n.PHONY: all test\n\nall : auth_plugin.so\n\nauth_plugin.so : auth_plugin.c\n\t$(CC) ${LOCAL_CFLAGS} -fPIC -shared $< -o $@ -I${R}/lib -I${R}/src\n\n${R}/lib/libmosquitto.so.${SOVERSION} :\n\t$(MAKE) -C ${R}/lib\n\n${R}/lib/libmosquitto.a :\n\t$(MAKE) -C ${R}/lib libmosquitto.a\n\nclean :\n\t-rm -f *.o random_client *.gcda *.gcno\n\ntest : all\n\t./test.py\n"
  },
  {
    "path": "test/random/auth_plugin.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <mosquitto.h>\n#include <mosquitto_broker.h>\n#include <mosquitto_plugin.h>\n\n\nint mosquitto_auth_plugin_version(void)\n{\n\treturn MOSQ_AUTH_PLUGIN_VERSION;\n}\n\n\nint mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\tsrandom(time(NULL));\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)\n{\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload)\n{\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg)\n{\n\tif(random() % 2 == 0){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_ACL_DENIED;\n\t}\n}\n\n\nint mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password)\n{\n\tif(random() % 2 == 0){\n\t\treturn MOSQ_ERR_SUCCESS;\n\t}else{\n\t\treturn MOSQ_ERR_AUTH;\n\t}\n}\n\n\nint mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len)\n{\n\treturn MOSQ_ERR_AUTH;\n}\n\n"
  },
  {
    "path": "test/random/pwfile",
    "content": "test:$6$cBP7e6sUriMSh8yf$+Z3E9P1g+Hui8zDJA+XJpTHl6+0eym0MtWokmOY4j1svAR5RtjZoXB4OQuHYzrGrdp1e8gXoqcKlcP+1lmepmg==\n"
  },
  {
    "path": "test/random/random.conf",
    "content": "per_listener_settings true\n\n# Unencrypted MQTT over TCP\nlistener 1883\n\nlistener 1884\npassword_file pwfile\n\nlistener 1885\nauth_plugin ./auth_plugin.so\n\nlistener 1886\npassword_file pwfile\nauth_plugin ./auth_plugin.so\n\n\n# Encrypted MQTT over TCP\nlistener 8883\ncafile ../ssl/all-ca.crt\ncertfile ../ssl/server.crt\nkeyfile ../ssl/server.key\n\nlistener 8884\ncafile ../ssl/all-ca.crt\ncertfile ../ssl/server.crt\nkeyfile ../ssl/server.key\npassword_file pwfile\n\nlistener 8885\ncafile ../ssl/all-ca.crt\ncertfile ../ssl/server.crt\nkeyfile ../ssl/server.key\nauth_plugin ./auth_plugin.so\n\nlistener 8886\ncafile ../ssl/all-ca.crt\ncertfile ../ssl/server.crt\nkeyfile ../ssl/server.key\npassword_file pwfile\nauth_plugin ./auth_plugin.so\n\n\n# Unencrypted MQTT over WebSockets\nlistener 8000\nprotocol websockets\n\nlistener 8001\nprotocol websockets\npassword_file pwfile\n\nlistener 8002\nprotocol websockets\nauth_plugin ./auth_plugin.so\n\nlistener 8003\nprotocol websockets\npassword_file pwfile\nauth_plugin ./auth_plugin.so\n\n\n# Encrypted MQTT over WebSockets\nlistener 4430\nprotocol websockets\ncafile ../ssl/all-ca.crt\ncertfile ../ssl/server.crt\nkeyfile ../ssl/server.key\n\nlistener 4431\nprotocol websockets\ncafile ../ssl/all-ca.crt\ncertfile ../ssl/server.crt\nkeyfile ../ssl/server.key\npassword_file pwfile\n\nlistener 4432\nprotocol websockets\ncafile ../ssl/all-ca.crt\ncertfile ../ssl/server.crt\nkeyfile ../ssl/server.key\nauth_plugin ./auth_plugin.so\n\nlistener 4433\nprotocol websockets\ncafile ../ssl/all-ca.crt\ncertfile ../ssl/server.crt\nkeyfile ../ssl/server.key\npassword_file pwfile\nauth_plugin ./auth_plugin.so\n\n\n#log_dest file mosquitto.log\n#log_type all\n"
  },
  {
    "path": "test/random/random_client.py",
    "content": "#!/usr/bin/env python3\n\nfrom pathlib import Path\nimport paho.mqtt.client as paho\nimport random\nimport sys\nimport time\n\n# This is a client that carries out randomised behaviour. It is intended for\n# use with the local config file. This file has multiple listeners configured:\n#   * 1883 - unencrypted MQTT over TCP with no authentication\n#   * 1884 - unencrypted MQTT over TCP with password authentication\n#   * 1885 - unencrypted MQTT over TCP with plugin authentication\n#   * 1886 - unencrypted MQTT over TCP with password and plugin authentication\n#\n#   * 8883 - encrypted MQTT over TCP with no authentication\n#   * 8884 - encrypted MQTT over TCP with password authentication\n#   * 8885 - encrypted MQTT over TCP with plugin authentication\n#   * 8886 - encrypted MQTT over TCP with password and plugin authentication\n#\n#   * 8000 - unencrypted MQTT over WebSockets with no authentication\n#   * 8001 - unencrypted MQTT over WebSockets with password authentication\n#   * 8002 - unencrypted MQTT over WebSockets with plugin authentication\n#   * 8003 - unencrypted MQTT over WebSockets with password and plugin authentication\n#\n#   * 4430 - encrypted MQTT over WebSockets with no authentication\n#   * 4431 - encrypted MQTT over WebSockets with password authentication\n#   * 4432 - encrypted MQTT over WebSockets with plugin authentication\n#   * 4433 - encrypted MQTT over WebSockets with password and plugin authentication\n#\n# The client randomly picks:\n#   * A port out of the list\n#   * Whether to use encryption\n#   * Whether to use WebSockets\n#   * Clean start or not\n#   * Session expiry interval or not\n#   * QoS to use when subscribing - topics \"outgoing/[client id]/message\" and \"response/#\"\n#   * Lifetime of connection\n# On a per publish message basis it chooses:\n#   * QoS of message\n#   * Topic of message \"outgoing/[0-max client]/message\"\n#   * Retain\n#   * Interval until next outgoing message\n\nports = [\n        {\"port\":1883, \"tls\":False, \"transport\":\"tcp\", \"auth\":False},\n        {\"port\":1884, \"tls\":False, \"transport\":\"tcp\", \"auth\":True},\n        {\"port\":1885, \"tls\":False, \"transport\":\"tcp\", \"auth\":True},\n        {\"port\":1886, \"tls\":False, \"transport\":\"tcp\", \"auth\":True},\n\n        {\"port\":8883, \"tls\":True, \"transport\":\"tcp\", \"auth\":False},\n        {\"port\":8884, \"tls\":True, \"transport\":\"tcp\", \"auth\":True},\n        {\"port\":8885, \"tls\":True, \"transport\":\"tcp\", \"auth\":True},\n        {\"port\":8886, \"tls\":True, \"transport\":\"tcp\", \"auth\":True},\n\n        {\"port\":8000, \"tls\":False, \"transport\":\"websockets\", \"auth\":False},\n        {\"port\":8001, \"tls\":False, \"transport\":\"websockets\", \"auth\":True},\n        {\"port\":8002, \"tls\":False, \"transport\":\"websockets\", \"auth\":True},\n        {\"port\":8003, \"tls\":False, \"transport\":\"websockets\", \"auth\":True},\n\n        {\"port\":4430, \"tls\":True, \"transport\":\"websockets\", \"auth\":False},\n        {\"port\":4431, \"tls\":True, \"transport\":\"websockets\", \"auth\":True},\n        {\"port\":4432, \"tls\":True, \"transport\":\"websockets\", \"auth\":True},\n        {\"port\":4433, \"tls\":True, \"transport\":\"websockets\", \"auth\":True},\n        ]\n\nbooleans = [True, False]\nqos_values = [0, 1, 2]\n\n\ndef on_connect(client, userdata, flags, rc):\n    global running\n    if rc == 0:\n        client.subscribe(\"response/#\", subscribe_qos)\n        client.subscribe(\"outgoing/%s/message\" % (client_id), subscribe_qos)\n    else:\n        running = False\n\n\ndef on_message(client, userdata, msg):\n    pass\n\n\ndef on_publish(client, userdata, mid):\n    pass\n\n\ndef on_disconnect(client, userdata, rc):\n    running = False\n\n\ndef do_publish(client):\n    retain = random.choice(booleans)\n    qos = random.choice(qos_values)\n    topic = \"outgoing/%d/message\" % (random.uniform(1, 1000))\n    payload = \"message\"\n\n    client.publish(topic, payload, qos, retain)\n\n    next_publish_time = time.time() + random.uniform(0.1, 2.0)\n\n\ndef main():\n    global running\n    global lifetime\n\n    mqttc = paho.Client(client_id, clean_session=clean_start, protocol=protocol, transport=transport)\n    mqttc.on_message = on_message\n    mqttc.on_publish = on_publish\n    mqttc.on_connect = on_connect\n    mqttc.on_disconnect = on_disconnect\n    if auth and random.choice(booleans):\n        if random.choice(booleans):\n            mqttc.username_pw_set(\"test\", \"password\")\n        else:\n            mqttc.username_pw_set(\"bad\", \"bad\")\n\n    if use_tls:\n        source_dir = Path(__file__).resolve().parent\n        ssl_dir = source_dir.parent / \"ssl\"\n        mqttc.tls_set(ca_certs=f\"{ssl_dir}/all-ca.crt\")\n\n    mqttc.connect(\"localhost\", port)\n    mqttc.loop_start()\n\n    while running:\n        time.sleep(0.1)\n        now = time.time()\n        if now > next_publish_time:\n            do_publish(mqttc)\n        if now > lifetime:\n            if random.choice(booleans):\n                mqttc.disconnect()\n                lifetime += 5.0\n            else:\n                running = False\n\n\np = random.choice(ports)\nport = p[\"port\"]\nuse_tls = p[\"tls\"]\ntransport = p[\"transport\"]\nauth = p[\"auth\"]\n\nclient_id = \"cid\"+sys.argv[1]\nclean_start = random.choice(booleans)\nsubscribe_qos = random.choice(qos_values)\nprotocol = paho.MQTTv311\nnext_publish_time = time.time() + random.uniform(0.1, 2.0)\nlifetime = time.time() + random.uniform(5.0, 10.0)\nrunning = True\n\nmain()\n"
  },
  {
    "path": "test/random/test.py",
    "content": "#!/usr/bin/env python3\n\nimport subprocess\nimport time\nimport sys\n\ndef next_client(clients):\n    if len(clients) == 0:\n        return\n\n    c = clients.pop()\n    args = [\"./random_client.py\", str(c)]\n\n    proc = subprocess.Popen(args, stderr=subprocess.DEVNULL)\n    proc.cid = c\n    return proc\n\n\ndef run_clients(max_clients):\n    clients = list(range(1, max_clients))\n    start_time = time.time()\n\n    running_clients = []\n    while True:\n        print(len(running_clients))\n        if len(running_clients) < max_clients:\n            c = next_client(clients)\n            if c is not None:\n                running_clients.append(c)\n        else:\n            time.sleep(0.1)\n\n        for c in running_clients:\n            c.poll()\n            if c.returncode is not None:\n                running_clients.remove(c)\n                clients.append(c.cid)\n                c.terminate()\n                c.wait()\n\n\nenv = mosq_test.env_add_ld_library_path()\n#broker = subprocess.Popen([mosq_test.get_build_root()+\"/src/mosquitto\", \"-c\", \"random.conf\"], env=env)\nrun_clients(1000)\n"
  },
  {
    "path": "test/ssl/all-ca.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 1 (0x1)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=GB, ST=Derbyshire, L=Derby, O=Mosquitto Project, OU=Testing, CN=Root CA\n        Validity\n            Not Before: Jul  7 22:27:28 2025 GMT\n            Not After : Jul  6 22:27:28 2030 GMT\n        Subject: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:b8:5b:81:cb:99:5e:85:66:42:fe:c4:f4:c6:ad:\n                    f9:9a:b4:c5:29:bb:05:5b:90:b2:a0:7d:ba:00:a3:\n                    f3:ec:8f:81:c4:45:98:79:6c:3e:04:77:26:dd:d9:\n                    9f:57:6a:dc:56:25:ba:60:c9:7d:cc:fe:40:e1:e9:\n                    75:02:a7:56:0e:bb:a8:a5:d1:b4:b1:57:62:d1:07:\n                    67:23:77:ae:3e:41:ba:22:6f:b8:f3:3d:ac:5f:1f:\n                    93:cb:62:33:93:61:0b:b1:33:be:ee:67:bb:b1:4b:\n                    2a:3e:c3:99:e9:ea:51:ca:79:ac:91:ce:e6:ee:99:\n                    86:85:f0:a4:11:91:2b:ef:9f:cf:6d:8c:8f:0a:f8:\n                    69:5f:42:c5:ee:90:ca:fe:da:6f:c6:20:56:9d:ac:\n                    eb:b0:64:ab:a5:fc:9d:c2:db:ac:a9:a7:40:e1:60:\n                    00:9f:87:f3:51:9f:64:7e:f8:4a:8e:7f:aa:95:f3:\n                    4f:04:b0:ea:ee:77:0b:3e:27:bf:9a:b4:62:1f:76:\n                    6d:a4:be:eb:e2:c3:c6:be:87:6d:f3:fb:ec:a0:09:\n                    41:ef:8d:35:90:3f:fb:88:f8:fe:e2:25:cd:ff:c2:\n                    72:13:d0:bd:fe:40:5e:9d:80:9f:7c:b0:ea:54:bd:\n                    69:70:1f:cf:2f:c3:97:40:ee:ef:97:d6:c6:1d:16:\n                    6b:01\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Subject Key Identifier: \n                C8:63:DE:29:66:24:95:5D:B2:37:6D:F6:A2:01:85:AE:31:0F:8E:45\n            X509v3 Authority Key Identifier: \n                4F:56:ED:BA:57:12:46:13:CF:96:E3:2C:C1:91:64:69:5B:9A:E7:05\n            X509v3 Basic Constraints: critical\n                CA:TRUE\n            X509v3 Key Usage: \n                Certificate Sign, CRL Sign\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        07:ba:ab:2e:42:0c:ac:98:05:af:a0:49:44:db:36:4f:a1:15:\n        40:ec:4d:46:ca:ae:f0:ad:aa:1c:d0:99:11:6c:32:ce:62:b5:\n        3d:71:ab:1d:ed:cb:3e:3a:38:de:20:25:6f:02:fd:1f:b9:d7:\n        8d:5f:d1:d4:46:ea:a7:c5:fa:70:fc:aa:0f:63:a3:bb:dc:93:\n        6b:9d:1c:9c:64:f4:3f:ac:09:60:b7:d5:3d:b2:c5:df:58:44:\n        24:a4:bf:a9:d2:c3:0c:9d:f0:06:b1:20:f6:7d:1b:55:c4:f8:\n        96:84:1d:b1:b9:3d:06:16:ab:fd:cf:97:1d:ff:6d:e2:6d:fe:\n        da:36:7e:4c:d6:f8:1c:df:b7:79:58:ce:ea:c4:50:3a:14:86:\n        f8:55:21:77:90:ec:bf:af:82:29:c8:a7:5c:91:b9:33:e0:ee:\n        8a:6c:80:f1:e1:a5:55:26:a1:46:bb:d1:eb:59:d1:1c:90:a4:\n        19:54:e7:68:0c:b6:c7:9e:b0:d0:e3:12:24:a4:92:96:1c:bd:\n        82:cc:1f:3d:f4:a6:de:69:77:83:25:3a:02:ee:64:38:f7:ae:\n        ef:06:09:06:3d:c2:19:0f:54:06:d3:e4:0c:e2:bd:be:e3:1c:\n        ac:a7:e0:c2:50:37:b4:8b:4b:0f:58:71:24:00:6b:7a:71:1e:\n        7b:f5:a2:71\n-----BEGIN CERTIFICATE-----\nMIIDsjCCApqgAwIBAgIBATANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJHQjET\nMBEGA1UECAwKRGVyYnlzaGlyZTEOMAwGA1UEBwwFRGVyYnkxGjAYBgNVBAoMEU1v\nc3F1aXR0byBQcm9qZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRAwDgYDVQQDDAdSb290\nIENBMB4XDTI1MDcwNzIyMjcyOFoXDTMwMDcwNjIyMjcyOFowZTELMAkGA1UEBhMC\nR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxGjAYBgNVBAoMEU1vc3F1aXR0byBQcm9q\nZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRMwEQYDVQQDDApTaWduaW5nIENBMIIBIjAN\nBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuFuBy5lehWZC/sT0xq35mrTFKbsF\nW5CyoH26AKPz7I+BxEWYeWw+BHcm3dmfV2rcViW6YMl9zP5A4el1AqdWDruopdG0\nsVdi0QdnI3euPkG6Im+48z2sXx+Ty2Izk2ELsTO+7me7sUsqPsOZ6epRynmskc7m\n7pmGhfCkEZEr75/PbYyPCvhpX0LF7pDK/tpvxiBWnazrsGSrpfydwtusqadA4WAA\nn4fzUZ9kfvhKjn+qlfNPBLDq7ncLPie/mrRiH3ZtpL7r4sPGvodt8/vsoAlB7401\nkD/7iPj+4iXN/8JyE9C9/kBenYCffLDqVL1pcB/PL8OXQO7vl9bGHRZrAQIDAQAB\no2AwXjAdBgNVHQ4EFgQUyGPeKWYklV2yN232ogGFrjEPjkUwHwYDVR0jBBgwFoAU\nT1btulcSRhPPluMswZFkaVua5wUwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMC\nAQYwDQYJKoZIhvcNAQELBQADggEBAAe6qy5CDKyYBa+gSUTbNk+hFUDsTUbKrvCt\nqhzQmRFsMs5itT1xqx3tyz46ON4gJW8C/R+5141f0dRG6qfF+nD8qg9jo7vck2ud\nHJxk9D+sCWC31T2yxd9YRCSkv6nSwwyd8AaxIPZ9G1XE+JaEHbG5PQYWq/3Plx3/\nbeJt/to2fkzW+Bzft3lYzurEUDoUhvhVIXeQ7L+vginIp1yRuTPg7opsgPHhpVUm\noUa70etZ0RyQpBlU52gMtseesNDjEiSkkpYcvYLMHz30pt5pd4MlOgLuZDj3ru8G\nCQY9whkPVAbT5Azivb7jHKyn4MJQN7SLSw9YcSQAa3pxHnv1onE=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIID0jCCArqgAwIBAgIUPWZteXB28AsU7emgSEDG3w7zqd8wDQYJKoZIhvcNAQEL\nBQAwcjELMAkGA1UEBhMCR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxDjAMBgNVBAcM\nBURlcmJ5MRowGAYDVQQKDBFNb3NxdWl0dG8gUHJvamVjdDEQMA4GA1UECwwHVGVz\ndGluZzEQMA4GA1UEAwwHUm9vdCBDQTAeFw0yNTA3MDcyMjI3MjhaFw0zNTA3MDUy\nMjI3MjhaMHIxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApEZXJieXNoaXJlMQ4wDAYD\nVQQHDAVEZXJieTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3QxEDAOBgNVBAsM\nB1Rlc3RpbmcxEDAOBgNVBAMMB1Jvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDyRXMUPa/pLzERcaE+/TkYmiH83MfGhJ2eSeEqY+vjuzePT/p3\nGDLuM5IcprukEuif+4HbvLQC8zIdNjO+Xwb/Acg7njQfNTV9b8IzOn3r/k4ZcB2G\nrK7Kae338xAmkTYjACrTUnbUsdRmbRKZZCIk69fxlC4xhCp+7sZyKc4FOkGI4dOe\nTVkLN3DQflzR7mF78yCgbr2HBXoF2Eazv008YqTDS2gL3JbXIpt2H7Z8fNZHupbk\nB0gydAxmMSUnwBeXLkVkKFOJNDjjGtY4nDKOpuirEx8K0GYs6Mo0iFTwi4IDxjzM\njP5uarn1hx5bAFpuN1r1jbzb0iopZAOhmG2TAgMBAAGjYDBeMB0GA1UdDgQWBBRP\nVu26VxJGE8+W4yzBkWRpW5rnBTAfBgNVHSMEGDAWgBRPVu26VxJGE8+W4yzBkWRp\nW5rnBTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsF\nAAOCAQEAjejhP+g9DbB/vgjUJbk5sRgBeq0sX8ghtq1B+LEgKV+l/h9dmsavChjF\nRWzEhfxK83iMJX8dAXbloYSgmXNSmh/7RfXQEBPbwzD9CyZXWa+HC4Rfv0YGvdua\nliyXVxHiND4wGiv/GyTOcGJ9P5zjB+svo7rnIHvC6vJyo0hp17AuBSZDWBWt096U\nQHwJ3DrLY09OpMwfLZaduEzH9Vpf+rkQrTAo0/jSmhCDPBtLx/c+PKptUiOjvsuc\nDlaKBh7s5OBjGDUUqWDmFhcQCEy4eg0KDaAfCUrjoXFK4yf+TAxVpNBCoUCaGOqk\nGvK7TZAfHoGSAxTuls/Y0N/StPSojw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/client-encrypted.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 7 (0x7)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA\n        Validity\n            Not Before: Jul  7 22:27:29 2025 GMT\n            Not After : Jul  6 22:27:29 2030 GMT\n        Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=test client encrypted\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:a1:dd:b6:77:f9:03:1b:e1:7c:23:96:b5:a7:b6:\n                    c0:2a:2e:f8:14:e9:06:d6:97:64:06:e1:ad:a9:5e:\n                    a3:4d:e8:fd:1a:2e:72:6e:55:b3:6b:37:36:27:c5:\n                    e7:4a:71:a2:79:1c:c2:69:97:ff:2b:3c:7f:58:ec:\n                    0d:c0:6e:65:93:44:a5:91:fa:75:f6:d9:a9:d8:2c:\n                    7e:a2:2c:c7:58:55:97:c8:5d:cc:2a:ff:fe:7c:78:\n                    aa:49:12:6c:4c:7e:97:09:9f:bd:2c:83:c2:36:30:\n                    35:ca:6d:43:a9:80:78:d2:56:14:a3:ce:92:6a:67:\n                    51:52:ac:16:0c:b7:4b:2b:52:fe:79:62:4e:4a:65:\n                    1f:e5:fa:b9:b3:6f:ae:d4:46:3d:d4:48:03:23:ab:\n                    94:e6:ad:bc:38:85:f1:a9:df:43:3a:27:38:1e:fd:\n                    b0:5d:36:40:23:69:b2:fe:14:30:93:a1:d1:6b:36:\n                    38:65:8a:e3:c7:0a:4b:b6:fc:c7:af:f3:5a:3a:22:\n                    ec:df:41:07:96:c0:14:4d:82:b8:25:40:49:4f:6d:\n                    ea:a8:44:1e:b9:c6:d0:6d:94:42:20:86:d1:62:05:\n                    4f:7a:00:27:f0:27:23:8c:f9:26:6d:2e:f4:61:62:\n                    79:26:ea:44:df:71:8e:b0:be:de:13:17:cb:77:14:\n                    a0:df\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            Netscape Comment: \n                OpenSSL Generated Certificate\n            X509v3 Subject Key Identifier: \n                BA:16:BE:F7:34:D2:63:6C:18:88:0A:92:5E:C3:3A:85:3A:09:38:56\n            X509v3 Authority Key Identifier: \n                C8:63:DE:29:66:24:95:5D:B2:37:6D:F6:A2:01:85:AE:31:0F:8E:45\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        05:f8:d2:f3:95:b8:8e:b4:74:d8:2a:17:88:d8:aa:28:a9:72:\n        1b:a1:c1:67:1c:af:7d:fe:9a:bb:b5:e6:4b:01:19:27:a4:ab:\n        fb:cd:aa:df:cb:52:61:22:43:be:d5:46:d7:64:bc:81:39:00:\n        98:27:db:e0:fe:b1:78:31:05:7c:bf:cc:9a:ad:5c:7b:d6:47:\n        7e:8f:c7:a8:31:b6:ac:21:d4:1a:85:b0:b4:84:84:b4:e7:fc:\n        d3:a0:c5:97:f6:ed:6b:93:18:22:68:6a:8b:81:cd:c5:11:02:\n        c6:9b:77:08:28:48:28:3b:77:bb:28:21:f7:9e:11:7a:d2:4f:\n        67:32:52:a7:aa:4a:59:ee:17:2d:20:f7:6b:61:3b:3f:7b:1b:\n        07:9b:82:cb:0e:d7:b1:9d:66:6d:fd:82:cd:5b:1b:66:e2:69:\n        c6:33:1a:d4:18:26:49:52:f8:32:b5:6d:6b:22:e6:bc:79:ef:\n        85:64:db:e0:76:53:9a:ce:3b:3e:a0:81:6a:27:26:ac:1d:48:\n        07:79:6b:e3:bb:19:2c:63:7d:86:fd:ed:67:fa:6f:8e:26:94:\n        08:42:d3:55:6b:41:d7:1b:6c:18:c0:12:3c:12:80:61:de:95:\n        3b:fe:4a:7c:db:ac:09:5d:6f:ed:1a:1a:5a:fb:f0:fd:3a:17:\n        77:93:af:3f\n-----BEGIN CERTIFICATE-----\nMIID4TCCAsmgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET\nMBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx\nEDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMjUwNzA3\nMjIyNzI5WhcNMzAwNzA2MjIyNzI5WjCBgjELMAkGA1UEBhMCR0IxGDAWBgNVBAgM\nD05vdHRpbmdoYW1zaGlyZTETMBEGA1UEBwwKTm90dGluZ2hhbTEPMA0GA1UECgwG\nU2VydmVyMRMwEQYDVQQLDApQcm9kdWN0aW9uMR4wHAYDVQQDDBV0ZXN0IGNsaWVu\ndCBlbmNyeXB0ZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCh3bZ3\n+QMb4XwjlrWntsAqLvgU6QbWl2QG4a2pXqNN6P0aLnJuVbNrNzYnxedKcaJ5HMJp\nl/8rPH9Y7A3AbmWTRKWR+nX22anYLH6iLMdYVZfIXcwq//58eKpJEmxMfpcJn70s\ng8I2MDXKbUOpgHjSVhSjzpJqZ1FSrBYMt0srUv55Yk5KZR/l+rmzb67URj3USAMj\nq5Tmrbw4hfGp30M6Jzge/bBdNkAjabL+FDCTodFrNjhliuPHCku2/Mev81o6Iuzf\nQQeWwBRNgrglQElPbeqoRB65xtBtlEIghtFiBU96ACfwJyOM+SZtLvRhYnkm6kTf\ncY6wvt4TF8t3FKDfAgMBAAGjfjB8MAwGA1UdEwEB/wQCMAAwLAYJYIZIAYb4QgEN\nBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBS6Fr73\nNNJjbBiICpJewzqFOgk4VjAfBgNVHSMEGDAWgBTIY94pZiSVXbI3bfaiAYWuMQ+O\nRTANBgkqhkiG9w0BAQsFAAOCAQEABfjS85W4jrR02CoXiNiqKKlyG6HBZxyvff6a\nu7XmSwEZJ6Sr+82q38tSYSJDvtVG12S8gTkAmCfb4P6xeDEFfL/Mmq1ce9ZHfo/H\nqDG2rCHUGoWwtISEtOf806DFl/bta5MYImhqi4HNxRECxpt3CChIKDt3uygh954R\netJPZzJSp6pKWe4XLSD3a2E7P3sbB5uCyw7XsZ1mbf2CzVsbZuJpxjMa1BgmSVL4\nMrVtayLmvHnvhWTb4HZTms47PqCBaicmrB1IB3lr47sZLGN9hv3tZ/pvjiaUCELT\nVWtB1xtsGMASPBKAYd6VO/5KfNusCV1v7RoaWvvw/ToXd5OvPw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/client-encrypted.key",
    "content": "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQCF9+wFpj0zV4f3eA\n5SC2TAICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIPTGlf0N4mOMEggTI\nXC1qxxC/+Pw5sPv6DTFF463G7U+vAANLrLmE30XQ4JFDQcLJ8cStUIZUf4V5mNvP\n3hLVo7zHSSyVp+7JbJvAAMeSSVpo3bXzp4oLHWIvbSxQV0bx6EJ3mS5FTbO5BVZR\nJ/VtZ20teJZzWoBykENLU5rAsbGcs7N5ZqV1uvaKmnVf4AfHMEESKc6KoTEMC6gf\nBW9qDhcw5q6t49fnneaoK48G3xck9tl/N2Z2Bc0Bj+ruS4mH8B4WoCvPyYJ12C0l\n5f449ZMGfxyQxXiSDl5H2gheGh5it3s1Lw+tLzlR+kfEIuliaZ7j/Y4rSupUa8pM\nErcOeZVDicNwSsVS02HgerV5CEcX+ZI8WsiMtPS29eaYGJy8EtKmAiZ0UjWgmxTJ\nIkPTgrOVqL8/6kOgACTErMi3RIExAlCiAFPtLcyeXQRwE6BoGhUSsGR+j2Tvub9k\nyhnCc6dsEjoy3/LYDDL3gUxy5/VV0hAvO5mIFcA2d/HAWKn0Sxwx/7Xd76uDEZos\n2/NmA3rISpbtNubOVkfiZ1zt3N3C5FBOa41uLRwZ/rs2gxBl/etCYJMXdicDMzmJ\nNTIp60AMIHQINlSYy/RecC5KaRLnL0UKVEr4jNtmdf/9fKzMBHz4QJLesivOWXgT\nZerdB44MficbMiPoYkxJ+IdBYkRNP2Sifl82NO7pbxIhkhNUZFAJRBo/SWVOQ4B2\nF0Cl7Vsa0pp0+71l76dF5Sxnhc+Vga1U3bURLDYFUlqOxhEImTINySY3QVkJpPmX\nGR/Jt15ttXGkpex1ZLngcrlD1QC2czuHwDBRbGeYIIjZJ6CmJPXOHm95iDlAWEQn\nbIcrokp4oZOXuDrUJT/vzSqmX0QpxkVw0HzPT3r7dddbb7AiXzXzCczaK5DVGbfG\nD9NimLgTyhVnQeRtPDTaI7JB0UpLRVI5dqT2TsFiulCiYfrufgEz2yKrpE8Njl8p\nQ3YBLCSTM3zzi7JxR/OfJkHlnK2sko9I5l5cA1D9uhu7QG4dShpXzxP0EA9lvXG7\nDl8DQuwzgUjSk2A/nA5bofbPtntyUihhdPZ8c2ni4Fj356fqU4FPUKmP978zKklR\nnLFuw1J9osv3UWn94glOHE4+nADhS8dCRicDRqpC0F72o6Jk0kUMyfaZYmj5OvQv\nxl5FpIa0QdSq+suP2JIkRC/CvbA+ugZJeL/Po8sFi6gd1eUdC6q5wJo/MgDUGdGA\nj953scDof+CvBFRCgMRUzbEgVVFoR84OImdlNuOOTLU0agodfJ3tRjI1prPu3WEh\nY48UP5bfqOSxwebdWpxXTr+o24Xk7ryyiWXbPLgTFuxj1lL2erEWu604ls4TETD9\njBCwfQGF1Q+lYTYxGGBNGMPuyttUGr3oR0JFyCYtUZuSZB9oSJwKN7XHPAPVamf0\nIcMoCNTbCHz+vhm6Mzca9m9lgHgsgHNvlSwHIFCTt6b9eWUxx6OKHxDoxBio2LE7\nEvfN0yyefseTJC9vND0IN54qYBG0Gw4E94ayhn7AP5zKe7ebPzKBmwEE0VCBUSHg\nCmv1PP12u7kCSyWuximn9kvxcBlwz3fhAoyN9BzRJc6NlVVURfgQEVu74VUckfaT\nt0hDukAZySz/9jtA7cn7BwyuINR3HLjL\n-----END ENCRYPTED PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/client-expired.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 5 (0x5)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA\n        Validity\n            Not Before: Aug 20 00:00:00 2012 GMT\n            Not After : Aug 21 00:00:00 2012 GMT\n        Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=test client expired\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:aa:fd:ae:95:66:d0:c6:47:c8:d3:c3:61:66:cb:\n                    4d:92:32:2a:6b:f1:66:9c:c1:22:a0:9e:1b:d7:e9:\n                    90:e3:7e:e5:76:e7:34:fb:d7:74:24:5a:d2:4b:9e:\n                    32:fd:c6:b4:7d:97:ff:e4:c8:b7:6f:e2:18:c4:c5:\n                    cd:58:88:21:e9:65:11:16:ba:72:dc:ef:3f:99:f3:\n                    d8:06:83:4d:42:2f:b7:a9:91:b4:31:45:77:23:18:\n                    6f:2f:a3:38:74:01:c0:f9:cd:dc:61:2d:79:5d:fc:\n                    2b:65:ad:01:77:a4:fe:84:12:4d:86:b7:7d:3b:41:\n                    43:c2:91:b9:c7:08:64:7c:3c:c0:9f:0d:fc:9c:55:\n                    e7:51:10:00:b4:5c:67:55:e3:92:dc:c2:4e:09:0b:\n                    b8:9d:e3:c8:b5:a4:82:63:d3:e0:5c:bf:0e:c8:12:\n                    76:d3:2f:2f:35:d8:4b:56:de:de:ba:21:5c:16:9f:\n                    b8:62:06:46:3e:d5:d3:6f:e6:7d:8a:98:b5:ed:96:\n                    a3:1e:3a:61:9e:27:f1:68:f1:63:2e:06:69:cf:49:\n                    33:f9:4c:f1:79:b6:6d:75:3e:04:53:22:51:ed:20:\n                    2f:60:e1:d4:ce:ac:25:ca:f0:a0:8e:85:8e:9d:06:\n                    a2:5b:7f:ea:9a:dc:a6:3f:9d:ff:89:28:a0:5b:17:\n                    7a:95\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            Netscape Comment: \n                OpenSSL Generated Certificate\n            X509v3 Subject Key Identifier: \n                16:D7:08:AC:DC:6C:6E:31:9C:35:76:7D:43:26:67:FC:5D:42:67:3E\n            X509v3 Authority Key Identifier: \n                C8:63:DE:29:66:24:95:5D:B2:37:6D:F6:A2:01:85:AE:31:0F:8E:45\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        57:ab:0f:6a:2e:3e:c7:3a:02:71:5d:08:ec:9f:1d:f5:13:90:\n        6c:d2:b7:bf:63:28:b4:4d:7b:5e:b2:e0:06:98:a9:42:1f:13:\n        2a:78:7d:f5:4c:15:06:c8:79:ba:14:58:c7:97:8e:84:da:f5:\n        17:58:16:a9:ff:06:70:a7:40:49:cc:ad:22:33:87:d0:f9:cd:\n        6a:32:77:61:0f:71:cf:f5:c4:79:3d:44:1e:58:96:b0:fd:e5:\n        d6:92:8b:88:f2:98:53:a9:b0:c8:24:9f:4a:d7:6c:a1:66:ea:\n        51:25:57:94:82:81:e7:bb:5d:51:a6:b7:48:bb:c0:ce:58:a6:\n        60:a6:a7:c6:d5:56:3f:a8:a8:cb:42:dc:f9:7b:e1:b8:c5:7f:\n        f9:3f:0b:c4:88:18:f5:8c:0b:9a:0f:57:a1:dc:88:ad:56:e9:\n        65:b6:1e:3d:0a:8b:e5:bf:46:40:4f:51:15:cc:eb:18:30:f6:\n        bc:66:e2:27:cb:5c:22:09:7d:d4:62:d4:77:7a:a6:66:f7:54:\n        e3:07:10:dd:a0:28:ee:71:ff:61:ff:bd:a2:44:0d:a5:6d:74:\n        44:76:58:a3:09:d9:17:6e:5f:b8:64:02:7f:96:64:b7:2a:3e:\n        31:1b:01:ed:42:db:61:68:f8:a3:7b:02:7e:b9:7c:9c:28:c1:\n        da:d3:ea:81\n-----BEGIN CERTIFICATE-----\nMIID3zCCAsegAwIBAgIBBTANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET\nMBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx\nEDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMTIwODIw\nMDAwMDAwWhcNMTIwODIxMDAwMDAwWjCBgDELMAkGA1UEBhMCR0IxGDAWBgNVBAgM\nD05vdHRpbmdoYW1zaGlyZTETMBEGA1UEBwwKTm90dGluZ2hhbTEPMA0GA1UECgwG\nU2VydmVyMRMwEQYDVQQLDApQcm9kdWN0aW9uMRwwGgYDVQQDDBN0ZXN0IGNsaWVu\ndCBleHBpcmVkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqv2ulWbQ\nxkfI08NhZstNkjIqa/FmnMEioJ4b1+mQ437lduc0+9d0JFrSS54y/ca0fZf/5Mi3\nb+IYxMXNWIgh6WURFrpy3O8/mfPYBoNNQi+3qZG0MUV3IxhvL6M4dAHA+c3cYS15\nXfwrZa0Bd6T+hBJNhrd9O0FDwpG5xwhkfDzAnw38nFXnURAAtFxnVeOS3MJOCQu4\nnePItaSCY9PgXL8OyBJ20y8vNdhLVt7euiFcFp+4YgZGPtXTb+Z9ipi17ZajHjph\nnifxaPFjLgZpz0kz+UzxebZtdT4EUyJR7SAvYOHUzqwlyvCgjoWOnQaiW3/qmtym\nP53/iSigWxd6lQIDAQABo34wfDAMBgNVHRMBAf8EAjAAMCwGCWCGSAGG+EIBDQQf\nFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUFtcIrNxs\nbjGcNXZ9QyZn/F1CZz4wHwYDVR0jBBgwFoAUyGPeKWYklV2yN232ogGFrjEPjkUw\nDQYJKoZIhvcNAQELBQADggEBAFerD2ouPsc6AnFdCOyfHfUTkGzSt79jKLRNe16y\n4AaYqUIfEyp4ffVMFQbIeboUWMeXjoTa9RdYFqn/BnCnQEnMrSIzh9D5zWoyd2EP\ncc/1xHk9RB5YlrD95daSi4jymFOpsMgkn0rXbKFm6lElV5SCgee7XVGmt0i7wM5Y\npmCmp8bVVj+oqMtC3Pl74bjFf/k/C8SIGPWMC5oPV6HciK1W6WW2Hj0Ki+W/RkBP\nURXM6xgw9rxm4ifLXCIJfdRi1Hd6pmb3VOMHEN2gKO5x/2H/vaJEDaVtdER2WKMJ\n2RduX7hkAn+WZLcqPjEbAe1C22Fo+KN7An65fJwowdrT6oE=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/client-expired.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCq/a6VZtDGR8jT\nw2Fmy02SMipr8WacwSKgnhvX6ZDjfuV25zT713QkWtJLnjL9xrR9l//kyLdv4hjE\nxc1YiCHpZREWunLc7z+Z89gGg01CL7epkbQxRXcjGG8vozh0AcD5zdxhLXld/Ctl\nrQF3pP6EEk2Gt307QUPCkbnHCGR8PMCfDfycVedREAC0XGdV45Lcwk4JC7id48i1\npIJj0+Bcvw7IEnbTLy812EtW3t66IVwWn7hiBkY+1dNv5n2KmLXtlqMeOmGeJ/Fo\n8WMuBmnPSTP5TPF5tm11PgRTIlHtIC9g4dTOrCXK8KCOhY6dBqJbf+qa3KY/nf+J\nKKBbF3qVAgMBAAECggEAPBREamQkyPZh/t3wdEDMsaiEtUatijhmJU9IczWy3ewx\nTfTw7egG+9sZds5QFlDiDBsDI2zO3zXvA/yIKSoz2CDVv0mloDFEBKDj723lEHNZ\nse/rA0DoGmG0d2V/KWuQVXVakJ58vWQkD2aZVGOZtegEa2g/TTmiSFQRlXhCblAL\nCEGKsp8H8QQePqirFcBOlHzZuLu4SVs49sQl6NYCX5U+PwMF7MQ5bqzmKqL/+Xq7\np+OMNL0tUp0xOSjC7hDoNM3La/bxu5HY92dV9jPY34QstuZzaRR8I/wO1dPc4tQZ\nualJyNEm4PRdonLKv+MBy+YTnjI6JMNIFQ4rmozJZQKBgQDeGkcZd9PFhGce75mn\n8McrKkqLmEFQvJD7gUq3YJqDgIkN4XxaTmBqEFewX2fCK4IybBxBBVTixy84sM2Z\nH7BkzjNgW5HkddudixJFPP6R/PXykeEkO+XE6EkeeDJRmnwUi7ZaVVeQ8sDsHbOE\nA7Eq7lTJ3rUdd9AolO7tCfU7QwKBgQDFFm40VL/qi6fbMIibR/IZiHkAmj4SIYVL\nDawQtSig9W3v3bYyZm7voIu/KK4aOm7bMVIXyGH8zPiTI2YAXl5vocW8boSJ5Gix\ndg3W2dpfQriwHC1DUwzpg/AvpH/AkWvKAw/vifDDBmBZFIISZVRWB+bRIahqEwqy\nrJq5xKOZRwKBgQCDgWOfvMdzJ9Y3Bv8f5PzInh3NUbU6rKvbfs5SjaxvOGfuBBix\nD78ejdad45935HMOj9ya0yFTtURMeMMDazPyO/VHlHBpqS8DtRh4Tokcv36Qxbdc\n0OpXEIJavChvEN1u/NpX2jgi5tk79MoZ3GXGWZ9yd58dd5eUr7pYN5EwKQKBgFML\nu44jc+bByA4NKlK8AyCNJ+eAFs2PAFp6vVkg7Ki+If/jnWUpUm94Z8o5uvrkSlfk\nNWI+FkPunoNpdA6NtR82vFpE+2YbL54vT2+Lxn9DXw0eIlhvA07WQHvixc3/uLqb\nhbh6mE+lPS3r/U8BEYNauwC+PPfNZEGbh2rll8X7AoGAPr2LA4+11Ki1X0tdnF6+\nP0Gye/m7Up/pPHHxszG64GLsQWEnvbG64CtYZCOZaNDIh70p2leDJbMRIOxjOF9U\n58gd+A8Rogd5HacEiU+pd22+eDG6hP+8Gww7gcShRHNv1jrgpKl3BrFW/oihQuLJ\nvHcy85UX7q1bNoZW/ZjLErQ=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/client-revoked.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 6 (0x6)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA\n        Validity\n            Not Before: Jul  7 22:27:29 2025 GMT\n            Not After : Jul  6 22:27:29 2030 GMT\n        Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=test client revoked\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:ae:b6:68:e8:c4:ef:75:5c:6c:db:bb:a2:04:78:\n                    49:8f:b5:81:9d:6f:e7:6d:60:09:fb:ad:cf:d3:60:\n                    14:b2:de:5b:f2:18:fb:1d:8a:40:4e:b0:7c:37:7b:\n                    df:30:0f:c9:a6:56:5e:da:a6:ba:82:6f:7a:64:6b:\n                    fe:7d:c0:64:27:13:bb:1e:ba:40:3b:bf:15:2d:ce:\n                    ff:fe:1f:80:33:12:1f:a8:d2:59:cf:72:94:a4:03:\n                    07:bc:0d:25:ca:53:76:6b:9c:76:7f:c7:1b:45:24:\n                    39:33:04:6e:53:54:d3:f5:c4:f2:b6:52:d0:e2:aa:\n                    ac:6b:2b:52:d6:09:70:00:3d:ce:62:5c:e5:61:e7:\n                    a8:cb:1e:6a:93:f0:0e:17:6d:a6:ed:81:1b:38:34:\n                    85:94:a0:36:7c:78:44:cd:7b:a4:91:00:94:5e:9b:\n                    2b:c3:62:62:fe:ed:63:60:4d:05:73:b0:a7:dc:12:\n                    7b:04:b1:18:88:f4:75:36:cd:60:6b:67:81:99:9a:\n                    8d:17:6a:99:41:a8:2d:fe:07:64:65:f1:ad:6c:21:\n                    79:d7:5b:35:00:de:94:6c:c3:9e:dd:6d:17:b5:e0:\n                    26:e1:71:a0:78:b3:ec:96:13:d8:2f:6b:62:50:ba:\n                    ea:0b:70:04:17:6d:a4:79:03:88:e8:33:ce:9d:b2:\n                    73:c5\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            Netscape Comment: \n                OpenSSL Generated Certificate\n            X509v3 Subject Key Identifier: \n                52:D2:DC:93:96:71:8E:C1:AD:9F:B4:D8:A7:6D:DB:08:C5:1C:C7:97\n            X509v3 Authority Key Identifier: \n                C8:63:DE:29:66:24:95:5D:B2:37:6D:F6:A2:01:85:AE:31:0F:8E:45\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        9e:24:32:86:72:08:e7:79:8f:6c:21:15:52:b8:1f:01:75:a5:\n        7d:a8:60:1e:f8:da:39:3d:0c:f9:8a:e1:1e:97:81:4d:76:27:\n        fa:97:a1:70:2c:79:26:ca:e6:f0:20:ec:1a:fd:2e:dc:73:58:\n        d7:24:d1:ef:c8:f4:2c:e9:4f:29:d5:a5:67:a0:b0:c3:e9:ec:\n        8e:67:9d:b6:02:cb:1b:c8:8d:cf:7b:80:65:f8:65:b2:bd:f1:\n        96:b9:9d:34:ba:18:51:b4:0b:e7:95:99:80:b9:46:19:d7:d2:\n        ab:a2:fb:67:1f:82:5d:73:66:40:0b:4b:b6:63:7a:08:6e:58:\n        9c:51:b8:c3:05:c3:84:52:cd:2b:20:3c:03:79:a8:bf:2e:fe:\n        2e:d6:50:9c:5e:1f:22:56:12:e8:f5:ab:01:18:41:be:56:21:\n        f6:53:15:83:8a:de:3a:66:f2:f4:0f:25:91:f7:93:85:25:e5:\n        80:36:45:15:89:a8:46:bb:c3:ba:d4:71:3d:f6:ee:b0:29:9c:\n        f9:e1:60:62:d8:f0:f6:4c:3e:71:66:fb:85:53:34:fb:f4:a7:\n        20:e5:43:ca:84:32:d8:5b:44:ab:9d:45:f4:3e:90:fa:0a:8a:\n        47:26:c4:85:99:e5:df:4c:68:eb:a2:8c:60:36:8b:07:00:ce:\n        3b:63:41:4a\n-----BEGIN CERTIFICATE-----\nMIID3zCCAsegAwIBAgIBBjANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET\nMBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx\nEDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMjUwNzA3\nMjIyNzI5WhcNMzAwNzA2MjIyNzI5WjCBgDELMAkGA1UEBhMCR0IxGDAWBgNVBAgM\nD05vdHRpbmdoYW1zaGlyZTETMBEGA1UEBwwKTm90dGluZ2hhbTEPMA0GA1UECgwG\nU2VydmVyMRMwEQYDVQQLDApQcm9kdWN0aW9uMRwwGgYDVQQDDBN0ZXN0IGNsaWVu\ndCByZXZva2VkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArrZo6MTv\ndVxs27uiBHhJj7WBnW/nbWAJ+63P02AUst5b8hj7HYpATrB8N3vfMA/JplZe2qa6\ngm96ZGv+fcBkJxO7HrpAO78VLc7//h+AMxIfqNJZz3KUpAMHvA0lylN2a5x2f8cb\nRSQ5MwRuU1TT9cTytlLQ4qqsaytS1glwAD3OYlzlYeeoyx5qk/AOF22m7YEbODSF\nlKA2fHhEzXukkQCUXpsrw2Ji/u1jYE0Fc7Cn3BJ7BLEYiPR1Ns1ga2eBmZqNF2qZ\nQagt/gdkZfGtbCF511s1AN6UbMOe3W0XteAm4XGgeLPslhPYL2tiULrqC3AEF22k\neQOI6DPOnbJzxQIDAQABo34wfDAMBgNVHRMBAf8EAjAAMCwGCWCGSAGG+EIBDQQf\nFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUUtLck5Zx\njsGtn7TYp23bCMUcx5cwHwYDVR0jBBgwFoAUyGPeKWYklV2yN232ogGFrjEPjkUw\nDQYJKoZIhvcNAQELBQADggEBAJ4kMoZyCOd5j2whFVK4HwF1pX2oYB742jk9DPmK\n4R6XgU12J/qXoXAseSbK5vAg7Br9LtxzWNck0e/I9CzpTynVpWegsMPp7I5nnbYC\nyxvIjc97gGX4ZbK98Za5nTS6GFG0C+eVmYC5RhnX0qui+2cfgl1zZkALS7Zjeghu\nWJxRuMMFw4RSzSsgPAN5qL8u/i7WUJxeHyJWEuj1qwEYQb5WIfZTFYOK3jpm8vQP\nJZH3k4Ul5YA2RRWJqEa7w7rUcT327rApnPnhYGLY8PZMPnFm+4VTNPv0pyDlQ8qE\nMthbRKudRfQ+kPoKikcmxIWZ5d9MaOuijGA2iwcAzjtjQUo=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/client-revoked.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCutmjoxO91XGzb\nu6IEeEmPtYGdb+dtYAn7rc/TYBSy3lvyGPsdikBOsHw3e98wD8mmVl7aprqCb3pk\na/59wGQnE7seukA7vxUtzv/+H4AzEh+o0lnPcpSkAwe8DSXKU3ZrnHZ/xxtFJDkz\nBG5TVNP1xPK2UtDiqqxrK1LWCXAAPc5iXOVh56jLHmqT8A4XbabtgRs4NIWUoDZ8\neETNe6SRAJRemyvDYmL+7WNgTQVzsKfcEnsEsRiI9HU2zWBrZ4GZmo0XaplBqC3+\nB2Rl8a1sIXnXWzUA3pRsw57dbRe14CbhcaB4s+yWE9gva2JQuuoLcAQXbaR5A4jo\nM86dsnPFAgMBAAECggEAF5OuHJtW5TOPzSdI+elxo98OmrxbMrtfHYObJB83K9wt\n9EHCwX3Cp9vRJ3uj5sx6nePR8RfG24tHrP2V3kp0OYHEIqVnvahPp1rj2NtPZZTw\niMu6KvB/dpKiHJJ5oxAYTvVSWHP6Dh6RSX0zljNAV044oroSTkRR+DRkfVXQs3dq\na21GlnzhTWvYqz91wPoIczBrJMTh1HUQeyK3a/Po0YHbif4ucUQq0uX1jZ09+evG\nKtQaD1ijRkoGgD7E+4LThGOVcMAYha7mufqPeuOH9vhuQL3LbmtmudIi9CF3E/jR\n+mVfSYQjU4Gi/h16L4v1z1kJjbUN8ifC/7NIqG32EQKBgQDoAjMhFpe00HIgfLhw\nCZpkmfOELuBquOtCKTLAMzowE0fKuIPnYUVXxr9iyGYG4xaF2CCECrLUKCfqbRuP\nMa6wtuJQa4vKNg3pn5rIrZe2rH2UwGh4XisJxxtAZyqTRDaKIcFZYeMAAUxpNlrq\nTpHKy3HnrNaYijdDHbrPwUofIwKBgQDAx3Ox5CndrpG21riwODVnOfCEtUShXH3Q\nT3lQxP/mCO7x1JYnkaAcCQhb9bkKKuR2z23UJC0zKEIoEtVxhXNfjhtpWsbN86bg\nMIlH2Zw51NE/QIN2drynyhrbj0HYdkwAdkzFGFaD7A2G1O0Hqumh/a5szqsWYFVU\nDEm+uXsD9wKBgGH569WhUNeO32NQyCKoK4cobGn50dO/27nI5CG+gGgk/EBjw3BG\n55211MTGlC98XtqO9sxMKFDn1FNvWCAUfw0pblE/2Xy/bwil2hu9E0CVf0L+LiAG\nxG4QozWDW7ttJwsWTiyM5evuoHId/i7Ml0zotWV82/L3C3dQar+phL+5AoGAO3UI\nrOQXOYUe8gp1ufwMFINdOEEEItR5BWeNnii0WEmHENUlXpzeiecLSfmWkZk7D53Y\nXOavfii7hsqQREwJkn4s3CigSmMMo/a0UJHASmHmC6ElKsNiWknOUMt1XoLV3Aqg\nkOV5wYRrg5tmY8gF+O1Z/7saL3OUvbBwij+Avm8CgYBFA5dOfQOEQX7FblFSyXPE\nlLnObDCCJ0E8YOCXpIc9KDJVbfPzB4rX++79Osb0NMQSYktAUc0CdWcGEdHlXTAA\n60ASxq6yg8Y6CbbB8+gnyqP7zBlgojzEViGQBHNu9QW9sGHl1bewWIoQkd/uV7yu\nhzCM+wks1pHTq+qliobDtw==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/client.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 4 (0x4)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA\n        Validity\n            Not Before: Jul  7 22:27:29 2025 GMT\n            Not After : Jul  6 22:27:29 2030 GMT\n        Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=test client\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:ac:1f:ca:f2:7e:89:98:fe:19:63:e6:c9:d2:0b:\n                    28:95:83:29:55:9a:85:a9:48:5b:a5:6c:0f:a2:ea:\n                    85:93:0a:14:25:2b:5e:aa:f2:2d:e2:d5:a5:37:d0:\n                    e5:e3:1e:4f:32:44:a2:46:6f:81:92:13:4c:a6:7b:\n                    2f:27:7c:5f:ae:db:1d:84:78:da:c4:f0:ba:2d:16:\n                    87:15:b7:8f:37:b2:1a:cf:64:38:49:e6:43:f5:a9:\n                    24:78:2d:39:4d:6f:6c:8d:7d:90:c6:07:ec:74:d0:\n                    69:be:c0:93:4a:cb:8a:cc:ba:ba:76:52:8b:33:33:\n                    43:d8:35:15:d7:a6:3e:52:37:3e:cc:3e:95:c5:0e:\n                    24:03:94:d3:8d:b0:2a:db:f5:1d:58:33:ee:f4:a8:\n                    20:88:1d:50:2f:b4:08:3c:11:6f:0a:08:af:67:26:\n                    60:8b:f4:4b:4d:12:3a:a6:b5:26:08:3f:13:a9:5d:\n                    64:48:25:75:a1:a8:ae:45:90:57:3e:c3:f1:d1:d2:\n                    2f:84:c4:d0:16:e4:fc:56:c4:5f:e2:c4:35:c0:fc:\n                    0c:f8:be:2a:45:49:1e:b8:3f:9f:0b:b3:9b:07:9c:\n                    fc:f9:e3:4f:4a:12:5f:03:f7:a1:40:70:df:8c:5b:\n                    b2:cd:c7:11:ed:b1:ba:4a:96:67:87:30:ac:e8:28:\n                    63:69\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            Netscape Comment: \n                OpenSSL Generated Certificate\n            X509v3 Subject Key Identifier: \n                B4:A4:7F:13:73:10:9B:B7:C4:94:64:22:E3:79:02:B0:99:78:D3:73\n            X509v3 Authority Key Identifier: \n                C8:63:DE:29:66:24:95:5D:B2:37:6D:F6:A2:01:85:AE:31:0F:8E:45\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        0b:53:02:33:dc:40:59:0f:4e:00:7e:7e:8d:b0:5d:65:3f:de:\n        fc:bd:f7:6d:c2:38:55:0c:26:97:d6:04:50:af:94:87:20:1e:\n        d8:cb:8f:22:4f:97:0e:f3:f6:89:34:c3:8d:60:f4:c0:a7:24:\n        23:65:12:10:39:5e:f5:db:10:a1:53:17:b0:90:fa:76:97:00:\n        be:22:2e:9a:8a:e8:a1:f5:b3:44:00:43:33:42:07:90:de:b6:\n        73:a4:c4:24:d7:c2:c0:e8:3f:fb:79:ef:27:2a:93:d8:5b:e7:\n        f7:d4:3b:55:38:b0:0f:59:9a:c2:52:f6:19:75:45:44:6c:1c:\n        ef:35:6b:23:7f:da:fb:07:94:36:12:1b:f7:7f:88:f4:19:66:\n        66:60:5f:58:7d:cd:ff:f6:18:96:8d:34:42:a7:fa:ad:42:da:\n        d7:10:b2:57:cd:48:9f:47:d5:a2:5d:fa:33:0c:46:b1:59:50:\n        03:3c:d8:89:76:d5:2f:b9:48:68:6b:50:c6:e8:0b:8d:a7:4e:\n        0a:0c:b4:0f:d5:79:30:02:41:4e:13:f5:b2:7c:03:02:9a:df:\n        77:80:c6:7d:83:72:0f:02:19:7c:57:cb:b6:7d:d5:50:12:44:\n        d4:b3:2f:a0:27:35:f0:11:f1:54:a9:6f:2f:8a:13:18:49:d9:\n        bb:ee:59:ae\n-----BEGIN CERTIFICATE-----\nMIID1jCCAr6gAwIBAgIBBDANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET\nMBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx\nEDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMjUwNzA3\nMjIyNzI5WhcNMzAwNzA2MjIyNzI5WjB4MQswCQYDVQQGEwJHQjEYMBYGA1UECAwP\nTm90dGluZ2hhbXNoaXJlMRMwEQYDVQQHDApOb3R0aW5naGFtMQ8wDQYDVQQKDAZT\nZXJ2ZXIxEzARBgNVBAsMClByb2R1Y3Rpb24xFDASBgNVBAMMC3Rlc3QgY2xpZW50\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArB/K8n6JmP4ZY+bJ0gso\nlYMpVZqFqUhbpWwPouqFkwoUJSteqvIt4tWlN9Dl4x5PMkSiRm+BkhNMpnsvJ3xf\nrtsdhHjaxPC6LRaHFbePN7Iaz2Q4SeZD9akkeC05TW9sjX2QxgfsdNBpvsCTSsuK\nzLq6dlKLMzND2DUV16Y+Ujc+zD6VxQ4kA5TTjbAq2/UdWDPu9KggiB1QL7QIPBFv\nCgivZyZgi/RLTRI6prUmCD8TqV1kSCV1oaiuRZBXPsPx0dIvhMTQFuT8VsRf4sQ1\nwPwM+L4qRUkeuD+fC7ObB5z8+eNPShJfA/ehQHDfjFuyzccR7bG6SpZnhzCs6Chj\naQIDAQABo34wfDAMBgNVHRMBAf8EAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM\nIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUtKR/E3MQm7fElGQi43kC\nsJl403MwHwYDVR0jBBgwFoAUyGPeKWYklV2yN232ogGFrjEPjkUwDQYJKoZIhvcN\nAQELBQADggEBAAtTAjPcQFkPTgB+fo2wXWU/3vy9923COFUMJpfWBFCvlIcgHtjL\njyJPlw7z9ok0w41g9MCnJCNlEhA5XvXbEKFTF7CQ+naXAL4iLpqK6KH1s0QAQzNC\nB5DetnOkxCTXwsDoP/t57ycqk9hb5/fUO1U4sA9ZmsJS9hl1RURsHO81ayN/2vsH\nlDYSG/d/iPQZZmZgX1h9zf/2GJaNNEKn+q1C2tcQslfNSJ9H1aJd+jMMRrFZUAM8\n2Il21S+5SGhrUMboC42nTgoMtA/VeTACQU4T9bJ8AwKa33eAxn2Dcg8CGXxXy7Z9\n1VASRNSzL6AnNfAR8VSpby+KExhJ2bvuWa4=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/client.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsH8ryfomY/hlj\n5snSCyiVgylVmoWpSFulbA+i6oWTChQlK16q8i3i1aU30OXjHk8yRKJGb4GSE0ym\ney8nfF+u2x2EeNrE8LotFocVt483shrPZDhJ5kP1qSR4LTlNb2yNfZDGB+x00Gm+\nwJNKy4rMurp2UoszM0PYNRXXpj5SNz7MPpXFDiQDlNONsCrb9R1YM+70qCCIHVAv\ntAg8EW8KCK9nJmCL9EtNEjqmtSYIPxOpXWRIJXWhqK5FkFc+w/HR0i+ExNAW5PxW\nxF/ixDXA/Az4vipFSR64P58Ls5sHnPz5409KEl8D96FAcN+MW7LNxxHtsbpKlmeH\nMKzoKGNpAgMBAAECggEACoacMtWdpQM++r7Bj0x65B/D/pXnQBmqbxSLZT9ZwUrv\nvfEnxiTuvO0rQB1QfaHcHnsXgF6vygbPnGuyj8PZlxgTt0bru/jrrbevtZqG0dVc\nsduTXKON+t8n2YiMdUmP1hw8ZvvTkDYxjUZ6C2wklg4COpukIxKqvrVKW2hxbbYU\n+TdnxWDcuBbQRKwVGrFGxn9+uyJLC+VigwTVP05jNh7xcbn0cvctlRDTychFEkrK\nadg75g8oum11pW9Pq7pS1OMhofLuk4mPH29ybyvr9MxiG627pF8lv1lk6BQmN9OZ\nyY8EMzlaomPFqEkJYSYiF4cFmV4LPgporUn0olaFXQKBgQDauPHT/uyDttTKN2nj\nAP5c2u5L/Bee2vBXUhydgOrZQi+9Y17TVNP787nMRtw1sMlrIKG48PsBbVQGsgjG\n5JquYSxM7GcyGueVYVNOM2Tw9ZO4Wxvknc+RZ4NTRAptM4KAK/RYz9zHZWa2mbmc\ntOtHEov1bbNm35lItcNmKJ2q/QKBgQDJdbmSJu1imTU5/uKp+83LF1suIhEHrT5y\nu0zCshqG/iX52EEYzlRBFsrLf7vibbqyBu1ZduBvWCMlUssvpB1AO5XNkyPCdapa\nU9y9pfWoPF3wziBHiCgummRKR9MwzOGM8GASM0V1Zenw7GR3iyd1cj66bNJKygr7\nzR9qiDcT3QKBgDlIbq1i+naUj65WTPkS7YtMG1TzNQx5srBr5OqrNNapqu8i81bN\nxKcb8fE6LboyDs5rwW86TcLV60fFoN7WSFybPor27yAEQ9qvnq3AcBNbfdCuq+N4\nIUCnp5FLJJ/s+aSv3lLUPbJLMFdqc15DU8tNZDJnBLFQpkiQshgzUvfRAoGBAKpg\nLoWk+EPXsEURA56gfuWQJiO42dA1OsgLERrjRz39OB65Pix9apH1daJur3YKOMcQ\nxrBPsfVYg7iv8XikAbzt534JP0fY/S4RGHEnJr+V9hiOKox0YQ8wsTqEzd3Kl8H7\nFDSwOcDUZOnE8h6Lh95ytQwythJcFrfnPPd6paHdAoGAZt4wAqga7a50WFq+TTDo\nVYNOQq6NhSuxMBuuGekhXsjQcwVf2NZ1idahkip3SusgYt5gaQrIH93YA1EEu/hv\noek/o898C7MH5myyonknErN5jAhb3Az6sDt9C3FEXia7BO2l4BalX88ujTUgfgYr\nzb89JL9s+Y9g3RiWdDvx3sw=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/crl-empty.pem",
    "content": "-----BEGIN X509 CRL-----\nMIIBwDCBqQIBATANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjETMBEGA1UE\nCAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3QxEDAOBgNV\nBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EXDTI1MDcwNzIyMjcyOVoY\nDzIxMDcwODI3MjIyNzI5WqAOMAwwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQAD\nggEBAHdkW722SMyoq3g1yYwmAE+KaPbo5PdhZBF7evGmZAfOlIXAgQGSJIVZLcjV\nR83mG11aN8HaWg05E75sAlBoP/fVnw6Ik7ubQA2aX2XrBkZwHsrkau1uuG9oeppP\nm6A9N+J2ZevBY3exnwsJYKfZlqWWqRKpr9upySPKcmIKxwawME4SREejmeObJs6H\n4Ig0QjUtBm5LXJk9t0KiBefz6yk8wAgw7ztpReBLhMzOyuVYBLLjaEufVvLS2yOt\n9D0rdd6/uZxFoze+z7yoVl11ksbIBoMfArxk95uI8zlpKsrjL/jGhTIaecPVu2GT\nYNCETZ5BQ+wy3d2Ho8Q/VVewbJU=\n-----END X509 CRL-----\n"
  },
  {
    "path": "test/ssl/crl.pem",
    "content": "-----BEGIN X509 CRL-----\nMIIB1jCBvwIBATANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjETMBEGA1UE\nCAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3QxEDAOBgNV\nBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EXDTI1MDcwNzIyMjcyOVoY\nDzIxMDcwODI3MjIyNzI5WjAUMBICAQYXDTI1MDcwNzIyMjcyOVqgDjAMMAoGA1Ud\nFAQDAgECMA0GCSqGSIb3DQEBCwUAA4IBAQAFMdOne9AVg3pLPClB2DFRUJYr3BJU\nvynwJlbm8koDrnHRPf3m3y36fM80sjTFLtE6j3S+2duSItDDx3kZU9Rpy7RGqRu/\n3torSc+MgQkZKLOJKbk8qJx5/zNGTUAxL1abTSdqAvGpA2gRryXbzg6wnQwshViO\nb4kb346Ag+Oa1Wwl703pCpbgRlltOTGY9UXIY/3+a04aNDUOqYUmJ1uv+i5dS2DZ\n+CAnKnBSKdjRsDxx2Xxou6aEKnSog7E8L23C9j3EFo5iL7bh27I9pUbAV7lvC2QZ\nknLKSNDjCvir1556M4Nx4FzirDMqP0OWTqmnVtZG3dIryXgH1dFirt9j\n-----END X509 CRL-----\n"
  },
  {
    "path": "test/ssl/dhparam",
    "content": "-----BEGIN DH PARAMETERS-----\nMIIBDAKCAQEAkfphfOyL/LKjILqlnFmwbxwZYQr7lx3nlxiSqy4g25j/hmADTrJH\nYzpwrcbNpXCUl6D4b7RtJzNMLwoavemYSdtOeVygMnDHDD1TSnO0ywZ4wlskPNta\nFL5DMJRDCrc90pLb3kU2fPr2OZLImXRr9WlxF1xipQZqpdcpJ9jjtwZgArc7yotV\nb6gOVOJa0K3KDb3NJrQD0FD4ZltcueQEfrhBirjfH1Zpb+oe4rHNDd/EDbHFqu1W\nFrl7igOZs3NJM64rl7dmPWHF9iYp7y0cAZRFD7n9ViKtE8jTSfPfn7jQf+pNRIV+\nMwKOk59F6jSRIFPT5Jv9zalagd5A6bLpDwIBAgICAOE=\n-----END DH PARAMETERS-----\n"
  },
  {
    "path": "test/ssl/gen.sh",
    "content": "# This file generates the keys and certificates used for testing mosquitto.\n# None of the keys are encrypted, so do not just use this script to generate\n# files for your own use.\n\nrm -f *.crt *.key *.csr\nfor a in root signing; do\n\trm -rf ${a}CA/\n\tmkdir -p ${a}CA/newcerts\n\ttouch ${a}CA/index.txt\n\techo 01 > ${a}CA/serial\n\techo 01 > ${a}CA/crlnumber\ndone\nrm -rf certs\n\nBASESUBJ=\"/C=GB/ST=Derbyshire/L=Derby/O=Mosquitto Project/OU=Testing\"\nSBASESUBJ=\"/C=GB/ST=Nottinghamshire/L=Nottingham/O=Server/OU=Production\"\nBBASESUBJ=\"/C=GB/ST=Nottinghamshire/L=Nottingham/O=Server/OU=Bridge\"\n\n# The root CA\nopenssl genrsa -out test-root-ca.key 2048\nopenssl req -new -x509 -days 3650 -key test-root-ca.key -out test-root-ca.crt -config openssl.cnf -subj \"${BASESUBJ}/CN=Root CA/\"\n\n# Another root CA that doesn't sign anything\nopenssl genrsa -out test-bad-root-ca.key 2048\nopenssl req -new -x509 -days 3650 -key test-bad-root-ca.key -out test-bad-root-ca.crt -config openssl.cnf -subj \"${BASESUBJ}/CN=Bad Root CA/\"\n\n# This is a root CA that has the exact same details as the real root CA, but is a different key and certificate. Effectively a \"fake\" CA.\nopenssl genrsa -out test-fake-root-ca.key 2048\nopenssl req -new -x509 -days 3650 -key test-fake-root-ca.key -out test-fake-root-ca.crt -config openssl.cnf -subj \"${BASESUBJ}/CN=Root CA/\"\n\n# An intermediate CA, signed by the root CA, used to sign server/client csrs.\nopenssl genrsa -out test-signing-ca.key 2048\nopenssl req -out test-signing-ca.csr -key test-signing-ca.key -new -config openssl.cnf -subj \"${BASESUBJ}/CN=Signing CA/\"\nopenssl ca -batch -config openssl.cnf -name CA_root -extensions v3_ca -out test-signing-ca.crt -infiles test-signing-ca.csr\nrm -f test-signing-ca.csr\n\n# An alternative intermediate CA, signed by the root CA, not used to sign anything.\nopenssl genrsa -out test-alt-ca.key 2048\nopenssl req -out test-alt-ca.csr -key test-alt-ca.key -new -config openssl.cnf -subj \"${BASESUBJ}/CN=Alternative Signing CA/\"\nopenssl ca -batch -config openssl.cnf -name CA_root -extensions v3_ca -out test-alt-ca.crt -infiles test-alt-ca.csr\nrm -f test-alt-ca.csr\n\n# Valid server key and certificate.\nopenssl genrsa -out server.key 2048\nopenssl req -new -key server.key -out server.csr -config openssl.cnf -subj \"${SBASESUBJ}/CN=localhost/\"\nopenssl ca -batch -config openssl.cnf -name CA_signing -out server.crt -infiles server.csr\nrm -f server.csr\n\n# Valid server key and certificate, with subjectAltName.\nopenssl genrsa -out server-san.key 2048\nopenssl req -new -key server-san.key -out server-san.csr -config openssl.cnf -subj \"${SBASESUBJ}/CN=san/\"\nopenssl ca -batch -config openssl.cnf -name CA_signing -out server-san.crt -extensions test_SAN -infiles server-san.csr\nrm -f server-san.csr\n\n# Expired server certificate\nopenssl genrsa -out server-expired.key 2048\nopenssl req -new -key server-expired.key -out server-expired.csr -config openssl.cnf -subj \"${SBASESUBJ}-expired/CN=localhost/\"\nopenssl ca -batch -config openssl.cnf -name CA_signing -days 1 -startdate 120820000000Z -enddate 120821000000Z -out server-expired.crt -infiles server-expired.csr\nrm -f server-expired.csr\n\n# Valid client key and certificate.\nopenssl genrsa -out client.key 2048\nopenssl req -new -key client.key -out client.csr -config openssl.cnf -subj \"${SBASESUBJ}/CN=test client/\"\nopenssl ca -batch -config openssl.cnf -name CA_signing -out client.crt -infiles client.csr\nrm -f client.csr\n\n# Expired client certificate\nopenssl genrsa -out client-expired.key 2048\nopenssl req -new -key client-expired.key -out client-expired.csr -config openssl.cnf -subj \"${SBASESUBJ}/CN=test client expired/\"\nopenssl ca -batch -config openssl.cnf -name CA_signing -days 1 -startdate 120820000000Z -enddate 120821000000Z -out client-expired.crt -infiles client-expired.csr\nrm -f client-expired.csr\n\n# Empty CRL file\nopenssl ca -batch -config openssl.cnf -name CA_signing -gencrl -out crl-empty.pem\n\n# Revoked client certificate\nopenssl genrsa -out client-revoked.key 2048\nopenssl req -new -key client-revoked.key -out client-revoked.csr -config openssl.cnf -subj \"${SBASESUBJ}/CN=test client revoked/\"\nopenssl ca -batch -config openssl.cnf -name CA_signing -out client-revoked.crt -infiles client-revoked.csr\nopenssl ca -batch -config openssl.cnf -name CA_signing -revoke client-revoked.crt\nopenssl ca -batch -config openssl.cnf -name CA_signing -gencrl -out crl.pem\nrm -f client-revoked.csr\n\n# Valid client key and certificate, encrypted (use \"password\" as password)\nopenssl genrsa -des3 -out client-encrypted.key -passout pass:password 2048\nopenssl req -new -key client-encrypted.key -out client-encrypted.csr -config openssl.cnf -subj \"${SBASESUBJ}/CN=test client encrypted/\" -passin pass:password\nopenssl ca -batch -config openssl.cnf -name CA_signing -out client-encrypted.crt -infiles client-encrypted.csr\nrm -f client-encrypted.csr\n\ncat test-signing-ca.crt test-root-ca.crt > all-ca.crt\n#mkdir certs\n#cp test-signing-ca.crt certs/test-signing-ca.pem\n#cp test-root-ca.crt certs/test-root.ca.pem\n#openssl rehash certs\n\nopenssl dhparam -out dhparam 2048\n"
  },
  {
    "path": "test/ssl/openssl.cnf",
    "content": "#\n# OpenSSL example configuration file.\n# This is mostly being used for generation of certificate requests.\n#\n\n# This definition stops the following lines choking if HOME isn't\n# defined.\nHOME\t\t\t= .\nRANDFILE\t\t= $ENV::HOME/.rnd\n\n# Extra OBJECT IDENTIFIER info:\n#oid_file\t\t= $ENV::HOME/.oid\noid_section\t\t= new_oids\n\n# To use this configuration file with the \"-extfile\" option of the\n# \"openssl x509\" utility, name here the section containing the\n# X.509v3 extensions to use:\n# extensions\t\t=\n# (Alternatively, use a configuration file that has only\n# X.509v3 extensions in its main [= default] section.)\n\n[ new_oids ]\n\n# We can add new OIDs in here for use by 'ca', 'req' and 'ts'.\n# Add a simple OID like this:\n# testoid1=1.2.3.4\n# Or use config file substitution like this:\n# testoid2=${testoid1}.5.6\n\n# Policies used by the TSA examples.\ntsa_policy1 = 1.2.3.4.1\ntsa_policy2 = 1.2.3.4.5.6\ntsa_policy3 = 1.2.3.4.5.7\n\n####################################################################\n[ ca ]\ndefault_ca\t= CA_default\t\t# The default ca section\n\n####################################################################\n[ CA_signing ]\n\ndir\t\t= ./signingCA\t\t# Where everything is kept\ncerts\t\t= $dir/certs\t\t# Where the issued certs are kept\ncrl_dir\t\t= $dir/crl\t\t# Where the issued crl are kept\ndatabase\t= $dir/index.txt\t# database index file.\n#unique_subject\t= no\t\t\t# Set to 'no' to allow creation of\n\t\t\t\t\t# several ctificates with same subject.\nnew_certs_dir\t= $dir/newcerts\t\t# default place for new certs.\n\ncertificate\t= test-signing-ca.crt \t# The CA certificate\nserial\t\t= $dir/serial \t\t# The current serial number\ncrlnumber\t= $dir/crlnumber\t# the current crl number\n\t\t\t\t\t# must be commented out to leave a V1 CRL\ncrl\t\t= $dir/crl.pem \t\t# The current CRL\nprivate_key\t= test-signing-ca.key # The private key\nRANDFILE\t= $dir/.rand\t# private random number file\n\nx509_extensions\t= usr_cert\t\t# The extentions to add to the cert\n\n# Comment out the following two lines for the \"traditional\"\n# (and highly broken) format.\nname_opt \t= ca_default\t\t# Subject Name options\ncert_opt \t= ca_default\t\t# Certificate field options\n\n# Extension copying option: use with caution.\n# copy_extensions = copy\n\n# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs\n# so this is commented out by default to leave a V1 CRL.\n# crlnumber must also be commented out to leave a V1 CRL.\n# crl_extensions\t= crl_ext\n\ndefault_days\t= 1825\t\t\t# how long to certify for\ndefault_crl_days= 30000\t\t\t# how long before next CRL\ndefault_md\t= default\t\t# use public key default MD\npreserve\t= no\t\t\t# keep passed DN ordering\n\n# A few difference way of specifying how similar the request should look\n# For type CA, the listed attributes must be the same, and the optional\n# and supplied fields are just that :-)\npolicy\t\t= policy_anything\n\n[ CA_inter ]\ndir = ./interCA\ncerts = $dir/certs\ncrl_dir = $dir/crl\ndatabase = $dir/index.txt\nnew_certs_dir = $dir/newcerts\n\ncertificate = test-inter-ca.crt\nserial = $dir/serial\ncrlnumber = $dir/crlnumber\ncrl = $dir/crl.pem\nprivate_key = test-inter-ca.key\nRANDFILE = $dir/.rand\n\n#x509_extensions = v3_ca\nx509_extensions = usr_cert\n\nname_opt = ca_default\ncert_opt = ca_default\n\ndefault_days = 1825\ndefault_crl_days = 30\ndefault_md = default\npreserve = no\n\npolicy = policy_match\nunique_subject = yes\n\n[ CA_root ]\ndir = ./rootCA\ncerts = $dir/certs\ncrl_dir = $dir/crl\ndatabase = $dir/index.txt\nnew_certs_dir = $dir/newcerts\n\ncertificate = test-root-ca.crt\nserial = $dir/serial\ncrlnumber = $dir/crlnumber\ncrl = $dir/crl.pem\nprivate_key = test-root-ca.key\nRANDFILE = $dir/.rand\n\nx509_extensions = v3_ca\n\nname_opt = ca_default\ncert_opt = ca_default\n\ndefault_days = 1825\ndefault_crl_days = 30\ndefault_md = default\npreserve = no\n\npolicy = policy_match\nunique_subject = yes\n\n# For the CA policy\n[ policy_match ]\ncountryName\t\t= match\nstateOrProvinceName\t= match\norganizationName\t= match\norganizationalUnitName\t= optional\ncommonName\t\t= supplied\nemailAddress\t\t= optional\n\n# For the 'anything' policy\n# At this point in time, you must list all acceptable 'object'\n# types.\n[ policy_anything ]\ncountryName\t\t= optional\nstateOrProvinceName\t= optional\nlocalityName\t\t= optional\norganizationName\t= optional\norganizationalUnitName\t= optional\ncommonName\t\t= supplied\nemailAddress\t\t= optional\n\n####################################################################\n[ req ]\ndefault_bits\t\t= 2048\ndefault_keyfile \t= privkey.pem\ndistinguished_name\t= req_distinguished_name\nattributes\t\t= req_attributes\nx509_extensions\t= v3_ca\t# The extentions to add to the self signed cert\n\n# Passwords for private keys if not present they will be prompted for\n# input_password = secret\n# output_password = secret\n\n# This sets a mask for permitted string types. There are several options.\n# default: PrintableString, T61String, BMPString.\n# pkix\t : PrintableString, BMPString (PKIX recommendation before 2004)\n# utf8only: only UTF8Strings (PKIX recommendation after 2004).\n# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).\n# MASK:XXXX a literal mask value.\n# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.\nstring_mask = utf8only\n\n# req_extensions = v3_req # The extensions to add to a certificate request\n\n[ req_distinguished_name ]\ncountryName\t\t\t= Country Name (2 letter code)\ncountryName_default\t\t= GB\ncountryName_min\t\t\t= 2\ncountryName_max\t\t\t= 2\n\nstateOrProvinceName\t\t= State or Province Name (full name)\nstateOrProvinceName_default\t= Derbyshire\n\nlocalityName\t\t\t= Locality Name (eg, city)\nlocalityName_default\t= Derby\n\n0.organizationName\t\t= Organization Name (eg, company)\n0.organizationName_default\t= Mosquitto Project\n\n# we can do this but it is not needed normally :-)\n#1.organizationName\t\t= Second Organization Name (eg, company)\n#1.organizationName_default\t= World Wide Web Pty Ltd\n\norganizationalUnitName\t\t= Organizational Unit Name (eg, section)\norganizationalUnitName_default\t= Testing\n\ncommonName\t\t\t= Common Name (e.g. server FQDN or YOUR name)\ncommonName_max\t\t\t= 64\n\nemailAddress\t\t\t= Email Address\nemailAddress_max\t\t= 64\n\n# SET-ex3\t\t\t= SET extension number 3\n\n[ req_attributes ]\nchallengePassword\t\t= A challenge password\nchallengePassword_min\t\t= 4\nchallengePassword_max\t\t= 20\n\nunstructuredName\t\t= An optional company name\n\n[ usr_cert ]\n\n# These extensions are added when 'ca' signs a request.\n\n# This goes against PKIX guidelines but some CAs do it and some software\n# requires this to avoid interpreting an end user certificate as a CA.\n\nbasicConstraints=critical,CA:FALSE\n\n# Here are some examples of the usage of nsCertType. If it is omitted\n# the certificate can be used for anything *except* object signing.\n\n# This is OK for an SSL server.\n# nsCertType\t\t\t= server\n\n# For an object signing certificate this would be used.\n# nsCertType = objsign\n\n# For normal client use this is typical\n# nsCertType = client, email\n\n# and for everything including object signing:\n# nsCertType = client, email, objsign\n\n# This is typical in keyUsage for a client certificate.\n# keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n\n# This will be displayed in Netscape's comment listbox.\nnsComment\t\t\t= \"OpenSSL Generated Certificate\"\n\n# PKIX recommendations harmless if included in all certificates.\nsubjectKeyIdentifier=hash\nauthorityKeyIdentifier=keyid,issuer\n\n# This stuff is for subjectAltName and issuerAltname.\n# Import the email address.\n# subjectAltName=email:copy\n# An alternative to produce certificates that aren't\n# deprecated according to PKIX.\n# subjectAltName=email:move\n\n# Copy subject details\n# issuerAltName=issuer:copy\n\n#nsCaRevocationUrl\t\t= http://www.domain.dom/ca-crl.pem\n#nsBaseUrl\n#nsRevocationUrl\n#nsRenewalUrl\n#nsCaPolicyUrl\n#nsSslServerName\n\n# This is required for TSA certificates.\n# extendedKeyUsage = critical,timeStamping\n\n[ v3_req ]\n\n# Extensions to add to a certificate request\n\nbasicConstraints = critical,CA:FALSE\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment\n\n[ v3_ca ]\n\n\n# Extensions for a typical CA\n\n\n# PKIX recommendation.\n\nsubjectKeyIdentifier=hash\n\nauthorityKeyIdentifier=keyid:always,issuer\n\n# This is what PKIX recommends but some broken software chokes on critical\n# extensions.\nbasicConstraints = critical,CA:true\n\n# Key usage: this is typical for a CA certificate. However since it will\n# prevent it being used as an test self-signed certificate it is best\n# left out by default.\nkeyUsage = cRLSign, keyCertSign\n\n# Some might want this also\n# nsCertType = sslCA, emailCA\n\n# Include email address in subject alt name: another PKIX recommendation\n# subjectAltName=email:copy\n# Copy issuer details\n# issuerAltName=issuer:copy\n\n# DER hex encoding of an extension: beware experts only!\n# obj=DER:02:03\n# Where 'obj' is a standard or added object\n# You can even override a supported extension:\n# basicConstraints= critical, DER:30:03:01:01:FF\n\n[ crl_ext ]\n\n# CRL extensions.\n# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.\n\n# issuerAltName=issuer:copy\nauthorityKeyIdentifier=keyid:always\n\n[ proxy_cert_ext ]\n# These extensions should be added when creating a proxy certificate\n\n# This goes against PKIX guidelines but some CAs do it and some software\n# requires this to avoid interpreting an end user certificate as a CA.\n\nbasicConstraints=critical,CA:FALSE\n\n# Here are some examples of the usage of nsCertType. If it is omitted\n# the certificate can be used for anything *except* object signing.\n\n# This is OK for an SSL server.\n# nsCertType\t\t\t= server\n\n# For an object signing certificate this would be used.\n# nsCertType = objsign\n\n# For normal client use this is typical\n# nsCertType = client, email\n\n# and for everything including object signing:\n# nsCertType = client, email, objsign\n\n# This is typical in keyUsage for a client certificate.\n# keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n\n# This will be displayed in Netscape's comment listbox.\nnsComment\t\t\t= \"OpenSSL Generated Certificate\"\n\n# PKIX recommendations harmless if included in all certificates.\nsubjectKeyIdentifier=hash\nauthorityKeyIdentifier=keyid,issuer\n\n# This stuff is for subjectAltName and issuerAltname.\n# Import the email address.\n# subjectAltName=email:copy\n# An alternative to produce certificates that aren't\n# deprecated according to PKIX.\n# subjectAltName=email:move\n\n# Copy subject details\n# issuerAltName=issuer:copy\n\n#nsCaRevocationUrl\t\t= http://www.domain.dom/ca-crl.pem\n#nsBaseUrl\n#nsRevocationUrl\n#nsRenewalUrl\n#nsCaPolicyUrl\n#nsSslServerName\n\n# This really needs to be in place for it to be a proxy certificate.\nproxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo\n\n####################################################################\n[ tsa ]\n\ndefault_tsa = tsa_config1\t# the default TSA section\n\n[ tsa_config1 ]\n\n# These are used by the TSA reply generation only.\ndir\t\t= ./demoCA\t\t# TSA root directory\nserial\t\t= $dir/tsaserial\t# The current serial number (mandatory)\ncrypto_device\t= builtin\t\t# OpenSSL engine to use for signing\nsigner_cert\t= $dir/tsacert.pem \t# The TSA signing certificate\n\t\t\t\t\t# (optional)\ncerts\t\t= $dir/cacert.pem\t# Certificate chain to include in reply\n\t\t\t\t\t# (optional)\nsigner_key\t= $dir/private/tsakey.pem # The TSA private key (optional)\n\ndefault_policy\t= tsa_policy1\t\t# Policy if request did not specify it\n\t\t\t\t\t# (optional)\nother_policies\t= tsa_policy2, tsa_policy3\t# acceptable policies (optional)\ndigests\t\t= md5, sha1\t\t# Acceptable message digests (mandatory)\naccuracy\t= secs:1, millisecs:500, microsecs:100\t# (optional)\nclock_precision_digits  = 0\t# number of digits after dot. (optional)\nordering\t\t= yes\t# Is ordering defined for timestamps?\n\t\t\t\t# (optional, default: no)\ntsa_name\t\t= yes\t# Must the TSA name be included in the reply?\n\t\t\t\t# (optional, default: no)\ness_cert_id_chain\t= no\t# Must the ESS cert id chain be included?\n\t\t\t\t# (optional, default: no)\n\n[ test_SAN ]\nbasicConstraints = critical,CA:FALSE\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid,issuer\nsubjectAltName = \"IP:127.0.0.1,DNS:localhost,IP:::1\"\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment\n"
  },
  {
    "path": "test/ssl/readme.txt",
    "content": "This directory contains certificates and keys required for SSL testing.\nThe CA key has password \"password\".\n"
  },
  {
    "path": "test/ssl/server-expired.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 3 (0x3)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA\n        Validity\n            Not Before: Aug 20 00:00:00 2012 GMT\n            Not After : Aug 21 00:00:00 2012 GMT\n        Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production-expired, CN=localhost\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:b3:c1:ba:78:89:96:f8:3d:54:e1:fc:56:8d:d0:\n                    2a:83:ae:8e:f1:76:39:4b:18:fb:4e:fc:36:c1:1e:\n                    a3:07:53:71:6a:0e:2e:c1:22:cd:a8:cc:f7:f5:cc:\n                    f8:2e:d4:d1:2e:7d:d0:d2:aa:a7:87:1f:4c:31:0f:\n                    96:28:80:d2:52:ea:25:0c:32:e1:bb:64:81:bf:b4:\n                    7b:11:b5:89:31:20:91:00:26:06:2f:09:f6:a7:c1:\n                    6a:00:73:c7:12:d1:dd:88:33:66:54:f0:d6:7f:15:\n                    1c:74:47:29:c7:03:2a:b9:c1:00:36:83:d9:48:b8:\n                    54:f5:bc:df:57:9f:85:2a:91:03:be:c6:4f:5b:03:\n                    68:aa:f8:e9:25:dd:5a:81:f2:c2:e8:0c:93:a0:05:\n                    d6:d1:77:49:ca:c5:1e:42:27:25:9d:fd:cc:e2:a7:\n                    4b:28:05:e6:f5:1e:b3:97:3d:c4:25:65:f2:5b:cf:\n                    3e:07:19:33:c0:fd:14:b4:a0:53:e1:6c:90:50:22:\n                    76:29:b0:7a:45:2e:33:cd:c7:39:99:12:7e:d9:15:\n                    f9:ec:5f:69:92:d4:1c:2c:57:8a:12:a9:fc:94:da:\n                    04:1a:17:18:7d:82:a3:03:8f:20:e8:f3:be:d2:61:\n                    86:42:49:b5:d5:6e:cd:3d:6d:1d:fb:8b:3e:39:31:\n                    df:95\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            Netscape Comment: \n                OpenSSL Generated Certificate\n            X509v3 Subject Key Identifier: \n                33:48:15:F4:7F:39:F2:A1:26:77:FB:15:D1:A5:DC:CC:A7:E2:E9:99\n            X509v3 Authority Key Identifier: \n                C8:63:DE:29:66:24:95:5D:B2:37:6D:F6:A2:01:85:AE:31:0F:8E:45\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        30:1e:c8:50:28:e4:10:60:d5:2a:82:65:0d:81:81:7b:bc:5c:\n        67:83:12:d3:e3:71:9d:91:2e:b9:ff:ca:e0:21:ad:74:45:72:\n        be:49:54:f6:f0:22:09:18:84:67:53:88:51:31:20:e7:0d:52:\n        bc:a9:ce:e2:72:27:b4:b9:da:28:03:19:53:46:46:ef:1e:ad:\n        3d:b1:21:df:ea:53:36:97:73:a1:a2:84:6e:9b:d1:b7:bc:7d:\n        3a:81:c9:35:76:fa:cf:70:c4:20:07:f0:c4:dd:c1:6c:0e:e2:\n        28:33:dc:ac:e5:90:b9:89:9d:8e:a6:2a:3e:36:b3:d7:17:f5:\n        76:08:96:c0:ca:07:4f:56:82:5b:93:c8:5d:fe:4c:7d:33:92:\n        ee:6e:25:0c:ff:bb:3d:7f:b1:c1:36:dc:d9:aa:1a:4f:88:21:\n        d4:67:77:8c:08:5c:ed:c2:6c:6b:92:78:4b:5a:23:a0:ed:9b:\n        27:63:d4:f1:0a:b3:b8:eb:95:01:fe:82:67:da:66:c9:51:d8:\n        5d:40:9f:69:bc:e5:4f:7c:19:45:ea:7c:d2:49:29:b7:b5:c6:\n        66:b2:66:8d:01:d4:bb:b0:2f:e9:c8:27:d1:93:f9:97:21:25:\n        f3:b2:69:69:0b:d3:40:c4:3e:23:43:6d:8f:9a:7c:2b:c7:44:\n        f0:99:f2:d8\n-----BEGIN CERTIFICATE-----\nMIID3DCCAsSgAwIBAgIBAzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET\nMBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx\nEDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMTIwODIw\nMDAwMDAwWhcNMTIwODIxMDAwMDAwWjB+MQswCQYDVQQGEwJHQjEYMBYGA1UECAwP\nTm90dGluZ2hhbXNoaXJlMRMwEQYDVQQHDApOb3R0aW5naGFtMQ8wDQYDVQQKDAZT\nZXJ2ZXIxGzAZBgNVBAsMElByb2R1Y3Rpb24tZXhwaXJlZDESMBAGA1UEAwwJbG9j\nYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs8G6eImW+D1U\n4fxWjdAqg66O8XY5Sxj7Tvw2wR6jB1Nxag4uwSLNqMz39cz4LtTRLn3Q0qqnhx9M\nMQ+WKIDSUuolDDLhu2SBv7R7EbWJMSCRACYGLwn2p8FqAHPHEtHdiDNmVPDWfxUc\ndEcpxwMqucEANoPZSLhU9bzfV5+FKpEDvsZPWwNoqvjpJd1agfLC6AyToAXW0XdJ\nysUeQiclnf3M4qdLKAXm9R6zlz3EJWXyW88+BxkzwP0UtKBT4WyQUCJ2KbB6RS4z\nzcc5mRJ+2RX57F9pktQcLFeKEqn8lNoEGhcYfYKjA48g6PO+0mGGQkm11W7NPW0d\n+4s+OTHflQIDAQABo34wfDAMBgNVHRMBAf8EAjAAMCwGCWCGSAGG+EIBDQQfFh1P\ncGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUM0gV9H858qEm\nd/sV0aXczKfi6ZkwHwYDVR0jBBgwFoAUyGPeKWYklV2yN232ogGFrjEPjkUwDQYJ\nKoZIhvcNAQELBQADggEBADAeyFAo5BBg1SqCZQ2BgXu8XGeDEtPjcZ2RLrn/yuAh\nrXRFcr5JVPbwIgkYhGdTiFExIOcNUrypzuJyJ7S52igDGVNGRu8erT2xId/qUzaX\nc6GihG6b0be8fTqByTV2+s9wxCAH8MTdwWwO4igz3KzlkLmJnY6mKj42s9cX9XYI\nlsDKB09WgluTyF3+TH0zku5uJQz/uz1/scE23NmqGk+IIdRnd4wIXO3CbGuSeEta\nI6Dtmydj1PEKs7jrlQH+gmfaZslR2F1An2m85U98GUXqfNJJKbe1xmayZo0B1Luw\nL+nIJ9GT+ZchJfOyaWkL00DEPiNDbY+afCvHRPCZ8tg=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/server-expired.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzwbp4iZb4PVTh\n/FaN0CqDro7xdjlLGPtO/DbBHqMHU3FqDi7BIs2ozPf1zPgu1NEufdDSqqeHH0wx\nD5YogNJS6iUMMuG7ZIG/tHsRtYkxIJEAJgYvCfanwWoAc8cS0d2IM2ZU8NZ/FRx0\nRynHAyq5wQA2g9lIuFT1vN9Xn4UqkQO+xk9bA2iq+Okl3VqB8sLoDJOgBdbRd0nK\nxR5CJyWd/czip0soBeb1HrOXPcQlZfJbzz4HGTPA/RS0oFPhbJBQInYpsHpFLjPN\nxzmZEn7ZFfnsX2mS1BwsV4oSqfyU2gQaFxh9gqMDjyDo877SYYZCSbXVbs09bR37\niz45Md+VAgMBAAECggEAB+U25uBcoPEpFz1eSN+cKm+cAcrFTlrdzCGWvxZ0wMrf\nUaVah0Tba/It/V4gILoPKQyOKkUKhpkVkrCvHsuchIqAj5L0wfNMO3Ec/oSVSGYR\n8F17MPNuIct3qwCEcMg5tue5DPrzroSTg1KOQqdAaAT5GMV9FJOO6cJx732UDtZz\nF13awaUuKWgc0MMq6rataIBhk2cnfFHsKmDkP+9LJ4MX4QHCUCep4hUWnqMHcR+Z\nPTC7K4hUvsjwCpaHUWvUTWRlH+wrHoq3by9dwVe4PsQ9yK/sX4vI1x0ER86iEJVj\ngNreaPFQIOWsgnoKfNMnLCJ8dyFaehFlzTggxswGyQKBgQDwF8/DSDfMz21VmiiB\nm13M3A/ZUhXmJCINXgsHvdK8xTihYUgIoyQRJGUvkuDV20ERemXH3t1vNe7AQbLQ\naP2QXDLOzvvHGPybapip7VdUMY7Zc4w0zAh8U98A6hle3cnm6GlpwOZZRhrBT2Wl\nZg4QESLSZg/MWuFp9oeuypYbqQKBgQC/qo+RnlMV1fHW1c2SmtHerm80hwMg6+Nf\nAIVLX06m7kv7GCR1pnYNmW2iAxrOW5bYh9czSENI3JnMOhoSvz4o6MwlPQU5wUmv\nYGuiAPMX+H1jeNi7gmMOsngHhtDLCMHRaOiip3N+BxdpFIF8u+VTlIIBYcoNYfaX\nQKMINMm4DQKBgQDrfO9zAqp4YBtFEucX+GOQQ2foJ/MCv/4GTm9TMIQ6UtawstIM\nZrdBeQkmGFIeb+bqVbrux1E5exSpzcatU80ggs3yumGJbqCVb4A9a2V0VwddkU+7\nmUPZbgoUw4gO3ErkCKEb8O/+MByd7losWGUCrUwSQbjNH3ZokD2U229PwQKBgFBX\nH00Lz4n0nyXNgxkz2kr8VVLwUQhouGsnHbiFX8OrWaAL86R5PTzgFkt1/7OGQsnK\nzxMI9GNDTRiFNk6raVPemUv2sw0Nj2R7B0LmIP/oQi8DBd47fmg3uQZ2pWil6BBu\naC1eAZRPRqneVZTCchNByejoY7iOWr318yDKd8+1AoGAE1i5p5+i48tETX0C5nmW\nwSXJqGcW+BCK76Y2k8x31jHgcvweRhAx4y2MzIE1964Rm03Q9qsE5D9ZinPhKGCW\nwszmQ4SRnnrcIInxNe/qlyXu6+VjYkOVrGOPqFO87VapumFIRHacGppO/9ZPy8+i\nRAryWvRvyfMGYElO4phPDSw=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/server-san.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 2 (0x2)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA\n        Validity\n            Not Before: Jul  7 22:27:29 2025 GMT\n            Not After : Jul  6 22:27:29 2030 GMT\n        Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=san\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:a9:25:cd:2f:17:90:aa:7f:7b:86:d5:ec:2e:1e:\n                    4f:b6:6b:62:87:59:70:b8:69:1d:3d:e4:f6:5e:d2:\n                    49:a5:26:70:c6:78:df:16:66:77:2e:87:8b:81:12:\n                    b5:41:46:cb:f2:28:fd:5f:dc:16:20:52:0d:dc:de:\n                    77:97:d8:6d:b2:44:53:3c:cd:c0:50:69:d9:a5:73:\n                    b7:48:a7:bd:01:bd:2b:82:f9:8d:3d:a4:94:0b:b9:\n                    bb:5a:c7:0b:0c:84:48:ac:3d:ee:dd:72:e7:c0:a8:\n                    cc:c5:85:c5:c3:df:6b:90:d7:e2:05:6c:1b:ec:37:\n                    57:57:98:e7:19:1c:19:38:68:97:3d:51:74:33:44:\n                    e4:33:3d:dc:a1:de:90:7a:37:fa:d1:95:e9:06:83:\n                    7c:92:6c:42:5c:27:d6:7f:4b:5b:73:4a:be:a7:24:\n                    c4:e3:3e:80:ba:7e:d3:c7:ad:80:d6:c4:db:d4:fb:\n                    e1:1f:e3:4f:4b:65:aa:1e:ab:ae:65:c1:08:df:1a:\n                    34:d2:a0:56:71:37:e3:cd:5a:c3:22:a9:49:bc:b1:\n                    71:22:59:03:88:2f:b8:2d:b0:a9:a4:53:d8:fd:a0:\n                    c3:a5:f2:e0:15:1e:40:22:ff:aa:65:e3:89:e3:8d:\n                    51:ec:b5:08:60:6d:45:a3:7d:82:27:93:e4:86:b4:\n                    3e:5d\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            X509v3 Subject Key Identifier: \n                24:AB:08:6C:BB:5A:DD:85:4E:A1:8B:E4:66:EA:F2:C8:BE:EE:51:B9\n            X509v3 Authority Key Identifier: \n                C8:63:DE:29:66:24:95:5D:B2:37:6D:F6:A2:01:85:AE:31:0F:8E:45\n            X509v3 Subject Alternative Name: \n                IP Address:127.0.0.1, DNS:localhost, IP Address:0:0:0:0:0:0:0:1\n            X509v3 Key Usage: \n                Digital Signature, Non Repudiation, Key Encipherment\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        78:4e:c7:8c:b8:96:51:ed:ee:e6:04:c5:61:fd:74:22:2f:00:\n        b7:4c:63:99:ae:35:31:4f:c2:13:f0:aa:64:04:1e:7d:df:bc:\n        16:27:67:d8:af:cd:8c:4f:d8:c1:48:f7:28:ec:00:25:98:3d:\n        ab:75:33:4c:9a:4d:a6:50:3c:d3:eb:62:d4:e8:32:17:5f:18:\n        7b:3d:45:d2:16:7c:2c:21:30:aa:75:f4:d0:68:bc:9f:94:2e:\n        f5:36:3b:94:b5:4e:6f:58:f5:8c:0b:ba:d4:f2:d9:01:1a:e3:\n        d1:8b:85:41:e5:95:8a:1b:1e:b2:a4:1f:78:b7:de:80:12:1d:\n        cd:ea:93:b5:1e:39:ad:5e:b8:78:ba:26:40:1e:c8:37:d9:b8:\n        d2:86:6f:5c:a9:5a:27:5b:19:3a:d2:c2:7d:cb:69:96:6c:17:\n        4c:00:39:2d:d0:6e:08:26:2b:2e:3c:df:9f:8b:29:85:65:bb:\n        b8:9e:4a:5d:ca:5c:91:58:6b:b2:f5:9a:09:37:c8:f7:2e:48:\n        df:a9:9a:ea:de:e5:d2:91:b2:c4:ad:49:0a:24:70:8f:da:e6:\n        21:8f:67:e4:e8:e4:b1:2f:d2:6e:c0:83:46:37:2a:ee:69:fb:\n        d6:09:b7:a4:0a:46:e8:3d:fc:c3:20:b7:0c:80:a3:a8:be:bd:\n        33:af:15:c6\n-----BEGIN CERTIFICATE-----\nMIID3TCCAsWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET\nMBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx\nEDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMjUwNzA3\nMjIyNzI5WhcNMzAwNzA2MjIyNzI5WjBwMQswCQYDVQQGEwJHQjEYMBYGA1UECAwP\nTm90dGluZ2hhbXNoaXJlMRMwEQYDVQQHDApOb3R0aW5naGFtMQ8wDQYDVQQKDAZT\nZXJ2ZXIxEzARBgNVBAsMClByb2R1Y3Rpb24xDDAKBgNVBAMMA3NhbjCCASIwDQYJ\nKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKklzS8XkKp/e4bV7C4eT7ZrYodZcLhp\nHT3k9l7SSaUmcMZ43xZmdy6Hi4EStUFGy/Io/V/cFiBSDdzed5fYbbJEUzzNwFBp\n2aVzt0invQG9K4L5jT2klAu5u1rHCwyESKw97t1y58CozMWFxcPfa5DX4gVsG+w3\nV1eY5xkcGTholz1RdDNE5DM93KHekHo3+tGV6QaDfJJsQlwn1n9LW3NKvqckxOM+\ngLp+08etgNbE29T74R/jT0tlqh6rrmXBCN8aNNKgVnE3481awyKpSbyxcSJZA4gv\nuC2wqaRT2P2gw6Xy4BUeQCL/qmXjieONUey1CGBtRaN9gieT5Ia0Pl0CAwEAAaOB\njDCBiTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQkqwhsu1rdhU6hi+Rm6vLIvu5R\nuTAfBgNVHSMEGDAWgBTIY94pZiSVXbI3bfaiAYWuMQ+ORTAsBgNVHREEJTAjhwR/\nAAABgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwCwYDVR0PBAQDAgXgMA0G\nCSqGSIb3DQEBCwUAA4IBAQB4TseMuJZR7e7mBMVh/XQiLwC3TGOZrjUxT8IT8Kpk\nBB5937wWJ2fYr82MT9jBSPco7AAlmD2rdTNMmk2mUDzT62LU6DIXXxh7PUXSFnws\nITCqdfTQaLyflC71NjuUtU5vWPWMC7rU8tkBGuPRi4VB5ZWKGx6ypB94t96AEh3N\n6pO1HjmtXrh4uiZAHsg32bjShm9cqVonWxk60sJ9y2mWbBdMADkt0G4IJisuPN+f\niymFZbu4nkpdylyRWGuy9ZoJN8j3LkjfqZrq3uXSkbLErUkKJHCP2uYhj2fk6OSx\nL9JuwINGNyruafvWCbekCkboPfzDILcMgKOovr0zrxXG\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/server-san.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCpJc0vF5Cqf3uG\n1ewuHk+2a2KHWXC4aR095PZe0kmlJnDGeN8WZncuh4uBErVBRsvyKP1f3BYgUg3c\n3neX2G2yRFM8zcBQadmlc7dIp70BvSuC+Y09pJQLubtaxwsMhEisPe7dcufAqMzF\nhcXD32uQ1+IFbBvsN1dXmOcZHBk4aJc9UXQzROQzPdyh3pB6N/rRlekGg3ySbEJc\nJ9Z/S1tzSr6nJMTjPoC6ftPHrYDWxNvU++Ef409LZaoeq65lwQjfGjTSoFZxN+PN\nWsMiqUm8sXEiWQOIL7gtsKmkU9j9oMOl8uAVHkAi/6pl44njjVHstQhgbUWjfYIn\nk+SGtD5dAgMBAAECggEABan04W9OEk97gOvX4UF1lNuqrHKTwjEO4BwzHcC345ZS\nb2rruXHL6txyEOvh8uIegSjbNyMFAKcOvMccRRLjtXTxPpd7KZYpyg8CY6XuP8ko\nVFXr41UH+g6JlgQVPCGP12ipf3PSC9L7LGAK28C29t4rvNe+ZZ4CyDWgA2i79XYJ\nK94iOVGrMoWfPiFXMHkC3HonLCQIeRP3Us2FtWBpDs0HEouGCTa/BWpkim5hnoCh\nTRW+ixopQOyN0bVPf1wVIin7UbNiIyF4jJfUNd5WQrstgwdGAlH65V8BaDO+sIsa\n1dHDsdzGaPQqsmGLhWJ298UmYDYp5k8jUUoMxQCFZwKBgQDYtvYng7OQDNI2K5yu\n2pv9QtobNaZ0ZyVvy/bbaAHvJvej6aKThW30JVk7zkyBk1dQkhSx7GgZoDYylx3E\n5CtQiT07GkG8DZiehfv6RZCRHqR3a2VgpqFQnfiFVniFaX4T7p4YB/9uGmxzWU4Q\nT9fbIge2dfE8O7Drz4wYMpAGRwKBgQDHz2fUTZb8DZgQpasCQBqFwQKnk7ZKAgvO\ncu3JO41/wUL2ZMfxeFIgMORFk+Xckm7KslykKnmX4etP1gxXqtAqR2JuWqv25Se8\nfjZi8TbNm33Poul7nZsYQNBByy9jpLl6PRCYRCB67HPCRYqyGswcw8fXj4wQWYEn\npYFJqzDUOwKBgQCX0dUwaXtp9xFtEbB6bnvJOQRC+5rZAUmgwGr32i1AtTPXiN10\nK42T9HZHB4dhXy9UKoKFAvEKwso1NtiMDqyphvt2ZDaY342DwKl98y4L/EOLxZkH\n1LQ+Hez0vFdCX10L6aanfzLal3hSdsXRd53ozjZJBOczIz4WdRfX+9QaEQKBgH4C\ntHV0tVqibBtbj5ysts1Rqw3qHxVPcwiw/HtjXlqKlGN1rY8AlbKNgvjKTDWt98dH\ncxtpWiPKK6++yyviosN3H4F/F2JupH/AjSYa/7ftbwuqr1rxS2WhQnWr4WgS85I+\nvp94n49GXb7QQqcONVmSsw6kDe4ltEk/nGjMWNAPAoGAb2ogcxUZN3exlQbqVLeM\nfw5LVTH2g8vIis1TKyBDaHZqCwsqmyKhEhVExVp8Ipsv6wg3aySgmVeJSddJsMIl\nTCGFi/Y5NbAsm+8VvcArhiJJkhTH+NXSUN23aS7Wm4NO6Su9NnrAFpEMcIxK6FsJ\n45t+LOhdZwXzPtYdJDQHM2s=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/server.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 1 (0x1)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA\n        Validity\n            Not Before: Jul  7 22:27:28 2025 GMT\n            Not After : Jul  6 22:27:28 2030 GMT\n        Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=localhost\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:9e:19:e7:52:51:89:c3:7b:41:a2:02:26:73:21:\n                    06:d8:77:f1:36:b7:0d:5c:94:d9:bb:78:35:c0:aa:\n                    6b:63:4a:3c:ff:c1:c2:0b:76:6e:86:1e:4b:a9:ee:\n                    18:34:b1:40:f1:99:6d:90:3d:5b:3d:eb:2e:c9:e0:\n                    bf:ee:37:38:31:a7:26:48:d3:35:59:f2:86:a4:0f:\n                    dc:33:c3:9e:b0:8b:b7:0c:2a:67:81:ea:24:4a:a2:\n                    3b:b7:82:93:74:d7:7d:a5:91:90:4d:6d:11:fc:40:\n                    2b:86:94:87:62:8e:46:c2:13:bd:47:96:ca:f4:02:\n                    6c:6b:f2:8b:ba:d9:9e:f5:fa:d5:d2:53:6d:b1:43:\n                    14:02:b4:c4:33:12:93:54:35:8f:7c:d2:41:f9:ab:\n                    13:7c:7e:23:9c:ff:9f:f1:4d:0c:6b:c0:dd:b9:84:\n                    16:b9:19:8a:38:23:87:1f:bd:0f:77:33:b1:91:70:\n                    9d:e4:00:9e:76:76:f0:a9:50:9c:8f:f8:f3:b4:19:\n                    d9:cd:26:ca:cb:ad:d4:bf:73:04:a7:c3:79:e1:b4:\n                    99:2c:be:8f:6b:70:3f:4d:8c:1e:28:6a:b6:f4:a7:\n                    a9:c2:f0:28:f3:55:86:7f:09:fe:2f:3e:4f:ba:55:\n                    ac:7e:7e:77:19:fb:55:44:d0:db:c2:60:99:28:64:\n                    d8:67\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            Netscape Comment: \n                OpenSSL Generated Certificate\n            X509v3 Subject Key Identifier: \n                7C:CC:33:7E:7A:79:EA:33:7B:56:77:63:2B:B1:CC:28:6F:EA:8A:8E\n            X509v3 Authority Key Identifier: \n                C8:63:DE:29:66:24:95:5D:B2:37:6D:F6:A2:01:85:AE:31:0F:8E:45\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        88:87:3b:39:bb:34:f5:2b:15:fc:8d:b2:6c:e2:3e:f6:5f:14:\n        0a:d4:48:73:1a:ab:08:42:48:a1:d0:ef:ec:b4:3c:f4:b6:f0:\n        d8:42:c1:af:e4:aa:3a:d9:86:7c:55:eb:1b:ae:76:f0:15:ee:\n        ec:b7:fb:d4:7e:0d:5e:dd:f4:6d:ee:71:06:c0:20:87:ba:50:\n        39:38:0e:79:63:9d:59:46:26:67:4e:41:09:af:75:40:1e:5b:\n        9d:8d:fa:2b:2a:25:8f:65:c0:21:fe:60:2e:d5:e3:bf:2e:ac:\n        b3:1c:fd:51:d8:bc:70:dd:26:2a:45:a3:2f:3d:26:2c:d0:c8:\n        a9:d5:29:64:84:e4:a8:79:8a:16:ab:3a:97:99:3c:25:af:04:\n        e9:29:1a:83:62:e6:a4:dc:83:2f:95:4a:2e:9d:cb:56:38:c5:\n        9f:dc:18:67:02:d4:07:11:31:d5:d1:e4:1a:b6:0b:93:b2:e8:\n        33:5d:62:58:d9:0d:10:27:d0:46:7a:b8:64:de:32:82:e7:d9:\n        e7:76:85:dd:14:d3:0f:bd:a5:05:e9:ed:03:a8:a1:8b:0a:61:\n        bd:51:6c:f1:e8:b0:3a:6c:fa:48:79:d7:da:90:53:96:d4:94:\n        da:fe:df:9c:1b:80:4a:d9:fa:5e:0d:c1:ee:a7:4f:17:64:24:\n        6e:56:fa:67\n-----BEGIN CERTIFICATE-----\nMIID1DCCArygAwIBAgIBATANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET\nMBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx\nEDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMjUwNzA3\nMjIyNzI4WhcNMzAwNzA2MjIyNzI4WjB2MQswCQYDVQQGEwJHQjEYMBYGA1UECAwP\nTm90dGluZ2hhbXNoaXJlMRMwEQYDVQQHDApOb3R0aW5naGFtMQ8wDQYDVQQKDAZT\nZXJ2ZXIxEzARBgNVBAsMClByb2R1Y3Rpb24xEjAQBgNVBAMMCWxvY2FsaG9zdDCC\nASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ4Z51JRicN7QaICJnMhBth3\n8Ta3DVyU2bt4NcCqa2NKPP/Bwgt2boYeS6nuGDSxQPGZbZA9Wz3rLsngv+43ODGn\nJkjTNVnyhqQP3DPDnrCLtwwqZ4HqJEqiO7eCk3TXfaWRkE1tEfxAK4aUh2KORsIT\nvUeWyvQCbGvyi7rZnvX61dJTbbFDFAK0xDMSk1Q1j3zSQfmrE3x+I5z/n/FNDGvA\n3bmEFrkZijgjhx+9D3czsZFwneQAnnZ28KlQnI/487QZ2c0mysut1L9zBKfDeeG0\nmSy+j2twP02MHihqtvSnqcLwKPNVhn8J/i8+T7pVrH5+dxn7VUTQ28JgmShk2GcC\nAwEAAaN+MHwwDAYDVR0TAQH/BAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBH\nZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFHzMM356eeoze1Z3YyuxzChv\n6oqOMB8GA1UdIwQYMBaAFMhj3ilmJJVdsjdt9qIBha4xD45FMA0GCSqGSIb3DQEB\nCwUAA4IBAQCIhzs5uzT1KxX8jbJs4j72XxQK1EhzGqsIQkih0O/stDz0tvDYQsGv\n5Ko62YZ8VesbrnbwFe7st/vUfg1e3fRt7nEGwCCHulA5OA55Y51ZRiZnTkEJr3VA\nHludjforKiWPZcAh/mAu1eO/LqyzHP1R2Lxw3SYqRaMvPSYs0Mip1SlkhOSoeYoW\nqzqXmTwlrwTpKRqDYuak3IMvlUounctWOMWf3BhnAtQHETHV0eQatguTsugzXWJY\n2Q0QJ9BGerhk3jKC59nndoXdFNMPvaUF6e0DqKGLCmG9UWzx6LA6bPpIedfakFOW\n1JTa/t+cG4BK2fpeDcHup08XZCRuVvpn\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/server.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCeGedSUYnDe0Gi\nAiZzIQbYd/E2tw1clNm7eDXAqmtjSjz/wcILdm6GHkup7hg0sUDxmW2QPVs96y7J\n4L/uNzgxpyZI0zVZ8oakD9wzw56wi7cMKmeB6iRKoju3gpN0132lkZBNbRH8QCuG\nlIdijkbCE71Hlsr0Amxr8ou62Z71+tXSU22xQxQCtMQzEpNUNY980kH5qxN8fiOc\n/5/xTQxrwN25hBa5GYo4I4cfvQ93M7GRcJ3kAJ52dvCpUJyP+PO0GdnNJsrLrdS/\ncwSnw3nhtJksvo9rcD9NjB4oarb0p6nC8CjzVYZ/Cf4vPk+6Vax+fncZ+1VE0NvC\nYJkoZNhnAgMBAAECggEACTSo63oj82Xx5GULqBh8NY6GVRFDjeh85RWSy60go59m\n/d1iVxiGRvjsnvBmKGtJxMeSQZvQ/EH9b3POuSgt9XYuHH9v09TzUgo6YCk7zDvW\nZLbzX/UqN85Ke9z2iQ8jGcjoBhobufxijGuJlouCQzqzAsAdAShC9+YfjLmvL9Nb\nwgwJvDs6PiVi/GvdFtoF7YPZsRGBChehI344X0GdDVgStwYX+O8hKRFwNjP1o3sJ\n5odv45cwxwXtTlvsnRIGSQHgYQmzyUHXnb/PvVfXKuq4n5+lQT5egCcZukyr9vo7\nhwDnlNPC1Z5w3ubyQdogv7Q1fBt0JskBjOXK4uiTgQKBgQDKox8/IPnEeFO7dxwf\nN/+oCamticjM6oAO5/Fb7DwLzzBLZBjiUmfieTSAOUuJU2hOpsWP30I3ySu30UZX\nbwxmdcRLIMJ7Q1HxOEEYCgCxdushOxOXdzW/H4sOPQspwzMQHibMCw+77igQpqwg\nQMAGKDZ7cMy+LNouDFdOWVzKjwKBgQDHvF3KL1mDgdEgOilWaQQ1TO81HQUE2maE\nql1vqSjdQpgUzzFPTP5C1dZA4vGI3F6dT3nJHpEGusxF+TtyJMWvY1rUyLJX684l\nCAnXVvI+1xI4dSNlYO7YS67SpKNKZiv6s4Q5o1o1RjKIRxt/7K2gT4IiR/qDn2ZJ\nfbpp47rgqQKBgHZEYnZL3rrmp6ggSo+F9XazvQ6F/mZq7zbD9MB7zkfuMvetgkCF\nbBBoQVYdGpMZ1SUifOgNm+5HQXbVc8KQE6KxVVGr2xZqIicxd/x5yhHJoE4S1spu\nTzYvSM+UnTFQtjrP/kDUq+g5hbTCMm/Ymrp9Od8t5LGSJ/z8QvB9g4TNAoGAGfEd\nPWVo+uuhfc4QEGkTYtjbOMrMHBVBu3llKVuPMy2zEwDWJraZT5T2fvb66Au3PjdU\nWgreS0F3xp7YWbrs8hq1cW2fvEukOqsQnCduzzqf4zVTo5czbmRmEHXRv5gFnkoy\noknVLZYwegLCT5st8eRhwpIWt4G8h08NJzOs0gECgYAzl0sBMIMuj5GLXhClCoLm\nXWKjAP8q1rd8nY9oWwzlLoKMsBn26SKZ+/mvhx3Yxr0FNYZR8nTYoFaOE2lEmPrm\n/tQQBhMS4ZjnF77RKAqM7GZWUA68himDjN6YEobl7GaANn2sCy39FSxpT8DHSGfn\n3eKrvNpUlEoGg3KJ26Zsqg==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/test-alt-ca.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 2 (0x2)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=GB, ST=Derbyshire, L=Derby, O=Mosquitto Project, OU=Testing, CN=Root CA\n        Validity\n            Not Before: Jul  7 22:27:28 2025 GMT\n            Not After : Jul  6 22:27:28 2030 GMT\n        Subject: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Alternative Signing CA\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:af:de:b9:88:7f:45:02:9c:72:21:85:f6:05:7b:\n                    66:2c:67:e1:80:3e:65:65:f8:09:fd:52:f0:53:0d:\n                    33:65:ab:c3:3c:03:ff:8d:ca:b7:d6:88:8e:e5:b8:\n                    2e:66:0e:81:8e:b9:dc:2d:13:22:49:12:5a:be:71:\n                    f7:2f:75:bb:aa:af:83:18:f6:ec:00:1d:19:2f:98:\n                    d4:44:a6:99:0d:22:be:fd:91:fc:68:58:9a:c4:9a:\n                    75:4a:06:6b:8d:5e:0d:3b:51:f2:bf:88:d7:a1:c3:\n                    30:47:86:4e:cb:30:a5:1b:6d:d7:b0:93:65:0f:b4:\n                    1c:bf:f2:16:a2:0a:4d:f7:1d:76:ba:5b:20:97:a5:\n                    a4:9f:4a:f2:4e:9f:7f:e4:19:66:31:5e:69:c8:7d:\n                    32:ae:ae:d5:61:d9:c9:a3:73:b9:81:f9:78:d0:b0:\n                    34:1a:7b:6b:38:01:5b:71:41:23:90:29:56:1d:22:\n                    f1:d3:e0:eb:ac:6c:d4:bf:46:fc:c4:78:cb:36:d4:\n                    f9:c2:47:98:0a:11:26:c1:26:2e:fc:ec:6b:b4:df:\n                    04:1f:84:36:06:c7:26:ac:bf:bf:e1:ab:62:39:6f:\n                    ba:79:5e:01:33:66:e6:13:4f:c7:d5:98:57:04:3e:\n                    28:94:7f:64:2e:92:3c:0c:5b:b0:2e:64:7a:71:e4:\n                    32:2b\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Subject Key Identifier: \n                42:84:86:43:AC:B2:A4:A9:74:E5:6B:B8:B1:B9:16:03:C4:3F:A2:9C\n            X509v3 Authority Key Identifier: \n                4F:56:ED:BA:57:12:46:13:CF:96:E3:2C:C1:91:64:69:5B:9A:E7:05\n            X509v3 Basic Constraints: critical\n                CA:TRUE\n            X509v3 Key Usage: \n                Certificate Sign, CRL Sign\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        ed:20:93:a4:3d:c4:c7:d4:02:45:ce:6a:9b:27:8a:c0:ba:4b:\n        52:64:e3:0e:e7:72:60:67:8c:ab:88:c4:d9:6f:a7:29:17:1b:\n        d7:2a:23:dd:b9:a4:9c:7a:95:cc:56:a9:09:07:8c:93:e6:52:\n        07:90:2f:b4:ae:53:39:cd:22:4b:16:80:d4:9d:96:2f:4a:c7:\n        ef:ab:02:1f:f2:8b:a1:3a:5b:e8:56:2d:65:74:a1:32:3d:9a:\n        5f:67:09:1c:fb:a0:20:76:98:c2:79:ce:1e:3e:0f:e3:16:51:\n        d5:a8:42:96:6c:8e:13:53:68:29:63:45:68:fe:7f:65:34:d7:\n        c0:e3:81:f6:cd:36:91:da:07:bf:ff:00:2e:13:55:af:63:d1:\n        d3:9b:40:44:9e:51:34:7e:0f:7e:01:b7:c2:f6:7c:42:0d:ac:\n        78:6a:cd:35:f7:06:e3:ac:89:59:b3:54:a2:5c:bb:98:02:0b:\n        e4:f3:1d:74:ab:25:8b:1b:13:5a:bc:74:47:0d:9b:d6:cf:0f:\n        eb:9a:9e:1c:10:82:97:da:df:59:71:9b:9a:2f:4d:e6:8a:f6:\n        fb:b4:af:d9:ee:ba:92:15:33:b0:56:2c:8d:bf:b3:ba:ab:a7:\n        d6:f8:d0:2f:10:8e:85:6d:47:89:5a:b5:49:95:58:7a:ae:60:\n        5a:c8:f5:67\n-----BEGIN CERTIFICATE-----\nMIIDvjCCAqagAwIBAgIBAjANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJHQjET\nMBEGA1UECAwKRGVyYnlzaGlyZTEOMAwGA1UEBwwFRGVyYnkxGjAYBgNVBAoMEU1v\nc3F1aXR0byBQcm9qZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRAwDgYDVQQDDAdSb290\nIENBMB4XDTI1MDcwNzIyMjcyOFoXDTMwMDcwNjIyMjcyOFowcTELMAkGA1UEBhMC\nR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxGjAYBgNVBAoMEU1vc3F1aXR0byBQcm9q\nZWN0MRAwDgYDVQQLDAdUZXN0aW5nMR8wHQYDVQQDDBZBbHRlcm5hdGl2ZSBTaWdu\naW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr965iH9FApxy\nIYX2BXtmLGfhgD5lZfgJ/VLwUw0zZavDPAP/jcq31oiO5bguZg6BjrncLRMiSRJa\nvnH3L3W7qq+DGPbsAB0ZL5jURKaZDSK+/ZH8aFiaxJp1SgZrjV4NO1Hyv4jXocMw\nR4ZOyzClG23XsJNlD7Qcv/IWogpN9x12ulsgl6Wkn0ryTp9/5BlmMV5pyH0yrq7V\nYdnJo3O5gfl40LA0GntrOAFbcUEjkClWHSLx0+DrrGzUv0b8xHjLNtT5wkeYChEm\nwSYu/OxrtN8EH4Q2BscmrL+/4atiOW+6eV4BM2bmE0/H1ZhXBD4olH9kLpI8DFuw\nLmR6ceQyKwIDAQABo2AwXjAdBgNVHQ4EFgQUQoSGQ6yypKl05Wu4sbkWA8Q/opww\nHwYDVR0jBBgwFoAUT1btulcSRhPPluMswZFkaVua5wUwDwYDVR0TAQH/BAUwAwEB\n/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAO0gk6Q9xMfUAkXOapsn\nisC6S1Jk4w7ncmBnjKuIxNlvpykXG9cqI925pJx6lcxWqQkHjJPmUgeQL7SuUznN\nIksWgNSdli9Kx++rAh/yi6E6W+hWLWV0oTI9ml9nCRz7oCB2mMJ5zh4+D+MWUdWo\nQpZsjhNTaCljRWj+f2U018DjgfbNNpHaB7//AC4TVa9j0dObQESeUTR+D34Bt8L2\nfEINrHhqzTX3BuOsiVmzVKJcu5gCC+TzHXSrJYsbE1q8dEcNm9bPD+uanhwQgpfa\n31lxm5ovTeaK9vu0r9nuupIVM7BWLI2/s7qrp9b40C8QjoVtR4latUmVWHquYFrI\n9Wc=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/test-alt-ca.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCv3rmIf0UCnHIh\nhfYFe2YsZ+GAPmVl+An9UvBTDTNlq8M8A/+NyrfWiI7luC5mDoGOudwtEyJJElq+\ncfcvdbuqr4MY9uwAHRkvmNREppkNIr79kfxoWJrEmnVKBmuNXg07UfK/iNehwzBH\nhk7LMKUbbdewk2UPtBy/8haiCk33HXa6WyCXpaSfSvJOn3/kGWYxXmnIfTKurtVh\n2cmjc7mB+XjQsDQae2s4AVtxQSOQKVYdIvHT4OusbNS/RvzEeMs21PnCR5gKESbB\nJi787Gu03wQfhDYGxyasv7/hq2I5b7p5XgEzZuYTT8fVmFcEPiiUf2QukjwMW7Au\nZHpx5DIrAgMBAAECggEAEb8Cr7BP8VyB87oFwjXacH6m9X7WUny93U8CKw848Xhs\ngeDRZ6hd9orfCHUWUXwDPLiqa+3zVrZAa9kqHSLfJfEB5IH9/GDzSqc8PBUnenjY\nFtQlSQ4vx8jiLu8I6UHlSegR+5u+TndYps75om0tK+BglFO7LeN5xzCRchZiGZ43\nb/dxGiDsyC1JDXiM14pa7EfxdNVFha8MmOo27kBGA6FczspMtUQyMAuyvy4/2u/I\n6Nx1CE8kTVfqepVupS16oiNIqNtrOr+wVxpk90j1vmR9kns8kZaTHjDy1liEyi4p\nxp/b5SKNJGrV456zGqwj3c/5n89rIWFdzGeyfg29EQKBgQDrdeNGEovpanH7dOlX\nmewskgNF4Ug+cvob9Yh1fowSDhT4shdCJBkCiSSBFX3xr2c1a6212SfxNth5LFLY\n/MWJecjanRuzq6sNv1VftwReCIOJjFsfhOL8ONJWIDXdV+gKtIDSv/d2fym1qWnl\nCJWZSk0BRSBFvYUG50PDA3XCswKBgQC/NhxgF3fXu6OIiB9w3txC2ZVfaV2qN+Pd\nO5ZHsx+e8JtbdCVfJWJQXOA6mama5Tldj8O+31jBnKGQtKzJDhyjHPBzYFHdZOfR\nIK3ZsuvYlgL8QogVlciTaLy2eIM3vYewM/sCDIXDAOP6dqPKdqLFVUZEalptgkRc\n8oYRSmuuqQKBgQCkfROLhTNWmbUM3GySdQYHUO2WaL4GWl4dIBb3NbN2fX3rCsay\nvvL10Ya94py8NTPdnt6Ydh6wJQdvBybNTTBWTMyi5DRQ/PEfRnXGytzzL/FsKrAR\nwcysNKnD3vaiLWH98IE6OT8P+d/Sd4pxpOCVWNGYvIjCD5aZ7v9ogcdHfwKBgD5d\n5+NvxCcZjL17qMWn6y/iyFXWiDZ9BFWkmd/JDQdKc2HhAE+IYgjUQk7az/c1zQA3\nZCFduBVugUQxqinp8G1DgyoewJT11KbhgdMACO0cAN1G1hw0PrfV8beSlzoXF6rh\nSX3hl7+DCtkm2UWwbGbw6XpnNheB5cprUE9TdswBAoGBAM62RzHFmwz7Qu9bImFE\nJE19hM6ib2SD2Llg+/75i6d6Z/X7wOJxHa1u1et8v0wo53uQMBo5A6PvE44HSW9S\nxgKj/FbDx3OWnrKjP0P1Jx32X5MV6pDAdw7kuCm8TBwTVGgVv34IKuToCBqwUMed\nlnk7BXKvKq8cCT0qz+9GYRqw\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/test-bad-root-ca.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIID2jCCAsKgAwIBAgIUPdXz5ys4khmrwxJhnGnx/jH4rmUwDQYJKoZIhvcNAQEL\nBQAwdjELMAkGA1UEBhMCR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxDjAMBgNVBAcM\nBURlcmJ5MRowGAYDVQQKDBFNb3NxdWl0dG8gUHJvamVjdDEQMA4GA1UECwwHVGVz\ndGluZzEUMBIGA1UEAwwLQmFkIFJvb3QgQ0EwHhcNMjUwNzA3MjIyNzI4WhcNMzUw\nNzA1MjIyNzI4WjB2MQswCQYDVQQGEwJHQjETMBEGA1UECAwKRGVyYnlzaGlyZTEO\nMAwGA1UEBwwFRGVyYnkxGjAYBgNVBAoMEU1vc3F1aXR0byBQcm9qZWN0MRAwDgYD\nVQQLDAdUZXN0aW5nMRQwEgYDVQQDDAtCYWQgUm9vdCBDQTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAIlSX40YsU38EmwfF4nY9d3D4K0iu25NUGkRwToG\nFihwY/SRr0DQEyBY9DaDOwUkVW5w2fG/ZEq5UoC9aUH45qfgo/aMHDuJmUv2grJy\nC/C1+ldaGnYVzv+G+vjYVeclvF+ygEO7UDhF895bHty77ULOtvF6wswgudpg+Q5n\niujQq9oTc+POM4R4sITdjvhFxqRjjKYS7AeyEYXy5tkpqmh7BLtmemeyohXfpKsQ\nkKzE+k97GiVn+7ToG07PH71FZ5mfMjLlDDSiUN7OlVeMs8xOaGJcTtneSmBqmZvq\n9wPNYt9nhbuPF6NMlBmgmWWW6t+QCrXWA5bsDRQLYj6iwdsCAwEAAaNgMF4wHQYD\nVR0OBBYEFGnU3jqeHtH9/EW3156LEIasHd9jMB8GA1UdIwQYMBaAFGnU3jqeHtH9\n/EW3156LEIasHd9jMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqG\nSIb3DQEBCwUAA4IBAQAYZ9WmExK6sYRvhr/K3Rq/PCivKAmj1DikaqNVJ7xcBqIa\nwLatT5rx6X117gL5ZGuFxi8linZYZr0BNaPR3xoBIMhxHnClm+egzt3vOtHqWUlZ\nS/35vHGCpCBr9A7PyXVlyucJdqF5VUM7eNhIj3ihL9UWUe3zQ0GOVQp3fH/NGXd0\nkC+uHeCrTIZWKf6KCq2IO6+BbM9JG2EXfnqLcjZ9NelnbkjPsb2wklZkEBZpzfi5\n2LMd/FvG/mN//MNUa3YudFDC/b8Qd1Mq+o4QaYcxn3jnz2+lP1bckJNOK1HarMIg\nvS0999UEWyROHgKRSvLe4aIkq4YgGMu/H2/yWrUO\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/test-bad-root-ca.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCJUl+NGLFN/BJs\nHxeJ2PXdw+CtIrtuTVBpEcE6BhYocGP0ka9A0BMgWPQ2gzsFJFVucNnxv2RKuVKA\nvWlB+Oan4KP2jBw7iZlL9oKycgvwtfpXWhp2Fc7/hvr42FXnJbxfsoBDu1A4RfPe\nWx7cu+1CzrbxesLMILnaYPkOZ4ro0KvaE3PjzjOEeLCE3Y74RcakY4ymEuwHshGF\n8ubZKapoewS7ZnpnsqIV36SrEJCsxPpPexolZ/u06BtOzx+9RWeZnzIy5Qw0olDe\nzpVXjLPMTmhiXE7Z3kpgapmb6vcDzWLfZ4W7jxejTJQZoJlllurfkAq11gOW7A0U\nC2I+osHbAgMBAAECggEADCqwbQFUErNgmnqcWX1P0DwSEXHM84MfVCZB9x2II9sZ\nY0qJIY/0ZRHpYu8SCp/85cUf7X7MqJ/U3IBrunAOiuJ10Vr51JiZ0xSRrefTtDPo\nOb1+lk0GXLa3HXDL46gA3n9W3lyfr4ZOV7qKq606FbvYLHsSliClw61Pdv3bkLCf\n91d+QycDkogIAOnV8MuGrJm5dQkRzRKF74+57UbV48z5PUiU5NwZOcFzgfhHZ0L/\ndhmIQ+Ks7TzDlwq22KpsczJr4tVBpoTIYSShXqVuzmIWz6zZNShT/9rmLHYw/DVS\nISmZ45f7teds37VhiMfyfECorFa7fD1Gz45MJW2d4QKBgQC/p6Kl3xFJrFvJIjPg\na266aTFRQ+bi/VgtJpEbgOpzLBh8GRCjRo8zRMEffylUo+UkiJVrlwM6hFEjg4o4\na6gPwGs73yymtlNT0c+sYmy5porQr6jieci49zc9UBM9ikyDaaGq4phZ8v6SU+Zt\n/jH81i0d+in7FOp8+cAkhUWZcQKBgQC3bOqBIuG9JoJ1XjnR6n9EF/qd+64VcwTR\n6eluSv7tr+Rckf7Wq0rKaUWCNTcWYubJZI4pDsVb9ZZkwtXBI8Kgou2D32/cDigZ\nR23h+r3YIzPm6F7lBo/+KJcPMEEMUMd+0ydf7loed19jJcP9i6O+PtvICNfNcLdw\nunpE+bLKCwKBgQC8fxqYO1ncdPndS5dsLR29l8JapAcMz5GO7rSfMV7locP/IgPc\nIoSrLv8mhEHZLk0rbm5PYDpbrlHDNReXwEKOI3kUbL6UxRQVh3DSogc/XM6Ay5O2\nE4NYcETTN9OEnmX8hcLsuGqRZU3+CyjCm9T8UIYVSrtJaFvsSRMymCVI8QKBgFgG\neeetML4QbA0dQgw+SAMKqugELz/16bs/URnv/bVdcu8F1VF59LN8n7HkDeK9ZdoC\nWsLTZt1B14HVirVcjvt+FRPzN4BYft/ayp3nMhI2mqLWoyuv4YxsOEo+swjQ/1wa\nw0ujXDZAvVMcfZkA2XzkN58gt0fNLwt3QlQ1rJqPAoGAeIj7uQZkjNqvK8No3ZCw\nHFJfi2hKRCDq+NMh9XX0MsnCFYNBytZSJ56iU2myD9fyv0QRykZUnFxo2L9n6QG6\n/I3CgxE7TQspJUU0tm8OdKBmjqpMCQj+h4LE0FtTJAl6+fUaaXLRWW/zRaeGxHnr\n+REogjCnh9r72F/2Hu4Efzw=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/test-fake-root-ca.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIID0jCCArqgAwIBAgIUGHRAnoTqm46p11DZWZXfAJzl2AswDQYJKoZIhvcNAQEL\nBQAwcjELMAkGA1UEBhMCR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxDjAMBgNVBAcM\nBURlcmJ5MRowGAYDVQQKDBFNb3NxdWl0dG8gUHJvamVjdDEQMA4GA1UECwwHVGVz\ndGluZzEQMA4GA1UEAwwHUm9vdCBDQTAeFw0yNTA3MDcyMjI3MjhaFw0zNTA3MDUy\nMjI3MjhaMHIxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApEZXJieXNoaXJlMQ4wDAYD\nVQQHDAVEZXJieTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3QxEDAOBgNVBAsM\nB1Rlc3RpbmcxEDAOBgNVBAMMB1Jvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDk+oDCU7JtFeX1k2CcsMGLDQhPNkI6hRDQ58qnke/nl19gyQnh\nSExfyEr4rCxG1wv3aTcckjOunxjjtLe65XDAVoOKpq/vjF0heaYne/7Fl5IYjkOP\n3lPj8YYe0sfd6wG39BYx00dJD0JaC1voTICDMVjxW6QYQtDqU8f21QyTrapd0i+c\neMjemG5pyV/bojjgXpyVhkfT6HZvOsETp/HAUYAEFK+fst8m49lCGpZWlnSqjG0i\nVCyktZ+pKgdkus4y5V0ng4beLTClSF/qSmDZ+6MASy+DyJvZX5JTwMVP74Sc+26O\nTrNUlGnvDDb1e7GHRv+GF0A4H03ppmnU3VU9AgMBAAGjYDBeMB0GA1UdDgQWBBRb\nbCvlBYBU16FlTHwMHZv0ra+UvjAfBgNVHSMEGDAWgBRbbCvlBYBU16FlTHwMHZv0\nra+UvjAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsF\nAAOCAQEAozJv0fV7WCpDDa5oxdpm1k67Yqklwged4/V2CuG5S7wIkLGJqzOldFCR\n2wtlDIb3N8aTTEgywo1EcqSjIRCWke+Lrzjof8ypeE6G6qgWkIOsh6UEeUuhUjUc\nmYcvRxwYLgh6c6reAMIi26SOWvRrvse+MgjJMb8C/j0jF3Ymqw9QiwbD9IsXnhe8\nolyxUYW4zgzZgF7Ae5dLJrom3q51J9LWGvEp9KhweG9gSbOxmkoD4IKx4JbVoRBs\nTaoLAc0LepwvFQchA/mR5r/ppB6GUFEHGvSJDmyMDrcZJ6QV5w+rPkarBZJJsbql\n2J+MLkMFsrhBq6ajFau4nOVnDPW2GQ==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/test-fake-root-ca.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDk+oDCU7JtFeX1\nk2CcsMGLDQhPNkI6hRDQ58qnke/nl19gyQnhSExfyEr4rCxG1wv3aTcckjOunxjj\ntLe65XDAVoOKpq/vjF0heaYne/7Fl5IYjkOP3lPj8YYe0sfd6wG39BYx00dJD0Ja\nC1voTICDMVjxW6QYQtDqU8f21QyTrapd0i+ceMjemG5pyV/bojjgXpyVhkfT6HZv\nOsETp/HAUYAEFK+fst8m49lCGpZWlnSqjG0iVCyktZ+pKgdkus4y5V0ng4beLTCl\nSF/qSmDZ+6MASy+DyJvZX5JTwMVP74Sc+26OTrNUlGnvDDb1e7GHRv+GF0A4H03p\npmnU3VU9AgMBAAECggEAC4Is3L3nK5HPsO2U2M51E9LDKac9yE760ha5IH32jSQv\n1rktmXIp5u1PgCGEyJSzYcTntueapS3fsUYnSKn45kasethqfFA70akzEpLDv27U\nS9t1BgyaS8xOV0tBx2Ox5Orly+BLs2s+BJeQj+OuC90vLey5gFl1wmlILuqoJKq1\nlkzVRJW1nzGH0FxUUnsI5ya4Eckp3+OG1HTNS+QD5zwV5c1kiP75dDuU/Va/9Msl\nVrV2VX9Rs1l/91T5PLs84AK1JrBtYJCpR/PfyYkBHOa+H2R2M3cINEOWq+UzziAc\nv6sBK1Pym5ZTVBn8i5Lt8H1ayXP2gkqskKPcRBaycwKBgQD9zvSKnL4Bx05jDNl4\nEv1VBmIIpHQaoZHdQr1WRMdD6TTPp+LkZLWOqsLFSRziAiLUexm4Ny7dRSq9dbGa\nH8c51OiFw5pGgn3jzpSErDQayXM6fxG1UZ4RYDVxorgMMvdNg3qn1dHPZ2vJG4iM\nnwJsoVA+eC1WozmWZ5KJYjfFlwKBgQDm9Kk/osllbbOOQj6y3SRd4T5xiPCgztd+\nmlQLBvt+I9ZpmPjKogN9PDPCkVNn76RCkWvNSWZUjT/QLOpjo7d6h4muAUN4gNTn\nrZG0EjsH/1ZFAfHUC5lm/KadrGNz5OLKPVPZ/heQk89Tz9w/EJMoDB/kkFKyW5Sg\ntEJURO9eSwKBgBJ7E0tUhnFStd54fQ1FNLUQNeszLlESGrDlvyuc7nV/cZz9OIQw\n4Rd2T6BV5oh+Z1LZc9H6EquB8c7B1yDF15fabOPwjjc8ITaJQD842sJokL9dqUhu\nnPfe7YVMt+ILg/5c6H14EELt4OdP3e1/VonaZSFnVsXMNNFC0WS3hiAZAoGAZfU0\nkodG6aQYVIEiNMwztc4uRujxccxejeGLoKKge/tOOKfzjWEgsTTWlNqbO5MrrMeO\nE76HkmQY+8oYX4xy/4C+Yzbjllspom2ZmSlDLjCm4SgOnlHQkwqOc6Ua8prlE+sn\nDWGC/ayDJrjovl6O2Gsh2UFtgJe1cYyii5kzIykCgYEAk19ynt7Ndw2fhAOLKp0R\nbS/B7XHotRsQtEQIo0lYHAWZZZqLh3CKsDsz62NbklKu92DKZVopDUQXWX9LvN5P\n3jdyV/6PclhiSgrDy3ogPvHoAJCT9iOktdpLQEgyrwcunTuKB/74rDxfH2EmCj4Y\n1f13ff3DuEzMffx+X87/g/s=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/test-root-ca.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIID0jCCArqgAwIBAgIUPWZteXB28AsU7emgSEDG3w7zqd8wDQYJKoZIhvcNAQEL\nBQAwcjELMAkGA1UEBhMCR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxDjAMBgNVBAcM\nBURlcmJ5MRowGAYDVQQKDBFNb3NxdWl0dG8gUHJvamVjdDEQMA4GA1UECwwHVGVz\ndGluZzEQMA4GA1UEAwwHUm9vdCBDQTAeFw0yNTA3MDcyMjI3MjhaFw0zNTA3MDUy\nMjI3MjhaMHIxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApEZXJieXNoaXJlMQ4wDAYD\nVQQHDAVEZXJieTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3QxEDAOBgNVBAsM\nB1Rlc3RpbmcxEDAOBgNVBAMMB1Jvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDyRXMUPa/pLzERcaE+/TkYmiH83MfGhJ2eSeEqY+vjuzePT/p3\nGDLuM5IcprukEuif+4HbvLQC8zIdNjO+Xwb/Acg7njQfNTV9b8IzOn3r/k4ZcB2G\nrK7Kae338xAmkTYjACrTUnbUsdRmbRKZZCIk69fxlC4xhCp+7sZyKc4FOkGI4dOe\nTVkLN3DQflzR7mF78yCgbr2HBXoF2Eazv008YqTDS2gL3JbXIpt2H7Z8fNZHupbk\nB0gydAxmMSUnwBeXLkVkKFOJNDjjGtY4nDKOpuirEx8K0GYs6Mo0iFTwi4IDxjzM\njP5uarn1hx5bAFpuN1r1jbzb0iopZAOhmG2TAgMBAAGjYDBeMB0GA1UdDgQWBBRP\nVu26VxJGE8+W4yzBkWRpW5rnBTAfBgNVHSMEGDAWgBRPVu26VxJGE8+W4yzBkWRp\nW5rnBTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsF\nAAOCAQEAjejhP+g9DbB/vgjUJbk5sRgBeq0sX8ghtq1B+LEgKV+l/h9dmsavChjF\nRWzEhfxK83iMJX8dAXbloYSgmXNSmh/7RfXQEBPbwzD9CyZXWa+HC4Rfv0YGvdua\nliyXVxHiND4wGiv/GyTOcGJ9P5zjB+svo7rnIHvC6vJyo0hp17AuBSZDWBWt096U\nQHwJ3DrLY09OpMwfLZaduEzH9Vpf+rkQrTAo0/jSmhCDPBtLx/c+PKptUiOjvsuc\nDlaKBh7s5OBjGDUUqWDmFhcQCEy4eg0KDaAfCUrjoXFK4yf+TAxVpNBCoUCaGOqk\nGvK7TZAfHoGSAxTuls/Y0N/StPSojw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/test-root-ca.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDyRXMUPa/pLzER\ncaE+/TkYmiH83MfGhJ2eSeEqY+vjuzePT/p3GDLuM5IcprukEuif+4HbvLQC8zId\nNjO+Xwb/Acg7njQfNTV9b8IzOn3r/k4ZcB2GrK7Kae338xAmkTYjACrTUnbUsdRm\nbRKZZCIk69fxlC4xhCp+7sZyKc4FOkGI4dOeTVkLN3DQflzR7mF78yCgbr2HBXoF\n2Eazv008YqTDS2gL3JbXIpt2H7Z8fNZHupbkB0gydAxmMSUnwBeXLkVkKFOJNDjj\nGtY4nDKOpuirEx8K0GYs6Mo0iFTwi4IDxjzMjP5uarn1hx5bAFpuN1r1jbzb0iop\nZAOhmG2TAgMBAAECggEAB1A2DMVemwu+pR0oq0MaRyDX/SRuWhLEw0jNpZRF7vuc\nI2pcUDNdzjemUHvEAaGZ020p2rKT6Ibjhg2N0FBZYLdIDmyp0XYEuwL1pMro5DKA\nCbU1tEwK1rdjOe86Fz/zl2jjjLovi3TMHu9p9qbBf8XSy0sYKIx/JT0bUR+Wmte7\n+3LrmbftOOAG+9GRr3twjQnadbncLI5getRtbR6vpOrdX/eYvF29k+UT8earGFp8\ngohB0wJUvHyPZUDnbqL96BbxZvOG3ZcRdVlft5PdlPkXY9cPSa0KDq4OabmFFwlt\ncM+/zIIGyxNc9Yh9yDRa5Da8Xtr/JjV1dmYgHteROQKBgQD7PbQj/rBGCmH0+E54\nSZGelSCwb/RifeAOOeG1Obms0gbUcadQ/KQiz5jZEaEKQnC1Ews/HSNKmByLQw0+\n2++2lFpxdpg/8JZoTRVggd05gXgkV9PIRMEvAUaiJiHtjdBAUJ74kbWUkZA3lMDI\ntVRHIcJgMHLtU3CPaiyq9/AbtwKBgQD23EAkFrl2zDiqwEMx0nNF4XbX8BSon3uk\nJWRPoGWLPq8ybWE6TgFKzCTjx+zsc0wSaI/lLb62dJq47v7jYRxEfSy/4mZ52gQf\nS2vZ65iQIKKe79oLPLjENi/A0mjM2df3Fx0CB3LJQwSEazKwe8V4B3KeK81idqkp\nwR/Qcy01BQKBgEtZUxhkfutSm9RDUA1lSwX7haVEvk93nuXFWDroyBXbm27Fcz+n\ntXY3OokHb3vLN1AnGP1huL7bZdwiTOuoPHlOft1+iuTKO+GmFJ4v9HAVszl7Gan7\nbNCzGkLxGsXK/UT8qOC1mnanPVBeDX9kWpVGu6vre9xPZPeuCR1xZJJ7AoGAc+Iu\n5gIY7DCwRU/d+0xsupg9vt7AA+xiEUtQTKTiJjy157k7FDC8II22n4shqFnzkwys\nyAvyZBpW64ud8cWLjIcqc6VnL7pthvdT2MflJXt8e5nixLWrkshRIHZlpgx5ek/K\nWUJ/2wTv4O2lrP1dVJxCbQfo8Vj8zlIPij4XMbUCgYAYJIeMgZcxF6kR3GpIbKBh\nz7lOPy4dSc/vfY9Kr/hn+0Y7wQb0qmRQxD50fsg39jpAVeqdgWppoaSUYX0vRr/w\n3od2/VydT2/pcJk6Qcm7lCsnwPgvL1pD0wYO+DlNYGVaXWGKhW4CvxQFnTTMM2ZS\n7Kgofn95IuQH3FOWYSMg6Q==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssl/test-signing-ca.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 1 (0x1)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=GB, ST=Derbyshire, L=Derby, O=Mosquitto Project, OU=Testing, CN=Root CA\n        Validity\n            Not Before: Jul  7 22:27:28 2025 GMT\n            Not After : Jul  6 22:27:28 2030 GMT\n        Subject: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:b8:5b:81:cb:99:5e:85:66:42:fe:c4:f4:c6:ad:\n                    f9:9a:b4:c5:29:bb:05:5b:90:b2:a0:7d:ba:00:a3:\n                    f3:ec:8f:81:c4:45:98:79:6c:3e:04:77:26:dd:d9:\n                    9f:57:6a:dc:56:25:ba:60:c9:7d:cc:fe:40:e1:e9:\n                    75:02:a7:56:0e:bb:a8:a5:d1:b4:b1:57:62:d1:07:\n                    67:23:77:ae:3e:41:ba:22:6f:b8:f3:3d:ac:5f:1f:\n                    93:cb:62:33:93:61:0b:b1:33:be:ee:67:bb:b1:4b:\n                    2a:3e:c3:99:e9:ea:51:ca:79:ac:91:ce:e6:ee:99:\n                    86:85:f0:a4:11:91:2b:ef:9f:cf:6d:8c:8f:0a:f8:\n                    69:5f:42:c5:ee:90:ca:fe:da:6f:c6:20:56:9d:ac:\n                    eb:b0:64:ab:a5:fc:9d:c2:db:ac:a9:a7:40:e1:60:\n                    00:9f:87:f3:51:9f:64:7e:f8:4a:8e:7f:aa:95:f3:\n                    4f:04:b0:ea:ee:77:0b:3e:27:bf:9a:b4:62:1f:76:\n                    6d:a4:be:eb:e2:c3:c6:be:87:6d:f3:fb:ec:a0:09:\n                    41:ef:8d:35:90:3f:fb:88:f8:fe:e2:25:cd:ff:c2:\n                    72:13:d0:bd:fe:40:5e:9d:80:9f:7c:b0:ea:54:bd:\n                    69:70:1f:cf:2f:c3:97:40:ee:ef:97:d6:c6:1d:16:\n                    6b:01\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Subject Key Identifier: \n                C8:63:DE:29:66:24:95:5D:B2:37:6D:F6:A2:01:85:AE:31:0F:8E:45\n            X509v3 Authority Key Identifier: \n                4F:56:ED:BA:57:12:46:13:CF:96:E3:2C:C1:91:64:69:5B:9A:E7:05\n            X509v3 Basic Constraints: critical\n                CA:TRUE\n            X509v3 Key Usage: \n                Certificate Sign, CRL Sign\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        07:ba:ab:2e:42:0c:ac:98:05:af:a0:49:44:db:36:4f:a1:15:\n        40:ec:4d:46:ca:ae:f0:ad:aa:1c:d0:99:11:6c:32:ce:62:b5:\n        3d:71:ab:1d:ed:cb:3e:3a:38:de:20:25:6f:02:fd:1f:b9:d7:\n        8d:5f:d1:d4:46:ea:a7:c5:fa:70:fc:aa:0f:63:a3:bb:dc:93:\n        6b:9d:1c:9c:64:f4:3f:ac:09:60:b7:d5:3d:b2:c5:df:58:44:\n        24:a4:bf:a9:d2:c3:0c:9d:f0:06:b1:20:f6:7d:1b:55:c4:f8:\n        96:84:1d:b1:b9:3d:06:16:ab:fd:cf:97:1d:ff:6d:e2:6d:fe:\n        da:36:7e:4c:d6:f8:1c:df:b7:79:58:ce:ea:c4:50:3a:14:86:\n        f8:55:21:77:90:ec:bf:af:82:29:c8:a7:5c:91:b9:33:e0:ee:\n        8a:6c:80:f1:e1:a5:55:26:a1:46:bb:d1:eb:59:d1:1c:90:a4:\n        19:54:e7:68:0c:b6:c7:9e:b0:d0:e3:12:24:a4:92:96:1c:bd:\n        82:cc:1f:3d:f4:a6:de:69:77:83:25:3a:02:ee:64:38:f7:ae:\n        ef:06:09:06:3d:c2:19:0f:54:06:d3:e4:0c:e2:bd:be:e3:1c:\n        ac:a7:e0:c2:50:37:b4:8b:4b:0f:58:71:24:00:6b:7a:71:1e:\n        7b:f5:a2:71\n-----BEGIN CERTIFICATE-----\nMIIDsjCCApqgAwIBAgIBATANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJHQjET\nMBEGA1UECAwKRGVyYnlzaGlyZTEOMAwGA1UEBwwFRGVyYnkxGjAYBgNVBAoMEU1v\nc3F1aXR0byBQcm9qZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRAwDgYDVQQDDAdSb290\nIENBMB4XDTI1MDcwNzIyMjcyOFoXDTMwMDcwNjIyMjcyOFowZTELMAkGA1UEBhMC\nR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxGjAYBgNVBAoMEU1vc3F1aXR0byBQcm9q\nZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRMwEQYDVQQDDApTaWduaW5nIENBMIIBIjAN\nBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuFuBy5lehWZC/sT0xq35mrTFKbsF\nW5CyoH26AKPz7I+BxEWYeWw+BHcm3dmfV2rcViW6YMl9zP5A4el1AqdWDruopdG0\nsVdi0QdnI3euPkG6Im+48z2sXx+Ty2Izk2ELsTO+7me7sUsqPsOZ6epRynmskc7m\n7pmGhfCkEZEr75/PbYyPCvhpX0LF7pDK/tpvxiBWnazrsGSrpfydwtusqadA4WAA\nn4fzUZ9kfvhKjn+qlfNPBLDq7ncLPie/mrRiH3ZtpL7r4sPGvodt8/vsoAlB7401\nkD/7iPj+4iXN/8JyE9C9/kBenYCffLDqVL1pcB/PL8OXQO7vl9bGHRZrAQIDAQAB\no2AwXjAdBgNVHQ4EFgQUyGPeKWYklV2yN232ogGFrjEPjkUwHwYDVR0jBBgwFoAU\nT1btulcSRhPPluMswZFkaVua5wUwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMC\nAQYwDQYJKoZIhvcNAQELBQADggEBAAe6qy5CDKyYBa+gSUTbNk+hFUDsTUbKrvCt\nqhzQmRFsMs5itT1xqx3tyz46ON4gJW8C/R+5141f0dRG6qfF+nD8qg9jo7vck2ud\nHJxk9D+sCWC31T2yxd9YRCSkv6nSwwyd8AaxIPZ9G1XE+JaEHbG5PQYWq/3Plx3/\nbeJt/to2fkzW+Bzft3lYzurEUDoUhvhVIXeQ7L+vginIp1yRuTPg7opsgPHhpVUm\noUa70etZ0RyQpBlU52gMtseesNDjEiSkkpYcvYLMHz30pt5pd4MlOgLuZDj3ru8G\nCQY9whkPVAbT5Azivb7jHKyn4MJQN7SLSw9YcSQAa3pxHnv1onE=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/ssl/test-signing-ca.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4W4HLmV6FZkL+\nxPTGrfmatMUpuwVbkLKgfboAo/Psj4HERZh5bD4Edybd2Z9XatxWJbpgyX3M/kDh\n6XUCp1YOu6il0bSxV2LRB2cjd64+Qboib7jzPaxfH5PLYjOTYQuxM77uZ7uxSyo+\nw5np6lHKeayRzubumYaF8KQRkSvvn89tjI8K+GlfQsXukMr+2m/GIFadrOuwZKul\n/J3C26ypp0DhYACfh/NRn2R++EqOf6qV808EsOrudws+J7+atGIfdm2kvuviw8a+\nh23z++ygCUHvjTWQP/uI+P7iJc3/wnIT0L3+QF6dgJ98sOpUvWlwH88vw5dA7u+X\n1sYdFmsBAgMBAAECggEACT7eYgfb7SqLo2JDElaCq5PZ3w9kvX3viP8oXZFBxOd0\nVXo8cAgR9I0szLh19l9C4JEkAs5oE2h23lckCdEhjqIyDlE97VAZtRKL3upihl3E\n4dnV6sdfhllVKXO2n9mglmKNdnOLJRReo7PEjse0RWHxWevVIG2Top9gYwvzdtQ1\nA6B7QYclU0J4r1st7XxvaJFJZPaobXkWUZNnzZnui0EIiULDWVgOCq5qaSaKqc6s\nEdTxG+/jWYBU3DA+/Sz2ZvQvbj1+j1OXJgRedIRW+r8LH2SF9ZQCGxY1Vw9hdfUD\n1yTf5nRjdbkh2wt23G1bbLGuhHVUTWylOez6qt+AZwKBgQD6P9vCjOoAFrPA31P7\nGOjLtIjrROZ+puRLh00JGZXxW/0A+vgNZuox9mfHlGaJWgg0zIoX9HPuXy3/6ogL\nQN9QDuX7EONoVcjfWysCQatIGS2kxkPrv0ZEX9dnJ33lA+rc8ByFEib1lsM9PQGK\nW3BlABIZRcJsXyF+XyTYK0+8MwKBgQC8mAakcNiX2G/OTwgu7SLSI+daSPebLTJ4\niomuZTuoMLsVE6T501389mRyZiEkBw3f6UBb2DZf8KIhMk9Y9Ba7uQRpMpo8Cd9N\nPHAs+Rv9UnWunw9HpKCqWyKiYwtBunKlQg5yQSvWe9j3VtUSszzX6XetuYm4DYqn\n3QK7la6H+wKBgH5LiipGmbYPvwpA645XBO4Bn/Q0oqsaqS7hCuTjz8OurCI5hsSk\nwt8SP0//Ojxpfqi+7ZanXXbY/Esi3yPmyo0J59FstYgreyQWS79oyvupEVsOYKry\nrpDFWd2KlcPl1TtJxur1vUnGm6QlTMi52yBuB7RPe47b9/hiJiMewK/3AoGAJUHX\nVhchAuZ0OAqu8C5SybbkFpcBq3tDVELyLiy7m199Jg3KcrxJ/hZjA6Kfe3GVUR3Q\nZBSTsWJldS9uM4GNGCrV7z5a7+93WNfOxWO1HtdyfjvYFew0/VKhxfjRGXwO+AzT\ns8iiM24mD77suxQDuhfaV8ymo2CxerYTuyE36I8CgYEAgXAAjUxGLPG11KvgzyOI\nHXBzg1/84vURHE6VsxcPWq6mw887qpmqkf6FJ9cU/J4r4PU3aGc1b8Ts0dGW2O+H\nDwEOE6ZeXinPO4mMif3eOt80zc4REF00cJjTNgE4bDPubhFFMXN6kLi9GO6pkdlc\n9c8/rdrxzTv16tvuveO28G4=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/unit/CMakeLists.txt",
    "content": "find_package(CUnit REQUIRED)\n\nadd_library(common-unit-test-header INTERFACE)\ntarget_include_directories(common-unit-test-header\n    INTERFACE\n        \"${mosquitto_SOURCE_DIR}/include\"\n        \"${mosquitto_SOURCE_DIR}/common\"\n        \"${mosquitto_SOURCE_DIR}/deps\"\n        \"${mosquitto_SOURCE_DIR}/lib\"\n        \"${mosquitto_SOURCE_DIR}/src\"\n        \"${mosquitto_SOURCE_DIR}/test\"\n)\ntarget_link_libraries(common-unit-test-header\n    INTERFACE\n        common-options\n        cJSON\n        CUnit::CUnit\n)\n\nadd_subdirectory(libcommon)\nadd_subdirectory(broker)\nadd_subdirectory(lib)\n"
  },
  {
    "path": "test/unit/Makefile",
    "content": "R=../..\ninclude ${R}/config.mk\n\n.PHONY: all check test test-compile ptest clean\n\nall :\n\ntest-compile:\n\t$(MAKE) -C libcommon $@\n\t$(MAKE) -C broker $@\n\t$(MAKE) -C lib $@\n\ncheck : test\n\nptest : test\ntest : test-compile\n\t$(MAKE) -C libcommon $@\n\t$(MAKE) -C broker $@\n\t$(MAKE) -C lib $@\n\nreallyclean : clean\nclean :\n\t$(MAKE) -C libcommon $@\n\t$(MAKE) -C broker $@\n\t$(MAKE) -C lib $@\n"
  },
  {
    "path": "test/unit/broker/CMakeLists.txt",
    "content": "# bridge-topic-test\nadd_library(bridge-topic-obj\n\tOBJECT\n\t../../../src/bridge_topic.c\n)\ntarget_compile_definitions(bridge-topic-obj PRIVATE WITH_BRIDGE WITH_BROKER)\ntarget_include_directories(bridge-topic-obj PRIVATE ${mosquitto_SOURCE_DIR}/libcommon)\ntarget_link_libraries(bridge-topic-obj PUBLIC common-unit-test-header)\n\nadd_executable(bridge-topic-test\n    bridge_topic_test.c\n    stubs.c\n)\ntarget_compile_definitions(bridge-topic-test PRIVATE WITH_BRIDGE WITH_BROKER)\ntarget_link_libraries(bridge-topic-test PRIVATE bridge-topic-obj common-unit-test-header libmosquitto_common OpenSSL::SSL)\nadd_test(NAME unit-bridge-topic-test COMMAND bridge-topic-test)\n\n# keepalive-test\nadd_executable(keepalive-test\n    keepalive_test.c\n    keepalive_stubs.c\n)\ntarget_compile_definitions(keepalive-test PRIVATE WITH_BROKER)\ntarget_link_libraries(keepalive-test PRIVATE common-unit-test-header libmosquitto_common OpenSSL::SSL)\nadd_test(NAME unit-keepalive-test COMMAND keepalive-test)\n\n# persist-read-test\nadd_library(persistence-read-obj\n    OBJECT\n        ../../../src/database.c\n        ../../../src/persist_read_v234.c\n        ../../../src/persist_read_v5.c\n        ../../../src/persist_read.c\n        ../../../src/retain.c\n        ../../../src/topic_tok.c\n)\ntarget_compile_definitions(persistence-read-obj PRIVATE WITH_PERSISTENCE WITH_BROKER)\ntarget_link_libraries(persistence-read-obj PUBLIC common-unit-test-header OpenSSL::SSL)\n\nadd_executable(persist-read-test\n    persist_read_test.c\n    persist_read_stubs.c\n    ../../../lib/packet_datatypes.c\n    ../../../lib/property_mosq.c\n    ../../../lib/util_mosq.c\n)\ntarget_compile_definitions(persist-read-test PRIVATE TEST_SOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\" WITH_PERSISTENCE WITH_BROKER)\ntarget_include_directories(persist-read-test PRIVATE ${mosquitto_SOURCE_DIR}/libcommon)\ntarget_link_libraries(persist-read-test PRIVATE persistence-read-obj libmosquitto_common)\nadd_test(NAME unit-persist-read-test COMMAND persist-read-test)\n\n# persist-write-test\nadd_library(persistence-write-obj\n    OBJECT\n        ../../../src/database.c\n        ../../../src/persist_read_v234.c\n        ../../../src/persist_read_v5.c\n        ../../../src/persist_read.c\n        ../../../src/persist_write_v5.c\n        ../../../src/persist_write.c\n        ../../../src/retain.c\n        ../../../src/subs.c\n        ../../../src/topic_tok.c\n)\ntarget_compile_definitions(persistence-write-obj PRIVATE WITH_PERSISTENCE WITH_BROKER)\ntarget_include_directories(persistence-write-obj PRIVATE ${mosquitto_SOURCE_DIR}/libcommon)\ntarget_link_libraries(persistence-write-obj PUBLIC common-unit-test-header)\n\nadd_executable(persist-write-test\n    persist_write_test.c\n    persist_write_stubs.c\n    ../../../lib/packet_datatypes.c\n    ../../../lib/property_mosq.c\n    ../../../lib/util_mosq.c\n    ../../../lib/packet_mosq.c\n)\ntarget_compile_definitions(persist-write-test PRIVATE TEST_SOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\" WITH_PERSISTENCE WITH_BROKER WITH_SYS_TREE)\ntarget_include_directories(persist-write-test PRIVATE ${mosquitto_SOURCE_DIR}/libcommon)\ntarget_link_libraries(persist-write-test PRIVATE persistence-write-obj OpenSSL::SSL libmosquitto_common)\nadd_test(NAME unit-persist-write-test COMMAND persist-write-test)\n\n# subs-test\nadd_library(subs-obj\n    OBJECT\n        ../../../lib/property_mosq.c\n        ../../../lib/packet_datatypes.c\n        ../../../src/database.c\n        ../../../src/subs.c\n        ../../../src/topic_tok.c\n)\ntarget_compile_definitions(subs-obj PRIVATE WITH_BROKER)\ntarget_include_directories(subs-obj PRIVATE ${mosquitto_SOURCE_DIR}/libcommon)\ntarget_link_libraries(subs-obj PUBLIC common-unit-test-header)\n\nadd_executable(subs-test\n    subs_stubs.c\n    subs_test.c\n)\n\ntarget_compile_definitions(subs-test PRIVATE WITH_PERSISTENCE WITH_BROKER WITH_SYS_TREE)\ntarget_link_libraries(subs-test PRIVATE common-unit-test-header subs-obj libmosquitto_common OpenSSL::SSL)\nadd_test(NAME unit-subs-test COMMAND subs-test)\n"
  },
  {
    "path": "test/unit/broker/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\ninclude ${R}/make/broker.mk\ninclude ${R}/make/unit-test.mk\n\n.PHONY: all check test test-compile clean coverage\n\nLOCAL_CFLAGS+=-coverage -ggdb\nLOCAL_CPPFLAGS+=-DWITH_BROKER -I${R}/src -I${R}/test -I${R}/lib -DTEST_SOURCE_DIR='\"$(realpath .)\"' -I${R}/lib -I${R}/libcommon\nLOCAL_LDFLAGS+=-coverage\nLOCAL_LDADD+=-lcunit ${LIBMOSQ_COMMON}\n\nALL_TESTS:=keepalive_test subs_test\n\nifeq ($(WITH_BRIDGE),yes)\n\tALL_TESTS+=bridge_topic_test\nendif\n\nifeq ($(WITH_PERSISTENCE),yes)\n\tALL_TESTS+=persist_read_test persist_write_test\nendif\n\nifeq ($(WITH_TLS),yes)\n\tLOCAL_LDADD+=-lssl -lcrypto\nendif\n\nBRIDGE_TOPIC_TEST_OBJS = \\\n\t\tbridge_topic_test.o \\\n\t\tstubs.o \\\n\nBRIDGE_TOPIC_OBJS = \\\n\t\t${R}/src/bridge_topic.o \\\n\t\t${R}/src/packet_datatypes.o \\\n\t\t${R}/src/property_mosq.o\n\nKEEPALIVE_TEST_OBJS = \\\n\t\tkeepalive_stubs.o \\\n\t\tkeepalive_test.o\n\nKEEPALIVE_OBJS =\n\nPERSIST_READ_TEST_OBJS = \\\n\t\tpersist_read_test.o \\\n\t\tpersist_read_stubs.o\n\nPERSIST_READ_OBJS = \\\n\t\t${R}/src/database.o \\\n\t\t${R}/src/packet_datatypes.o \\\n\t\t${R}/src/persist_read.o \\\n\t\t${R}/src/persist_read_v234.o \\\n\t\t${R}/src/persist_read_v5.o \\\n\t\t${R}/src/property_mosq.o \\\n\t\t${R}/src/retain.o \\\n\t\t${R}/src/topic_tok.o \\\n\t\t${R}/src/util_mosq.o\n\nPERSIST_WRITE_TEST_OBJS = \\\n\t\tpersist_write_test.o \\\n\t\tpersist_write_stubs.o\n\nPERSIST_WRITE_OBJS = \\\n\t\t${R}/src/database.o \\\n\t\t${R}/src/packet_datatypes.o \\\n\t\t${R}/src/packet_mosq.o \\\n\t\t${R}/src/persist_read.o \\\n\t\t${R}/src/persist_read_v234.o \\\n\t\t${R}/src/persist_read_v5.o \\\n\t\t${R}/src/persist_write.o \\\n\t\t${R}/src/persist_write_v5.o \\\n\t\t${R}/src/property_mosq.o \\\n\t\t${R}/src/retain.o \\\n\t\t${R}/src/subs.o \\\n\t\t${R}/src/topic_tok.o \\\n\t\t${R}/src/util_mosq.o\n\nSUBS_TEST_OBJS = \\\n\t\tsubs_test.o \\\n\t\tsubs_stubs.o\n\nSUBS_OBJS = \\\n\t\t${R}/src/database.o \\\n\t\t${R}/src/packet_datatypes.o \\\n\t\t${R}/src/property_mosq.o \\\n\t\t${R}/src/subs.o \\\n\t\t${R}/src/topic_tok.o\n\nall : test-compile\n\ncheck : test\n\nbridge_topic_test : ${BRIDGE_TOPIC_TEST_OBJS} ${BRIDGE_TOPIC_OBJS}\n\t$(CROSS_COMPILE)$(CC) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LDADD)\n\nkeepalive_test : ${KEEPALIVE_TEST_OBJS} ${KEEPALIVE_OBJS}\n\t$(CROSS_COMPILE)$(CC) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LDADD)\n\npersist_read_test : ${PERSIST_READ_TEST_OBJS} ${PERSIST_READ_OBJS}\n\t$(CROSS_COMPILE)$(CC) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LDADD)\n\npersist_write_test : ${PERSIST_WRITE_TEST_OBJS} ${PERSIST_WRITE_OBJS}\n\t$(CROSS_COMPILE)$(CC) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LDADD)\n\nsubs_test : ${SUBS_TEST_OBJS} ${SUBS_OBJS}\n\t$(CROSS_COMPILE)$(CC) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LDADD)\n\n\n${BRIDGE_TOPIC_TEST_OBJS} : %.o: %.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n${KEEPALIVE_TEST_OBJS} : %.o: %.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n${PERSIST_READ_TEST_OBJS} : %.o: %.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n${PERSIST_WRITE_TEST_OBJS} : %.o: %.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n${SUBS_TEST_OBJS} : %.o: %.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\n\n${R}/src/bridge_topic.o : ${R}/src/bridge_topic.c\n\t$(MAKE) -C ${R}/src/ bridge_topic.o\n\n${R}/src/database.o : ${R}/src/database.c\n\t$(MAKE) -C ${R}/src/ database.o\n\n${R}/src/packet_datatypes.o : ${R}/lib/packet_datatypes.c\n\t$(MAKE) -C ${R}/src/ packet_datatypes.o\n\n${R}/src/packet_mosq.o : ${R}/lib/packet_mosq.c\n\t$(MAKE) -C ${R}/src/ packet_mosq.o\n\n${R}/src/persist_read.o : ${R}/src/persist_read.c\n\t$(MAKE) -C ${R}/src/ persist_read.o\n\n${R}/src/persist_read_v234.o : ${R}/src/persist_read_v234.c\n\t$(MAKE) -C ${R}/src/ persist_read_v234.o\n\n${R}/src/persist_read_v5.o : ${R}/src/persist_read_v5.c\n\t$(MAKE) -C ${R}/src/ persist_read_v5.o\n\n${R}/src/persist_write.o : ${R}/src/persist_write.c\n\t$(MAKE) -C ${R}/src/ persist_write.o\n\n${R}/src/persist_write_v5.o : ${R}/src/persist_write_v5.c\n\t$(MAKE) -C ${R}/src/ persist_write_v5.o\n\n${R}/src/property_mosq.o : ${R}/lib/property_mosq.c\n\t$(MAKE) -C ${R}/src/ property_mosq.o\n\n${R}/src/retain.o : ${R}/src/retain.c\n\t$(MAKE) -C ${R}/src/ retain.o\n\n${R}/src/subs.o : ${R}/src/subs.c\n\t$(MAKE) -C ${R}/src/ subs.o\n\n${R}/src/topic_tok.o : ${R}/src/topic_tok.c\n\t$(MAKE) -C ${R}/src/ topic_tok.o\n\n${R}/src/util_mosq.o : ${R}/lib/util_mosq.c\n\t$(MAKE) -C ${R}/src/ util_mosq.o\n\nbuild : ${ALL_TESTS}\n\ntest : build\n\tset -e; for t in ${ALL_TESTS}; do ${SANITIZER_COMMAND} ./$${t}; done\n\ntest-compile: build\n\nclean :\n\t-rm -rf ${ALL_TESTS}\n\t-rm -rf *.o *.gcda *.gcno coverage.info *.vglog out/\n\ncoverage :\n\tlcov --capture --directory . --output-file coverage.info\n\tgenhtml coverage.info --output-directory out\n"
  },
  {
    "path": "test/unit/broker/bridge_topic_test.c",
    "content": "#include \"config.h\"\n#include <stdio.h>\n\n#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"mosquitto_broker_internal.h\"\n#include \"property_mosq.h\"\n#include \"packet_mosq.h\"\n\n\nstatic void map_valid_helper(const char *topic, const char *local_prefix, const char *remote_prefix, const char *incoming, const char *expected)\n{\n\tstruct mosquitto mosq;\n\tstruct mosquitto__bridge bridge;\n\tchar *map_topic;\n\tint rc;\n\n\tmemset(&mosq, 0, sizeof(struct mosquitto));\n\tmemset(&bridge, 0, sizeof(struct mosquitto__bridge));\n\n\tmosq.bridge = &bridge;\n\n\trc = bridge__add_topic(&bridge, topic, bd_in, 0, local_prefix, remote_prefix);\n\tCU_ASSERT_EQUAL(rc, 0);\n\n\tmap_topic = strdup(incoming);\n\trc = bridge__remap_topic_in(&mosq, &map_topic);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_PTR_NOT_NULL(map_topic);\n\tif(map_topic){\n\t\tCU_ASSERT_STRING_EQUAL(map_topic, expected);\n\t\tfree(map_topic);\n\t}\n\n\tbridge__cleanup_topics(&bridge);\n}\n\n\nstatic void map_invalid_helper(const char *topic, const char *local_prefix, const char *remote_prefix)\n{\n\tstruct mosquitto mosq;\n\tstruct mosquitto__bridge bridge;\n\tint rc;\n\n\tmemset(&mosq, 0, sizeof(struct mosquitto));\n\tmemset(&bridge, 0, sizeof(struct mosquitto__bridge));\n\n\tmosq.bridge = &bridge;\n\n\trc = bridge__add_topic(&bridge, topic, bd_in, 0, local_prefix, remote_prefix);\n\tCU_ASSERT_NOT_EQUAL(rc, 0);\n\n\tbridge__cleanup_topics(&bridge);\n}\n\n\nstatic void TEST_remap_valid(void)\n{\n\t/* Examples from man page */\n\tmap_valid_helper(\"pattern\", \"L/\", \"R/\", \"R/pattern\", \"L/pattern\");\n\tmap_valid_helper(\"pattern\", \"L/\", NULL, \"pattern\", \"L/pattern\");\n\tmap_valid_helper(\"pattern\", NULL, \"R/\", \"R/pattern\", \"pattern\");\n\tmap_valid_helper(\"pattern\", NULL, NULL, \"pattern\", \"pattern\");\n\tmap_valid_helper(NULL, \"local\", \"remote\", \"remote\", \"local\");\n}\n\n\nstatic void TEST_remap_invalid(void)\n{\n\t/* Examples from man page */\n\tmap_invalid_helper(NULL, \"L/\", NULL);\n\tmap_invalid_helper(NULL, NULL, \"R/\");\n\tmap_invalid_helper(NULL, NULL, NULL);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_bridge_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Bridge remap\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit Bridge remap test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Remap valid\", TEST_remap_valid)\n\t\t\t|| !CU_add_test(test_suite, \"Remap invalid\", TEST_remap_invalid)\n\t\t\t){\n\n\t\tprintf(\"Error adding Bridge remap CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tunsigned int fails;\n\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tif(CU_initialize_registry() != CUE_SUCCESS){\n\t\tprintf(\"Error initializing CUnit registry.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| init_bridge_tests()\n\t\t\t){\n\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tCU_basic_set_mode(CU_BRM_VERBOSE);\n\tCU_basic_run_tests();\n\tfails = CU_get_number_of_failures();\n\tCU_cleanup_registry();\n\n\treturn (int)fails;\n}\n\n"
  },
  {
    "path": "test/unit/broker/files/persist_read/corrupt-header-long.test-db",
    "content": "corruptcorruptcorruptcorruptcorruptcorrupt\n"
  },
  {
    "path": "test/unit/broker/files/persist_read/corrupt-header-short.test-db",
    "content": "corrupt\n"
  },
  {
    "path": "test/unit/broker/files/persist_read/empty.test-db",
    "content": ""
  },
  {
    "path": "test/unit/broker/keepalive_stubs.c",
    "content": "#include <time.h>\n\n#include <logging_mosq.h>\n#include <mosquitto_broker_internal.h>\n#include <net_mosq.h>\n#include <send_mosq.h>\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\tUNUSED(mosq);\n\tUNUSED(priority);\n\tUNUSED(fmt);\n\n\treturn 0;\n}\n\n\nbool net__is_connected(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\treturn true;\n}\n\n\nvoid loop__update_next_event(time_t new_ms)\n{\n\tUNUSED(new_ms);\n}\n"
  },
  {
    "path": "test/unit/broker/keepalive_test.c",
    "content": "/* Tests for keepalive add/remove/update. */\n\n#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"keepalive.c\"\n\n#include \"mosquitto_internal.h\"\n#include \"mosquitto_broker_internal.h\"\n\nstruct mosquitto_db db;\n\n\nvoid do_disconnect(struct mosquitto *context, int reason)\n{\n\tUNUSED(reason);\n\n#ifndef WITH_OLD_KEEPALIVE\n\tkeepalive__remove(context);\n#else\n\tUNUSED(context);\n#endif\n}\n\n\n#ifndef WITH_OLD_KEEPALIVE\n\n\nstatic void TEST_single_client(void)\n{\n\tstruct mosquitto context;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(db));\n\tmemset(&context, 0, sizeof(context));\n\n\tdb.now_s = 1000;\n\tdb.config = calloc(1, sizeof(struct mosquitto__config));\n\tCU_ASSERT_PTR_NOT_NULL(db.config);\n\tif(db.config == NULL){\n\t\treturn;\n\t}\n\tdb.config->max_keepalive = 2000;\n\n\tcontext.id = strdup(\"clientid1\");\n\tcontext.keepalive = 60;\n\tcontext.last_msg_in = db.now_s;\n\n\trc = keepalive__init();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tfree(context.id);\n\t\treturn;\n\t}\n\tCU_ASSERT_EQUAL(keepalive_list_max, 3001);\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list);\n\n\trc = keepalive__add(&context);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list[context.last_msg_in + context.keepalive*3/2]);\n\n\tkeepalive__check();\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list[context.last_msg_in + context.keepalive*3/2]);\n\n\t/* Should be just before the client expires */\n\tdb.now_s = 1090;\n\tkeepalive__check();\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list[context.last_msg_in + context.keepalive*3/2]);\n\n\t/* Should be just as the client expires */\n\tdb.now_s = 1091;\n\tkeepalive__check();\n\tCU_ASSERT_PTR_NULL(keepalive_list[context.last_msg_in + context.keepalive*3/2]);\n\n\tkeepalive__cleanup();\n\n\tfree(db.config);\n\tfree(context.id);\n}\n\n\nstatic void TEST_single_client_update(void)\n{\n\tstruct mosquitto context;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(db));\n\tmemset(&context, 0, sizeof(context));\n\n\tdb.now_s = 1000;\n\tdb.config = calloc(1, sizeof(struct mosquitto__config));\n\tCU_ASSERT_PTR_NOT_NULL(db.config);\n\tif(db.config == NULL){\n\t\treturn;\n\t}\n\tdb.config->max_keepalive = 2000;\n\n\tcontext.id = strdup(\"clientid1\");\n\tcontext.keepalive = 60;\n\tcontext.last_msg_in = db.now_s;\n\n\trc = keepalive__init();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tfree(context.id);\n\t\treturn;\n\t}\n\tCU_ASSERT_EQUAL(keepalive_list_max, 3001);\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list);\n\n\trc = keepalive__add(&context);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list[context.last_msg_in + context.keepalive*3/2]);\n\n\tkeepalive__check();\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list[context.last_msg_in + context.keepalive*3/2]);\n\n\tdb.now_s = 1090;\n\tkeepalive__check();\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list[context.last_msg_in + context.keepalive*3/2]);\n\n\t/* Receive a new message and do an update */\n\tkeepalive__update(&context);\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list[context.last_msg_in + context.keepalive*3/2]);\n\n\tkeepalive__cleanup();\n\n\tfree(db.config);\n\tfree(context.id);\n}\n\n\nstatic void TEST_over_max_keepalive(void)\n{\n\tstruct mosquitto context;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(db));\n\tmemset(&context, 0, sizeof(context));\n\n\tdb.now_s = 1000;\n\tdb.config = calloc(1, sizeof(struct mosquitto__config));\n\tCU_ASSERT_PTR_NOT_NULL(db.config);\n\tif(db.config == NULL){\n\t\treturn;\n\t}\n\tdb.config->max_keepalive = 2000;\n\n\tcontext.id = strdup(\"clientid1\");\n\t/* Client keepalive too big. This won't be allowed at connection time, but\n\t * may occur if max_keepalive is lowered and the config reloaded only. The\n\t * client will end up being expired most likely. */\n\tcontext.keepalive = 2001;\n\tcontext.last_msg_in = db.now_s;\n\n\trc = keepalive__init();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tfree(context.id);\n\t\treturn;\n\t}\n\tCU_ASSERT_EQUAL(keepalive_list_max, 3001);\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list);\n\n\trc = keepalive__add(&context);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list[(context.last_msg_in + context.keepalive*3/2) % keepalive_list_max]);\n\n\tkeepalive__cleanup();\n\n\tfree(db.config);\n\tfree(context.id);\n}\n\n\nstatic void TEST_100k_random_clients(void)\n{\n\tstruct mosquitto *contexts;\n\tint rc;\n\tconst int client_count = 100000;\n\tint /*client_total,*/ cur_count;\n\n\t/* This is a very crude test, adding 100k clients with random keepalive and\n\t * random last message in. */\n\n\tsrand((unsigned int)time(NULL));\n\n\tmemset(&db, 0, sizeof(db));\n\n\tcontexts = calloc((size_t)client_count, sizeof(struct mosquitto));\n\tdb.now_s = 1000;\n\tdb.config = calloc(1, sizeof(struct mosquitto__config));\n\tif(db.config == NULL){\n\t\tfree(contexts);\n\t\treturn;\n\t}\n\tCU_ASSERT_PTR_NOT_NULL(db.config);\n\tdb.config->max_keepalive = 0;\n\n\tfor(int i=0; i<client_count; i++){\n\t\tcontexts[i].id = strdup(\"clientid\");\n\t\t/* coverity[dont_call] - we don't care about rand() not being cryptographically secure here */\n\t\tcontexts[i].keepalive = (uint16_t)rand() % UINT16_MAX;\n\t\tcontexts[i].last_msg_in = rand() % 60000;\n\t}\n\n\trc = keepalive__init();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\tfree(contexts);\n\t\tfree(db.config);\n\t\treturn;\n\t}\n\tCU_ASSERT_EQUAL(keepalive_list_max, 98303);\n\tCU_ASSERT_PTR_NOT_NULL(keepalive_list);\n\n\tfor(int i=0; i<client_count; i++){\n\t\trc = keepalive__add(&contexts[i]);\n\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t}\n\n\t/* Count clients */\n\t/* client_total = 0; */\n\tfor(int i=0; i<keepalive_list_max; i++){\n\t\tstruct mosquitto *ctx;\n\n\t\tDL_COUNT2(keepalive_list[i], ctx, cur_count, keepalive_next);\n\t\t/* client_total += cur_count; */\n\t}\n\t/* FIXME\n\tCU_ASSERT_EQUAL(client_total, client_count);\n\t*/\n\n\tfor(db.now_s = 1000; db.now_s < 100000; db.now_s++){\n\t\tkeepalive__check();\n\t}\n\tkeepalive__cleanup();\n\n\tfor(int i=0; i<client_count; i++){\n\t\tfree(contexts[i].id);\n\t}\n\tfree(contexts);\n\tfree(db.config);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_keepalive_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Keepalive\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit keepalive test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"single client\", TEST_single_client)\n\t\t\t|| !CU_add_test(test_suite, \"single client update\", TEST_single_client_update)\n\t\t\t|| !CU_add_test(test_suite, \"keepalive > max_keepalive\", TEST_over_max_keepalive)\n\t\t\t|| !CU_add_test(test_suite, \"100k random clients\", TEST_100k_random_clients)\n\t\t\t){\n\n\t\tprintf(\"Error adding keepalive CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n#endif\n\n\nint main(int argc, char *argv[])\n{\n\tunsigned int fails = 0;\n\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n#ifndef WITH_OLD_KEEPALIVE\n\tif(CU_initialize_registry() != CUE_SUCCESS){\n\t\tprintf(\"Error initializing CUnit registry.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| init_keepalive_tests()\n\t\t\t){\n\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tCU_basic_set_mode(CU_BRM_VERBOSE);\n\tCU_basic_run_tests();\n\tfails = CU_get_number_of_failures();\n\tCU_cleanup_registry();\n#endif\n\n\treturn (int)fails;\n}\n"
  },
  {
    "path": "test/unit/broker/persist_read_stubs.c",
    "content": "#include <time.h>\n\n#include <logging_mosq.h>\n#include <mosquitto_broker_internal.h>\n#include <net_mosq.h>\n#include <send_mosq.h>\n#include <callbacks.h>\n#ifndef WITH_SYS_TREE\n#  define WITH_SYS_TREE\n#endif\n#include <sys_tree.h>\n\nextern char *last_sub;\nextern int last_qos;\nextern uint32_t last_identifier;\n\nstruct mosquitto *context__init(void)\n{\n\tstruct mosquitto *m;\n\n\tm = mosquitto_calloc(1, sizeof(struct mosquitto));\n\tif(m){\n\t\tm->msgs_in.inflight_maximum = 20;\n\t\tm->msgs_out.inflight_maximum = 20;\n\t\tm->msgs_in.inflight_quota = 20;\n\t\tm->msgs_out.inflight_quota = 20;\n\t}\n\treturn m;\n}\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\tUNUSED(mosq);\n\tUNUSED(priority);\n\tUNUSED(fmt);\n\n\treturn 0;\n}\n\n\nbool net__is_connected(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\treturn false;\n}\n\n\nint net__socket_close(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint net__socket_shutdown(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pingreq(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void *payload, uint8_t qos, bool retain, mosquitto_property *properties, int access)\n{\n\tUNUSED(context);\n\tUNUSED(topic);\n\tUNUSED(payloadlen);\n\tUNUSED(payload);\n\tUNUSED(qos);\n\tUNUSED(retain);\n\tUNUSED(properties);\n\tUNUSED(access);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint acl__find_acls(struct mosquitto *context)\n{\n\tUNUSED(context);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint sub__add(struct mosquitto *context, const struct mosquitto_subscription *sub)\n{\n\tUNUSED(context);\n\n\tlast_sub = strdup(sub->topic_filter);\n\tlast_qos = sub->options & 0x03;\n\tlast_identifier = sub->identifier;\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid callback__on_disconnect(struct mosquitto *mosq, int rc, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(rc);\n\tUNUSED(props);\n}\n\n\nvoid context__add_to_by_id(struct mosquitto *context)\n{\n\tif(context->in_by_id == false){\n\t\tcontext->in_by_id = true;\n\t\tHASH_ADD_KEYPTR(hh_id, db.contexts_by_id, context->id, strlen(context->id), context);\n\t}\n}\n\n\nvoid context__send_will(struct mosquitto *context)\n{\n\tUNUSED(context);\n}\n\n\nvoid plugin_persist__handle_retain_msg_set(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_retain_msg_delete(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_base_msg_add(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__process_retain_events(bool force)\n{\n\tUNUSED(force);\n}\n\n\nvoid plugin_persist__queue_retain_event(struct mosquitto__base_msg *msg, int event)\n{\n\tUNUSED(msg);\n\tUNUSED(event);\n}\n\n\nint session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)\n{\n\tUNUSED(context);\n\tUNUSED(expiry_time);\n\treturn 0;\n}\n\n\nvoid mosquitto_log_printf(int level, const char *fmt, ...)\n{\n\tUNUSED(level);\n\tUNUSED(fmt);\n}\nstruct mosquitto__subhier *sub__add_hier_entry(struct mosquitto__subhier *parent, struct mosquitto__subhier **sibling, const char *topic, uint16_t len)\n{\n\tUNUSED(parent);\n\tUNUSED(sibling);\n\tUNUSED(topic);\n\tUNUSED(len);\n\n\treturn NULL;\n}\n\n\nvoid plugin_persist__handle_client_msg_add(struct mosquitto *context, const struct mosquitto__client_msg *cmsg)\n{\n\tUNUSED(context);\n\tUNUSED(cmsg);\n}\n\n\nvoid plugin_persist__handle_client_msg_delete(struct mosquitto *context, const struct mosquitto__client_msg *cmsg)\n{\n\tUNUSED(context);\n\tUNUSED(cmsg);\n}\n\n\nvoid plugin_persist__handle_client_msg_update(struct mosquitto *context, const struct mosquitto__client_msg *cmsg)\n{\n\tUNUSED(context);\n\tUNUSED(cmsg);\n}\n\n\nvoid plugin_persist__handle_client_msg_clear(struct mosquitto *context, uint8_t direction)\n{\n\tUNUSED(context);\n\tUNUSED(direction);\n}\n\n\nvoid plugin_persist__handle_base_msg_delete(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_subscription_delete(struct mosquitto *context, char *sub)\n{\n\tUNUSED(context);\n\tUNUSED(sub);\n}\n\n\nint sub__messages_queue(const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto__base_msg **base_msg)\n{\n\tUNUSED(source_id);\n\tUNUSED(topic);\n\tUNUSED(qos);\n\tUNUSED(retain);\n\t*base_msg = NULL;\n\treturn 0;\n}\n#ifdef WITH_SYS_TREE\n\n\nvoid metrics__int_inc(enum mosq_metric_type m, int64_t value)\n{\n\tUNUSED(m); UNUSED(value);\n}\n\n\nvoid metrics__int_dec(enum mosq_metric_type m, int64_t value)\n{\n\tUNUSED(m); UNUSED(value);\n}\n#endif\n\n\nint send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, uint32_t subscription_identifier, const mosquitto_property *store_props, uint32_t expiry_interval)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(topic);\n\tUNUSED(payloadlen);\n\tUNUSED(payload);\n\tUNUSED(qos);\n\tUNUSED(retain);\n\tUNUSED(dup);\n\tUNUSED(subscription_identifier);\n\tUNUSED(store_props);\n\tUNUSED(expiry_interval);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(reason_code);\n\tUNUSED(properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint sub__init(void)\n{\n\treturn MOSQ_ERR_SUCCESS;\n}\n"
  },
  {
    "path": "test/unit/broker/persist_read_test.c",
    "content": "/* Tests for persistence.\n *\n * FIXME - these need to be aggressive about finding failures, at the moment\n * they are just confirming that good behaviour works. */\n\n#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n#include \"path_helper.h\"\n\n#include \"mosquitto.h\"\n#include \"mosquitto_broker_internal.h\"\n#include \"persist.h\"\n#include \"property_mosq.h\"\n#include \"property_common.h\"\n\nchar *last_sub = NULL;\nint last_qos;\nuint32_t last_identifier;\n\nstruct mosquitto_db db;\n\n\nstatic void dummy_vprintf(const char *fmt, va_list va)\n{\n\tUNUSED(fmt);\n\tUNUSED(va);\n}\n\n\nstatic void test_cleanup(void)\n{\n\tstruct mosquitto *ctxt, *ctxt_tmp;\n\n\tHASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){\n\t\tHASH_DELETE(hh_id, db.contexts_by_id, ctxt);\n\t\tmosquitto_free(ctxt->username);\n\t\tmosquitto_free(ctxt->id);\n\t\tdb__messages_delete(ctxt, true);\n\t\tmosquitto_free(ctxt);\n\t}\n\tdb__close();\n}\n\n\nstatic void TEST_persistence_disabled(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\ttest_cleanup();\n}\n\n\nstatic void TEST_empty_file(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/empty.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\ttest_cleanup();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n}\n\n\nstatic void TEST_corrupt_header(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/corrupt-header-short.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_ERRNO);\n\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/corrupt-header-long.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, 1);\n}\n\n\nstatic void TEST_unsupported_version(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/unsupported-version.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n}\n\n\nstatic void TEST_v3_config_ok(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v3-cfg.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.last_db_id, 0x7856341200000000);\n}\n\n\nstatic void TEST_v4_config_ok(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v4-cfg.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.last_db_id, 0x7856341200000000);\n}\n\n\nstatic void TEST_v3_config_truncated(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v3-cfg-truncated.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_UNKNOWN);\n\tCU_ASSERT_EQUAL(db.last_db_id, 0);\n}\n\n\nstatic void TEST_v3_config_bad_dbid(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v3-cfg-bad-dbid.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(db.last_db_id, 0);\n}\n\n\nstatic void TEST_v3_bad_chunk(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v3-bad-chunk.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.last_db_id, 0x17);\n}\n\n\nstatic void TEST_v3_message_store(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v3-message-store.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.msg_store_count, 1);\n\tCU_ASSERT_EQUAL(db.msg_store_bytes, 7);\n\tCU_ASSERT_PTR_NOT_NULL(db.msg_store);\n\tif(db.msg_store){\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.store_id, 1);\n\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.source_id, \"source_id\");\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.source_mid, 2);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.qos, 2);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.retain, 1);\n\t\tCU_ASSERT_PTR_NOT_NULL(db.msg_store->data.topic);\n\t\tif(db.msg_store->data.topic){\n\t\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.topic, \"topic\");\n\t\t}\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.payloadlen, 7);\n\t\tif(db.msg_store->data.payloadlen == 7){\n\t\t\tCU_ASSERT_NSTRING_EQUAL(db.msg_store->data.payload, \"payload\", 7);\n\t\t}\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v3_client(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto *context;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v3-client.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_PTR_NOT_NULL(db.contexts_by_id);\n\tHASH_FIND(hh_id, db.contexts_by_id, \"client-id\", strlen(\"client-id\"), context);\n\tCU_ASSERT_PTR_NOT_NULL(context);\n\tif(context){\n\t\tCU_ASSERT_PTR_NULL(context->msgs_in.inflight);\n\t\tCU_ASSERT_PTR_NULL(context->msgs_out.inflight);\n\t\tCU_ASSERT_EQUAL(context->last_mid, 0x5287);\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v3_client_message(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto *context;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v3-client-message.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\tconfig.max_inflight_messages = 20;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_PTR_NOT_NULL(db.contexts_by_id);\n\tHASH_FIND(hh_id, db.contexts_by_id, \"client-id\", strlen(\"client-id\"), context);\n\tCU_ASSERT_PTR_NOT_NULL(context);\n\tif(context){\n\t\tCU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight);\n\t\tif(context->msgs_out.inflight){\n\t\t\tCU_ASSERT_PTR_NULL(context->msgs_out.inflight->next);\n\t\t\tCU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight->base_msg);\n\t\t\tif(context->msgs_out.inflight->base_msg){\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->ref_count, 1);\n\t\t\t\tCU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->base_msg->data.source_id, \"source_id\");\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.source_mid, 2);\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.qos, 2);\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.retain, 1);\n\t\t\t\tCU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight->base_msg->data.topic);\n\t\t\t\tif(context->msgs_out.inflight->base_msg->data.topic){\n\t\t\t\t\tCU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->base_msg->data.topic, \"topic\");\n\t\t\t\t}\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.payloadlen, 7);\n\t\t\t\tif(context->msgs_out.inflight->base_msg->data.payloadlen == 7){\n\t\t\t\t\tCU_ASSERT_NSTRING_EQUAL(context->msgs_out.inflight->base_msg->data.payload, \"payload\", 7);\n\t\t\t\t}\n\t\t\t}\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.mid, 0x73);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.qos, 1);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.retain, 0);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.direction, mosq_md_out);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.state, mosq_ms_wait_for_puback);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.dup, 0);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.subscription_identifier, 0);\n\t\t}\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v3_retain(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tretain__init();\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v3-retain.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.msg_store_count, 1);\n\tCU_ASSERT_EQUAL(db.msg_store_bytes, 7);\n\tCU_ASSERT_PTR_NOT_NULL(db.msg_store);\n\tif(db.msg_store){\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.store_id, 0x54);\n\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.source_id, \"source_id\");\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.source_mid, 2);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.qos, 2);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.retain, 1);\n\t\tCU_ASSERT_PTR_NOT_NULL(db.msg_store->data.topic);\n\t\tif(db.msg_store->data.topic){\n\t\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.topic, \"topic\");\n\t\t}\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.payloadlen, 7);\n\t\tif(db.msg_store->data.payloadlen == 7){\n\t\t\tCU_ASSERT_NSTRING_EQUAL(db.msg_store->data.payload, \"payload\", 7);\n\t\t}\n\t}\n\tCU_ASSERT_PTR_NOT_NULL(db.retains);\n\tif(db.retains){\n\t\tCU_ASSERT_STRING_EQUAL(db.retains->topic, \"\");\n\t\tCU_ASSERT_PTR_NOT_NULL(db.retains->children);\n\t\tif(db.retains->children){\n\t\t\tCU_ASSERT_STRING_EQUAL(db.retains->children->topic, \"\");\n\t\t\tCU_ASSERT_PTR_NOT_NULL(db.retains->children->children);\n\t\t\tif(db.retains->children->children){\n\t\t\t\tCU_ASSERT_STRING_EQUAL(db.retains->children->children->topic, \"topic\");\n\t\t\t}\n\t\t}\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v3_sub(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto *context;\n\tint rc;\n\n\tlast_sub = NULL;\n\tlast_qos = -1;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v3-sub.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_PTR_NOT_NULL(db.contexts_by_id);\n\tHASH_FIND(hh_id, db.contexts_by_id, \"client-id\", strlen(\"client-id\"), context);\n\tCU_ASSERT_PTR_NOT_NULL(context);\n\tif(context){\n\t\tCU_ASSERT_PTR_NOT_NULL(last_sub);\n\t\tif(last_sub){\n\t\t\tCU_ASSERT_STRING_EQUAL(last_sub, \"subscription\")\n\t\t\tfree(last_sub);\n\t\t}\n\t\tCU_ASSERT_EQUAL(last_qos, 1);\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v4_message_store(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v4-message-store.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.msg_store_count, 1);\n\tCU_ASSERT_EQUAL(db.msg_store_bytes, 7);\n\tCU_ASSERT_PTR_NOT_NULL(db.msg_store);\n\tif(db.msg_store){\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.store_id, 0xFEDCBA9876543210);\n\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.source_id, \"source_id\");\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.source_mid, 0x88);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.qos, 1);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.retain, 0);\n\t\tCU_ASSERT_PTR_NOT_NULL(db.msg_store->data.topic);\n\t\tif(db.msg_store->data.topic){\n\t\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.topic, \"topic\");\n\t\t}\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.payloadlen, 7);\n\t\tif(db.msg_store->data.payloadlen == 7){\n\t\t\tCU_ASSERT_NSTRING_EQUAL(db.msg_store->data.payload, \"payload\", 7);\n\t\t}\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_config_ok(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-cfg.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.last_db_id, 0x7856341200000000);\n}\n\n\nstatic void TEST_v5_config_truncated(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v5-cfg-truncated.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, 1);\n\tCU_ASSERT_EQUAL(db.last_db_id, 0);\n}\n\n\nstatic void TEST_v5_bad_chunk(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v5-bad-chunk.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.last_db_id, 0x17);\n}\n\n\nstatic void TEST_v6_message_store(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-message-store.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.msg_store_count, 1);\n\tCU_ASSERT_EQUAL(db.msg_store_bytes, 7);\n\tCU_ASSERT_PTR_NOT_NULL(db.msg_store);\n\tif(db.msg_store){\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.store_id, 1);\n\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.source_id, \"source_id\");\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.source_mid, 2);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.qos, 2);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.retain, 1);\n\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.topic, \"topic\");\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.payloadlen, 7);\n\t\tif(db.msg_store->data.payloadlen == 7){\n\t\t\tCU_ASSERT_NSTRING_EQUAL(db.msg_store->data.payload, \"payload\", 7);\n\t\t}\n\t\tCU_ASSERT_PTR_NULL(db.msg_store->data.properties);\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_message_store_props(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto__listener listener;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tmemset(&listener, 0, sizeof(struct mosquitto__listener));\n\tdb.config = &config;\n\n\tlistener.port = 1883;\n\tconfig.listeners = &listener;\n\tconfig.listener_count = 1;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-message-store-props.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.msg_store_count, 1);\n\tCU_ASSERT_EQUAL(db.msg_store_bytes, 7);\n\tCU_ASSERT_PTR_NOT_NULL(db.msg_store);\n\tif(db.msg_store){\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.store_id, 1);\n\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.source_id, \"source_id\");\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.source_mid, 2);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.qos, 2);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.retain, 1);\n\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.topic, \"topic\");\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.payloadlen, 7);\n\t\tif(db.msg_store->data.payloadlen == 7){\n\t\t\tCU_ASSERT_NSTRING_EQUAL(db.msg_store->data.payload, \"payload\", 7);\n\t\t}\n\t\tCU_ASSERT_PTR_NOT_NULL(db.msg_store->data.properties);\n\t\tif(db.msg_store->data.properties){\n\t\t\tCU_ASSERT_EQUAL(db.msg_store->data.properties->identifier, 1);\n\t\t\tCU_ASSERT_EQUAL(db.msg_store->data.properties->value.i8, 1);\n\t\t}\n\t\tCU_ASSERT_PTR_NOT_NULL(db.msg_store->source_listener);\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v5_client(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto *context;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v5-client.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_PTR_NOT_NULL(db.contexts_by_id);\n\tHASH_FIND(hh_id, db.contexts_by_id, \"client-id\", strlen(\"client-id\"), context);\n\tCU_ASSERT_PTR_NOT_NULL(context);\n\tif(context){\n\t\tCU_ASSERT_PTR_NULL(context->msgs_in.inflight);\n\t\tCU_ASSERT_PTR_NULL(context->msgs_out.inflight);\n\t\tCU_ASSERT_EQUAL(context->last_mid, 0x5287);\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_client(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto *context;\n\tstruct mosquitto__listener listener;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tmemset(&listener, 0, sizeof(struct mosquitto__listener));\n\tdb.config = &config;\n\n\tlistener.port = 1883;\n\tconfig.per_listener_settings = true;\n\tconfig.listeners = &listener;\n\tconfig.listener_count = 1;\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-client.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_PTR_NOT_NULL(db.contexts_by_id);\n\tHASH_FIND(hh_id, db.contexts_by_id, \"client-id\", strlen(\"client-id\"), context);\n\tCU_ASSERT_PTR_NOT_NULL(context);\n\tif(context){\n\t\tCU_ASSERT_PTR_NULL(context->msgs_in.inflight);\n\t\tCU_ASSERT_PTR_NULL(context->msgs_out.inflight);\n\t\tCU_ASSERT_EQUAL(context->last_mid, 0x5287);\n\t\tCU_ASSERT_EQUAL(context->listener, &listener);\n\t\tCU_ASSERT_PTR_NOT_NULL(context->username);\n\t\tif(context->username){\n\t\t\tCU_ASSERT_STRING_EQUAL(context->username, \"usrname\");\n\t\t}\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_client_message(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto *context;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-client-message.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_PTR_NOT_NULL(db.contexts_by_id);\n\tHASH_FIND(hh_id, db.contexts_by_id, \"client-id\", strlen(\"client-id\"), context);\n\tCU_ASSERT_PTR_NOT_NULL(context);\n\tif(context){\n\t\tCU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight);\n\t\tif(context->msgs_out.inflight){\n\t\t\tCU_ASSERT_PTR_NULL(context->msgs_out.inflight->next);\n\t\t\tCU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight->base_msg);\n\t\t\tif(context->msgs_out.inflight->base_msg){\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->ref_count, 1);\n\t\t\t\tCU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->base_msg->data.source_id, \"source_id\");\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.source_mid, 2);\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.qos, 2);\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.retain, 1);\n\t\t\t\tCU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->base_msg->data.topic, \"topic\");\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.payloadlen, 7);\n\t\t\t\tif(context->msgs_out.inflight->base_msg->data.payloadlen == 7){\n\t\t\t\t\tCU_ASSERT_NSTRING_EQUAL(context->msgs_out.inflight->base_msg->data.payload, \"payload\", 7);\n\t\t\t\t}\n\t\t\t}\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.mid, 0x73);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.qos, 1);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.retain, 0);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.direction, mosq_md_out);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.state, mosq_ms_wait_for_puback);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.dup, 0);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.subscription_identifier, 0);\n\t\t}\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_client_message_props(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto *context;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-client-message-props.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_PTR_NOT_NULL(db.contexts_by_id);\n\tHASH_FIND(hh_id, db.contexts_by_id, \"client-id\", strlen(\"client-id\"), context);\n\tCU_ASSERT_PTR_NOT_NULL(context);\n\tif(context){\n\t\tCU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight);\n\t\tif(context->msgs_out.inflight){\n\t\t\tCU_ASSERT_PTR_NULL(context->msgs_out.inflight->next);\n\t\t\tCU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight->base_msg);\n\t\t\tif(context->msgs_out.inflight->base_msg){\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->ref_count, 1);\n\t\t\t\tCU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->base_msg->data.source_id, \"source_id\");\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.source_mid, 2);\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.qos, 2);\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.retain, 1);\n\t\t\t\tCU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->base_msg->data.topic, \"topic\");\n\t\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->base_msg->data.payloadlen, 7);\n\t\t\t\tif(context->msgs_out.inflight->base_msg->data.payloadlen == 7){\n\t\t\t\t\tCU_ASSERT_NSTRING_EQUAL(context->msgs_out.inflight->base_msg->data.payload, \"payload\", 7);\n\t\t\t\t}\n\t\t\t}\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.mid, 0x73);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.qos, 1);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.retain, 0);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.direction, mosq_md_out);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.state, mosq_ms_wait_for_puback);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.dup, 0);\n\t\t\tCU_ASSERT_EQUAL(context->msgs_out.inflight->data.subscription_identifier, 1);\n\t\t}\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_retain(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-retain.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\tretain__init();\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(db.msg_store_count, 1);\n\tCU_ASSERT_EQUAL(db.msg_store_bytes, 7);\n\tCU_ASSERT_PTR_NOT_NULL(db.msg_store);\n\tif(db.msg_store){\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.store_id, 0x54);\n\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.source_id, \"source_id\");\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.source_mid, 2);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.qos, 2);\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.retain, 1);\n\t\tCU_ASSERT_STRING_EQUAL(db.msg_store->data.topic, \"topic\");\n\t\tCU_ASSERT_EQUAL(db.msg_store->data.payloadlen, 7);\n\t\tif(db.msg_store->data.payloadlen == 7){\n\t\t\tCU_ASSERT_NSTRING_EQUAL(db.msg_store->data.payload, \"payload\", 7);\n\t\t}\n\t}\n\tCU_ASSERT_PTR_NOT_NULL(db.retains);\n\tif(db.retains){\n\t\tCU_ASSERT_STRING_EQUAL(db.retains->topic, \"\");\n\t\tCU_ASSERT_PTR_NOT_NULL(db.retains->children);\n\t\tif(db.retains->children){\n\t\t\tCU_ASSERT_STRING_EQUAL(db.retains->children->topic, \"\");\n\t\t\tCU_ASSERT_PTR_NOT_NULL(db.retains->children->children);\n\t\t\tif(db.retains->children->children){\n\t\t\t\tCU_ASSERT_STRING_EQUAL(db.retains->children->children->topic, \"topic\");\n\t\t\t}\n\t\t}\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_sub(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto *context;\n\tint rc;\n\n\tlast_sub = NULL;\n\tlast_qos = -1;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-sub.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_PTR_NOT_NULL(db.contexts_by_id);\n\tHASH_FIND(hh_id, db.contexts_by_id, \"client-id\", strlen(\"client-id\"), context);\n\tCU_ASSERT_PTR_NOT_NULL(context);\n\tif(context){\n\t\tCU_ASSERT_PTR_NOT_NULL(last_sub);\n\t\tif(last_sub){\n\t\t\tCU_ASSERT_STRING_EQUAL(last_sub, \"subscription\")\n\t\t\tfree(last_sub);\n\t\t}\n\t\tCU_ASSERT_EQUAL(last_qos, 1);\n\t\tCU_ASSERT_EQUAL(last_identifier, 0x7623);\n\t}\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_base_msg_topic_0(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tlast_sub = NULL;\n\tlast_qos = -1;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-base-msg-topic-0.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\n\ttest_cleanup();\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_persist_read_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Persist read\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit persist read test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Persistence disabled\", TEST_persistence_disabled)\n\t\t\t|| !CU_add_test(test_suite, \"Empty file\", TEST_empty_file)\n\t\t\t|| !CU_add_test(test_suite, \"Corrupt header\", TEST_corrupt_header)\n\t\t\t|| !CU_add_test(test_suite, \"Unsupported version\", TEST_unsupported_version)\n\t\t\t|| !CU_add_test(test_suite, \"v3 config ok\", TEST_v3_config_ok)\n\t\t\t|| !CU_add_test(test_suite, \"v3 config bad truncated\", TEST_v3_config_truncated)\n\t\t\t|| !CU_add_test(test_suite, \"v3 config bad dbid\", TEST_v3_config_bad_dbid)\n\t\t\t|| !CU_add_test(test_suite, \"v3 bad chunk\", TEST_v3_bad_chunk)\n\t\t\t|| !CU_add_test(test_suite, \"v3 message store\", TEST_v3_message_store)\n\t\t\t|| !CU_add_test(test_suite, \"v3 client\", TEST_v3_client)\n\t\t\t|| !CU_add_test(test_suite, \"v3 client message\", TEST_v3_client_message)\n\t\t\t|| !CU_add_test(test_suite, \"v3 retain\", TEST_v3_retain)\n\t\t\t|| !CU_add_test(test_suite, \"v3 sub\", TEST_v3_sub)\n\t\t\t|| !CU_add_test(test_suite, \"v4 config ok\", TEST_v4_config_ok)\n\t\t\t|| !CU_add_test(test_suite, \"v4 message store\", TEST_v4_message_store)\n\t\t\t|| !CU_add_test(test_suite, \"v5 client\", TEST_v5_client)\n\t\t\t|| !CU_add_test(test_suite, \"v5 config bad truncated\", TEST_v5_config_truncated)\n\t\t\t|| !CU_add_test(test_suite, \"v5 bad chunk\", TEST_v5_bad_chunk)\n\t\t\t|| !CU_add_test(test_suite, \"v6 config ok\", TEST_v6_config_ok)\n\t\t\t|| !CU_add_test(test_suite, \"v6 message store\", TEST_v6_message_store)\n\t\t\t|| !CU_add_test(test_suite, \"v6 message store+props\", TEST_v6_message_store_props)\n\t\t\t|| !CU_add_test(test_suite, \"v6 client\", TEST_v6_client)\n\t\t\t|| !CU_add_test(test_suite, \"v6 client message\", TEST_v6_client_message)\n\t\t\t|| !CU_add_test(test_suite, \"v6 client message+props\", TEST_v6_client_message_props)\n\t\t\t|| !CU_add_test(test_suite, \"v6 retain\", TEST_v6_retain)\n\t\t\t|| !CU_add_test(test_suite, \"v6 sub\", TEST_v6_sub)\n\t\t\t|| !CU_add_test(test_suite, \"v6 base msg topic 0\", TEST_v6_base_msg_topic_0)\n\t\t\t){\n\n\t\tprintf(\"Error adding persist CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\n\nint main(int argc, char *argv[])\n{\n\tunsigned int fails;\n\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tlibcommon_vprintf = dummy_vprintf;\n\n\tif(CU_initialize_registry() != CUE_SUCCESS){\n\t\tprintf(\"Error initializing CUnit registry.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| init_persist_read_tests()\n\t\t\t){\n\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tCU_basic_set_mode(CU_BRM_VERBOSE);\n\tCU_basic_run_tests();\n\tfails = CU_get_number_of_failures();\n\tCU_cleanup_registry();\n\n\treturn (int)fails;\n}\n"
  },
  {
    "path": "test/unit/broker/persist_write_stubs.c",
    "content": "#include <time.h>\n\n#include <logging_mosq.h>\n#include <mosquitto_broker_internal.h>\n#include <net_mosq.h>\n#include <send_mosq.h>\n#include <callbacks.h>\n#include <sys_tree.h>\n\nextern uint64_t last_retained;\nextern char *last_sub;\nextern int last_qos;\n\nstruct mosquitto *context__init(void)\n{\n\treturn mosquitto_calloc(1, sizeof(struct mosquitto));\n}\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\tUNUSED(mosq);\n\tUNUSED(priority);\n\tUNUSED(fmt);\n\n\treturn 0;\n}\n\n\nbool net__is_connected(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\treturn false;\n}\n\n\nint net__socket_close(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint net__socket_shutdown(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pingreq(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void *payload, uint8_t qos, bool retain, mosquitto_property *properties, int access)\n{\n\tUNUSED(context);\n\tUNUSED(topic);\n\tUNUSED(payloadlen);\n\tUNUSED(payload);\n\tUNUSED(qos);\n\tUNUSED(retain);\n\tUNUSED(properties);\n\tUNUSED(access);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint acl__find_acls(struct mosquitto *context)\n{\n\tUNUSED(context);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, uint32_t subscription_identifier, const mosquitto_property *store_props, uint32_t expiry_interval)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(topic);\n\tUNUSED(payloadlen);\n\tUNUSED(payload);\n\tUNUSED(qos);\n\tUNUSED(retain);\n\tUNUSED(dup);\n\tUNUSED(subscription_identifier);\n\tUNUSED(store_props);\n\tUNUSED(expiry_interval);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(reason_code);\n\tUNUSED(properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid callback__on_disconnect(struct mosquitto *mosq, int rc, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(rc);\n\tUNUSED(props);\n}\n\n\nvoid callback__on_publish(struct mosquitto *mosq, int mid, int reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(reason_code);\n\tUNUSED(properties);\n}\n\n\nvoid do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(reason_code);\n\tUNUSED(properties);\n}\n\n\nint handle__packet(struct mosquitto *context)\n{\n\tUNUSED(context);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nssize_t net__read(struct mosquitto *mosq, void *buf, size_t count)\n{\n\tUNUSED(mosq);\n\tUNUSED(buf);\n\tUNUSED(count);\n\treturn 1;\n}\n\n\nssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count)\n{\n\tUNUSED(mosq);\n\tUNUSED(buf);\n\tUNUSED(count);\n\treturn 1;\n}\n\n\nvoid context__add_to_by_id(struct mosquitto *context)\n{\n\tif(context->in_by_id == false){\n\t\tcontext->in_by_id = true;\n\t\tHASH_ADD_KEYPTR(hh_id, db.contexts_by_id, context->id, strlen(context->id), context);\n\t}\n}\n\n\nvoid context__send_will(struct mosquitto *context)\n{\n\tUNUSED(context);\n}\n\n\nvoid plugin_persist__handle_client_msg_add(struct mosquitto *context, const struct mosquitto__client_msg *cmsg)\n{\n\tUNUSED(context);\n\tUNUSED(cmsg);\n}\n\n\nvoid plugin_persist__handle_client_msg_delete(struct mosquitto *context, const struct mosquitto__client_msg *cmsg)\n{\n\tUNUSED(context);\n\tUNUSED(cmsg);\n}\n\n\nvoid plugin_persist__handle_client_msg_update(struct mosquitto *context, const struct mosquitto__client_msg *cmsg)\n{\n\tUNUSED(context);\n\tUNUSED(cmsg);\n}\n\n\nvoid plugin_persist__handle_client_msg_clear(struct mosquitto *context, uint8_t direction)\n{\n\tUNUSED(context);\n\tUNUSED(direction);\n}\n\n\nvoid plugin_persist__handle_base_msg_add(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_base_msg_delete(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_retain_msg_set(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_retain_msg_delete(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_subscription_delete(struct mosquitto *context, char *sub)\n{\n\tUNUSED(context);\n\tUNUSED(sub);\n}\n\n\nvoid plugin_persist__process_retain_events(bool force)\n{\n\tUNUSED(force);\n}\n\n\nvoid plugin_persist__queue_retain_event(struct mosquitto__base_msg *msg, int event)\n{\n\tUNUSED(msg);\n\tUNUSED(event);\n}\n\n\nint session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)\n{\n\tUNUSED(context);\n\tUNUSED(expiry_time);\n\treturn 0;\n}\n\n\nvoid mosquitto_log_printf(int level, const char *fmt, ...)\n{\n\tUNUSED(level);\n\tUNUSED(fmt);\n}\n\n\nint keepalive__update(struct mosquitto *context)\n{\n\tUNUSED(context);\n\treturn 0;\n}\n\n\nint mux__add_out(struct mosquitto *context)\n{\n\tUNUSED(context);\n\treturn 0;\n}\n\n\nint mux__remove_out(struct mosquitto *context)\n{\n\tUNUSED(context);\n\treturn 0;\n}\n\n\nssize_t net__read_ws(struct mosquitto *mosq, void *buf, size_t count)\n{\n\tUNUSED(mosq);\n\tUNUSED(buf);\n\tUNUSED(count);\n\treturn 0;\n}\n\n\nvoid ws__prepare_packet(struct mosquitto *mosq, struct mosquitto__packet *packet)\n{\n\tUNUSED(mosq);\n\tUNUSED(packet);\n}\n\n\nint send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(reason_code);\n\tUNUSED(properties);\n\treturn 0;\n}\n\n#ifdef WITH_SYS_TREE\n\n\nvoid metrics__int_inc(enum mosq_metric_type m, int64_t value)\n{\n\tUNUSED(m); UNUSED(value);\n}\n\n\nvoid metrics__int_dec(enum mosq_metric_type m, int64_t value)\n{\n\tUNUSED(m); UNUSED(value);\n}\n#endif\n"
  },
  {
    "path": "test/unit/broker/persist_write_test.c",
    "content": "/* Tests for persistence.\n *\n * FIXME - these need to be aggressive about finding failures, at the moment\n * they are just confirming that good behaviour works. */\n\n#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n#include \"path_helper.h\"\n\n#include \"mosquitto_broker_internal.h\"\n#include \"persist.h\"\n\nuint64_t last_retained;\nchar *last_sub = NULL;\nint last_qos;\n\nstruct mosquitto_db db;\n\n\nstatic void dummy_vprintf(const char *fmt, va_list va)\n{\n\tUNUSED(fmt);\n\tUNUSED(va);\n}\n\n\nstatic void test_cleanup(void)\n{\n\tstruct mosquitto *ctxt, *ctxt_tmp;\n\n\tHASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){\n\t\tHASH_DELETE(hh_id, db.contexts_by_id, ctxt);\n\t\tmosquitto_free(ctxt->username);\n\t\tmosquitto_free(ctxt->id);\n\t\tdb__messages_delete(ctxt, true);\n\t\tsub__clean_session(ctxt);\n\t\tmosquitto_free(ctxt);\n\t}\n\n\tdb__close();\n}\n\n\n/* read entire file into memory */\nstatic int file_read(const char *filename, uint8_t **data, size_t *len)\n{\n\tFILE *fptr;\n\tsize_t rc;\n\tlong llen;\n\n\tfptr = fopen(filename, \"rb\");\n\tif(!fptr){\n\t\treturn 1;\n\t}\n\n\tfseek(fptr, 0, SEEK_END);\n\tllen = ftell(fptr);\n\tif(llen < 0){\n\t\tfclose(fptr);\n\t\treturn 1;\n\t}\n\t*len = (size_t)llen;\n\t*data = malloc(*len);\n\tif(!(*data)){\n\t\tfclose(fptr);\n\t\treturn 1;\n\t}\n\tfseek(fptr, 0, SEEK_SET);\n\trc = fread(*data, 1, *len, fptr);\n\tfclose(fptr);\n\n\tif(rc == *len){\n\t\treturn 0;\n\t}else{\n\t\t*len = 0;\n\t\tfree(*data);\n\t\treturn 1;\n\t}\n}\n\n\n/* Crude file diff, only for small files */\nstatic int file_diff(const char *one, const char *two)\n{\n\tsize_t len1, len2;\n\tuint8_t *data1 = NULL, *data2 = NULL;\n\tint rc = 1;\n\n\tif(file_read(one, &data1, &len1)){\n\t\treturn 1;\n\t}\n\n\tif(file_read(two, &data2, &len2)){\n\t\tfree(data1);\n\t\treturn 1;\n\t}\n\n\tif(len1 == len2){\n\t\trc = memcmp(data1, data2, len1);\n\t}\n\tfree(data1);\n\tfree(data2);\n\n\treturn rc;\n}\n\n\nstatic void TEST_persistence_disabled(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\tconfig.persistence = true;\n\n\trc = persist__backup(false);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\n\tconfig.persistence_filepath = \"disabled.db\";\n\trc = persist__backup(false);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\ttest_cleanup();\n}\n\n\nstatic void TEST_empty_file(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\n\tconfig.persistence_filepath = \"empty.db\";\n\trc = persist__backup(false);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_write/empty.test-db\");\n\tCU_ASSERT_EQUAL(0, file_diff(persistence_filepath, \"empty.db\"));\n\tunlink(\"empty.db\");\n\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_config_ok(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-cfg.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tconfig.persistence_filepath = \"v6-cfg.db\";\n\trc = persist__backup(true);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_EQUAL(0, file_diff(persistence_filepath, \"v6-cfg.db\"));\n\tunlink(\"v6-cfg.db\");\n\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_message_store_no_ref(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-message-store.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tconfig.persistence_filepath = \"v6-message-store-no-ref.db\";\n\trc = persist__backup(true);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tchar persistence_filepath_no_ref[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath_no_ref, \"/files/persist_write/v6-message-store-no-ref.test-db\");\n\tCU_ASSERT_EQUAL(0, file_diff(persistence_filepath_no_ref, \"v6-message-store-no-ref.db\"));\n\tunlink(\"v6-message-store-no-ref.db\");\n\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_message_store_props(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto__listener listener;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tmemset(&listener, 0, sizeof(struct mosquitto__listener));\n\tdb.config = &config;\n\tlistener.port = 1883;\n\tconfig.per_listener_settings = true;\n\tconfig.listeners = &listener;\n\tconfig.listener_count = 1;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-message-store-props.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tconfig.persistence_filepath = \"v6-message-store-props.db\";\n\trc = persist__backup(true);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_EQUAL(0, file_diff(persistence_filepath, \"v6-message-store-props.db\"));\n\tunlink(\"v6-message-store-props.db\");\n\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_client(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto__listener listener;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tmemset(&listener, 0, sizeof(struct mosquitto__listener));\n\tdb.config = &config;\n\tlistener.port = 1883;\n\tconfig.per_listener_settings = true;\n\tconfig.listeners = &listener;\n\tconfig.listener_count = 1;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-client.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tconfig.persistence_filepath = \"v6-client.db\";\n\trc = persist__backup(true);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_EQUAL(0, file_diff(persistence_filepath, \"v6-client.db\"));\n\tunlink(\"v6-client.db\");\n\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_client_message(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto__listener listener;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tmemset(&listener, 0, sizeof(struct mosquitto__listener));\n\tdb.config = &config;\n\tlistener.port = 1883;\n\tconfig.per_listener_settings = true;\n\tconfig.listeners = &listener;\n\tconfig.listener_count = 1;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-client-message.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tconfig.persistence_filepath = \"v6-client-message.db\";\n\trc = persist__backup(true);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_EQUAL(0, file_diff(persistence_filepath, \"v6-client-message.db\"));\n\tunlink(\"v6-client-message.db\");\n\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_client_message_props(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto__listener listener;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tmemset(&listener, 0, sizeof(struct mosquitto__listener));\n\tdb.config = &config;\n\tlistener.port = 1883;\n\tconfig.per_listener_settings = true;\n\tconfig.listeners = &listener;\n\tconfig.listener_count = 1;\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-client-message-props.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_PTR_NOT_NULL(db.msg_store);\n\tif(db.msg_store){\n\t\tCU_ASSERT_PTR_NOT_NULL(db.msg_store->source_listener);\n\t\tif(db.msg_store->source_listener){\n\t\t\tCU_ASSERT_EQUAL(db.msg_store->source_listener->port, 1883);\n\t\t}\n\t}\n\n\tconfig.persistence_filepath = \"v6-client-message-props.db\";\n\trc = persist__backup(true);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_EQUAL(0, file_diff(persistence_filepath, \"v6-client-message-props.db\"));\n\t//unlink(\"v6-client-message-props.db\");\n\n\ttest_cleanup();\n}\n\n\nstatic void TEST_v6_sub(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto__listener listener;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tmemset(&listener, 0, sizeof(struct mosquitto__listener));\n\tdb.config = &config;\n\tlistener.port = 1883;\n\tconfig.per_listener_settings = true;\n\tconfig.listeners = &listener;\n\tconfig.listener_count = 1;\n\n\tdb__open(&config);\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_read/v6-sub.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tconfig.persistence_filepath = \"v6-sub.db\";\n\trc = persist__backup(true);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_EQUAL(0, file_diff(persistence_filepath, \"v6-sub.db\"));\n\tunlink(\"v6-sub.db\");\n\n\ttest_cleanup();\n}\n\n\n#if 0\n\n\nNOT WORKING\nstatic void TEST_v5_full(void)\n{\n\tstruct mosquitto__config config;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tdb.config = &config;\n\n\tdb__open(&config);\n\n\tconfig.persistence = true;\n\tchar persistence_filepath[4096];\n\tcat_sourcedir_with_relpath(persistence_filepath, \"/files/persist_write/v5-full.test-db\");\n\tconfig.persistence_filepath = persistence_filepath;\n\trc = persist__restore();\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tconfig.persistence_filepath = \"v5-full.db\";\n\trc = persist__backup(true);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\n\tCU_ASSERT_EQUAL(0, file_diff(persistence_filepath, \"v5-full.db\"));\n\tunlink(\"v5-full.db\");\n}\n#endif\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint main(int argc, char *argv[])\n{\n\tCU_pSuite test_suite = NULL;\n\tunsigned int fails;\n\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tlibcommon_vprintf = dummy_vprintf;\n\n\tif(CU_initialize_registry() != CUE_SUCCESS){\n\t\tprintf(\"Error initializing CUnit registry.\\n\");\n\t\treturn 1;\n\t}\n\n\ttest_suite = CU_add_suite(\"Persist write\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit persist write test suite.\\n\");\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Persistence disabled\", TEST_persistence_disabled)\n\t\t\t|| !CU_add_test(test_suite, \"Empty file\", TEST_empty_file)\n\t\t\t|| !CU_add_test(test_suite, \"v6 config ok\", TEST_v6_config_ok)\n\t\t\t|| !CU_add_test(test_suite, \"v6 message store (message has no refs)\", TEST_v6_message_store_no_ref)\n\t\t\t|| !CU_add_test(test_suite, \"v6 message store + props\", TEST_v6_message_store_props)\n\t\t\t|| !CU_add_test(test_suite, \"v6 client\", TEST_v6_client)\n\t\t\t|| !CU_add_test(test_suite, \"v6 client message\", TEST_v6_client_message)\n\t\t\t|| !CU_add_test(test_suite, \"v6 client message+props\", TEST_v6_client_message_props)\n\t\t\t|| !CU_add_test(test_suite, \"v6 sub\", TEST_v6_sub)\n\t        //|| !CU_add_test(test_suite, \"v5 full\", TEST_v5_full)\n\t\t\t){\n\n\t\tprintf(\"Error adding persist CUnit tests.\\n\");\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tCU_basic_set_mode(CU_BRM_VERBOSE);\n\tCU_basic_run_tests();\n\tfails = CU_get_number_of_failures();\n\tCU_cleanup_registry();\n\n\treturn (int)fails;\n}\n"
  },
  {
    "path": "test/unit/broker/stubs.c",
    "content": "#include \"config.h\"\n\n#include <sys/types.h>\n#include \"callbacks.h\"\n#include \"logging_mosq.h\"\n#include \"net_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n\nstruct mosquitto_db {\n\n};\n\nstruct mosquitto__base_msg {\n\n};\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\tUNUSED(mosq);\n\tUNUSED(priority);\n\tUNUSED(fmt);\n\n\treturn 0;\n}\n\n\nint mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void *payload, uint8_t qos, bool retain, int access)\n{\n\tUNUSED(context);\n\tUNUSED(topic);\n\tUNUSED(payloadlen);\n\tUNUSED(payload);\n\tUNUSED(qos);\n\tUNUSED(retain);\n\tUNUSED(access);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nbool net__is_connected(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\treturn false;\n}\n\n\nint net__socket_close(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pingreq(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid callback__on_disconnect(struct mosquitto *mosq, int rc, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(rc);\n\tUNUSED(props);\n}\n\n\nvoid callback__on_publish(struct mosquitto *mosq, int mid, int reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(reason_code);\n\tUNUSED(properties);\n}\n\n\nvoid do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(reason_code);\n\tUNUSED(properties);\n}\n\n\nint handle__packet(struct mosquitto *context)\n{\n\tUNUSED(context);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nssize_t net__read(struct mosquitto *mosq, void *buf, size_t count)\n{\n\tUNUSED(mosq);\n\tUNUSED(buf);\n\tUNUSED(count);\n\treturn 1;\n}\n\n\nssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count)\n{\n\tUNUSED(mosq);\n\tUNUSED(buf);\n\tUNUSED(count);\n\treturn 1;\n}\n\n\nvoid plugin_persist__handle_retain_set(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_retain_remove(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__process_retain_events(bool force)\n{\n\tUNUSED(force);\n}\n\n\nvoid plugin_persist__queue_retain_event(struct mosquitto__base_msg *msg, int event)\n{\n\tUNUSED(msg);\n\tUNUSED(event);\n}\n\n\nvoid ws__prepare_packet(struct mosquitto *mosq, struct mosquitto__packet *packet)\n{\n\tUNUSED(mosq);\n\tUNUSED(packet);\n}\n\n\nssize_t net__read_ws(struct mosquitto *mosq, void *buf, size_t count)\n{\n\tUNUSED(mosq);\n\tUNUSED(buf);\n\tUNUSED(count);\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/broker/subs_stubs.c",
    "content": "#include <time.h>\n\n#include <logging_mosq.h>\n#include <mosquitto_broker_internal.h>\n#include <net_mosq.h>\n#include <send_mosq.h>\n#include <util_mosq.h>\n#include <logging_mosq.h>\n#include <persist.h>\n#include <sys_tree.h>\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\tUNUSED(mosq);\n\tUNUSED(priority);\n\tUNUSED(fmt);\n\n\treturn 0;\n}\n\n\nbool net__is_connected(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\treturn false;\n}\n\n\nint send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, uint32_t subscription_identifier, const mosquitto_property *store_props, uint32_t expiry_interval)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(topic);\n\tUNUSED(payloadlen);\n\tUNUSED(payload);\n\tUNUSED(qos);\n\tUNUSED(retain);\n\tUNUSED(dup);\n\tUNUSED(subscription_identifier);\n\tUNUSED(store_props);\n\tUNUSED(expiry_interval);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(reason_code);\n\tUNUSED(properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(properties);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void *payload, uint8_t qos, bool retain, mosquitto_property *properties, int access)\n{\n\tUNUSED(context);\n\tUNUSED(topic);\n\tUNUSED(payloadlen);\n\tUNUSED(payload);\n\tUNUSED(qos);\n\tUNUSED(retain);\n\tUNUSED(properties);\n\tUNUSED(access);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nuint16_t mosquitto__mid_generate(struct mosquitto *mosq)\n{\n\tstatic uint16_t mid = 1;\n\n\tUNUSED(mosq);\n\n\treturn ++mid;\n}\n\n\nint persist__backup(bool shutdown)\n{\n\tUNUSED(shutdown);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint persist__restore(void)\n{\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint retain__init(void)\n{\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid retain__expiry_check(void)\n{\n}\n\n\nvoid retain__clean(struct mosquitto__retainhier **retainhier)\n{\n\tUNUSED(retainhier);\n}\n\n\nint retain__queue(struct mosquitto *context, const struct mosquitto_subscription *sub)\n{\n\tUNUSED(context);\n\tUNUSED(sub);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint retain__store(const char *topic, struct mosquitto__base_msg *stored, char **split_topics, bool persist)\n{\n\tUNUSED(topic);\n\tUNUSED(stored);\n\tUNUSED(split_topics);\n\tUNUSED(persist);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid util__decrement_receive_quota(struct mosquitto *mosq)\n{\n\tif(mosq->msgs_in.inflight_quota > 0){\n\t\tmosq->msgs_in.inflight_quota--;\n\t}\n}\n\n\nvoid util__decrement_send_quota(struct mosquitto *mosq)\n{\n\tif(mosq->msgs_out.inflight_quota > 0){\n\t\tmosq->msgs_out.inflight_quota--;\n\t}\n}\n\n\nvoid util__increment_receive_quota(struct mosquitto *mosq)\n{\n\tmosq->msgs_in.inflight_quota++;\n}\n\n\nvoid util__increment_send_quota(struct mosquitto *mosq)\n{\n\tmosq->msgs_out.inflight_quota++;\n}\n\n\nvoid plugin_persist__handle_client_msg_add(struct mosquitto *context, const struct mosquitto__client_msg *cmsg)\n{\n\tUNUSED(context);\n\tUNUSED(cmsg);\n}\n\n\nvoid plugin_persist__handle_client_msg_delete(struct mosquitto *context, const struct mosquitto__client_msg *cmsg)\n{\n\tUNUSED(context);\n\tUNUSED(cmsg);\n}\n\n\nvoid plugin_persist__handle_client_msg_update(struct mosquitto *context, const struct mosquitto__client_msg *cmsg)\n{\n\tUNUSED(context);\n\tUNUSED(cmsg);\n}\n\n\nvoid plugin_persist__handle_client_msg_clear(struct mosquitto *context, uint8_t direction)\n{\n\tUNUSED(context);\n\tUNUSED(direction);\n}\n\n\nvoid plugin_persist__handle_base_msg_add(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_base_msg_delete(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_retain_msg_add(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_retain_msg_delete(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_subscription_delete(struct mosquitto *context, char *sub)\n{\n\tUNUSED(context);\n\tUNUSED(sub);\n}\n\n\nint session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)\n{\n\tUNUSED(context);\n\tUNUSED(expiry_time);\n\treturn 0;\n}\n\n#ifdef WITH_SYS_TREE\n\n\nvoid metrics__int_inc(enum mosq_metric_type m, int64_t value)\n{\n\tUNUSED(m); UNUSED(value);\n}\n\n\nvoid metrics__int_dec(enum mosq_metric_type m, int64_t value)\n{\n\tUNUSED(m); UNUSED(value);\n}\n#endif\n"
  },
  {
    "path": "test/unit/broker/subs_test.c",
    "content": "/* Tests for subscription adding/removing\n *\n * FIXME - these need to be aggressive about finding failures, at the moment\n * they are just confirming that good behaviour works. */\n\n#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"mosquitto_broker_internal.h\"\n\nstruct mosquitto_db db;\n\n\nstatic void hier_quick_check(struct mosquitto__subhier **sub, struct mosquitto *context, const char *topic)\n{\n\tif(sub != NULL){\n\t\tCU_ASSERT_EQUAL((*sub)->topic_len, strlen(topic));\n\t\tCU_ASSERT_STRING_EQUAL((*sub)->topic, topic);\n\t\tif(context){\n\t\t\tCU_ASSERT_PTR_NOT_NULL((*sub)->subs);\n\t\t\tif((*sub)->subs){\n\t\t\t\tCU_ASSERT_PTR_EQUAL((*sub)->subs->context, context);\n\t\t\t\tCU_ASSERT_PTR_NULL((*sub)->subs->next);\n\t\t\t}\n\t\t}else{\n\t\t\tCU_ASSERT_PTR_NULL((*sub)->subs);\n\t\t}\n\t\t(*sub) = (*sub)->children;\n\t}\n}\n\n\nstatic void TEST_sub_add_single(void)\n{\n\tstruct mosquitto__config config;\n\tstruct mosquitto__listener listener;\n\tstruct mosquitto context;\n\tstruct mosquitto__subhier *subhier;\n\tstruct mosquitto_subscription sub;\n\tint rc;\n\n\tmemset(&db, 0, sizeof(struct mosquitto_db));\n\tmemset(&config, 0, sizeof(struct mosquitto__config));\n\tmemset(&listener, 0, sizeof(struct mosquitto__listener));\n\tmemset(&context, 0, sizeof(struct mosquitto));\n\tmemset(&sub, 0, sizeof(sub));\n\n\tcontext.id = \"client\";\n\n\tdb.config = &config;\n\tlistener.port = 1883;\n\tconfig.listeners = &listener;\n\tconfig.listener_count = 1;\n\n\tdb__open(&config);\n\n\tsub.topic_filter = \"a/b/c/d/e\";\n\trc = sub__add(&context, &sub);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(db.shared_subs);\n\tCU_ASSERT_PTR_NOT_NULL(db.normal_subs);\n\tif(db.normal_subs){\n\t\tsubhier = db.normal_subs;\n\n\t\thier_quick_check(&subhier, NULL, \"\");\n\t\thier_quick_check(&subhier, NULL, \"\");\n\t\thier_quick_check(&subhier, NULL, \"a\");\n\t\thier_quick_check(&subhier, NULL, \"b\");\n\t\thier_quick_check(&subhier, NULL, \"c\");\n\t\thier_quick_check(&subhier, NULL, \"d\");\n\t\thier_quick_check(&subhier, &context, \"e\");\n\t\tCU_ASSERT_PTR_NULL(subhier);\n\t}\n\tmosquitto_free(context.subs);\n\tdb__close();\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint main(int argc, char *argv[])\n{\n\tCU_pSuite test_suite = NULL;\n\tunsigned int fails;\n\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tif(CU_initialize_registry() != CUE_SUCCESS){\n\t\tprintf(\"Error initializing CUnit registry.\\n\");\n\t\treturn 1;\n\t}\n\n\ttest_suite = CU_add_suite(\"Subs\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit Subs test suite.\\n\");\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Sub add single\", TEST_sub_add_single)\n\t\t\t){\n\n\t\tprintf(\"Error adding Subs CUnit tests.\\n\");\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tCU_basic_set_mode(CU_BRM_VERBOSE);\n\tCU_basic_run_tests();\n\tfails = CU_get_number_of_failures();\n\tCU_cleanup_registry();\n\n\treturn (int)fails;\n}\n"
  },
  {
    "path": "test/unit/lib/CMakeLists.txt",
    "content": "add_executable(lib-test\n\tdatatype_read.c\n\tdatatype_write.c\n\tproperty_read.c\n\tproperty_user_read.c\n\tproperty_write.c\n\tstubs.c\n\t# main test files\n\ttest.c\n\t../../../lib/packet_datatypes.c\n\t../../../lib/packet_mosq.c\n\t../../../lib/property_mosq.c\n\t../../../lib/util_mosq.c\n)\n\ntarget_include_directories(lib-test PRIVATE ${mosquitto_SOURCE_DIR}/libcommon)\ntarget_link_libraries(lib-test PRIVATE common-unit-test-header OpenSSL::SSL libmosquitto_common)\nadd_test(NAME unit-lib-test COMMAND lib-test)\n"
  },
  {
    "path": "test/unit/lib/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all check test test-compile clean coverage\n\nLOCAL_CFLAGS+=-coverage\nLOCAL_CPPFLAGS+=-I${R}/src -I${R}/test -I${R}/lib -I${R}/libcommon -DTEST_SOURCE_DIR='\"$(realpath .)\"'\nLOCAL_LDFLAGS+=-coverage\nLOCAL_LDADD+=-lcunit ${LIBMOSQ_COMMON}\n\nifeq ($(WITH_TLS),yes)\n\tLOCAL_LDADD+=-lssl -lcrypto\nendif\n\nTEST_OBJS = \\\n\ttest.o \\\n\tdatatype_read.o \\\n\tdatatype_write.o \\\n\tproperty_read.o \\\n\tproperty_user_read.o \\\n\tproperty_write.o \\\n\tstubs.o \\\n\nLIB_OBJS = \\\n\t${R}/lib/packet_datatypes.o \\\n\t${R}/lib/packet_mosq.o \\\n\t${R}/lib/property_mosq.o \\\n\t${R}/lib/util_mosq.o\n\n\nall : test-compile\n\ncheck : test\n\nlib_test : ${TEST_OBJS} ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CC) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LDADD)\n\n${TEST_OBJS} : %.o: %.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nlib_stubs.o : stubs.c\n\t${CROSS_COMPILE}$(CC) $(LIB_LOCAL_CPPFLAGS) $(LIB_LOCAL_CFLAGS) $(CFLAGS) $(CPPFLAGS) -c $< -o $@\n\n${R}/lib/misc_mosq.o : ${R}/common/misc_mosq.c\n\t$(MAKE) -C ${R}/lib/ misc_mosq.o\n\n${R}/lib/packet_datatypes.o : ${R}/lib/packet_datatypes.c\n\t$(MAKE) -C ${R}/lib/ packet_datatypes.o\n\n${R}/lib/packet_mosq.o : ${R}/lib/packet_mosq.c\n\t$(MAKE) -C ${R}/lib/ packet_mosq.o\n\n${R}/lib/property_mosq.o : ${R}/lib/property_mosq.c\n\t$(MAKE) -C ${R}/lib/ property_mosq.o\n\n${R}/lib/util_mosq.o : ${R}/lib/util_mosq.c\n\t$(MAKE) -C ${R}/lib/ util_mosq.o\n\n${R}/lib/utf8_mosq.o : ${R}/common/utf8_mosq.c\n\t$(MAKE) -C ${R}/lib/ utf8_mosq.o\n\nbuild : lib_test\n\ntest : build\n\t./lib_test\n\ntest-compile: build\n\nclean :\n\t-rm -rf lib_test\n\t-rm -rf *.o *.gcda *.gcno coverage.info out/\n\ncoverage :\n\tlcov --capture --directory . --output-file coverage.info\n\tgenhtml coverage.info --output-directory out\n"
  },
  {
    "path": "test/unit/lib/datatype_read.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"packet_mosq.h\"\n\n\nstatic void byte_read_helper(\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint8_t value_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tuint8_t value = 0;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = packet__read_byte(&packet, &value);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(value, value_expected);\n}\n\n\nstatic void uint16_read_helper(\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint16_t value_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tuint16_t value = 0;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = packet__read_uint16(&packet, &value);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(value, value_expected);\n}\n\n\nstatic void uint32_read_helper(\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint32_t value_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tuint32_t value = 0;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = packet__read_uint32(&packet, &value);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(value, value_expected);\n}\n\n\nstatic void varint_read_helper(\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint32_t value_expected,\n\t\tuint8_t bytes_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tuint32_t value = UINT32_MAX;\n\tuint8_t bytes = UINT8_MAX;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = packet__read_varint(&packet, &value, &bytes);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(value, value_expected);\n\tCU_ASSERT_EQUAL(bytes, bytes_expected);\n}\n\n\nstatic void binary_read_helper(\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tconst uint8_t *value_expected,\n\t\tint length_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tuint8_t *value = NULL;\n\tuint16_t length = UINT16_MAX;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = packet__read_binary(&packet, (uint8_t **)&value, &length);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tif(value_expected){\n\t\t/* FIXME - this should be a memcmp */\n\t\tCU_ASSERT_NSTRING_EQUAL(value, value_expected, length_expected);\n\t}else{\n\t\tCU_ASSERT_EQUAL(value, NULL);\n\t}\n\tCU_ASSERT_EQUAL(length, length_expected);\n\tfree(value);\n}\n\n\nstatic void string_read_helper(\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tconst uint8_t *value_expected,\n\t\tuint16_t length_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tuint8_t *value = NULL;\n\tuint16_t length = UINT16_MAX;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = packet__read_string(&packet, (char **)&value, &length);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tif(value_expected){\n\t\tif(value){\n\t\t\tCU_ASSERT_NSTRING_EQUAL(value, value_expected, length_expected);\n\t\t}\n\t}else{\n\t\tCU_ASSERT_PTR_NULL(value);\n\t}\n\tCU_ASSERT_EQUAL(length, length_expected);\n\tfree(value);\n}\n\n\nstatic void bytes_read_helper(\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tconst uint8_t *value_expected,\n\t\tint count)\n{\n\tstruct mosquitto__packet_in packet;\n\tuint8_t value[count];\n\tint rc;\n\tint i;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = packet__read_bytes(&packet, value, (uint32_t)count);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tCU_ASSERT_EQUAL(packet.pos, (uint32_t)count);\n\t}\n\tif(value_expected){\n\t\tfor(i=0; i<count; i++){\n\t\t\tCU_ASSERT_EQUAL(value[i], value_expected[i]);\n\t\t}\n\t}\n}\n\n\n/* ========================================================================\n * BYTE INTEGER TESTS\n * ======================================================================== */\n\n\n/* This tests reading a Byte from an incoming packet.\n *\n * It tests:\n *  * Empty packets\n */\nstatic void TEST_byte_read_empty(void)\n{\n\t/* Empty packet */\n\tbyte_read_helper(NULL, 0, MOSQ_ERR_MALFORMED_PACKET, 0);\n}\n\n\n/* This tests reading a Byte from an incoming packet.\n *\n * It tests:\n *  * Success at boundaries\n */\nstatic void TEST_byte_read_success(void)\n{\n\tuint8_t payload[20];\n\n\t/* 0 value */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tbyte_read_helper(payload, 1, MOSQ_ERR_SUCCESS, 0x00);\n\n\t/* Middle */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x1F;\n\tbyte_read_helper(payload, 1, MOSQ_ERR_SUCCESS, 0x1F);\n\n\t/* 255 value */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0xFF;\n\tbyte_read_helper(payload, 1, MOSQ_ERR_SUCCESS, 0xFF);\n\n}\n\n\n/* ========================================================================\n * TWO BYTE INTEGER TESTS\n * ======================================================================== */\n\n\n/* This tests reading a Two Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Empty packets\n */\nstatic void TEST_uint16_read_empty(void)\n{\n\t/* Empty packet */\n\tuint16_read_helper(NULL, 0, MOSQ_ERR_MALFORMED_PACKET, 0);\n}\n\n\n/* This tests reading a Two Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Truncated packets\n */\nstatic void TEST_uint16_read_truncated(void)\n{\n\tuint8_t payload[20];\n\n\t/* 1 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x38;\n\tuint16_read_helper(payload, 1, MOSQ_ERR_MALFORMED_PACKET, 0);\n}\n\n\n/* This tests reading a Two Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Success at boundaries\n *  * Endianness\n */\nstatic void TEST_uint16_read_success(void)\n{\n\tuint8_t payload[20];\n\n\t/* 0 value */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tpayload[1] = 0x00;\n\tuint16_read_helper(payload, 2, MOSQ_ERR_SUCCESS, 0x0000);\n\n\t/* Endian check */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x38;\n\tpayload[1] = 0xF3;\n\tuint16_read_helper(payload, 2, MOSQ_ERR_SUCCESS, 0x38F3);\n\n\t/* 65,535 value */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0xFF;\n\tpayload[1] = 0xFF;\n\tuint16_read_helper(payload, 2, MOSQ_ERR_SUCCESS, 0xFFFF);\n\n}\n\n\n/* ========================================================================\n * FOUR BYTE INTEGER TESTS\n * ======================================================================== */\n\n\n/* This tests reading a Four Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Empty packets\n */\nstatic void TEST_uint32_read_empty(void)\n{\n\t/* Empty packet */\n\tuint32_read_helper(NULL, 0, MOSQ_ERR_MALFORMED_PACKET, 0);\n}\n\n\n/* This tests reading a Four Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Truncated packets\n */\nstatic void TEST_uint32_read_truncated(void)\n{\n\tuint8_t payload[20];\n\n\t/* 1 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x38;\n\tuint32_read_helper(payload, 1, MOSQ_ERR_MALFORMED_PACKET, 0);\n\n\t/* 2 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x38;\n\tpayload[1] = 0x38;\n\tuint32_read_helper(payload, 2, MOSQ_ERR_MALFORMED_PACKET, 0);\n\n\t/* 3 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x38;\n\tpayload[1] = 0x38;\n\tpayload[2] = 0x38;\n\tuint32_read_helper(payload, 3, MOSQ_ERR_MALFORMED_PACKET, 0);\n}\n\n\n/* This tests reading a Four Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Success at boundaries\n *  * Endianness\n */\nstatic void TEST_uint32_read_success(void)\n{\n\tuint8_t payload[20];\n\n\t/* 0 value */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tpayload[1] = 0x00;\n\tpayload[2] = 0x00;\n\tpayload[3] = 0x00;\n\tuint32_read_helper(payload, 4, MOSQ_ERR_SUCCESS, 0x00000000);\n\n\t/* Endian check */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x12;\n\tpayload[1] = 0x34;\n\tpayload[2] = 0x56;\n\tpayload[3] = 0x78;\n\tuint32_read_helper(payload, 4, MOSQ_ERR_SUCCESS, 0x12345678);\n\n\t/* Biggest value */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0xFF;\n\tpayload[1] = 0xFF;\n\tpayload[2] = 0xFF;\n\tpayload[3] = 0xFF;\n\tuint32_read_helper(payload, 4, MOSQ_ERR_SUCCESS, 0xFFFFFFFF);\n\n}\n\n\n/* ========================================================================\n * VARIABLE BYTE INTEGER TESTS\n * ======================================================================== */\n\n\n/* This tests reading a Variable Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Empty packet\n */\nstatic void TEST_varint_read_empty(void)\n{\n\t/* Empty packet */\n\tvarint_read_helper(NULL, 0, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n}\n\n\n/* This tests reading a Variable Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Truncated packets (varint encoding is longer than data)\n */\nstatic void TEST_varint_read_truncated(void)\n{\n\tuint8_t payload[20];\n\n\t/* Varint bigger than packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x80;\n\tvarint_read_helper(payload, 1, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n\n\t/* Varint bigger than packet */\n\tmemset(payload, 1, sizeof(payload));\n\tpayload[0] = 0x80;\n\tpayload[1] = 0x80;\n\tvarint_read_helper(payload, 2, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n\n\t/* Varint bigger than packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x80;\n\tpayload[1] = 0x80;\n\tpayload[2] = 0x80;\n\tvarint_read_helper(payload, 3, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n\n\t/* Varint bigger than packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x80;\n\tpayload[1] = 0x80;\n\tpayload[2] = 0x80;\n\tpayload[3] = 0x80;\n\tvarint_read_helper(payload, 4, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n}\n\n\n/* This tests reading a Variable Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Correct values on boundaries of 1, 2, 3, 4 byte encodings\n */\nstatic void TEST_varint_read_boundaries(void)\n{\n\tuint8_t payload[20];\n\n\t/* Value = 0 */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tvarint_read_helper(payload, 1, MOSQ_ERR_SUCCESS, 0, 1);\n\n\t/* Value = 127 (just beneath the crossover to two bytes */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x7F;\n\tvarint_read_helper(payload, 1, MOSQ_ERR_SUCCESS, 127, 1);\n\n\t/* Value = 128 (just after the crossover to two bytes */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x80;\n\tpayload[1] = 0x01;\n\tvarint_read_helper(payload, 2, MOSQ_ERR_SUCCESS, 128, 2);\n\n\t/* Value = 16383 (just before the crossover to three bytes */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0xFF;\n\tpayload[1] = 0x7F;\n\tvarint_read_helper(payload, 2, MOSQ_ERR_SUCCESS, 16383, 2);\n\n\t/* Value = 16384 (just after the crossover to three bytes */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x80;\n\tpayload[1] = 0x80;\n\tpayload[2] = 0x01;\n\tvarint_read_helper(payload, 3, MOSQ_ERR_SUCCESS, 16384, 3);\n\n\t/* Value = 2,097,151 (just before the crossover to four bytes */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0xFF;\n\tpayload[1] = 0xFF;\n\tpayload[2] = 0x7F;\n\tvarint_read_helper(payload, 3, MOSQ_ERR_SUCCESS, 2097151, 3);\n\n\t/* Value = 2,097,152 (just after the crossover to four bytes */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x80;\n\tpayload[1] = 0x80;\n\tpayload[2] = 0x80;\n\tpayload[3] = 0x01;\n\tvarint_read_helper(payload, 4, MOSQ_ERR_SUCCESS, 2097152, 4);\n\n\t/* Value = 268,435,455 (highest value allowed) */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0xFF;\n\tpayload[1] = 0xFF;\n\tpayload[2] = 0xFF;\n\tpayload[3] = 0x7F;\n\tvarint_read_helper(payload, 4, MOSQ_ERR_SUCCESS, 268435455, 4);\n}\n\n\n/* This tests reading a Variable Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Too long encoding (5 bytes)\n */\nstatic void TEST_varint_read_5_bytes(void)\n{\n\tuint8_t payload[20];\n\n\t/* Value = 268,435,456 (one higher than highest value allowed) */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x80;\n\tpayload[1] = 0x80;\n\tpayload[2] = 0x80;\n\tpayload[3] = 0x80;\n\tpayload[4] = 0x01;\n\tvarint_read_helper(payload, 5, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n}\n\n\n/* This tests reading a Variable Byte Integer from an incoming packet.\n *\n * It tests:\n *  * Overlong encodings (e.g. 2 bytes to encode \"1\")\n */\nstatic void TEST_varint_read_overlong_encoding(void)\n{\n\tuint8_t payload[20];\n\n\t/* Overlong encoding of 0 (1 byte value encoded as 2 bytes) */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x80;\n\tpayload[1] = 0x00;\n\tvarint_read_helper(payload, 2, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n\n\t/* Overlong encoding of 127 (1 byte value encoded as 2 bytes) */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0xFF;\n\tpayload[1] = 0x00;\n\tvarint_read_helper(payload, 2, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n\n\t/* Overlong encoding of 128 (2 byte value encoded as 3 bytes) */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x80;\n\tpayload[1] = 0x81;\n\tpayload[2] = 0x00;\n\tvarint_read_helper(payload, 3, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n\n\t/* Overlong encoding of 16,383 (2 byte value encoded as 3 bytes) */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0xFF;\n\tpayload[1] = 0xFF;\n\tpayload[2] = 0x00;\n\tvarint_read_helper(payload, 3, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n\n\t/* Overlong encoding of 16,384 (3 byte value encoded as 4 bytes) */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x80;\n\tpayload[1] = 0x80;\n\tpayload[2] = 0x81;\n\tpayload[3] = 0x00;\n\tvarint_read_helper(payload, 4, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n\n\t/* Overlong encoding of 2,097,151 (3 byte value encoded as 4 bytes) */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0xFF;\n\tpayload[1] = 0xFF;\n\tpayload[2] = 0xFF;\n\tpayload[3] = 0x00;\n\tvarint_read_helper(payload, 4, MOSQ_ERR_MALFORMED_PACKET, UINT32_MAX, UINT8_MAX);\n}\n\n\n/* ========================================================================\n * UTF-8 STRING TESTS\n * ======================================================================== */\n\n\n/* This tests reading a UTF-8 Encoded String from an incoming packet.\n *\n * It tests:\n *  * Empty packet\n */\nstatic void TEST_string_read_empty(void)\n{\n\tstring_read_helper(NULL, 0, MOSQ_ERR_MALFORMED_PACKET, NULL, UINT16_MAX);\n}\n\n\n/* This tests reading a UTF-8 Encoded String from an incoming packet.\n *\n * It tests:\n *  * Truncated packets\n */\nstatic void TEST_string_read_truncated(void)\n{\n\tuint8_t payload[20];\n\n\t/* 1 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x02;\n\tstring_read_helper(payload, 1, MOSQ_ERR_MALFORMED_PACKET, NULL, UINT16_MAX);\n\n\t/* 2 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x02;\n\tpayload[1] = 0x02;\n\tstring_read_helper(payload, 2, MOSQ_ERR_MALFORMED_PACKET, NULL, UINT16_MAX);\n\n\t/* 3 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tpayload[1] = 0x02;\n\tpayload[2] = 'a';\n\tstring_read_helper(payload, 3, MOSQ_ERR_MALFORMED_PACKET, NULL, UINT16_MAX);\n\n\t/* 3 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tpayload[1] = 0x03;\n\tpayload[2] = 'a';\n\tpayload[3] = 'b';\n\tstring_read_helper(payload, 4, MOSQ_ERR_MALFORMED_PACKET, NULL, UINT16_MAX);\n}\n\n\n/* This tests reading a UTF-8 Encoded String from an incoming packet.\n *\n * It tests:\n *  * Empty string\n */\nstatic void TEST_string_read_empty_string(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tpayload[1] = 0x00;\n\tstring_read_helper(payload, 2, MOSQ_ERR_SUCCESS, (const uint8_t *)\"\", 0);\n}\n\n\n/* This tests reading a UTF-8 Encoded String from an incoming packet.\n *\n * It tests:\n *  * Valid string\n */\nstatic void TEST_string_read_valid_string(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tpayload[1] = 0x0b;\n\tpayload[2] = 't';\n\tpayload[3] = 'e';\n\tpayload[4] = 's';\n\tpayload[5] = 't';\n\tpayload[6] = ' ';\n\tpayload[7] = 's';\n\tpayload[8] = 't';\n\tpayload[9] = 'r';\n\tpayload[10] = 'i';\n\tpayload[11] = 'n';\n\tpayload[12] = 'g';\n\tstring_read_helper(payload, 13, MOSQ_ERR_SUCCESS, (const uint8_t *)\"test string\", 11);\n}\n\n\n/* This tests reading a UTF-8 Encoded String from an incoming packet.\n *\n * It tests:\n *  * Malformed UTF-8 (note, comprehensive UTF-8 tests are elsewhere)\n */\nstatic void TEST_string_read_malformed_string(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tpayload[1] = 0x07;\n\tpayload[2] = 't';\n\tpayload[3] = 'e';\n\tpayload[4] = 's';\n\tpayload[5] = 't';\n\tpayload[6] = 0xED; /* U+D800 - single UTF-16 surrogate */\n\tpayload[7] = 0xA0;\n\tpayload[8] = 0x80;\n\tstring_read_helper(payload, 9, MOSQ_ERR_MALFORMED_UTF8, NULL, 0);\n}\n\n\n/* This tests reading a UTF-8 Encoded String from an incoming packet.\n *\n * It tests:\n *  * MQTT-1.5.4-3 - Is 0xEF 0xBB 0xBF treated correctly.\n */\nstatic void TEST_string_read_mqtt_1_5_4_3(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tpayload[1] = 0x0b;\n\tpayload[2] = 't';\n\tpayload[3] = 'e';\n\tpayload[4] = 's';\n\tpayload[5] = 't';\n\tpayload[6] = 0xEF; /* U+FEFF - zero with no-break space */\n\tpayload[7] = 0xBB;\n\tpayload[8] = 0xBF;\n\tpayload[9] = 't';\n\tpayload[10] = 'e';\n\tpayload[11] = 's';\n\tpayload[12] = 't';\n\tstring_read_helper(payload, 13, MOSQ_ERR_SUCCESS, &payload[2], 11);\n}\n\n\n/* ========================================================================\n * BINARY DATA TESTS\n * ======================================================================== */\n\n\n/* This tests reading Binary Data from an incoming packet.\n *\n * It tests:\n *  * Empty packet\n */\nstatic void TEST_binary_data_read_empty(void)\n{\n\tbinary_read_helper(NULL, 0, MOSQ_ERR_MALFORMED_PACKET, NULL, UINT16_MAX);\n}\n\n\n/* This tests reading Binary Data from an incoming packet.\n *\n * It tests:\n *  * Truncated packets\n */\nstatic void TEST_binary_data_read_truncated(void)\n{\n\tuint8_t payload[20];\n\n\t/* 1 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x02;\n\tbinary_read_helper(payload, 1, MOSQ_ERR_MALFORMED_PACKET, NULL, UINT16_MAX);\n\n\t/* 2 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x02;\n\tpayload[1] = 0x02;\n\tbinary_read_helper(payload, 2, MOSQ_ERR_MALFORMED_PACKET, NULL, UINT16_MAX);\n\n\t/* 3 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tpayload[1] = 0x02;\n\tpayload[2] = 'a';\n\tbinary_read_helper(payload, 3, MOSQ_ERR_MALFORMED_PACKET, NULL, UINT16_MAX);\n\n\t/* 3 byte packet */\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 0x00;\n\tpayload[1] = 0x03;\n\tpayload[2] = 'a';\n\tpayload[3] = 'b';\n\tbinary_read_helper(payload, 4, MOSQ_ERR_MALFORMED_PACKET, NULL, UINT16_MAX);\n}\n\n\n/* ========================================================================\n * MULTIPLE BYTE TESTS\n * ======================================================================== */\n\n\n/* This tests reading multiple bytes (payload) from an incoming packet.\n *\n * It tests:\n *  * Empty packets\n */\nstatic void TEST_bytes_read_empty(void)\n{\n\t/* Empty packet */\n\tbytes_read_helper(NULL, 0, MOSQ_ERR_SUCCESS, NULL, 0);\n}\n\n\n/* This tests reading multiple bytes (payload) from an incoming packet.\n *\n * It tests:\n *  * Truncated packets\n */\nstatic void TEST_bytes_read_truncated(void)\n{\n\tbytes_read_helper(NULL, 0, MOSQ_ERR_MALFORMED_PACKET, NULL, 1);\n}\n\n\n/* This tests reading multiple bytes from an incoming packet.\n *\n * It tests:\n *  * Success\n */\nstatic void TEST_bytes_read_success(void)\n{\n\tuint8_t payload[20];\n\tuint8_t i;\n\n\tfor(i=0; i<20; i++){\n\t\tpayload[i] = (uint8_t)(i*2);\n\t}\n\tbytes_read_helper(payload, 20, MOSQ_ERR_SUCCESS, payload, 20);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_datatype_read_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Datatype read\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Byte read (empty packet)\", TEST_byte_read_empty)\n\t\t\t|| !CU_add_test(test_suite, \"Byte read (success values)\", TEST_byte_read_success)\n\t\t\t|| !CU_add_test(test_suite, \"Two Byte Integer read (empty packet)\", TEST_uint16_read_empty)\n\t\t\t|| !CU_add_test(test_suite, \"Two Byte Integer read (truncated packet)\", TEST_uint16_read_truncated)\n\t\t\t|| !CU_add_test(test_suite, \"Two Byte Integer read (success values)\", TEST_uint16_read_success)\n\t\t\t|| !CU_add_test(test_suite, \"Four Byte Integer read (empty packet)\", TEST_uint32_read_empty)\n\t\t\t|| !CU_add_test(test_suite, \"Four Byte Integer read (truncated packet)\", TEST_uint32_read_truncated)\n\t\t\t|| !CU_add_test(test_suite, \"Four Byte Integer read (success values)\", TEST_uint32_read_success)\n\t\t\t|| !CU_add_test(test_suite, \"Variable Byte Integer read (empty packet)\", TEST_varint_read_empty)\n\t\t\t|| !CU_add_test(test_suite, \"Variable Byte Integer read (truncated packets)\", TEST_varint_read_truncated)\n\t\t\t|| !CU_add_test(test_suite, \"Variable Byte Integer read (encoding boundaries)\", TEST_varint_read_boundaries)\n\t\t\t|| !CU_add_test(test_suite, \"Variable Byte Integer read (five byte encoding)\", TEST_varint_read_5_bytes)\n\t\t\t|| !CU_add_test(test_suite, \"Variable Byte Integer read (overlong encodings)\", TEST_varint_read_overlong_encoding)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 string read (empty packet)\", TEST_string_read_empty)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 string read (truncated packet)\", TEST_string_read_truncated)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 string read (empty string)\", TEST_string_read_empty_string)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 string read (valid string)\", TEST_string_read_valid_string)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 string read (malformed string)\", TEST_string_read_malformed_string)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 string read (MQTT-1.5.4-3)\", TEST_string_read_mqtt_1_5_4_3)\n\t\t\t|| !CU_add_test(test_suite, \"Binary Data read (empty packet)\", TEST_binary_data_read_empty)\n\t\t\t|| !CU_add_test(test_suite, \"Binary Data read (truncated packet)\", TEST_binary_data_read_truncated)\n\t\t\t|| !CU_add_test(test_suite, \"Bytes read (empty packet)\", TEST_bytes_read_empty)\n\t\t\t|| !CU_add_test(test_suite, \"Bytes read (truncated packet)\", TEST_bytes_read_truncated)\n\t\t\t|| !CU_add_test(test_suite, \"Bytes read (success)\", TEST_bytes_read_success)\n\t\t\t){\n\n\t\tprintf(\"Error adding Datatype read CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/lib/datatype_write.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include <arpa/inet.h>\n\n#include \"packet_mosq.h\"\n\n\n/* ========================================================================\n * BYTE TESTS\n * ======================================================================== */\n\n\n/* This tests writing a Byte to an incoming packet.  */\nstatic void TEST_byte_write(void)\n{\n\tstruct mosquitto__packet *packet;\n\tint i;\n\tint rc;\n\n\trc = packet__alloc(&packet, 0, 260);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tpacket->pos = 0; /* We don't need the command or RL parts, so make indexing easier below */\n\tfor(i=0; i<256; i++){\n\t\tpacket__write_byte(packet, (uint8_t)(255-i));\n\t}\n\n\tCU_ASSERT_EQUAL(packet->pos, 256);\n\tfor(i=0; i<256; i++){\n\t\tCU_ASSERT_EQUAL(packet->payload[i], (uint8_t)(255-i));\n\t}\n\tfree(packet);\n}\n\n\n/* ========================================================================\n * TWO BYTE INTEGER TESTS\n * ======================================================================== */\n\n\n/* This tests writing a Two Byte Integer to an incoming packet.  */\nstatic void TEST_uint16_write(void)\n{\n\tuint16_t *payload16;\n\tstruct mosquitto__packet *packet;\n\tint i;\n\tint rc;\n\n\trc = packet__alloc(&packet, 0, 650);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tpacket->pos = 0; /* We don't need the command or RL parts, so make indexing easier below */\n\tfor(i=0; i<325; i++){\n\t\tpacket__write_uint16(packet, (uint16_t)(100*i));\n\t}\n\n\tCU_ASSERT_EQUAL(packet->pos, 650);\n\tpayload16 = (uint16_t *)packet->payload;\n\tfor(i=0; i<325; i++){\n\t\tCU_ASSERT_EQUAL(payload16[i], htons((uint16_t)(100*i)));\n\t}\n\tfree(packet);\n}\n\n\n/* ========================================================================\n * FOUR BYTE INTEGER TESTS\n * ======================================================================== */\n\n\n/* This tests writing a Four Byte Integer to an incoming packet.  */\nstatic void TEST_uint32_write(void)\n{\n\tuint32_t *payload32;\n\tstruct mosquitto__packet *packet;\n\tint i;\n\n\tpacket = calloc(1, sizeof(struct mosquitto__packet) + 42000);\n\tCU_ASSERT_PTR_NOT_NULL(packet);\n\tif(packet == NULL){\n\t\treturn;\n\t}\n\n\tpacket->packet_length = 42000;\n\n\tfor(i=0; i<10500; i++){\n\t\tpacket__write_uint32(packet, (uint32_t)(1000*i));\n\t}\n\n\tCU_ASSERT_EQUAL(packet->pos, 42000);\n\tpayload32 = (uint32_t *)packet->payload;\n\tfor(i=0; i<10500; i++){\n\t\tCU_ASSERT_EQUAL(payload32[i], htonl((uint32_t)(1000*i)));\n\t}\n\tfree(packet);\n}\n\n\n/* ========================================================================\n * UTF-8 STRING TESTS\n * ======================================================================== */\n\n\n/* This tests writing a UTF-8 String to an incoming packet.  */\nstatic void TEST_string_write(void)\n{\n\tstruct mosquitto__packet *packet;\n\n\tpacket = calloc(1, sizeof(struct mosquitto__packet) + 100);\n\tCU_ASSERT_PTR_NOT_NULL(packet);\n\tif(packet == NULL){\n\t\treturn;\n\t}\n\n\tpacket->packet_length = 100;\n\n\tpacket__write_string(packet, \"first test\", strlen(\"first test\"));\n\tpacket__write_string(packet, \"second test\", strlen(\"second test\"));\n\n\tCU_ASSERT_EQUAL(packet->pos, 2+10+2+11);\n\tCU_ASSERT_EQUAL(packet->payload[0], 0);\n\tCU_ASSERT_EQUAL(packet->payload[1], 10);\n\tCU_ASSERT_NSTRING_EQUAL(packet->payload+2, \"first test\", 10);\n\tCU_ASSERT_EQUAL(packet->payload[2+10+0], 0);\n\tCU_ASSERT_EQUAL(packet->payload[2+10+1], 11);\n\tCU_ASSERT_NSTRING_EQUAL(packet->payload+2+10+2, \"second test\", 11);\n\n\tfree(packet);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_datatype_write_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Datatype write\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Byte write\", TEST_byte_write)\n\t\t\t|| !CU_add_test(test_suite, \"Two Byte Integer write\", TEST_uint16_write)\n\t\t\t|| !CU_add_test(test_suite, \"Four Byte Integer write\", TEST_uint32_write)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 String write\", TEST_string_write)\n\t\t\t){\n\n\t\tprintf(\"Error adding Datatype write CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/lib/property_read.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"property_common.h\"\n#include \"property_mosq.h\"\n#include \"packet_mosq.h\"\n\n\nstatic void byte_prop_read_helper(\n\t\tint command,\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint8_t identifier,\n\t\tuint8_t value_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = property__read_all(command, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.i8, value_expected);\n\t\tCU_ASSERT_PTR_EQUAL(properties->next, NULL);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 2);\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n}\n\n\nstatic void duplicate_byte_helper(int command, uint8_t identifier)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 4; /* Proplen = (Identifier + byte)*2 */\n\tpayload[1] = identifier;\n\tpayload[2] = 1;\n\tpayload[3] = identifier;\n\tpayload[4] = 0;\n\n\tbyte_prop_read_helper(command, payload, 5, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, 1);\n}\n\n\nstatic void bad_byte_helper(int command, uint8_t identifier)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 2; /* Proplen = Identifier + byte */\n\tpayload[1] = identifier;\n\tpayload[2] = 2; /* 0, 1 are only valid values */\n\n\tbyte_prop_read_helper(command, payload, 3, MOSQ_ERR_PROTOCOL, identifier, 0);\n}\n\n\nstatic void int32_prop_read_helper(\n\t\tint command,\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint8_t identifier,\n\t\tuint32_t value_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = property__read_all(command, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tif(rc != rc_expected){\n\t\tprintf(\"%d / %d\\n\", rc, rc_expected);\n\t}\n\tCU_ASSERT_EQUAL(packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.i32, value_expected);\n\t\tCU_ASSERT_PTR_EQUAL(properties->next, NULL);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 5);\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n}\n\n\nstatic void duplicate_int32_helper(int command, uint8_t identifier)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 10; /* Proplen = (Identifier + int32)*2 */\n\tpayload[1] = identifier;\n\tpayload[2] = 1;\n\tpayload[3] = 1;\n\tpayload[4] = 1;\n\tpayload[5] = 1;\n\tpayload[6] = identifier;\n\tpayload[7] = 0;\n\tpayload[8] = 0;\n\tpayload[9] = 0;\n\tpayload[10] = 0;\n\n\tint32_prop_read_helper(command, payload, 11, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, 1);\n}\n\n\nstatic void int16_prop_read_helper(\n\t\tint command,\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint8_t identifier,\n\t\tuint16_t value_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = property__read_all(command, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.i16, value_expected);\n\t\tCU_ASSERT_PTR_EQUAL(properties->next, NULL);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 3);\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n}\n\n\nstatic void duplicate_int16_helper(int command, uint8_t identifier)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 6; /* Proplen = (Identifier + int16)*2 */\n\tpayload[1] = identifier;\n\tpayload[2] = 1;\n\tpayload[3] = 1;\n\tpayload[4] = identifier;\n\tpayload[5] = 0;\n\tpayload[6] = 0;\n\n\tint16_prop_read_helper(command, payload, 7, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, 1);\n}\n\n\nstatic void string_prop_read_helper(\n\t\tint command,\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint8_t identifier,\n\t\tconst char *value_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = property__read_all(command, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected));\n\t\tCU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected);\n\t\tCU_ASSERT_PTR_EQUAL(properties->next, NULL);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 1+2+strlen(value_expected));\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n}\n\n\nstatic void duplicate_string_helper(int command, uint8_t identifier)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = identifier;\n\tpayload[2] = 0;\n\tpayload[3] = 1; /* 1 length string */\n\tpayload[4] = 'h';\n\tpayload[5] = identifier;\n\tpayload[6] = 0;\n\tpayload[7] = 1;\n\tpayload[8] = 'h';\n\n\tstring_prop_read_helper(command, payload, 9, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, \"\");\n}\n\n\nstatic void bad_string_helper(uint8_t identifier)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 6;\n\tpayload[1] = identifier;\n\tpayload[2] = 0;\n\tpayload[3] = 3; /* 1 length string */\n\tpayload[4] = 'h';\n\tpayload[5] = 0; /* 0 in string not allowed */\n\tpayload[6] = 'h';\n\n\tstring_prop_read_helper(CMD_PUBLISH, payload, 7, MOSQ_ERR_MALFORMED_UTF8, identifier, \"\");\n}\n\n\nstatic void binary_prop_read_helper(\n\t\tint command,\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint8_t identifier,\n\t\tconst uint8_t *value_expected,\n\t\tint len_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = property__read_all(command, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.bin.len, len_expected);\n\t\tCU_ASSERT_EQUAL(memcmp(properties->value.bin.v, value_expected, (size_t)len_expected), 0);\n\t\tCU_ASSERT_PTR_EQUAL(properties->next, NULL);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 1+2+(unsigned int)len_expected);\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n}\n\n\nstatic void duplicate_binary_helper(int command, uint8_t identifier)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = identifier;\n\tpayload[2] = 0;\n\tpayload[3] = 1; /* 2 length binary */\n\tpayload[4] = 'h';\n\tpayload[5] = identifier;\n\tpayload[6] = 0;\n\tpayload[7] = 1;\n\tpayload[8] = 'h';\n\n\tstring_prop_read_helper(command, payload, 9, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, \"\");\n}\n\n\nstatic void string_pair_prop_read_helper(\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint8_t identifier,\n\t\tconst char *name_expected,\n\t\tconst char *value_expected,\n\t\tbool expect_multiple)\n{\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = property__read_all(CMD_CONNECT, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(properties->name.len, strlen(name_expected));\n\t\tCU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected));\n\t\tCU_ASSERT_STRING_EQUAL(properties->name.v, name_expected);\n\t\tCU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected);\n\t\tif(expect_multiple){\n\t\t\tCU_ASSERT_PTR_NOT_NULL(properties->next);\n\t\t}else{\n\t\t\tCU_ASSERT_PTR_NULL(properties->next);\n\t\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 1+2+strlen(name_expected)+2+strlen(value_expected));\n\t\t}\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_NULL(properties);\n}\n\n\nstatic void varint_prop_read_helper(\n\t\tuint8_t *payload,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tuint8_t identifier,\n\t\tuint32_t value_expected)\n{\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = remaining_length;\n\trc = property__read_all(CMD_PUBLISH, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tif(rc != rc_expected){\n\t\tprintf(\"%d / %d\\n\", rc, rc_expected);\n\t}\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.varint, value_expected);\n\t\tCU_ASSERT_PTR_NULL(properties->next);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), mosquitto_varint_bytes(value_expected)+1);\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_NULL(properties);\n}\n\n\nstatic void packet_helper_reason_string_user_property(int command)\n{\n\tuint8_t payload[24] = {\n\t\t23,\n\t\tMQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n',\n\t\tMQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'\n\t};\n\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties, *p;\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = sizeof(payload);;\n\trc = property__read_all(command, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(properties);\n\tif(properties){\n\t\tCU_ASSERT_PTR_NOT_NULL(properties->next);\n\t\tp = properties;\n\n\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING);\n\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"reason\");\n\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"reason\"));\n\n\t\tp = p->next;\n\t\tif(p){\n\t\t\tCU_ASSERT_PTR_NULL(p->next);\n\n\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY);\n\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"value\");\n\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"value\"));\n\t\t\tCU_ASSERT_STRING_EQUAL(p->name.v, \"name\");\n\t\t\tCU_ASSERT_EQUAL(p->name.len, strlen(\"name\"));\n\t\t}\n\n\t\tmosquitto_property_free_all(&properties);\n\t}\n}\n\n\n/* ========================================================================\n * NO PROPERTIES\n * ======================================================================== */\n\n\nstatic void TEST_no_properties(void)\n{\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties = NULL;\n\tuint8_t payload[5];\n\tint rc;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tmemset(payload, 0, sizeof(payload));\n\tpacket.payload = payload;\n\tpacket.remaining_length = 1;\n\trc = property__read_all(CMD_CONNECT, &packet, &properties);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tCU_ASSERT_EQUAL(packet.pos, 1);\n\tif(properties){\n\t\tmosquitto_property_free_all(&properties);\n\t}\n}\n\n\nstatic void TEST_truncated(void)\n{\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties = NULL;\n\tuint8_t payload[5];\n\tint rc;\n\n\t/* Zero length packet */\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tmemset(payload, 0, sizeof(payload));\n\tpacket.payload = payload;\n\tpacket.remaining_length = 0;\n\trc = property__read_all(CMD_CONNECT, &packet, &properties);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET);\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tCU_ASSERT_EQUAL(packet.pos, 0);\n\n\t/* Proplen > 0 but not enough data */\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 2;\n\tpacket.payload = payload;\n\tpacket.remaining_length = 1;\n\trc = property__read_all(CMD_CONNECT, &packet, &properties);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET);\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tCU_ASSERT_EQUAL(packet.pos, 1);\n\n\t/* Proplen > 0 but not enough data */\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 4;\n\tpayload[1] = MQTT_PROP_PAYLOAD_FORMAT_INDICATOR;\n\tpacket.payload = payload;\n\tpacket.remaining_length = 2;\n\trc = property__read_all(CMD_CONNECT, &packet, &properties);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET);\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tCU_ASSERT_EQUAL(packet.pos, 2);\n\tif(properties){\n\t\tmosquitto_property_free_all(&properties);\n\t}\n}\n\n\n/* ========================================================================\n * INVALID PROPERTY ID\n * ======================================================================== */\n\n\nstatic void TEST_invalid_property_id(void)\n{\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties = NULL;\n\tuint8_t payload[5];\n\tint rc;\n\n\t/* ID = 0 */\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 4;\n\tpacket.payload = payload;\n\tpacket.remaining_length = 2;\n\trc = property__read_all(CMD_CONNECT, &packet, &properties);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET);\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tCU_ASSERT_EQUAL(packet.pos, 2);\n\n\t/* ID = 4 */\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tmemset(payload, 0, sizeof(payload));\n\tpayload[0] = 4;\n\tpayload[1] = 4;\n\tpacket.payload = payload;\n\tpacket.remaining_length = 2;\n\trc = property__read_all(CMD_CONNECT, &packet, &properties);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET);\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tCU_ASSERT_EQUAL(packet.pos, 2);\n\tif(properties){\n\t\tmosquitto_property_free_all(&properties);\n\t}\n}\n\n\n/* ========================================================================\n * SINGLE PROPERTIES\n * ======================================================================== */\n\n\nstatic void TEST_single_payload_format_indicator(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 2; /* Proplen = Identifier + byte */\n\tpayload[1] = MQTT_PROP_PAYLOAD_FORMAT_INDICATOR;\n\tpayload[2] = 1;\n\n\tbyte_prop_read_helper(CMD_PUBLISH, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n}\n\n\nstatic void TEST_single_request_problem_information(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 2; /* Proplen = Identifier + byte */\n\tpayload[1] = MQTT_PROP_REQUEST_PROBLEM_INFORMATION;\n\tpayload[2] = 1;\n\n\tbyte_prop_read_helper(CMD_CONNECT, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1);\n}\n\n\nstatic void TEST_single_request_response_information(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 2; /* Proplen = Identifier + byte */\n\tpayload[1] = MQTT_PROP_REQUEST_RESPONSE_INFORMATION;\n\tpayload[2] = 1;\n\n\tbyte_prop_read_helper(CMD_CONNECT, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1);\n}\n\n\nstatic void TEST_single_maximum_qos(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 2; /* Proplen = Identifier + byte */\n\tpayload[1] = MQTT_PROP_MAXIMUM_QOS;\n\tpayload[2] = 1;\n\n\tbyte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_QOS, 1);\n}\n\n\nstatic void TEST_single_retain_available(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 2; /* Proplen = Identifier + byte */\n\tpayload[1] = MQTT_PROP_RETAIN_AVAILABLE;\n\tpayload[2] = 1;\n\n\tbyte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_RETAIN_AVAILABLE, 1);\n}\n\n\nstatic void TEST_single_wildcard_subscription_available(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 2; /* Proplen = Identifier + byte */\n\tpayload[1] = MQTT_PROP_WILDCARD_SUB_AVAILABLE;\n\tpayload[2] = 0;\n\n\tbyte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0);\n}\n\n\nstatic void TEST_single_subscription_identifier_available(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 2; /* Proplen = Identifier + byte */\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE;\n\tpayload[2] = 0;\n\n\tbyte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0);\n}\n\n\nstatic void TEST_single_shared_subscription_available(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 2; /* Proplen = Identifier + byte */\n\tpayload[1] = MQTT_PROP_SHARED_SUB_AVAILABLE;\n\tpayload[2] = 1;\n\n\tbyte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SHARED_SUB_AVAILABLE, 1);\n}\n\n\nstatic void TEST_single_message_expiry_interval(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 5; /* Proplen = Identifier + int32 */\n\tpayload[1] = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL;\n\tpayload[2] = 0x12;\n\tpayload[3] = 0x23;\n\tpayload[4] = 0x34;\n\tpayload[5] = 0x45;\n\n\tint32_prop_read_helper(CMD_WILL, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 0x12233445);\n}\n\n\nstatic void TEST_single_session_expiry_interval(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 5; /* Proplen = Identifier + int32 */\n\tpayload[1] = MQTT_PROP_SESSION_EXPIRY_INTERVAL;\n\tpayload[2] = 0x45;\n\tpayload[3] = 0x34;\n\tpayload[4] = 0x23;\n\tpayload[5] = 0x12;\n\n\tint32_prop_read_helper(CMD_CONNACK, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x45342312);\n}\n\n\nstatic void TEST_single_will_delay_interval(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 5; /* Proplen = Identifier + int32 */\n\tpayload[1] = MQTT_PROP_WILL_DELAY_INTERVAL;\n\tpayload[2] = 0x45;\n\tpayload[3] = 0x34;\n\tpayload[4] = 0x23;\n\tpayload[5] = 0x12;\n\n\tint32_prop_read_helper(CMD_WILL, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_WILL_DELAY_INTERVAL, 0x45342312);\n}\n\n\nstatic void TEST_single_maximum_packet_size(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 5; /* Proplen = Identifier + int32 */\n\tpayload[1] = MQTT_PROP_MAXIMUM_PACKET_SIZE;\n\tpayload[2] = 0x45;\n\tpayload[3] = 0x34;\n\tpayload[4] = 0x23;\n\tpayload[5] = 0x12;\n\n\tint32_prop_read_helper(CMD_CONNECT, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0x45342312);\n}\n\n\nstatic void TEST_single_server_keep_alive(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 3; /* Proplen = Identifier + int16 */\n\tpayload[1] = MQTT_PROP_SERVER_KEEP_ALIVE;\n\tpayload[2] = 0x45;\n\tpayload[3] = 0x34;\n\n\tint16_prop_read_helper(CMD_CONNACK, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_KEEP_ALIVE, 0x4534);\n}\n\n\nstatic void TEST_single_receive_maximum(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 3; /* Proplen = Identifier + int16 */\n\tpayload[1] = MQTT_PROP_RECEIVE_MAXIMUM;\n\tpayload[2] = 0x68;\n\tpayload[3] = 0x42;\n\n\tint16_prop_read_helper(CMD_CONNACK, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_RECEIVE_MAXIMUM, 0x6842);\n}\n\n\nstatic void TEST_single_topic_alias_maximum(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 3; /* Proplen = Identifier + int16 */\n\tpayload[1] = MQTT_PROP_TOPIC_ALIAS_MAXIMUM;\n\tpayload[2] = 0x68;\n\tpayload[3] = 0x42;\n\n\tint16_prop_read_helper(CMD_CONNECT, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x6842);\n}\n\n\nstatic void TEST_single_topic_alias(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 3; /* Proplen = Identifier + int16 */\n\tpayload[1] = MQTT_PROP_TOPIC_ALIAS;\n\tpayload[2] = 0x68;\n\tpayload[3] = 0x42;\n\n\tint16_prop_read_helper(CMD_PUBLISH, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS, 0x6842);\n}\n\n\nstatic void TEST_single_content_type(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = MQTT_PROP_CONTENT_TYPE;\n\tpayload[2] = 0x00;\n\tpayload[3] = 0x05;\n\tpayload[4] = 'h';\n\tpayload[5] = 'e';\n\tpayload[6] = 'l';\n\tpayload[7] = 'l';\n\tpayload[8] = 'o';\n\n\tstring_prop_read_helper(CMD_PUBLISH, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CONTENT_TYPE, \"hello\");\n}\n\n\nstatic void TEST_single_response_topic(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = MQTT_PROP_RESPONSE_TOPIC;\n\tpayload[2] = 0x00;\n\tpayload[3] = 0x05;\n\tpayload[4] = 'h';\n\tpayload[5] = 'e';\n\tpayload[6] = 'l';\n\tpayload[7] = 'l';\n\tpayload[8] = 'o';\n\n\tstring_prop_read_helper(CMD_WILL, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_TOPIC, \"hello\");\n}\n\n\nstatic void TEST_single_assigned_client_identifier(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER;\n\tpayload[2] = 0x00;\n\tpayload[3] = 0x05;\n\tpayload[4] = 'h';\n\tpayload[5] = 'e';\n\tpayload[6] = 'l';\n\tpayload[7] = 'l';\n\tpayload[8] = 'o';\n\n\tstring_prop_read_helper(CMD_CONNACK, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, \"hello\");\n}\n\n\nstatic void TEST_single_authentication_method(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = MQTT_PROP_AUTHENTICATION_METHOD;\n\tpayload[2] = 0x00;\n\tpayload[3] = 0x05;\n\tpayload[4] = 'h';\n\tpayload[5] = 'e';\n\tpayload[6] = 'l';\n\tpayload[7] = 'l';\n\tpayload[8] = 'o';\n\n\tstring_prop_read_helper(CMD_AUTH, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_METHOD, \"hello\");\n}\n\n\nstatic void TEST_single_response_information(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = MQTT_PROP_RESPONSE_INFORMATION;\n\tpayload[2] = 0x00;\n\tpayload[3] = 0x05;\n\tpayload[4] = 'h';\n\tpayload[5] = 'e';\n\tpayload[6] = 'l';\n\tpayload[7] = 'l';\n\tpayload[8] = 'o';\n\n\tstring_prop_read_helper(CMD_CONNACK, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_INFORMATION, \"hello\");\n}\n\n\nstatic void TEST_single_server_reference(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = MQTT_PROP_SERVER_REFERENCE;\n\tpayload[2] = 0x00;\n\tpayload[3] = 0x05;\n\tpayload[4] = 'h';\n\tpayload[5] = 'e';\n\tpayload[6] = 'l';\n\tpayload[7] = 'l';\n\tpayload[8] = 'o';\n\n\tstring_prop_read_helper(CMD_CONNACK, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_REFERENCE, \"hello\");\n}\n\n\nstatic void TEST_single_reason_string(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = MQTT_PROP_REASON_STRING;\n\tpayload[2] = 0x00;\n\tpayload[3] = 0x05;\n\tpayload[4] = 'h';\n\tpayload[5] = 'e';\n\tpayload[6] = 'l';\n\tpayload[7] = 'l';\n\tpayload[8] = 'o';\n\n\tstring_prop_read_helper(CMD_PUBCOMP, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_REASON_STRING, \"hello\");\n}\n\n\nstatic void TEST_single_correlation_data(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = MQTT_PROP_CORRELATION_DATA;\n\tpayload[2] = 0x00;\n\tpayload[3] = 0x05;\n\tpayload[4] = 1;\n\tpayload[5] = 'e';\n\tpayload[6] = 0;\n\tpayload[7] = 'l';\n\tpayload[8] = 9;\n\n\tbinary_prop_read_helper(CMD_PUBLISH, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CORRELATION_DATA, &payload[4], 5);\n}\n\n\nstatic void TEST_single_authentication_data(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 8;\n\tpayload[1] = MQTT_PROP_AUTHENTICATION_DATA;\n\tpayload[2] = 0x00;\n\tpayload[3] = 0x05;\n\tpayload[4] = 1;\n\tpayload[5] = 'e';\n\tpayload[6] = 0;\n\tpayload[7] = 'l';\n\tpayload[8] = 9;\n\n\tbinary_prop_read_helper(CMD_CONNECT, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_DATA, &payload[4], 5);\n}\n\n\nstatic void TEST_single_user_property(void)\n{\n\tuint8_t payload[20];\n\n\tpayload[0] = 9;\n\tpayload[1] = MQTT_PROP_USER_PROPERTY;\n\tpayload[2] = 0;\n\tpayload[3] = 2;\n\tpayload[4] = 'z';\n\tpayload[5] = 'a';\n\tpayload[6] = 0;\n\tpayload[7] = 2;\n\tpayload[8] = 'b';\n\tpayload[9] = 'c';\n\n\tstring_pair_prop_read_helper(payload, 10, MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, \"za\", \"bc\", false);\n}\n\n\nstatic void TEST_single_subscription_identifier(void)\n{\n\tuint8_t payload[20];\n\n\tpayload[0] = 2;\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[2] = 0;\n\tvarint_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0);\n\n\tpayload[0] = 2;\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[2] = 0x7F;\n\tvarint_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 127);\n\n\tpayload[0] = 3;\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[2] = 0x80;\n\tpayload[3] = 0x01;\n\tvarint_prop_read_helper(payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 128);\n\n\tpayload[0] = 3;\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[2] = 0xFF;\n\tpayload[3] = 0x7F;\n\tvarint_prop_read_helper(payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16383);\n\n\tpayload[0] = 4;\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[2] = 0x80;\n\tpayload[3] = 0x80;\n\tpayload[4] = 0x01;\n\tvarint_prop_read_helper(payload, 5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16384);\n\n\tpayload[0] = 4;\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[2] = 0xFF;\n\tpayload[3] = 0xFF;\n\tpayload[4] = 0x7F;\n\tvarint_prop_read_helper(payload, 5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097151);\n\n\tpayload[0] = 5;\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[2] = 0x80;\n\tpayload[3] = 0x80;\n\tpayload[4] = 0x80;\n\tpayload[5] = 0x01;\n\tvarint_prop_read_helper(payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097152);\n\n\n\tpayload[0] = 5;\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[2] = 0xFF;\n\tpayload[3] = 0xFF;\n\tpayload[4] = 0xFF;\n\tpayload[5] = 0x7F;\n\tvarint_prop_read_helper(payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 268435455);\n}\n\n\n/* ========================================================================\n * DUPLICATE PROPERTIES\n * ======================================================================== */\n\n\nstatic void TEST_duplicate_payload_format_indicator(void)\n{\n\tduplicate_byte_helper(CMD_PUBLISH, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR);\n}\n\n\nstatic void TEST_duplicate_request_problem_information(void)\n{\n\tduplicate_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_PROBLEM_INFORMATION);\n}\n\n\nstatic void TEST_duplicate_request_response_information(void)\n{\n\tduplicate_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_RESPONSE_INFORMATION);\n}\n\n\nstatic void TEST_duplicate_maximum_qos(void)\n{\n\tduplicate_byte_helper(CMD_CONNACK, MQTT_PROP_MAXIMUM_QOS);\n}\n\n\nstatic void TEST_duplicate_retain_available(void)\n{\n\tduplicate_byte_helper(CMD_CONNACK, MQTT_PROP_RETAIN_AVAILABLE);\n}\n\n\nstatic void TEST_duplicate_wildcard_subscription_available(void)\n{\n\tduplicate_byte_helper(CMD_CONNACK, MQTT_PROP_WILDCARD_SUB_AVAILABLE);\n}\n\n\nstatic void TEST_duplicate_subscription_identifier_available(void)\n{\n\tduplicate_byte_helper(CMD_CONNACK, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE);\n}\n\n\nstatic void TEST_duplicate_shared_subscription_available(void)\n{\n\tduplicate_byte_helper(CMD_CONNACK, MQTT_PROP_SHARED_SUB_AVAILABLE);\n}\n\n\nstatic void TEST_duplicate_message_expiry_interval(void)\n{\n\tduplicate_int32_helper(CMD_PUBLISH, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL);\n}\n\n\nstatic void TEST_duplicate_session_expiry_interval(void)\n{\n\tduplicate_int32_helper(CMD_DISCONNECT, MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n}\n\n\nstatic void TEST_duplicate_will_delay_interval(void)\n{\n\tduplicate_int32_helper(CMD_WILL, MQTT_PROP_WILL_DELAY_INTERVAL);\n}\n\n\nstatic void TEST_duplicate_maximum_packet_size(void)\n{\n\tduplicate_int32_helper(CMD_CONNECT, MQTT_PROP_MAXIMUM_PACKET_SIZE);\n}\n\n\nstatic void TEST_duplicate_server_keep_alive(void)\n{\n\tduplicate_int16_helper(CMD_CONNACK, MQTT_PROP_SERVER_KEEP_ALIVE);\n}\n\n\nstatic void TEST_duplicate_receive_maximum(void)\n{\n\tduplicate_int16_helper(CMD_CONNACK, MQTT_PROP_RECEIVE_MAXIMUM);\n}\n\n\nstatic void TEST_duplicate_topic_alias_maximum(void)\n{\n\tduplicate_int16_helper(CMD_CONNECT, MQTT_PROP_TOPIC_ALIAS_MAXIMUM);\n}\n\n\nstatic void TEST_duplicate_topic_alias(void)\n{\n\tduplicate_int16_helper(CMD_PUBLISH, MQTT_PROP_TOPIC_ALIAS);\n}\n\n\nstatic void TEST_duplicate_content_type(void)\n{\n\tduplicate_string_helper(CMD_PUBLISH, MQTT_PROP_CONTENT_TYPE);\n}\n\n\nstatic void TEST_duplicate_response_topic(void)\n{\n\tduplicate_string_helper(CMD_PUBLISH, MQTT_PROP_RESPONSE_TOPIC);\n}\n\n\nstatic void TEST_duplicate_assigned_client_identifier(void)\n{\n\tduplicate_string_helper(CMD_CONNACK, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER);\n}\n\n\nstatic void TEST_duplicate_authentication_method(void)\n{\n\tduplicate_string_helper(CMD_AUTH, MQTT_PROP_AUTHENTICATION_METHOD);\n}\n\n\nstatic void TEST_duplicate_response_information(void)\n{\n\tduplicate_string_helper(CMD_CONNACK, MQTT_PROP_RESPONSE_INFORMATION);\n}\n\n\nstatic void TEST_duplicate_server_reference(void)\n{\n\tduplicate_string_helper(CMD_CONNACK, MQTT_PROP_SERVER_REFERENCE);\n}\n\n\nstatic void TEST_duplicate_reason_string(void)\n{\n\tduplicate_string_helper(CMD_PUBACK, MQTT_PROP_REASON_STRING);\n}\n\n\nstatic void TEST_duplicate_correlation_data(void)\n{\n\tduplicate_binary_helper(CMD_PUBLISH, MQTT_PROP_CORRELATION_DATA);\n}\n\n\nstatic void TEST_duplicate_authentication_data(void)\n{\n\tduplicate_binary_helper(CMD_CONNACK, MQTT_PROP_AUTHENTICATION_DATA);\n}\n\n\nstatic void TEST_duplicate_user_property(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 18; /* Proplen = (Identifier + byte)*2 */\n\tpayload[1] = MQTT_PROP_USER_PROPERTY;\n\tpayload[2] = 0;\n\tpayload[3] = 2;\n\tpayload[4] = 'a';\n\tpayload[5] = 'b';\n\tpayload[6] = 0;\n\tpayload[7] = 2;\n\tpayload[8] = 'g';\n\tpayload[9] = 'h';\n\tpayload[10] = MQTT_PROP_USER_PROPERTY;\n\tpayload[11] = 0;\n\tpayload[12] = 2;\n\tpayload[13] = 'c';\n\tpayload[14] = 'd';\n\tpayload[15] = 0;\n\tpayload[16] = 2;\n\tpayload[17] = 'e';\n\tpayload[18] = 'f';\n\n\tstring_pair_prop_read_helper(payload, 19, MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, \"ab\", \"gh\", true);\n}\n\n\nstatic void TEST_duplicate_subscription_identifier(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 4; /* Proplen = (Identifier + byte)*2 */\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[2] = 0x80;\n\tpayload[3] = 0x02;\n\tpayload[4] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[5] = 0x04;\n\n\tvarint_prop_read_helper(payload, 5, MOSQ_ERR_MALFORMED_PACKET, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0);\n}\n\n\n/* ========================================================================\n * BAD PROPERTY VALUES\n * ======================================================================== */\n\n\nstatic void TEST_bad_request_problem_information(void)\n{\n\tbad_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_PROBLEM_INFORMATION);\n}\n\n\nstatic void TEST_bad_request_response_information(void)\n{\n\tbad_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_RESPONSE_INFORMATION);\n}\n\n\nstatic void TEST_bad_maximum_qos(void)\n{\n\tbad_byte_helper(CMD_CONNACK, MQTT_PROP_MAXIMUM_QOS);\n}\n\n\nstatic void TEST_bad_retain_available(void)\n{\n\tbad_byte_helper(CMD_CONNACK, MQTT_PROP_RETAIN_AVAILABLE);\n}\n\n\nstatic void TEST_bad_wildcard_sub_available(void)\n{\n\tbad_byte_helper(CMD_CONNACK, MQTT_PROP_WILDCARD_SUB_AVAILABLE);\n}\n\n\nstatic void TEST_bad_subscription_id_available(void)\n{\n\tbad_byte_helper(CMD_CONNACK, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE);\n}\n\n\nstatic void TEST_bad_shared_sub_available(void)\n{\n\tbad_byte_helper(CMD_CONNACK, MQTT_PROP_SHARED_SUB_AVAILABLE);\n}\n\n\nstatic void TEST_bad_maximum_packet_size(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 5; /* Proplen = Identifier + int32 */\n\tpayload[1] = MQTT_PROP_MAXIMUM_PACKET_SIZE;\n\tpayload[2] = 0;\n\tpayload[3] = 0;\n\tpayload[4] = 0;\n\tpayload[5] = 0; /* 0 is invalid */\n\n\tint32_prop_read_helper(CMD_CONNACK, payload, 6, MOSQ_ERR_PROTOCOL, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0);\n}\n\n\nstatic void TEST_bad_receive_maximum(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 3; /* Proplen = Identifier + int16 */\n\tpayload[1] = MQTT_PROP_RECEIVE_MAXIMUM;\n\tpayload[2] = 0;\n\tpayload[3] = 0; /* 0 is invalid */\n\n\tint32_prop_read_helper(CMD_CONNECT, payload, 4, MOSQ_ERR_PROTOCOL, MQTT_PROP_RECEIVE_MAXIMUM, 0);\n}\n\n\nstatic void TEST_bad_topic_alias(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 3; /* Proplen = Identifier + int16 */\n\tpayload[1] = MQTT_PROP_TOPIC_ALIAS;\n\tpayload[2] = 0;\n\tpayload[3] = 0; /* 0 is invalid */\n\n\tint32_prop_read_helper(CMD_PUBLISH, payload, 4, MOSQ_ERR_PROTOCOL, MQTT_PROP_TOPIC_ALIAS, 0);\n}\n\n\nstatic void TEST_bad_content_type(void)\n{\n\tbad_string_helper(MQTT_PROP_CONTENT_TYPE);\n}\n\n\nstatic void TEST_bad_subscription_identifier(void)\n{\n\tuint8_t payload[20];\n\n\tmemset(&payload, 0, sizeof(payload));\n\tpayload[0] = 6;\n\tpayload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER;\n\tpayload[2] = 0xFF;\n\tpayload[3] = 0xFF;\n\tpayload[4] = 0xFF;\n\tpayload[5] = 0xFF;\n\tpayload[6] = 0x01;\n\n\tvarint_prop_read_helper(payload, 7, MOSQ_ERR_MALFORMED_PACKET, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0);\n}\n\n\n/* ========================================================================\n * CONTROL PACKET TESTS\n * ======================================================================== */\n\n\nstatic void TEST_packet_connect(void)\n{\n\tuint8_t payload[] = {\n\t\t0,\n\t\tMQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00,\n\t\tMQTT_PROP_RECEIVE_MAXIMUM, 0x00, 0x05,\n\t\tMQTT_PROP_MAXIMUM_PACKET_SIZE, 0x12, 0x45, 0x00, 0x00,\n\t\tMQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x00, 0x02,\n\t\tMQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1,\n\t\tMQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1,\n\t\tMQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e',\n\t\tMQTT_PROP_AUTHENTICATION_METHOD, 0x00, 0x04, 'n', 'o', 'n', 'e',\n\t\tMQTT_PROP_AUTHENTICATION_DATA, 0x00, 0x02, 1, 2\n\t};\n\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties, *p;\n\tint rc;\n\n\tpayload[0] = sizeof(payload)-1;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = sizeof(payload);;\n\trc = property__read_all(CMD_CONNECT, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tp = properties;\n\tCU_ASSERT_PTR_NOT_NULL(properties);\n\tif(p){\n\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n\t\tCU_ASSERT_EQUAL(p->value.i32, 0x12450000);\n\n\t\tp = p->next;\n\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\tif(p){\n\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RECEIVE_MAXIMUM);\n\t\t\tCU_ASSERT_EQUAL(p->value.i16, 0x0005);\n\n\t\t\tp = p->next;\n\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\tif(p){\n\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MAXIMUM_PACKET_SIZE);\n\t\t\t\tCU_ASSERT_EQUAL(p->value.i32, 0x12450000);\n\n\t\t\t\tp = p->next;\n\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\tif(p){\n\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_TOPIC_ALIAS_MAXIMUM);\n\t\t\t\t\tCU_ASSERT_EQUAL(p->value.i16, 0x0002);\n\n\t\t\t\t\tp = p->next;\n\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\tif(p){\n\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REQUEST_PROBLEM_INFORMATION);\n\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.i8, 1);\n\n\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REQUEST_RESPONSE_INFORMATION);\n\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.i8, 1);\n\n\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY);\n\t\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"value\");\n\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"value\"));\n\t\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->name.v, \"name\");\n\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->name.len, strlen(\"name\"));\n\n\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_METHOD);\n\t\t\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"none\");\n\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"none\"));\n\n\t\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NULL(p->next);\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_DATA);\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.bin.v[0], 1);\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.bin.v[1], 2);\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, 2);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tmosquitto_property_free_all(&properties);\n}\n\n\nstatic void TEST_packet_connack(void)\n{\n\tuint8_t payload[] = {\n\t\t0,\n\t\tMQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00,\n\t\tMQTT_PROP_RECEIVE_MAXIMUM, 0x00, 0x05,\n\t\tMQTT_PROP_MAXIMUM_QOS, 1,\n\t\tMQTT_PROP_RETAIN_AVAILABLE, 0,\n\t\tMQTT_PROP_MAXIMUM_PACKET_SIZE, 0x12, 0x45, 0x00, 0x00,\n\t\tMQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, 0x00, 0x02, 'a', 'b',\n\t\tMQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x00, 0x02,\n\t\tMQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n',\n\t\tMQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e',\n\t\tMQTT_PROP_WILDCARD_SUB_AVAILABLE, 0,\n\t\tMQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0,\n\t\tMQTT_PROP_SHARED_SUB_AVAILABLE, 0,\n\t\tMQTT_PROP_SERVER_KEEP_ALIVE, 0x00, 0xFF,\n\t\tMQTT_PROP_RESPONSE_INFORMATION, 0x00, 0x03, 'r', 's', 'p',\n\t\tMQTT_PROP_SERVER_REFERENCE, 0x00, 0x04, 's', 'e', 'r', 'v',\n\t\tMQTT_PROP_AUTHENTICATION_METHOD, 0x00, 0x04, 'n', 'o', 'n', 'e',\n\t\tMQTT_PROP_AUTHENTICATION_DATA, 0x00, 0x02, 1, 2\n\t};\n\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties, *p;\n\tint rc;\n\n\tpayload[0] = sizeof(payload)-1;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = sizeof(payload);;\n\trc = property__read_all(CMD_CONNACK, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(properties);\n\tp = properties;\n\n\tCU_ASSERT_PTR_NOT_NULL(p);\n\tif(p){\n\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n\t\tCU_ASSERT_EQUAL(p->value.i32, 0x12450000);\n\n\t\tp = p->next;\n\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\tif(p){\n\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RECEIVE_MAXIMUM);\n\t\t\tCU_ASSERT_EQUAL(p->value.i16, 0x0005);\n\n\t\t\tp = p->next;\n\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\tif(p){\n\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MAXIMUM_QOS);\n\t\t\t\tCU_ASSERT_EQUAL(p->value.i8, 1);\n\n\t\t\t\tp = p->next;\n\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\tif(p){\n\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RETAIN_AVAILABLE);\n\t\t\t\t\tCU_ASSERT_EQUAL(p->value.i8, 0);\n\n\t\t\t\t\tp = p->next;\n\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\tif(p){\n\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MAXIMUM_PACKET_SIZE);\n\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.i32, 0x12450000);\n\n\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER);\n\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"ab\");\n\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"ab\"));\n\n\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_TOPIC_ALIAS_MAXIMUM);\n\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.i16, 0x0002);\n\n\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING);\n\t\t\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"reason\");\n\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"reason\"));\n\n\t\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY);\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"value\");\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"value\"));\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->name.v, \"name\");\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->name.len, strlen(\"name\"));\n\n\t\t\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_WILDCARD_SUB_AVAILABLE);\n\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.i8, 0);\n\n\t\t\t\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE);\n\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.i8, 0);\n\n\t\t\t\t\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SHARED_SUB_AVAILABLE);\n\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.i8, 0);\n\n\t\t\t\t\t\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SERVER_KEEP_ALIVE);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.i16, 0x00FF);\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RESPONSE_INFORMATION);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"rsp\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"rsp\"));\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SERVER_REFERENCE);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"serv\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"serv\"));\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_METHOD);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"none\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"none\"));\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NULL(p->next);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_DATA);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.bin.v[0], 1);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.bin.v[1], 2);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, 2);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tmosquitto_property_free_all(&properties);\n}\n\n\nstatic void TEST_packet_publish(void)\n{\n\tuint8_t payload[] = {\n\t\t0,\n\t\tMQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1,\n\t\tMQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00,\n\t\tMQTT_PROP_TOPIC_ALIAS, 0x00, 0x02,\n\t\tMQTT_PROP_RESPONSE_TOPIC, 0, 6, 'r', 'e', 's', 'p', 'o', 'n',\n\t\tMQTT_PROP_CORRELATION_DATA, 0x00, 0x02, 1, 2,\n\t\tMQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e',\n\t\tMQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0x04,\n\t\tMQTT_PROP_CONTENT_TYPE, 0, 5, 'e', 'm', 'p', 't', 'y'\n\t};\n\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties, *p;\n\tint rc;\n\n\tpayload[0] = sizeof(payload)-1;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = sizeof(payload);;\n\trc = property__read_all(CMD_PUBLISH, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tp = properties;\n\n\tCU_ASSERT_PTR_NOT_NULL(p);\n\tif(p){\n\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR);\n\t\tCU_ASSERT_EQUAL(p->value.i8, 1);\n\n\t\tp = p->next;\n\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\tif(p){\n\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL);\n\t\t\tCU_ASSERT_EQUAL(p->value.i32, 0x12450000);\n\n\t\t\tp = p->next;\n\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\tif(p){\n\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_TOPIC_ALIAS);\n\t\t\t\tCU_ASSERT_EQUAL(p->value.i16, 0x0002);\n\n\t\t\t\tp = p->next;\n\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\tif(p){\n\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RESPONSE_TOPIC);\n\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"respon\");\n\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"respon\"));\n\n\t\t\t\t\tp = p->next;\n\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\tif(p){\n\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_CORRELATION_DATA);\n\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.bin.v[0], 1);\n\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.bin.v[1], 2);\n\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.bin.len, 2);\n\n\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY);\n\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"value\");\n\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"value\"));\n\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->name.v, \"name\");\n\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->name.len, strlen(\"name\"));\n\n\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SUBSCRIPTION_IDENTIFIER);\n\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.varint, 0x00000004);\n\n\t\t\t\t\t\t\t\tp = p->next;\n\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\t\t\t\t\tif(p){\n\t\t\t\t\t\t\t\t\tCU_ASSERT_PTR_NULL(p->next);\n\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_CONTENT_TYPE);\n\t\t\t\t\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"empty\");\n\t\t\t\t\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"empty\"));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tmosquitto_property_free_all(&properties);\n}\n\n\nstatic void TEST_packet_puback(void)\n{\n\tpacket_helper_reason_string_user_property(CMD_PUBACK);\n}\n\n\nstatic void TEST_packet_pubrec(void)\n{\n\tpacket_helper_reason_string_user_property(CMD_PUBREC);\n}\n\n\nstatic void TEST_packet_pubrel(void)\n{\n\tpacket_helper_reason_string_user_property(CMD_PUBREL);\n}\n\n\nstatic void TEST_packet_pubcomp(void)\n{\n\tpacket_helper_reason_string_user_property(CMD_PUBCOMP);\n}\n\n\nstatic void TEST_packet_subscribe(void)\n{\n\tuint8_t payload[] = {\n\t\t0,\n\t\tMQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e',\n\t\tMQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0x04\n\t};\n\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties, *p;\n\tint rc;\n\n\tpayload[0] = sizeof(payload)-1;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = sizeof(payload);;\n\trc = property__read_all(CMD_SUBSCRIBE, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tp = properties;\n\n\tCU_ASSERT_PTR_NOT_NULL(p);\n\tif(p){\n\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY);\n\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"value\");\n\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"value\"));\n\t\tCU_ASSERT_STRING_EQUAL(p->name.v, \"name\");\n\t\tCU_ASSERT_EQUAL(p->name.len, strlen(\"name\"));\n\n\t\tp = p->next;\n\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\tif(p){\n\t\t\tCU_ASSERT_PTR_NULL(p->next);\n\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SUBSCRIPTION_IDENTIFIER);\n\t\t\tCU_ASSERT_EQUAL(p->value.varint, 0x00000004);\n\t\t}\n\t}\n\n\tmosquitto_property_free_all(&properties);\n}\n\n\nstatic void TEST_packet_suback(void)\n{\n\tpacket_helper_reason_string_user_property(CMD_SUBACK);\n}\n\n\nstatic void TEST_packet_unsubscribe(void)\n{\n\tuint8_t payload[] = {\n\t\t0,\n\t\tMQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'\n\t};\n\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties, *p;\n\tint rc;\n\n\tpayload[0] = sizeof(payload)-1;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = sizeof(payload);;\n\trc = property__read_all(CMD_UNSUBSCRIBE, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tp = properties;\n\n\tCU_ASSERT_PTR_NOT_NULL(p);\n\tif(p){\n\t\tCU_ASSERT_PTR_NULL(p->next);\n\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY);\n\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"value\");\n\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"value\"));\n\t\tCU_ASSERT_STRING_EQUAL(p->name.v, \"name\");\n\t\tCU_ASSERT_EQUAL(p->name.len, strlen(\"name\"));\n\t}\n\n\tmosquitto_property_free_all(&properties);\n}\n\n\nstatic void TEST_packet_unsuback(void)\n{\n\tpacket_helper_reason_string_user_property(CMD_UNSUBACK);\n}\n\n\nstatic void TEST_packet_disconnect(void)\n{\n\tuint8_t payload[] = {\n\t\t0,\n\t\tMQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00,\n\t\tMQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n',\n\t\tMQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'\n\t};\n\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties, *p;\n\tint rc;\n\n\tpayload[0] = sizeof(payload)-1;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = sizeof(payload);;\n\trc = property__read_all(CMD_DISCONNECT, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tp = properties;\n\n\tCU_ASSERT_PTR_NOT_NULL(p);\n\tif(p){\n\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n\t\tCU_ASSERT_EQUAL(p->value.i32, 0x12450000);\n\n\t\tp = p->next;\n\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\tif(p){\n\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING);\n\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"reason\");\n\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"reason\"));\n\n\t\t\tp = p->next;\n\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\tif(p){\n\t\t\t\tCU_ASSERT_PTR_NULL(p->next);\n\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY);\n\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"value\");\n\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"value\"));\n\t\t\t\tCU_ASSERT_STRING_EQUAL(p->name.v, \"name\");\n\t\t\t\tCU_ASSERT_EQUAL(p->name.len, strlen(\"name\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tmosquitto_property_free_all(&properties);\n}\n\n\nstatic void TEST_packet_auth(void)\n{\n\tuint8_t payload[] = {\n\t\t0,\n\t\tMQTT_PROP_AUTHENTICATION_METHOD, 0x00, 0x04, 'n', 'o', 'n', 'e',\n\t\tMQTT_PROP_AUTHENTICATION_DATA, 0x00, 0x02, 1, 2,\n\t\tMQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n',\n\t\tMQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'\n\t};\n\n\tstruct mosquitto__packet_in packet;\n\tmosquitto_property *properties, *p;\n\tint rc;\n\n\tpayload[0] = sizeof(payload)-1;\n\n\tmemset(&packet, 0, sizeof(struct mosquitto__packet_in));\n\tpacket.payload = payload;\n\tpacket.remaining_length = sizeof(payload);;\n\trc = property__read_all(CMD_AUTH, &packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tp = properties;\n\n\tCU_ASSERT_PTR_NOT_NULL(p);\n\tif(p){\n\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_METHOD);\n\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"none\");\n\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"none\"));\n\n\t\tp = p->next;\n\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\tif(p){\n\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_DATA);\n\t\t\tCU_ASSERT_EQUAL(p->value.bin.v[0], 1);\n\t\t\tCU_ASSERT_EQUAL(p->value.bin.v[1], 2);\n\t\t\tCU_ASSERT_EQUAL(p->value.s.len, 2);\n\n\t\t\tp = p->next;\n\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\tif(p){\n\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p->next);\n\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING);\n\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"reason\");\n\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"reason\"));\n\n\t\t\t\tp = p->next;\n\t\t\t\tCU_ASSERT_PTR_NOT_NULL(p);\n\t\t\t\tif(p){\n\t\t\t\t\tCU_ASSERT_PTR_NULL(p->next);\n\t\t\t\t\tCU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY);\n\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->value.s.v, \"value\");\n\t\t\t\t\tCU_ASSERT_EQUAL(p->value.s.len, strlen(\"value\"));\n\t\t\t\t\tCU_ASSERT_STRING_EQUAL(p->name.v, \"name\");\n\t\t\t\t\tCU_ASSERT_EQUAL(p->name.len, strlen(\"name\"));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tmosquitto_property_free_all(&properties);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_property_read_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Property read\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit Property read test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Truncated packet\", TEST_truncated)\n\t\t\t|| !CU_add_test(test_suite, \"Invalid property ID\", TEST_invalid_property_id)\n\t\t\t|| !CU_add_test(test_suite, \"No properties\", TEST_no_properties)\n\t\t\t|| !CU_add_test(test_suite, \"Single Payload Format Indicator\", TEST_single_payload_format_indicator)\n\t\t\t|| !CU_add_test(test_suite, \"Single Request Problem Information\", TEST_single_request_problem_information)\n\t\t\t|| !CU_add_test(test_suite, \"Single Request Response Information\", TEST_single_request_response_information)\n\t\t\t|| !CU_add_test(test_suite, \"Single Maximum QoS\", TEST_single_maximum_qos)\n\t\t\t|| !CU_add_test(test_suite, \"Single Retain Available\", TEST_single_retain_available)\n\t\t\t|| !CU_add_test(test_suite, \"Single Wildcard Subscription Available\", TEST_single_wildcard_subscription_available)\n\t\t\t|| !CU_add_test(test_suite, \"Single Subscription Identifier Available\", TEST_single_subscription_identifier_available)\n\t\t\t|| !CU_add_test(test_suite, \"Single Shared Subscription Available\", TEST_single_shared_subscription_available)\n\t\t\t|| !CU_add_test(test_suite, \"Single Message Expiry Interval\", TEST_single_message_expiry_interval)\n\t\t\t|| !CU_add_test(test_suite, \"Single Session Expiry Interval\", TEST_single_session_expiry_interval)\n\t\t\t|| !CU_add_test(test_suite, \"Single Will Delay Interval\", TEST_single_will_delay_interval)\n\t\t\t|| !CU_add_test(test_suite, \"Single Maximum Packet Size\", TEST_single_maximum_packet_size)\n\t\t\t|| !CU_add_test(test_suite, \"Single Server Keep Alive\", TEST_single_server_keep_alive)\n\t\t\t|| !CU_add_test(test_suite, \"Single Receive Maximum\", TEST_single_receive_maximum)\n\t\t\t|| !CU_add_test(test_suite, \"Single Topic Alias Maximum\", TEST_single_topic_alias_maximum)\n\t\t\t|| !CU_add_test(test_suite, \"Single Topic Alias\", TEST_single_topic_alias)\n\t\t\t|| !CU_add_test(test_suite, \"Single Content Type\", TEST_single_content_type)\n\t\t\t|| !CU_add_test(test_suite, \"Single Response Topic\", TEST_single_response_topic)\n\t\t\t|| !CU_add_test(test_suite, \"Single Assigned Client Identifier\", TEST_single_assigned_client_identifier)\n\t\t\t|| !CU_add_test(test_suite, \"Single Authentication Method\", TEST_single_authentication_method)\n\t\t\t|| !CU_add_test(test_suite, \"Single Response Information\", TEST_single_response_information)\n\t\t\t|| !CU_add_test(test_suite, \"Single Server Reference\", TEST_single_server_reference)\n\t\t\t|| !CU_add_test(test_suite, \"Single Reason String\", TEST_single_reason_string)\n\t\t\t|| !CU_add_test(test_suite, \"Single Correlation Data\", TEST_single_correlation_data)\n\t\t\t|| !CU_add_test(test_suite, \"Single Authentication Data\", TEST_single_authentication_data)\n\t\t\t|| !CU_add_test(test_suite, \"Single User Property\", TEST_single_user_property)\n\t\t\t|| !CU_add_test(test_suite, \"Single Subscription Identifier\", TEST_single_subscription_identifier)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Payload Format Indicator\", TEST_duplicate_payload_format_indicator)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Request Problem Information\", TEST_duplicate_request_problem_information)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Request Response Information\", TEST_duplicate_request_response_information)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Maximum QoS\", TEST_duplicate_maximum_qos)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Retain Available\", TEST_duplicate_retain_available)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Wildcard Subscription Available\", TEST_duplicate_wildcard_subscription_available)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Subscription Identifier Available\", TEST_duplicate_subscription_identifier_available)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Shared Subscription Available\", TEST_duplicate_shared_subscription_available)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Message Expiry Interval\", TEST_duplicate_message_expiry_interval)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Session Expiry Interval\", TEST_duplicate_session_expiry_interval)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Will Delay Interval\", TEST_duplicate_will_delay_interval)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Maximum Packet Size\", TEST_duplicate_maximum_packet_size)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Server Keep Alive\", TEST_duplicate_server_keep_alive)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Receive Maximum\", TEST_duplicate_receive_maximum)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Topic Alias Maximum\", TEST_duplicate_topic_alias_maximum)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Topic Alias\", TEST_duplicate_topic_alias)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Content Type\", TEST_duplicate_content_type)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Response Topic\", TEST_duplicate_response_topic)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Assigned Client ID\", TEST_duplicate_assigned_client_identifier)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Authentication Method\", TEST_duplicate_authentication_method)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Response Information\", TEST_duplicate_response_information)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Server Reference\", TEST_duplicate_server_reference)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Reason String\", TEST_duplicate_reason_string)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Correlation Data\", TEST_duplicate_correlation_data)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Authentication Data\", TEST_duplicate_authentication_data)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate User Property\", TEST_duplicate_user_property)\n\t\t\t|| !CU_add_test(test_suite, \"Duplicate Subscription Identifier\", TEST_duplicate_subscription_identifier)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Request Problem Information\", TEST_bad_request_problem_information)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Request Response Information\", TEST_bad_request_response_information)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Maximum QoS\", TEST_bad_maximum_qos)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Retain Available\", TEST_bad_retain_available)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Wildcard Subscription Available\", TEST_bad_wildcard_sub_available)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Subscription Identifier Available\", TEST_bad_subscription_id_available)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Shared Subscription Available\", TEST_bad_shared_sub_available)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Maximum Packet Size\", TEST_bad_maximum_packet_size)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Receive Maximum\", TEST_bad_receive_maximum)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Topic Alias\", TEST_bad_topic_alias)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Content Type\", TEST_bad_content_type)\n\t\t\t|| !CU_add_test(test_suite, \"Bad Subscription Identifier\", TEST_bad_subscription_identifier)\n\t\t\t|| !CU_add_test(test_suite, \"Packet CONNECT\", TEST_packet_connect)\n\t\t\t|| !CU_add_test(test_suite, \"Packet CONNACK\", TEST_packet_connack)\n\t\t\t|| !CU_add_test(test_suite, \"Packet PUBLISH\", TEST_packet_publish)\n\t\t\t|| !CU_add_test(test_suite, \"Packet PUBACK\", TEST_packet_puback)\n\t\t\t|| !CU_add_test(test_suite, \"Packet PUBREC\", TEST_packet_pubrec)\n\t\t\t|| !CU_add_test(test_suite, \"Packet PUBREL\", TEST_packet_pubrel)\n\t\t\t|| !CU_add_test(test_suite, \"Packet PUBCOMP\", TEST_packet_pubcomp)\n\t\t\t|| !CU_add_test(test_suite, \"Packet SUBSCRIBE\", TEST_packet_subscribe)\n\t\t\t|| !CU_add_test(test_suite, \"Packet SUBACK\", TEST_packet_suback)\n\t\t\t|| !CU_add_test(test_suite, \"Packet UNSUBSCRIBE\", TEST_packet_unsubscribe)\n\t\t\t|| !CU_add_test(test_suite, \"Packet UNSUBACK\", TEST_packet_unsuback)\n\t\t\t|| !CU_add_test(test_suite, \"Packet DISCONNECT\", TEST_packet_disconnect)\n\t\t\t|| !CU_add_test(test_suite, \"Packet AUTH\", TEST_packet_auth)\n\t\t\t){\n\n\t\tprintf(\"Error adding Property read CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/lib/property_user_read.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"property_mosq.h\"\n#include \"packet_mosq.h\"\n\n\nstatic void generate_full_proplist(mosquitto_property **proplist)\n{\n\tint rc;\n\n\t/* This isn't a valid proplist for sending, because it contains every\n\t * property. Very useful for testing though. */\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_int32(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_CONTENT_TYPE, \"application/json\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_TOPIC, \"response/topic\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_binary(proplist, MQTT_PROP_CORRELATION_DATA, \"correlation-data\", strlen(\"correlation-data\"));\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_varint(proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 63);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_int32(proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, \"mosquitto-test\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, 180);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_AUTHENTICATION_METHOD, \"basic\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_binary(proplist, MQTT_PROP_AUTHENTICATION_DATA, \"password\", strlen(\"password\"));\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_int32(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, 1800);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_INFORMATION, \"response\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_SERVER_REFERENCE, \"localhost\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_REASON_STRING, \"reason\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_int16(proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS, 15);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_MAXIMUM_QOS, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_RETAIN_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_string_pair(proplist, MQTT_PROP_USER_PROPERTY, \"user-agent\", \"mosquitto/test\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_int32(proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n}\n\n\nstatic void generate_partial_proplist(mosquitto_property **proplist)\n{\n\tint rc;\n\n\t// BYTE MISSING: MQTT_PROP_PAYLOAD_FORMAT_INDICATOR\n\trc = mosquitto_property_add_int32(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t// STRING MISSING: MQTT_PROP_CONTENT_TYPE\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_TOPIC, \"response/topic\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t// BINARY MISSING: MQTT_PROP_CORRELATION_DATA\n\t// VARINT MISSING: MQTT_PROP_SUBSCRIPTION_IDENTIFIER\n\t// INT32 MISSING: MQTT_PROP_SESSION_EXPIRY_INTERVAL\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, \"mosquitto-test\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t// INT16 MISSING: MQTT_PROP_SERVER_KEEP_ALIVE\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_AUTHENTICATION_METHOD, \"basic\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_binary(proplist, MQTT_PROP_AUTHENTICATION_DATA, \"password\", strlen(\"password\"));\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_int32(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, 1800);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_INFORMATION, \"response\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_SERVER_REFERENCE, \"localhost\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_string(proplist, MQTT_PROP_REASON_STRING, \"reason\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_int16(proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS, 15);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_MAXIMUM_QOS, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_RETAIN_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t// STRING PAIR MISSING: MQTT_PROP_USER_PROPERTY\n\trc = mosquitto_property_add_int32(proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\trc = mosquitto_property_add_byte(proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n}\n\n\n/* ========================================================================\n * SINGLE READ\n * ======================================================================== */\n\n\nstatic void read_byte_helper(const mosquitto_property *proplist, int identifier, uint8_t expected_value)\n{\n\tconst mosquitto_property *prop;\n\tuint8_t value;\n\n\tprop = mosquitto_property_read_byte(proplist, identifier, &value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_EQUAL(value, expected_value);\n}\n\n\nstatic void read_int16_helper(const mosquitto_property *proplist, int identifier, uint16_t expected_value)\n{\n\tconst mosquitto_property *prop;\n\tuint16_t value;\n\n\tprop = mosquitto_property_read_int16(proplist, identifier, &value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_EQUAL(value, expected_value);\n}\n\n\nstatic void read_int32_helper(const mosquitto_property *proplist, int identifier, uint32_t expected_value)\n{\n\tconst mosquitto_property *prop;\n\tuint32_t value;\n\n\tprop = mosquitto_property_read_int32(proplist, identifier, &value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_EQUAL(value, expected_value);\n}\n\n\nstatic void read_varint_helper(const mosquitto_property *proplist, int identifier, uint32_t expected_value)\n{\n\tconst mosquitto_property *prop;\n\tuint32_t value;\n\n\tprop = mosquitto_property_read_varint(proplist, identifier, &value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_EQUAL(value, expected_value);\n}\n\n\nstatic void read_binary_helper(const mosquitto_property *proplist, int identifier, const void *expected_value, uint16_t expected_length)\n{\n\tconst mosquitto_property *prop;\n\tvoid *value = NULL;\n\tuint16_t length;\n\n\tprop = mosquitto_property_read_binary(proplist, identifier, &value, &length, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_EQUAL(length, expected_length);\n\tif(expected_value){\n\t\tCU_ASSERT_PTR_NOT_NULL(value);\n\t\tif(value){\n\t\t\tCU_ASSERT_NSTRING_EQUAL(value, expected_value, expected_length);\n\t\t}\n\t}else{\n\t\tCU_ASSERT_PTR_NULL(value);\n\t}\n\tSAFE_FREE(value);\n}\n\n\nstatic void read_string_helper(const mosquitto_property *proplist, int identifier, const char *expected_value)\n{\n\tconst mosquitto_property *prop;\n\tchar *value = NULL;\n\n\tprop = mosquitto_property_read_string(proplist, identifier, &value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tif(expected_value){\n\t\tCU_ASSERT_PTR_NOT_NULL(value);\n\t\tif(value){\n\t\t\tCU_ASSERT_STRING_EQUAL(value, expected_value);\n\t\t}\n\t}else{\n\t\tCU_ASSERT_PTR_NULL(value);\n\t}\n\tSAFE_FREE(value);\n}\n\n\nstatic void read_string_pair_helper(const mosquitto_property *proplist, int identifier, const char *expected_key, const char *expected_value)\n{\n\tconst mosquitto_property *prop;\n\tchar *key = NULL, *value = NULL;\n\n\tprop = mosquitto_property_read_string_pair(proplist, identifier, &key, &value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\n\tif(expected_key){\n\t\tCU_ASSERT_PTR_NOT_NULL(key);\n\t\tif(key){\n\t\t\tCU_ASSERT_STRING_EQUAL(key, expected_key);\n\t\t}\n\t}else{\n\t\tCU_ASSERT_PTR_NULL(key);\n\t}\n\n\tif(expected_value){\n\t\tCU_ASSERT_PTR_NOT_NULL(value);\n\t\tif(value){\n\t\t\tCU_ASSERT_STRING_EQUAL(value, expected_value);\n\t\t}\n\t}else{\n\t\tCU_ASSERT_PTR_NULL(value);\n\t}\n\tSAFE_FREE(key);\n\tSAFE_FREE(value);\n}\n\n\nstatic void TEST_read_null_binary(void)\n{\n\tint rc;\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\n\trc = mosquitto_property_add_binary(&proplist, MQTT_PROP_CORRELATION_DATA, NULL, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tread_binary_helper(proplist, MQTT_PROP_CORRELATION_DATA, NULL, 0);\n\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\n\tread_binary_helper(proplist_copy, MQTT_PROP_CORRELATION_DATA, NULL, 0);\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\nstatic void TEST_read_null_string(void)\n{\n\tint rc;\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\n\trc = mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, NULL);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tread_string_helper(proplist, MQTT_PROP_CONTENT_TYPE, NULL);\n\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\n\tread_string_helper(proplist_copy, MQTT_PROP_CONTENT_TYPE, NULL);\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\nstatic void TEST_read_null_string_pair(void)\n{\n\tint rc;\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\n\trc = mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, NULL, NULL);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tread_string_pair_helper(proplist, MQTT_PROP_USER_PROPERTY, NULL, NULL);\n\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\n\tread_string_pair_helper(proplist_copy, MQTT_PROP_USER_PROPERTY, NULL, NULL);\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\nstatic void TEST_read_single_byte(void)\n{\n\tint rc;\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\n\tgenerate_full_proplist(&proplist);\n\tif(!proplist){\n\t\treturn;\n\t}\n\n\tread_byte_helper(proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n\tread_byte_helper(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1);\n\tread_byte_helper(proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1);\n\tread_byte_helper(proplist, MQTT_PROP_MAXIMUM_QOS, 0);\n\tread_byte_helper(proplist, MQTT_PROP_RETAIN_AVAILABLE, 0);\n\tread_byte_helper(proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0);\n\tread_byte_helper(proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0);\n\tread_byte_helper(proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\n\tread_byte_helper(proplist_copy, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n\tread_byte_helper(proplist_copy, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1);\n\tread_byte_helper(proplist_copy, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1);\n\tread_byte_helper(proplist_copy, MQTT_PROP_MAXIMUM_QOS, 0);\n\tread_byte_helper(proplist_copy, MQTT_PROP_RETAIN_AVAILABLE, 0);\n\tread_byte_helper(proplist_copy, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0);\n\tread_byte_helper(proplist_copy, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0);\n\tread_byte_helper(proplist_copy, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\nstatic void TEST_read_single_int16(void)\n{\n\tint rc;\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\n\tgenerate_full_proplist(&proplist);\n\tif(!proplist){\n\t\treturn;\n\t}\n\n\tread_int16_helper(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, 180);\n\tread_int16_helper(proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024);\n\tread_int16_helper(proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64);\n\tread_int16_helper(proplist, MQTT_PROP_TOPIC_ALIAS, 15);\n\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\n\tread_int16_helper(proplist_copy, MQTT_PROP_SERVER_KEEP_ALIVE, 180);\n\tread_int16_helper(proplist_copy, MQTT_PROP_RECEIVE_MAXIMUM, 1024);\n\tread_int16_helper(proplist_copy, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64);\n\tread_int16_helper(proplist_copy, MQTT_PROP_TOPIC_ALIAS, 15);\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\nstatic void TEST_read_single_int32(void)\n{\n\tint rc;\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\n\tgenerate_full_proplist(&proplist);\n\tif(!proplist){\n\t\treturn;\n\t}\n\n\tread_int32_helper(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600);\n\tread_int32_helper(proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400);\n\tread_int32_helper(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, 1800);\n\tread_int32_helper(proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000);\n\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\n\tread_int32_helper(proplist_copy, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600);\n\tread_int32_helper(proplist_copy, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400);\n\tread_int32_helper(proplist_copy, MQTT_PROP_WILL_DELAY_INTERVAL, 1800);\n\tread_int32_helper(proplist_copy, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000);\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\nstatic void TEST_read_single_varint(void)\n{\n\tint rc;\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\n\tgenerate_full_proplist(&proplist);\n\tif(!proplist){\n\t\treturn;\n\t}\n\n\tread_varint_helper(proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 63);\n\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\n\tread_varint_helper(proplist_copy, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 63);\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\nstatic void TEST_read_single_binary(void)\n{\n\tint rc;\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\n\tgenerate_full_proplist(&proplist);\n\tif(!proplist){\n\t\treturn;\n\t}\n\n\tread_binary_helper(proplist, MQTT_PROP_CORRELATION_DATA, \"correlation-data\", strlen(\"correlation-data\"));\n\tread_binary_helper(proplist, MQTT_PROP_AUTHENTICATION_DATA, \"password\", strlen(\"password\"));\n\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\n\tif(proplist_copy){\n\t\tread_binary_helper(proplist_copy, MQTT_PROP_CORRELATION_DATA, \"correlation-data\", strlen(\"correlation-data\"));\n\t\tread_binary_helper(proplist_copy, MQTT_PROP_AUTHENTICATION_DATA, \"password\", strlen(\"password\"));\n\t}\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\nstatic void TEST_read_single_string(void)\n{\n\tint rc;\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\n\tgenerate_full_proplist(&proplist);\n\tif(!proplist){\n\t\treturn;\n\t}\n\n\tread_string_helper(proplist, MQTT_PROP_CONTENT_TYPE, \"application/json\");\n\tread_string_helper(proplist, MQTT_PROP_RESPONSE_TOPIC, \"response/topic\");\n\tread_string_helper(proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, \"mosquitto-test\");\n\tread_string_helper(proplist, MQTT_PROP_AUTHENTICATION_METHOD, \"basic\");\n\tread_string_helper(proplist, MQTT_PROP_RESPONSE_INFORMATION, \"response\");\n\tread_string_helper(proplist, MQTT_PROP_SERVER_REFERENCE, \"localhost\");\n\tread_string_helper(proplist, MQTT_PROP_REASON_STRING, \"reason\");\n\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\n\tif(proplist_copy){\n\t\tread_string_helper(proplist_copy, MQTT_PROP_CONTENT_TYPE, \"application/json\");\n\t\tread_string_helper(proplist_copy, MQTT_PROP_RESPONSE_TOPIC, \"response/topic\");\n\t\tread_string_helper(proplist_copy, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, \"mosquitto-test\");\n\t\tread_string_helper(proplist_copy, MQTT_PROP_AUTHENTICATION_METHOD, \"basic\");\n\t\tread_string_helper(proplist_copy, MQTT_PROP_RESPONSE_INFORMATION, \"response\");\n\t\tread_string_helper(proplist_copy, MQTT_PROP_SERVER_REFERENCE, \"localhost\");\n\t\tread_string_helper(proplist_copy, MQTT_PROP_REASON_STRING, \"reason\");\n\t}\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\nstatic void TEST_read_single_string_pair(void)\n{\n\tint rc;\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\n\tgenerate_full_proplist(&proplist);\n\tif(!proplist){\n\t\treturn;\n\t}\n\n\tread_string_pair_helper(proplist, MQTT_PROP_USER_PROPERTY, \"user-agent\", \"mosquitto/test\");\n\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\n\tif(proplist_copy){\n\t\tread_string_pair_helper(proplist_copy, MQTT_PROP_USER_PROPERTY, \"user-agent\", \"mosquitto/test\");\n\t}\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\n/* ========================================================================\n * MISSING READ\n * ======================================================================== */\n\n\nstatic void missing_read_helper(mosquitto_property *proplist)\n{\n\tconst mosquitto_property *prop;\n\tuint8_t byte_value;\n\tuint16_t int16_value;\n\tuint32_t int32_value;\n\tchar *key, *value;\n\tuint16_t length;\n\n\t/* MISSING */\n\tprop = mosquitto_property_read_byte(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &byte_value, false);\n\tCU_ASSERT_PTR_NULL(prop);\n\n\t/* NOT MISSING */\n\tprop = mosquitto_property_read_int32(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, &int32_value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_EQUAL(int32_value, 1800);\n\n\t/* MISSING */\n\tvalue = NULL;\n\tprop = mosquitto_property_read_string(proplist, MQTT_PROP_CONTENT_TYPE, &value, false);\n\tCU_ASSERT_PTR_NULL(prop);\n\tSAFE_FREE(value);\n\n\t/* NOT MISSING */\n\tvalue = NULL;\n\tprop = mosquitto_property_read_string(proplist, MQTT_PROP_RESPONSE_TOPIC, &value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_PTR_NOT_NULL(value);\n\tif(value){\n\t\tCU_ASSERT_STRING_EQUAL(value, \"response/topic\");\n\t\tSAFE_FREE(value);\n\t}\n\n\t/* MISSING */\n\tprop = mosquitto_property_read_binary(proplist, MQTT_PROP_CORRELATION_DATA, (void **)&value, &length, false);\n\tCU_ASSERT_PTR_NULL(prop);\n\tCU_ASSERT_PTR_NULL(value);\n\tSAFE_FREE(value);\n\n\t/* NOT MISSING */\n\tprop = mosquitto_property_read_byte(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, &byte_value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_EQUAL(byte_value, 1);\n\n\t/* MISSING */\n\tprop = mosquitto_property_read_varint(proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &int32_value, false);\n\tCU_ASSERT_PTR_NULL(prop);\n\n\t/* NOT MISSING */\n\tvalue = NULL;\n\tprop = mosquitto_property_read_string(proplist, MQTT_PROP_SERVER_REFERENCE, &value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_PTR_NOT_NULL(value);\n\tif(value){\n\t\tCU_ASSERT_STRING_EQUAL(value, \"localhost\");\n\t\tSAFE_FREE(value);\n\t}\n\n\t/* MISSING */\n\tprop = mosquitto_property_read_int32(proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, &int32_value, false);\n\tCU_ASSERT_PTR_NULL(prop);\n\n\t/* NOT MISSING */\n\tvalue = NULL;\n\tprop = mosquitto_property_read_binary(proplist, MQTT_PROP_AUTHENTICATION_DATA, (void **)&value, &length, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_PTR_NOT_NULL(value);\n\tif(value){\n\t\tCU_ASSERT_NSTRING_EQUAL(value, \"password\", strlen(\"password\"));\n\t\tCU_ASSERT_EQUAL(length, strlen(\"password\"));\n\t\tSAFE_FREE(value);\n\t}\n\n\t/* MISSING */\n\tprop = mosquitto_property_read_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, &int16_value, false);\n\tCU_ASSERT_PTR_NULL(prop);\n\n\t/* NOT MISSING */\n\tprop = mosquitto_property_read_int16(proplist, MQTT_PROP_RECEIVE_MAXIMUM, &int16_value, false);\n\tCU_ASSERT_PTR_NOT_NULL(prop);\n\tCU_ASSERT_EQUAL(int16_value, 1024);\n\n\t/* MISSING */\n\tprop = mosquitto_property_read_string_pair(proplist, MQTT_PROP_USER_PROPERTY, &key, &value, false);\n\tSAFE_FREE(key);\n\tSAFE_FREE(value);\n\tCU_ASSERT_PTR_NULL(prop);\n}\n\n\nstatic void TEST_read_missing(void)\n{\n\tmosquitto_property *proplist = NULL, *proplist_copy = NULL;\n\tint rc;\n\n\tgenerate_partial_proplist(&proplist);\n\tif(!proplist){\n\t\treturn;\n\t}\n\n\tmissing_read_helper(proplist);\n\trc = mosquitto_property_copy_all(&proplist_copy, proplist);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist_copy);\n\tif(proplist_copy){\n\t\tmissing_read_helper(proplist_copy);\n\t}\n\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&proplist_copy);\n}\n\n\n/* ========================================================================\n * STRING TO PROPERTY INFO\n * ======================================================================== */\n\n\nstatic void string_to_property_info_helper(const char *str, int rc_expected, int identifier_expected, int type_expected)\n{\n\tint rc;\n\tint identifier, type;\n\n\trc = mosquitto_string_to_property_info(str, &identifier, &type);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tif(rc == MOSQ_ERR_SUCCESS){\n\t\tCU_ASSERT_EQUAL(identifier, identifier_expected);\n\t\tCU_ASSERT_EQUAL(type, type_expected);\n\t}\n}\n\n\nstatic void TEST_string_to_property_info(void)\n{\n\tstring_to_property_info_helper(\"payload-format-indicator\", MOSQ_ERR_SUCCESS, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, MQTT_PROP_TYPE_BYTE);\n\tstring_to_property_info_helper(\"message-expiry-interval\", MOSQ_ERR_SUCCESS, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT32);\n\tstring_to_property_info_helper(\"content-type\", MOSQ_ERR_SUCCESS, MQTT_PROP_CONTENT_TYPE, MQTT_PROP_TYPE_STRING);\n\tstring_to_property_info_helper(\"response-topic\", MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_TOPIC, MQTT_PROP_TYPE_STRING);\n\tstring_to_property_info_helper(\"correlation-data\", MOSQ_ERR_SUCCESS, MQTT_PROP_CORRELATION_DATA, MQTT_PROP_TYPE_BINARY);\n\tstring_to_property_info_helper(\"subscription-identifier\", MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, MQTT_PROP_TYPE_VARINT);\n\tstring_to_property_info_helper(\"session-expiry-interval\", MOSQ_ERR_SUCCESS, MQTT_PROP_SESSION_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT32);\n\tstring_to_property_info_helper(\"assigned-client-identifier\", MOSQ_ERR_SUCCESS, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, MQTT_PROP_TYPE_STRING);\n\tstring_to_property_info_helper(\"server-keep-alive\", MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_KEEP_ALIVE, MQTT_PROP_TYPE_INT16);\n\tstring_to_property_info_helper(\"authentication-method\", MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_METHOD, MQTT_PROP_TYPE_STRING);\n\tstring_to_property_info_helper(\"authentication-data\", MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_DATA, MQTT_PROP_TYPE_BINARY);\n\tstring_to_property_info_helper(\"request-problem-information\", MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, MQTT_PROP_TYPE_BYTE);\n\tstring_to_property_info_helper(\"will-delay-interval\", MOSQ_ERR_SUCCESS, MQTT_PROP_WILL_DELAY_INTERVAL, MQTT_PROP_TYPE_INT32);\n\tstring_to_property_info_helper(\"request-response-information\", MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, MQTT_PROP_TYPE_BYTE);\n\tstring_to_property_info_helper(\"response-information\", MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_INFORMATION, MQTT_PROP_TYPE_STRING);\n\tstring_to_property_info_helper(\"server-reference\", MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_REFERENCE, MQTT_PROP_TYPE_STRING);\n\tstring_to_property_info_helper(\"reason-string\", MOSQ_ERR_SUCCESS, MQTT_PROP_REASON_STRING, MQTT_PROP_TYPE_STRING);\n\tstring_to_property_info_helper(\"receive-maximum\", MOSQ_ERR_SUCCESS, MQTT_PROP_RECEIVE_MAXIMUM, MQTT_PROP_TYPE_INT16);\n\tstring_to_property_info_helper(\"topic-alias-maximum\", MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, MQTT_PROP_TYPE_INT16);\n\tstring_to_property_info_helper(\"topic-alias\", MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS, MQTT_PROP_TYPE_INT16);\n\tstring_to_property_info_helper(\"maximum-qos\", MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_QOS, MQTT_PROP_TYPE_BYTE);\n\tstring_to_property_info_helper(\"retain-available\", MOSQ_ERR_SUCCESS, MQTT_PROP_RETAIN_AVAILABLE, MQTT_PROP_TYPE_BYTE);\n\tstring_to_property_info_helper(\"user-property\", MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, MQTT_PROP_TYPE_STRING_PAIR);\n\tstring_to_property_info_helper(\"maximum-packet-size\", MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_PACKET_SIZE, MQTT_PROP_TYPE_INT32);\n\tstring_to_property_info_helper(\"wildcard-subscription-available\", MOSQ_ERR_SUCCESS, MQTT_PROP_WILDCARD_SUB_AVAILABLE, MQTT_PROP_TYPE_BYTE);\n\tstring_to_property_info_helper(\"subscription-identifier-available\", MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, MQTT_PROP_TYPE_BYTE);\n\tstring_to_property_info_helper(\"shared-subscription-available\", MOSQ_ERR_SUCCESS, MQTT_PROP_SHARED_SUB_AVAILABLE, MQTT_PROP_TYPE_BYTE);\n\n\tstring_to_property_info_helper(\"payload-format-indicator1\", MOSQ_ERR_INVAL, 0, 0);\n\tstring_to_property_info_helper(\"payload\", MOSQ_ERR_INVAL, 0, 0);\n\tstring_to_property_info_helper(\"\", MOSQ_ERR_INVAL, 0, 0);\n\tstring_to_property_info_helper(NULL, MOSQ_ERR_INVAL, 0, 0);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_property_user_read_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Property user read\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit Property user read test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Read single byte\", TEST_read_single_byte)\n\t\t\t|| !CU_add_test(test_suite, \"Read single int16\", TEST_read_single_int16)\n\t\t\t|| !CU_add_test(test_suite, \"Read single int32\", TEST_read_single_int32)\n\t\t\t|| !CU_add_test(test_suite, \"Read single varint\", TEST_read_single_varint)\n\t\t\t|| !CU_add_test(test_suite, \"Read single binary\", TEST_read_single_binary)\n\t\t\t|| !CU_add_test(test_suite, \"Read single string\", TEST_read_single_string)\n\t\t\t|| !CU_add_test(test_suite, \"Read single string pair\", TEST_read_single_string_pair)\n\t\t\t|| !CU_add_test(test_suite, \"Read missing\", TEST_read_missing)\n\t\t\t|| !CU_add_test(test_suite, \"Read NULL binary\", TEST_read_null_binary)\n\t\t\t|| !CU_add_test(test_suite, \"Read NULL string\", TEST_read_null_string)\n\t\t\t|| !CU_add_test(test_suite, \"Read NULL string pair\", TEST_read_null_string_pair)\n\t\t\t|| !CU_add_test(test_suite, \"String to property info\", TEST_string_to_property_info)\n\t\t\t){\n\n\t\tprintf(\"Error adding Property Add CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/lib/property_write.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"property_common.h\"\n#include \"property_mosq.h\"\n#include \"packet_mosq.h\"\n\n\nstatic void byte_prop_write_helper(\n\t\tint command,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tint identifier,\n\t\tuint8_t value_expected)\n{\n\tmosquitto_property property;\n\tstruct mosquitto__packet *packet;\n\tstruct mosquitto__packet_in in_packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&property, 0, sizeof(mosquitto_property));\n\n\tproperty.identifier = identifier;\n\tproperty.value.i8 = value_expected;\n\tproperty.property_type = MQTT_PROP_TYPE_BYTE;\n\n\trc = packet__alloc(&packet, 0, mosquitto_property_get_length_all(&property)+11);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tpacket->pos = 0; /* Make indexing easier */\n\tproperty__write_all(packet, &property, true);\n\n\tin_packet.remaining_length = packet->remaining_length;\n\tin_packet.packet_length = packet->packet_length;\n\tin_packet.pos = 0;\n\tin_packet.payload = packet->payload;\n\n\trc = property__read_all(command, &in_packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(in_packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_identifier(properties), identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.i8, value_expected);\n\t\tCU_ASSERT_EQUAL(properties->property_type, MQTT_PROP_TYPE_BYTE);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_type(properties), MQTT_PROP_TYPE_BYTE);\n\t\tCU_ASSERT_PTR_EQUAL(properties->next, NULL);\n\t\tCU_ASSERT_PTR_EQUAL(mosquitto_property_next(properties), NULL);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 2);\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tfree(packet);\n}\n\n\nstatic void int32_prop_write_helper(\n\t\tint command,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tint identifier,\n\t\tuint32_t value_expected)\n{\n\tmosquitto_property property;\n\tstruct mosquitto__packet *packet;\n\tstruct mosquitto__packet_in in_packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&property, 0, sizeof(mosquitto_property));\n\n\tproperty.identifier = identifier;\n\tproperty.value.i32 = value_expected;\n\tproperty.property_type = MQTT_PROP_TYPE_INT32;\n\n\trc = packet__alloc(&packet, 0, mosquitto_property_get_length_all(&property)+11);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tpacket->pos = 0; /* Make indexing easier */\n\tproperty__write_all(packet, &property, true);\n\n\tin_packet.remaining_length = packet->remaining_length;\n\tin_packet.packet_length = packet->packet_length;\n\tin_packet.pos = 0;\n\tin_packet.payload = packet->payload;\n\n\trc = property__read_all(command, &in_packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(in_packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_identifier(properties), identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.i32, value_expected);\n\t\tCU_ASSERT_EQUAL(properties->property_type, MQTT_PROP_TYPE_INT32);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_type(properties), MQTT_PROP_TYPE_INT32);\n\t\tCU_ASSERT_PTR_EQUAL(properties->next, NULL);\n\t\tCU_ASSERT_PTR_EQUAL(mosquitto_property_next(properties), NULL);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 5);\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tfree(packet);\n}\n\n\nstatic void int16_prop_write_helper(\n\t\tint command,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tint identifier,\n\t\tuint16_t value_expected)\n{\n\tmosquitto_property property;\n\tstruct mosquitto__packet *packet;\n\tstruct mosquitto__packet_in in_packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&property, 0, sizeof(mosquitto_property));\n\n\tproperty.identifier = identifier;\n\tproperty.value.i16 = value_expected;\n\tproperty.property_type = MQTT_PROP_TYPE_INT16;\n\n\trc = packet__alloc(&packet, 0, mosquitto_property_get_length_all(&property)+11);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tpacket->pos = 0; /* Make indexing easier */\n\tproperty__write_all(packet, &property, true);\n\n\tin_packet.remaining_length = packet->remaining_length;\n\tin_packet.packet_length = packet->packet_length;\n\tin_packet.pos = 0;\n\tin_packet.payload = packet->payload;\n\n\trc = property__read_all(command, &in_packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(in_packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_identifier(properties), identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.i16, value_expected);\n\t\tCU_ASSERT_EQUAL(properties->property_type, MQTT_PROP_TYPE_INT16);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_type(properties), MQTT_PROP_TYPE_INT16);\n\t\tCU_ASSERT_PTR_EQUAL(properties->next, NULL);\n\t\tCU_ASSERT_PTR_EQUAL(mosquitto_property_next(properties), NULL);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 3);\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tfree(packet);\n}\n\n\nstatic void string_prop_write_helper(\n\t\tint command,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tint identifier,\n\t\tconst char *value_expected)\n{\n\tmosquitto_property property;\n\tstruct mosquitto__packet *packet;\n\tstruct mosquitto__packet_in in_packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&property, 0, sizeof(mosquitto_property));\n\n\tproperty.identifier = identifier;\n\tproperty.property_type = MQTT_PROP_TYPE_STRING;\n\tproperty.value.s.v = strdup(value_expected);\n\tCU_ASSERT_PTR_NOT_NULL(property.value.s.v);\n\tif(!property.value.s.v){\n\t\treturn;\n\t}\n\n\tproperty.value.s.len = (uint16_t)strlen(value_expected);\n\n\trc = packet__alloc(&packet, 0, mosquitto_property_get_length_all(&property)+11);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tpacket->pos = 0; /* Make indexing easier */\n\tproperty__write_all(packet, &property, true);\n\n\tin_packet.remaining_length = packet->remaining_length;\n\tin_packet.packet_length = packet->packet_length;\n\tin_packet.pos = 0;\n\tin_packet.payload = packet->payload;\n\n\trc = property__read_all(command, &in_packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(in_packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_identifier(properties), identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected));\n\t\tCU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected);\n\t\tCU_ASSERT_EQUAL(properties->property_type, MQTT_PROP_TYPE_STRING);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_type(properties), MQTT_PROP_TYPE_STRING);\n\t\tCU_ASSERT_PTR_EQUAL(properties->next, NULL);\n\t\tCU_ASSERT_PTR_EQUAL(mosquitto_property_next(properties), NULL);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 1+2+strlen(value_expected));\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tfree(property.value.s.v);\n\tfree(packet);\n}\n\n\nstatic void binary_prop_write_helper(\n\t\tint command,\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tint identifier,\n\t\tconst uint8_t *value_expected,\n\t\tuint16_t len_expected)\n{\n\tmosquitto_property property;\n\tstruct mosquitto__packet *packet;\n\tstruct mosquitto__packet_in in_packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&property, 0, sizeof(mosquitto_property));\n\n\tproperty.identifier = identifier;\n\tproperty.property_type = MQTT_PROP_TYPE_BINARY;\n\tproperty.value.bin.v = malloc(len_expected);\n\tCU_ASSERT_PTR_NOT_NULL(property.value.bin.v);\n\tif(!property.value.bin.v){\n\t\treturn;\n\t}\n\n\tmemcpy(property.value.bin.v, value_expected, len_expected);\n\tproperty.value.bin.len = len_expected;\n\n\trc = packet__alloc(&packet, 0, mosquitto_property_get_length_all(&property)+11);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tpacket->pos = 0; /* Make indexing easier */\n\tproperty__write_all(packet, &property, true);\n\n\tin_packet.remaining_length = packet->remaining_length;\n\tin_packet.packet_length = packet->packet_length;\n\tin_packet.pos = 0;\n\tin_packet.payload = packet->payload;\n\n\trc = property__read_all(command, &in_packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(in_packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_identifier(properties), identifier);\n\t\tCU_ASSERT_EQUAL(properties->value.bin.len, len_expected);\n\t\tCU_ASSERT_EQUAL(memcmp(properties->value.bin.v, value_expected, len_expected), 0);\n\t\tCU_ASSERT_EQUAL(properties->property_type, MQTT_PROP_TYPE_BINARY);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_type(properties), MQTT_PROP_TYPE_BINARY);\n\t\tCU_ASSERT_PTR_EQUAL(properties->next, NULL);\n\t\tCU_ASSERT_PTR_EQUAL(mosquitto_property_next(properties), NULL);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 1+2+len_expected);\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_EQUAL(properties, NULL);\n\tfree(property.value.bin.v);\n\tfree(packet);\n}\n\n\nstatic void string_pair_prop_write_helper(\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tint identifier,\n\t\tconst char *name_expected,\n\t\tconst char *value_expected,\n\t\tbool expect_multiple)\n{\n\tmosquitto_property property;\n\tstruct mosquitto__packet *packet;\n\tstruct mosquitto__packet_in in_packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&property, 0, sizeof(mosquitto_property));\n\n\tproperty.identifier = identifier;\n\tproperty.property_type = MQTT_PROP_TYPE_STRING_PAIR;\n\tproperty.value.s.v = strdup(value_expected);\n\tCU_ASSERT_PTR_NOT_NULL(property.value.s.v);\n\tif(!property.value.s.v){\n\t\treturn;\n\t}\n\tproperty.value.s.len = (uint16_t)strlen(value_expected);\n\n\tproperty.name.v = strdup(name_expected);\n\tCU_ASSERT_PTR_NOT_NULL(property.name.v);\n\tif(!property.name.v){\n\t\treturn;\n\t}\n\n\tproperty.name.len = (uint16_t)strlen(name_expected);\n\n\trc = packet__alloc(&packet, 0, mosquitto_property_get_length_all(&property)+11);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tpacket->pos = 0; /* Make indexing easier */\n\tproperty__write_all(packet, &property, true);\n\n\tin_packet.remaining_length = packet->remaining_length;\n\tin_packet.packet_length = packet->packet_length;\n\tin_packet.pos = 0;\n\tin_packet.payload = packet->payload;\n\n\trc = property__read_all(CMD_CONNECT, &in_packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(in_packet.pos, remaining_length);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_identifier(properties), identifier);\n\t\tCU_ASSERT_EQUAL(properties->name.len, strlen(name_expected));\n\t\tCU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected));\n\t\tCU_ASSERT_STRING_EQUAL(properties->name.v, name_expected);\n\t\tCU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected);\n\t\tCU_ASSERT_EQUAL(properties->property_type, MQTT_PROP_TYPE_STRING_PAIR);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_type(properties), MQTT_PROP_TYPE_STRING_PAIR);\n\t\tif(expect_multiple){\n\t\t\tCU_ASSERT_PTR_NOT_NULL(properties->next);\n\t\t\tCU_ASSERT_PTR_NOT_NULL(mosquitto_property_next(properties));\n\t\t}else{\n\t\t\tCU_ASSERT_PTR_NULL(properties->next);\n\t\t\tCU_ASSERT_PTR_NULL(mosquitto_property_next(properties));\n\t\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 1+2+strlen(name_expected)+2+strlen(value_expected));\n\t\t}\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_NULL(properties);\n\tfree(property.value.s.v);\n\tfree(property.name.v);\n\tfree(packet);\n}\n\n\nstatic void varint_prop_write_helper(\n\t\tuint32_t remaining_length,\n\t\tint rc_expected,\n\t\tint identifier,\n\t\tuint32_t value_expected)\n{\n\tmosquitto_property property;\n\tstruct mosquitto__packet *packet;\n\tstruct mosquitto__packet_in in_packet;\n\tmosquitto_property *properties;\n\tint rc;\n\n\tmemset(&property, 0, sizeof(mosquitto_property));\n\n\tproperty.identifier = identifier;\n\tproperty.property_type = MQTT_PROP_TYPE_VARINT;\n\tproperty.value.varint = value_expected;\n\n\tCU_ASSERT_EQUAL(remaining_length, mosquitto_property_get_length_all(&property)+1);\n\n\trc = packet__alloc(&packet, 0, mosquitto_property_get_length_all(&property)+11);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tif(rc != MOSQ_ERR_SUCCESS){\n\t\treturn;\n\t}\n\n\tpacket->pos = 0; /* Make indexing easier */\n\tproperty__write_all(packet, &property, true);\n\n\tin_packet.remaining_length = packet->remaining_length;\n\tin_packet.packet_length = packet->packet_length;\n\tin_packet.pos = 0;\n\tin_packet.payload = packet->payload;\n\n\trc = property__read_all(CMD_PUBLISH, &in_packet, &properties);\n\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tif(properties){\n\t\tCU_ASSERT_EQUAL(properties->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_identifier(properties), identifier);\n\t\tCU_ASSERT_EQUAL(properties->property_type, MQTT_PROP_TYPE_VARINT);\n\t\tCU_ASSERT_EQUAL(mosquitto_property_type(properties), MQTT_PROP_TYPE_VARINT);\n\t\tCU_ASSERT_EQUAL(properties->value.varint, value_expected);\n\t\tCU_ASSERT_PTR_NULL(properties->next);\n\t\tCU_ASSERT_PTR_NULL(mosquitto_property_next(properties));\n\t\tif(value_expected < 128){\n\t\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 2);\n\t\t}else if(value_expected < 16384){\n\t\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 3);\n\t\t}else if(value_expected < 2097152){\n\t\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 4);\n\t\t}else if(value_expected < 268435456){\n\t\t\tCU_ASSERT_EQUAL(mosquitto_property_get_length_all(properties), 5);\n\t\t}else{\n\t\t\tCU_FAIL(\"Incorrect varint value.\");\n\t\t}\n\t\tmosquitto_property_free_all(&properties);\n\t}\n\tCU_ASSERT_PTR_NULL(properties);\n\tfree(packet);\n}\n\n\n/* ========================================================================\n * BAD IDENTIFIER\n * ======================================================================== */\n\n\nstatic void TEST_bad_identifier(void)\n{\n\tmosquitto_property property;\n\tstruct mosquitto__packet *packet;\n\tint rc;\n\n\tmemset(&property, 0, sizeof(property));\n\tpacket = calloc(1, sizeof(struct mosquitto__packet) + 10);\n\tCU_ASSERT_PTR_NOT_NULL(packet);\n\tif(packet == NULL){\n\t\treturn;\n\t}\n\n\tproperty.identifier = 0xFFFF;\n\tproperty.property_type = MQTT_PROP_TYPE_BYTE;\n\tpacket->packet_length = 10;\n\tpacket->remaining_length = 8;\n\trc = property__write_all(packet, &property, true);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); // We don't check invalid identifier types here\n\tfree(packet);\n}\n\n\n/* ========================================================================\n * SINGLE PROPERTIES\n * ======================================================================== */\n\n\nstatic void TEST_single_payload_format_indicator(void)\n{\n\tbyte_prop_write_helper(CMD_PUBLISH, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1);\n}\n\n\nstatic void TEST_single_request_problem_information(void)\n{\n\tbyte_prop_write_helper(CMD_CONNECT, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1);\n}\n\n\nstatic void TEST_single_request_response_information(void)\n{\n\tbyte_prop_write_helper(CMD_CONNECT, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1);\n}\n\n\nstatic void TEST_single_maximum_qos(void)\n{\n\tbyte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_QOS, 1);\n}\n\n\nstatic void TEST_single_retain_available(void)\n{\n\tbyte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_RETAIN_AVAILABLE, 1);\n}\n\n\nstatic void TEST_single_wildcard_subscription_available(void)\n{\n\tbyte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0);\n}\n\n\nstatic void TEST_single_subscription_identifier_available(void)\n{\n\tbyte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0);\n}\n\n\nstatic void TEST_single_shared_subscription_available(void)\n{\n\tbyte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SHARED_SUB_AVAILABLE, 1);\n}\n\n\nstatic void TEST_single_message_expiry_interval(void)\n{\n\tint32_prop_write_helper(CMD_PUBLISH, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 0x12233445);\n}\n\n\nstatic void TEST_single_session_expiry_interval(void)\n{\n\tint32_prop_write_helper(CMD_CONNACK, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x45342312);\n}\n\n\nstatic void TEST_single_will_delay_interval(void)\n{\n\tint32_prop_write_helper(CMD_WILL, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_WILL_DELAY_INTERVAL, 0x45342312);\n}\n\n\nstatic void TEST_single_maximum_packet_size(void)\n{\n\tint32_prop_write_helper(CMD_CONNECT, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0x45342312);\n}\n\n\nstatic void TEST_single_server_keep_alive(void)\n{\n\tint16_prop_write_helper(CMD_CONNACK, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_KEEP_ALIVE, 0x4534);\n}\n\n\nstatic void TEST_single_receive_maximum(void)\n{\n\tint16_prop_write_helper(CMD_CONNACK, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_RECEIVE_MAXIMUM, 0x6842);\n}\n\n\nstatic void TEST_single_topic_alias_maximum(void)\n{\n\tint16_prop_write_helper(CMD_CONNECT, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x6842);\n}\n\n\nstatic void TEST_single_topic_alias(void)\n{\n\tint16_prop_write_helper(CMD_PUBLISH, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS, 0x6842);\n}\n\n\nstatic void TEST_single_content_type(void)\n{\n\tstring_prop_write_helper(CMD_PUBLISH, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CONTENT_TYPE, \"hello\");\n}\n\n\nstatic void TEST_single_response_topic(void)\n{\n\tstring_prop_write_helper(CMD_WILL, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_TOPIC, \"hello\");\n}\n\n\nstatic void TEST_single_assigned_client_identifier(void)\n{\n\tstring_prop_write_helper(CMD_CONNACK, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, \"hello\");\n}\n\n\nstatic void TEST_single_authentication_method(void)\n{\n\tstring_prop_write_helper(CMD_CONNECT, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_METHOD, \"hello\");\n}\n\n\nstatic void TEST_single_response_information(void)\n{\n\tstring_prop_write_helper(CMD_CONNACK, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_INFORMATION, \"hello\");\n}\n\n\nstatic void TEST_single_server_reference(void)\n{\n\tstring_prop_write_helper(CMD_CONNACK, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_REFERENCE, \"hello\");\n}\n\n\nstatic void TEST_single_reason_string(void)\n{\n\tstring_prop_write_helper(CMD_PUBREC, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_REASON_STRING, \"hello\");\n}\n\n\nstatic void TEST_single_correlation_data(void)\n{\n\tuint8_t payload[5] = {1, 'e', 0, 'l', 9};\n\n\tbinary_prop_write_helper(CMD_PUBLISH, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CORRELATION_DATA, payload, 5);\n}\n\n\nstatic void TEST_single_authentication_data(void)\n{\n\tuint8_t payload[5] = {1, 'e', 0, 'l', 9};\n\n\tbinary_prop_write_helper(CMD_CONNECT, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_DATA, payload, 5);\n}\n\n\nstatic void TEST_single_user_property(void)\n{\n\tstring_pair_prop_write_helper(10, MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, \"za\", \"bc\", false);\n}\n\n\nstatic void TEST_single_subscription_identifier(void)\n{\n\tvarint_prop_write_helper(3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0);\n\tvarint_prop_write_helper(3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 127);\n\tvarint_prop_write_helper(4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 128);\n\tvarint_prop_write_helper(4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16383);\n\tvarint_prop_write_helper(5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16384);\n\tvarint_prop_write_helper(5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097151);\n\tvarint_prop_write_helper(6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097152);\n\tvarint_prop_write_helper(6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 268435455);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_property_write_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Property write\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit Property write test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Bad identifier\", TEST_bad_identifier)\n\t\t\t|| !CU_add_test(test_suite, \"Single Payload Format Indicator\", TEST_single_payload_format_indicator)\n\t\t\t|| !CU_add_test(test_suite, \"Single Request Problem Information\", TEST_single_request_problem_information)\n\t\t\t|| !CU_add_test(test_suite, \"Single Request Response Information\", TEST_single_request_response_information)\n\t\t\t|| !CU_add_test(test_suite, \"Single Maximum QoS\", TEST_single_maximum_qos)\n\t\t\t|| !CU_add_test(test_suite, \"Single Retain Available\", TEST_single_retain_available)\n\t\t\t|| !CU_add_test(test_suite, \"Single Wildcard Subscription Available\", TEST_single_wildcard_subscription_available)\n\t\t\t|| !CU_add_test(test_suite, \"Single Subscription Identifier Available\", TEST_single_subscription_identifier_available)\n\t\t\t|| !CU_add_test(test_suite, \"Single Shared Subscription Available\", TEST_single_shared_subscription_available)\n\t\t\t|| !CU_add_test(test_suite, \"Single Message Expiry Interval\", TEST_single_message_expiry_interval)\n\t\t\t|| !CU_add_test(test_suite, \"Single Session Expiry Interval\", TEST_single_session_expiry_interval)\n\t\t\t|| !CU_add_test(test_suite, \"Single Will Delay Interval\", TEST_single_will_delay_interval)\n\t\t\t|| !CU_add_test(test_suite, \"Single Maximum Packet Size\", TEST_single_maximum_packet_size)\n\t\t\t|| !CU_add_test(test_suite, \"Single Server Keep Alive\", TEST_single_server_keep_alive)\n\t\t\t|| !CU_add_test(test_suite, \"Single Receive Maximum\", TEST_single_receive_maximum)\n\t\t\t|| !CU_add_test(test_suite, \"Single Topic Alias Maximum\", TEST_single_topic_alias_maximum)\n\t\t\t|| !CU_add_test(test_suite, \"Single Topic Alias\", TEST_single_topic_alias)\n\t\t\t|| !CU_add_test(test_suite, \"Single Content Type\", TEST_single_content_type)\n\t\t\t|| !CU_add_test(test_suite, \"Single Response Topic\", TEST_single_response_topic)\n\t\t\t|| !CU_add_test(test_suite, \"Single Assigned Client Identifier\", TEST_single_assigned_client_identifier)\n\t\t\t|| !CU_add_test(test_suite, \"Single Authentication Method\", TEST_single_authentication_method)\n\t\t\t|| !CU_add_test(test_suite, \"Single Response Information\", TEST_single_response_information)\n\t\t\t|| !CU_add_test(test_suite, \"Single Server Reference\", TEST_single_server_reference)\n\t\t\t|| !CU_add_test(test_suite, \"Single Reason String\", TEST_single_reason_string)\n\t\t\t|| !CU_add_test(test_suite, \"Single Correlation Data\", TEST_single_correlation_data)\n\t\t\t|| !CU_add_test(test_suite, \"Single Authentication Data\", TEST_single_authentication_data)\n\t\t\t|| !CU_add_test(test_suite, \"Single User Property\", TEST_single_user_property)\n\t\t\t|| !CU_add_test(test_suite, \"Single Subscription Identifier\", TEST_single_subscription_identifier)\n\t\t\t){\n\n\t\tprintf(\"Error adding Property read CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/lib/publish_test.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include <mosquitto_internal.h>\n#include <util_mosq.h>\n\n\nstatic void TEST_maximum_packet_size(void)\n{\n\tstruct mosquitto mosq;\n\tint rc;\n\n\tmemset(&mosq, 0, sizeof(struct mosquitto));\n\n\tmosq.maximum_packet_size = 5;\n\trc = mosquitto_publish(&mosq, NULL, \"topic/oversize\", strlen(\"payload\"), \"payload\", 0, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_OVERSIZE_PACKET);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_publish_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Publish\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit Publish test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"v5: Maximum packet size\", TEST_maximum_packet_size)\n\t\t\t){\n\n\t\tprintf(\"Error adding Publish CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/lib/stubs.c",
    "content": "#include \"config.h\"\n\n#include <sys/types.h>\n#include \"callbacks.h\"\n#include \"logging_mosq.h\"\n#include \"net_mosq.h\"\n#include \"read_handle.h\"\n#include \"send_mosq.h\"\n\nstruct mosquitto_db {\n\n};\n\nstruct mosquitto__base_msg {\n\n};\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\tUNUSED(mosq);\n\tUNUSED(priority);\n\tUNUSED(fmt);\n\n\treturn 0;\n}\n\n\nbool net__is_connected(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\treturn false;\n}\n\n\nint net__socket_close(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint net__socket_shutdown(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pingreq(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nvoid callback__on_disconnect(struct mosquitto *mosq, int rc, const mosquitto_property *props)\n{\n\tUNUSED(mosq);\n\tUNUSED(rc);\n\tUNUSED(props);\n}\n\n\nvoid callback__on_publish(struct mosquitto *mosq, int mid, int reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(mid);\n\tUNUSED(reason_code);\n\tUNUSED(properties);\n}\n\n\nvoid do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties)\n{\n\tUNUSED(mosq);\n\tUNUSED(reason_code);\n\tUNUSED(properties);\n}\n\n\nint handle__packet(struct mosquitto *context)\n{\n\tUNUSED(context);\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nssize_t net__read(struct mosquitto *mosq, void *buf, size_t count)\n{\n\tUNUSED(mosq);\n\tUNUSED(buf);\n\tUNUSED(count);\n\treturn 1;\n}\n\n\nssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count)\n{\n\tUNUSED(mosq);\n\tUNUSED(buf);\n\tUNUSED(count);\n\treturn 1;\n}\n\n\nvoid plugin_persist__handle_retain_set(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__handle_retain_remove(struct mosquitto__base_msg *msg)\n{\n\tUNUSED(msg);\n}\n\n\nvoid plugin_persist__process_retain_events(bool force)\n{\n\tUNUSED(force);\n}\n\n\nvoid plugin_persist__queue_retain_event(struct mosquitto__base_msg *msg, int event)\n{\n\tUNUSED(msg);\n\tUNUSED(event);\n}\n\n\nvoid ws__prepare_packet(struct mosquitto *mosq, struct mosquitto__packet *packet)\n{\n\tUNUSED(mosq);\n\tUNUSED(packet);\n}\n\n\nssize_t net__read_ws(struct mosquitto *mosq, void *buf, size_t count)\n{\n\tUNUSED(mosq);\n\tUNUSED(buf);\n\tUNUSED(count);\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/lib/test.c",
    "content": "#include \"config.h\"\n#include <stdio.h>\n\n#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\nint init_datatype_read_tests(void);\nint init_datatype_write_tests(void);\nint init_property_read_tests(void);\nint init_property_user_read_tests(void);\nint init_property_write_tests(void);\n\n\nint main(int argc, char *argv[])\n{\n\tunsigned int fails;\n\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tif(CU_initialize_registry() != CUE_SUCCESS){\n\t\tprintf(\"Error initializing CUnit registry.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| init_datatype_read_tests()\n\t\t\t|| init_datatype_write_tests()\n\t\t\t|| init_property_read_tests()\n\t\t\t|| init_property_user_read_tests()\n\t\t\t|| init_property_write_tests()\n\t\t\t){\n\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tCU_basic_set_mode(CU_BRM_NORMAL);\n\tCU_basic_run_tests();\n\tfails = CU_get_number_of_failures();\n\tCU_cleanup_registry();\n\n\treturn (int)fails;\n}\n\n"
  },
  {
    "path": "test/unit/libcommon/CMakeLists.txt",
    "content": "add_executable(libcommon-test\n\tbase64_test.c\n\tfile_test.c\n\tproperty_add.c\n\tproperty_value.c\n\tstrings_test.c\n\ttest.c\n\ttopic_test.c\n\ttrim_test.c\n\tutf8.c\n)\n\ntarget_include_directories(libcommon-test\n\tPRIVATE\n\t\t${mosquitto_SOURCE_DIR}/libcommon\n)\ntarget_link_libraries(libcommon-test\n\tPRIVATE\n\t\tcommon-unit-test-header\n\t\tOpenSSL::SSL\n\t\tlibmosquitto_common\n)\nadd_test(NAME unit-libcommon-test COMMAND libcommon-test)\n"
  },
  {
    "path": "test/unit/libcommon/Makefile",
    "content": "R=../../..\ninclude ${R}/config.mk\n\n.PHONY: all check test test-compile clean coverage\n\nLOCAL_CFLAGS+=-coverage\nLOCAL_CPPFLAGS+=-I${R}/libcommon -DTEST_SOURCE_DIR='\"$(realpath .)\"'\nLOCAL_LDFLAGS+=-coverage\nLOCAL_LDADD+=-lcunit ${LIBMOSQ_COMMON}\n\nifeq ($(WITH_TLS),yes)\n\tLOCAL_LDADD+=-lssl -lcrypto\nendif\n\nTEST_OBJS = \\\n\tbase64_test.o \\\n\tfile_test.o \\\n\tproperty_add.o \\\n\tproperty_value.o \\\n\tstrings_test.o \\\n\ttest.o \\\n\ttopic_test.o \\\n\ttrim_test.o \\\n\tutf8.o\n\nLIB_OBJS =\n\n\nall : test-compile\n\ncheck : test\n\nlibcommon_test : ${TEST_OBJS} ${LIB_OBJS}\n\t$(CROSS_COMPILE)$(CC) $(LOCAL_LDFLAGS) -o $@ $^ $(LOCAL_LDADD)\n\n${TEST_OBJS} : %.o: %.c\n\t${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(LOCAL_CFLAGS) -c $< -o $@\n\nlib_stubs.o : stubs.c\n\t${CROSS_COMPILE}$(CC) $(LIB_LOCAL_CPPFLAGS) $(LIB_LOCAL_CFLAGS) $(CFLAGS) $(CPPFLAGS) -c $< -o $@\n\nbuild : libcommon_test\n\ntest : build\n\t./libcommon_test\n\ntest-compile: build\n\nclean :\n\t-rm -rf libcommon_test\n\t-rm -rf *.o *.gcda *.gcno coverage.info\n"
  },
  {
    "path": "test/unit/libcommon/base64_test.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"property_common.h\"\n\n\n//int mosquitto_base64_encode(const unsigned char *in, size_t in_len, char **encoded);\n\n\n//int mosquitto_base64_decode(const char *in, unsigned char **decoded, unsigned int *decoded_len);\n\n\n#ifdef WITH_TLS\n\n\nstatic void check_encode(const char *input, size_t in_len, const char *expected_output)\n{\n\tchar *encoded;\n\tint rc = mosquitto_base64_encode((const unsigned char *)input, in_len, &encoded);\n\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_STRING_EQUAL(encoded, expected_output);\n\tif(strcmp(encoded, expected_output)){\n\t\tprintf(\"%s || %s\\n\", encoded, expected_output);\n\t}\n\tmosquitto_free(encoded);\n}\n\n\nstatic void check_decode(const char *input, int expected_rc, const char *expected_output, unsigned int expected_len)\n{\n\tunsigned char *decoded;\n\tunsigned int len;\n\tint rc = mosquitto_base64_decode(input, &decoded, &len);\n\n\tCU_ASSERT_EQUAL(rc, expected_rc);\n\tif(rc != expected_rc){\n\t\tprintf(\"rc: %d||%d\\n\", rc, expected_rc);\n\t}\n\tif(len != expected_len){\n\t\tprintf(\"len: %d||%d\\n\", len, expected_len);\n\t}\n\tCU_ASSERT_EQUAL(len, expected_len);\n\tif(decoded){\n\t\tCU_ASSERT_EQUAL(memcmp(decoded, expected_output, len), 0);\n\t\tmosquitto_free(decoded);\n\t}\n}\n\n\nstatic void TEST_encode_empty(void)\n{\n\tcheck_encode(\"\", 0, \"\");\n}\n\n\nstatic void TEST_encode_string_lengths(void)\n{\n\tcheck_encode(\"a\", 1, \"YQ==\");\n\tcheck_encode(\"ab\", 2, \"YWI=\");\n\tcheck_encode(\"abc\", 3, \"YWJj\");\n\tcheck_encode(\"abcd\", 4, \"YWJjZA==\");\n\tcheck_encode(\"abcde\", 5, \"YWJjZGU=\");\n\tcheck_encode(\"abcdef\", 6, \"YWJjZGVm\");\n\tcheck_encode(\"abcdefg\", 7, \"YWJjZGVmZw==\");\n}\n\n\nstatic void TEST_encode_binary(void)\n{\n\tconst char a[1] = {0};\n\tconst char b[2] = {0, 1};\n\tconst char c[3] = {0, 1, 2};\n\tconst char d[4] = {0, 1, 2, 3};\n\tconst char e[5] = {0, 1, 2, 3, 4};\n\tconst char f[6] = {0, 1, 2, 3, 4, 5};\n\tconst char g[7] = {0, 1, 2, 3, 4, 5, 6};\n\n\tcheck_encode(a, 1, \"AA==\");\n\tcheck_encode(b, 2, \"AAE=\");\n\tcheck_encode(c, 3, \"AAEC\");\n\tcheck_encode(d, 4, \"AAECAw==\");\n\tcheck_encode(e, 5, \"AAECAwQ=\");\n\tcheck_encode(f, 6, \"AAECAwQF\");\n\tcheck_encode(g, 7, \"AAECAwQFBg==\");\n}\n\n\nstatic void TEST_decode_empty(void)\n{\n\tcheck_decode(\"\", 1, \"\", 0);\n}\n\n\nstatic void TEST_decode_invalid(void)\n{\n\tcheck_decode(\"abc\", 1, \"\", 0);\n}\n\n\nstatic void TEST_decode_string_lengths(void)\n{\n\tcheck_decode(\"YQ==\", MOSQ_ERR_SUCCESS, \"a\", 1);\n\tcheck_decode(\"YWI=\", MOSQ_ERR_SUCCESS, \"ab\", 2);\n\tcheck_decode(\"YWJj\", MOSQ_ERR_SUCCESS, \"abc\", 3);\n\tcheck_decode(\"YWJjZA==\", MOSQ_ERR_SUCCESS, \"abcd\", 4);\n\tcheck_decode(\"YWJjZGU=\", MOSQ_ERR_SUCCESS, \"abcde\", 5);\n\tcheck_decode(\"YWJjZGVm\", MOSQ_ERR_SUCCESS, \"abcdef\", 6);\n\tcheck_decode(\"YWJjZGVmZw==\", MOSQ_ERR_SUCCESS, \"abcdefg\", 7);\n}\n\n\nstatic void TEST_decode_binary(void)\n{\n\tconst char a[1] = {0};\n\tconst char b[2] = {0, 1};\n\tconst char c[3] = {0, 1, 2};\n\tconst char d[4] = {0, 1, 2, 3};\n\tconst char e[5] = {0, 1, 2, 3, 4};\n\tconst char f[6] = {0, 1, 2, 3, 4, 5};\n\tconst char g[7] = {0, 1, 2, 3, 4, 5, 6};\n\n\tcheck_decode(\"AA==\", MOSQ_ERR_SUCCESS, a, 1);\n\tcheck_decode(\"AAE=\", MOSQ_ERR_SUCCESS, b, 2);\n\tcheck_decode(\"AAEC\", MOSQ_ERR_SUCCESS, c, 3);\n\tcheck_decode(\"AAECAw==\", MOSQ_ERR_SUCCESS, d, 4);\n\tcheck_decode(\"AAECAwQ=\", MOSQ_ERR_SUCCESS, e, 5);\n\tcheck_decode(\"AAECAwQF\", MOSQ_ERR_SUCCESS, f, 6);\n\tcheck_decode(\"AAECAwQFBg==\", MOSQ_ERR_SUCCESS, g, 7);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_base64_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"base64\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit base64 test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Encode Empty\", TEST_encode_empty)\n\t\t\t|| !CU_add_test(test_suite, \"Encode String lengths\", TEST_encode_string_lengths)\n\t\t\t|| !CU_add_test(test_suite, \"Encode Binary\", TEST_encode_binary)\n\t\t\t|| !CU_add_test(test_suite, \"Decode Empty\", TEST_decode_empty)\n\t\t\t|| !CU_add_test(test_suite, \"Decode Invalid\", TEST_decode_invalid)\n\t\t\t|| !CU_add_test(test_suite, \"Decode String lengths\", TEST_decode_string_lengths)\n\t\t\t|| !CU_add_test(test_suite, \"Decode Binary\", TEST_decode_binary)\n\t\t\t){\n\n\t\tprintf(\"Error adding Property Add CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "test/unit/libcommon/file_test.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n#ifndef WIN32\n#  include <unistd.h>\n#endif\n#include <stdlib.h>\n\n#include \"mosquitto.h\"\n\n#define ALLOW_SYMLINKS \"MOSQUITTO_UNSAFE_ALLOW_SYMLINKS\"\n#define SYMLINK \"test_symlink\"\n#define DATAFILE \"test_data\"\n\n#ifndef WIN32\n\n\nstatic bool symlink_test_init(void)\n{\n\tunsetenv(ALLOW_SYMLINKS);\n\n\t/* Create a file to open */\n\tFILE *fptr = mosquitto_fopen(DATAFILE, \"wb\", false);\n\tCU_ASSERT_PTR_NOT_NULL(fptr);\n\tif(!fptr){\n\t\treturn false;\n\t}\n\tfclose(fptr);\n\n\t/* Add a symlink */\n\tint rc = symlink(DATAFILE, SYMLINK);\n\tCU_ASSERT_EQUAL(rc, 0);\n\treturn rc == 0?true:false;\n}\n\n\nstatic void symlink_test_cleanup(void)\n{\n\tunlink(SYMLINK);\n\tunlink(DATAFILE);\n\tunsetenv(ALLOW_SYMLINKS);\n}\n#endif\n\n\n#ifndef WIN32\n\n\nstatic void TEST_restrict_read_default(void)\n{\n\tFILE *fptr;\n\n\tif(!symlink_test_init()){\n\t\treturn;\n\t}\n\n\t/* No restrict read, so symlink ok */\n\tfptr = mosquitto_fopen(SYMLINK, \"rb\", false);\n\tCU_ASSERT_PTR_NOT_NULL(fptr);\n\tif(fptr){\n\t\tfclose(fptr);\n\t}\n\n\t/* Restricted read, so symlink not allowed */\n\tfptr = mosquitto_fopen(SYMLINK, \"rb\", true);\n\tCU_ASSERT_PTR_NULL(fptr);\n\tif(fptr){\n\t\tfclose(fptr);\n\t}\n\n\tsymlink_test_cleanup();\n}\n\n\nstatic void TEST_restrict_read_with_symlinks(void)\n{\n\tFILE *fptr;\n\n\tif(!symlink_test_init()){\n\t\treturn;\n\t}\n\n\tint rc = setenv(ALLOW_SYMLINKS, \"1\", true);\n\tCU_ASSERT_EQUAL(rc, 0);\n\n\t/* No restrict read, so symlink ok */\n\tfptr = mosquitto_fopen(SYMLINK, \"rb\", false);\n\tCU_ASSERT_PTR_NOT_NULL(fptr);\n\tif(fptr){\n\t\tfclose(fptr);\n\t}\n\n\t/* Restricted read but with override so symlink ok */\n\tfptr = mosquitto_fopen(SYMLINK, \"rb\", true);\n\tCU_ASSERT_PTR_NOT_NULL(fptr);\n\tif(fptr){\n\t\tfclose(fptr);\n\t}\n\n\tsymlink_test_cleanup();\n}\n#endif\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_file_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"file\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit file test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n#ifndef WIN32\n\t\t\t|| !CU_add_test(test_suite, \"Restrict read default\", TEST_restrict_read_default)\n\t\t\t|| !CU_add_test(test_suite, \"Restrict read with symlinks\", TEST_restrict_read_with_symlinks)\n#endif\n\t\t\t){\n\n\t\tprintf(\"Error adding file CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/libcommon/property_add.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"property_common.h\"\n\n\nstatic void check_count(mosquitto_property *proplist, int expected)\n{\n\tmosquitto_property *p;\n\tint count;\n\n\tif(proplist == NULL){\n\t\tCU_ASSERT_EQUAL(expected, 0);\n\t\treturn;\n\t}\n\n\tp = proplist;\n\tcount = 0;\n\twhile(p){\n\t\tcount++;\n\t\tp = p->next;\n\t}\n\tCU_ASSERT_EQUAL(count, expected);\n}\n\n\n/* ========================================================================\n * BAD IDENTIFIER\n * ======================================================================== */\n\n\nstatic void bad_add_byte_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_byte(&proplist, identifier, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_PTR_NULL(proplist);\n}\n\n\nstatic void bad_add_int16_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_int16(&proplist, identifier, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_PTR_NULL(proplist);\n}\n\n\nstatic void bad_add_int32_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_int32(&proplist, identifier, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_PTR_NULL(proplist);\n}\n\n\nstatic void bad_add_varint_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_varint(&proplist, identifier, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_PTR_NULL(proplist);\n}\n\n\nstatic void bad_add_binary_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_binary(&proplist, identifier, \"test\", 4);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_PTR_NULL(proplist);\n}\n\n\nstatic void bad_add_string_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_string(&proplist, identifier, \"test\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_PTR_NULL(proplist);\n}\n\n\nstatic void bad_add_string_pair_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_string_pair(&proplist, identifier, \"key\", \"value\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_PTR_NULL(proplist);\n}\n\n\nstatic void TEST_add_bad_byte(void)\n{\n\tbad_add_byte_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL);\n\tbad_add_byte_helper(MQTT_PROP_CONTENT_TYPE);\n\tbad_add_byte_helper(MQTT_PROP_RESPONSE_TOPIC);\n\tbad_add_byte_helper(MQTT_PROP_CORRELATION_DATA);\n\tbad_add_byte_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER);\n\tbad_add_byte_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n\tbad_add_byte_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER);\n\tbad_add_byte_helper(MQTT_PROP_SERVER_KEEP_ALIVE);\n\tbad_add_byte_helper(MQTT_PROP_AUTHENTICATION_METHOD);\n\tbad_add_byte_helper(MQTT_PROP_AUTHENTICATION_DATA);\n\tbad_add_byte_helper(MQTT_PROP_WILL_DELAY_INTERVAL);\n\tbad_add_byte_helper(MQTT_PROP_RESPONSE_INFORMATION);\n\tbad_add_byte_helper(MQTT_PROP_SERVER_REFERENCE);\n\tbad_add_byte_helper(MQTT_PROP_REASON_STRING);\n\tbad_add_byte_helper(MQTT_PROP_RECEIVE_MAXIMUM);\n\tbad_add_byte_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM);\n\tbad_add_byte_helper(MQTT_PROP_TOPIC_ALIAS);\n\tbad_add_byte_helper(MQTT_PROP_USER_PROPERTY);\n\tbad_add_byte_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE);\n}\n\n\nstatic void TEST_add_bad_int16(void)\n{\n\tbad_add_int16_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR);\n\tbad_add_int16_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL);\n\tbad_add_int16_helper(MQTT_PROP_CONTENT_TYPE);\n\tbad_add_int16_helper(MQTT_PROP_RESPONSE_TOPIC);\n\tbad_add_int16_helper(MQTT_PROP_CORRELATION_DATA);\n\tbad_add_int16_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER);\n\tbad_add_int16_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n\tbad_add_int16_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER);\n\tbad_add_int16_helper(MQTT_PROP_AUTHENTICATION_METHOD);\n\tbad_add_int16_helper(MQTT_PROP_AUTHENTICATION_DATA);\n\tbad_add_int16_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION);\n\tbad_add_int16_helper(MQTT_PROP_WILL_DELAY_INTERVAL);\n\tbad_add_int16_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION);\n\tbad_add_int16_helper(MQTT_PROP_RESPONSE_INFORMATION);\n\tbad_add_int16_helper(MQTT_PROP_SERVER_REFERENCE);\n\tbad_add_int16_helper(MQTT_PROP_REASON_STRING);\n\tbad_add_int16_helper(MQTT_PROP_MAXIMUM_QOS);\n\tbad_add_int16_helper(MQTT_PROP_RETAIN_AVAILABLE);\n\tbad_add_int16_helper(MQTT_PROP_USER_PROPERTY);\n\tbad_add_int16_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE);\n\tbad_add_int16_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE);\n\tbad_add_int16_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE);\n\tbad_add_int16_helper(MQTT_PROP_SHARED_SUB_AVAILABLE);\n}\n\n\nstatic void TEST_add_bad_int32(void)\n{\n\tbad_add_int32_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR);\n\tbad_add_int32_helper(MQTT_PROP_CONTENT_TYPE);\n\tbad_add_int32_helper(MQTT_PROP_RESPONSE_TOPIC);\n\tbad_add_int32_helper(MQTT_PROP_CORRELATION_DATA);\n\tbad_add_int32_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER);\n\tbad_add_int32_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER);\n\tbad_add_int32_helper(MQTT_PROP_SERVER_KEEP_ALIVE);\n\tbad_add_int32_helper(MQTT_PROP_AUTHENTICATION_METHOD);\n\tbad_add_int32_helper(MQTT_PROP_AUTHENTICATION_DATA);\n\tbad_add_int32_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION);\n\tbad_add_int32_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION);\n\tbad_add_int32_helper(MQTT_PROP_RESPONSE_INFORMATION);\n\tbad_add_int32_helper(MQTT_PROP_SERVER_REFERENCE);\n\tbad_add_int32_helper(MQTT_PROP_REASON_STRING);\n\tbad_add_int32_helper(MQTT_PROP_RECEIVE_MAXIMUM);\n\tbad_add_int32_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM);\n\tbad_add_int32_helper(MQTT_PROP_TOPIC_ALIAS);\n\tbad_add_int32_helper(MQTT_PROP_MAXIMUM_QOS);\n\tbad_add_int32_helper(MQTT_PROP_RETAIN_AVAILABLE);\n\tbad_add_int32_helper(MQTT_PROP_USER_PROPERTY);\n\tbad_add_int32_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE);\n\tbad_add_int32_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE);\n\tbad_add_int32_helper(MQTT_PROP_SHARED_SUB_AVAILABLE);\n}\n\n\nstatic void TEST_add_bad_varint(void)\n{\n\tbad_add_varint_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR);\n\tbad_add_varint_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL);\n\tbad_add_varint_helper(MQTT_PROP_CONTENT_TYPE);\n\tbad_add_varint_helper(MQTT_PROP_RESPONSE_TOPIC);\n\tbad_add_varint_helper(MQTT_PROP_CORRELATION_DATA);\n\tbad_add_varint_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n\tbad_add_varint_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER);\n\tbad_add_varint_helper(MQTT_PROP_SERVER_KEEP_ALIVE);\n\tbad_add_varint_helper(MQTT_PROP_AUTHENTICATION_METHOD);\n\tbad_add_varint_helper(MQTT_PROP_AUTHENTICATION_DATA);\n\tbad_add_varint_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION);\n\tbad_add_varint_helper(MQTT_PROP_WILL_DELAY_INTERVAL);\n\tbad_add_varint_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION);\n\tbad_add_varint_helper(MQTT_PROP_RESPONSE_INFORMATION);\n\tbad_add_varint_helper(MQTT_PROP_SERVER_REFERENCE);\n\tbad_add_varint_helper(MQTT_PROP_REASON_STRING);\n\tbad_add_varint_helper(MQTT_PROP_RECEIVE_MAXIMUM);\n\tbad_add_varint_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM);\n\tbad_add_varint_helper(MQTT_PROP_TOPIC_ALIAS);\n\tbad_add_varint_helper(MQTT_PROP_MAXIMUM_QOS);\n\tbad_add_varint_helper(MQTT_PROP_RETAIN_AVAILABLE);\n\tbad_add_varint_helper(MQTT_PROP_USER_PROPERTY);\n\tbad_add_varint_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE);\n\tbad_add_varint_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE);\n\tbad_add_varint_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE);\n\tbad_add_varint_helper(MQTT_PROP_SHARED_SUB_AVAILABLE);\n}\n\n\nstatic void TEST_add_bad_binary(void)\n{\n\tbad_add_binary_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR);\n\tbad_add_binary_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL);\n\tbad_add_binary_helper(MQTT_PROP_CONTENT_TYPE);\n\tbad_add_binary_helper(MQTT_PROP_RESPONSE_TOPIC);\n\tbad_add_binary_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER);\n\tbad_add_binary_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n\tbad_add_binary_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER);\n\tbad_add_binary_helper(MQTT_PROP_SERVER_KEEP_ALIVE);\n\tbad_add_binary_helper(MQTT_PROP_AUTHENTICATION_METHOD);\n\tbad_add_binary_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION);\n\tbad_add_binary_helper(MQTT_PROP_WILL_DELAY_INTERVAL);\n\tbad_add_binary_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION);\n\tbad_add_binary_helper(MQTT_PROP_RESPONSE_INFORMATION);\n\tbad_add_binary_helper(MQTT_PROP_SERVER_REFERENCE);\n\tbad_add_binary_helper(MQTT_PROP_REASON_STRING);\n\tbad_add_binary_helper(MQTT_PROP_RECEIVE_MAXIMUM);\n\tbad_add_binary_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM);\n\tbad_add_binary_helper(MQTT_PROP_TOPIC_ALIAS);\n\tbad_add_binary_helper(MQTT_PROP_MAXIMUM_QOS);\n\tbad_add_binary_helper(MQTT_PROP_RETAIN_AVAILABLE);\n\tbad_add_binary_helper(MQTT_PROP_USER_PROPERTY);\n\tbad_add_binary_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE);\n\tbad_add_binary_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE);\n\tbad_add_binary_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE);\n\tbad_add_binary_helper(MQTT_PROP_SHARED_SUB_AVAILABLE);\n}\n\n\nstatic void TEST_add_bad_string(void)\n{\n\tbad_add_string_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR);\n\tbad_add_string_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL);\n\tbad_add_string_helper(MQTT_PROP_CORRELATION_DATA);\n\tbad_add_string_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER);\n\tbad_add_string_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n\tbad_add_string_helper(MQTT_PROP_SERVER_KEEP_ALIVE);\n\tbad_add_string_helper(MQTT_PROP_AUTHENTICATION_DATA);\n\tbad_add_string_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION);\n\tbad_add_string_helper(MQTT_PROP_WILL_DELAY_INTERVAL);\n\tbad_add_string_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION);\n\tbad_add_string_helper(MQTT_PROP_RECEIVE_MAXIMUM);\n\tbad_add_string_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM);\n\tbad_add_string_helper(MQTT_PROP_TOPIC_ALIAS);\n\tbad_add_string_helper(MQTT_PROP_MAXIMUM_QOS);\n\tbad_add_string_helper(MQTT_PROP_RETAIN_AVAILABLE);\n\tbad_add_string_helper(MQTT_PROP_USER_PROPERTY);\n\tbad_add_string_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE);\n\tbad_add_string_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE);\n\tbad_add_string_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE);\n\tbad_add_string_helper(MQTT_PROP_SHARED_SUB_AVAILABLE);\n}\n\n\nstatic void TEST_add_bad_string_pair(void)\n{\n\tbad_add_string_pair_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR);\n\tbad_add_string_pair_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL);\n\tbad_add_string_pair_helper(MQTT_PROP_CONTENT_TYPE);\n\tbad_add_string_pair_helper(MQTT_PROP_RESPONSE_TOPIC);\n\tbad_add_string_pair_helper(MQTT_PROP_CORRELATION_DATA);\n\tbad_add_string_pair_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER);\n\tbad_add_string_pair_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n\tbad_add_string_pair_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER);\n\tbad_add_string_pair_helper(MQTT_PROP_SERVER_KEEP_ALIVE);\n\tbad_add_string_pair_helper(MQTT_PROP_AUTHENTICATION_METHOD);\n\tbad_add_string_pair_helper(MQTT_PROP_AUTHENTICATION_DATA);\n\tbad_add_string_pair_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION);\n\tbad_add_string_pair_helper(MQTT_PROP_WILL_DELAY_INTERVAL);\n\tbad_add_string_pair_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION);\n\tbad_add_string_pair_helper(MQTT_PROP_RESPONSE_INFORMATION);\n\tbad_add_string_pair_helper(MQTT_PROP_SERVER_REFERENCE);\n\tbad_add_string_pair_helper(MQTT_PROP_REASON_STRING);\n\tbad_add_string_pair_helper(MQTT_PROP_RECEIVE_MAXIMUM);\n\tbad_add_string_pair_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM);\n\tbad_add_string_pair_helper(MQTT_PROP_TOPIC_ALIAS);\n\tbad_add_string_pair_helper(MQTT_PROP_MAXIMUM_QOS);\n\tbad_add_string_pair_helper(MQTT_PROP_RETAIN_AVAILABLE);\n\tbad_add_string_pair_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE);\n\tbad_add_string_pair_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE);\n\tbad_add_string_pair_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE);\n\tbad_add_string_pair_helper(MQTT_PROP_SHARED_SUB_AVAILABLE);\n}\n\n\n/* ========================================================================\n * SINGLE ADD\n * ======================================================================== */\n\n\nstatic void single_add_byte_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_byte(&proplist, identifier, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\tif(proplist){\n\t\tCU_ASSERT_EQUAL(proplist->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(proplist->value.i8, 1);\n\t\tCU_ASSERT_PTR_NULL(proplist->next);\n\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n}\n\n\nstatic void single_add_int16_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_int16(&proplist, identifier, 11234);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\tif(proplist){\n\t\tCU_ASSERT_EQUAL(proplist->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(proplist->value.i16, 11234);\n\t\tCU_ASSERT_PTR_NULL(proplist->next);\n\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n}\n\n\nstatic void single_add_int32_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_int32(&proplist, identifier, 765432);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\tif(proplist){\n\t\tCU_ASSERT_EQUAL(proplist->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(proplist->value.i32, 765432);\n\t\tCU_ASSERT_PTR_NULL(proplist->next);\n\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n}\n\n\nstatic void single_add_varint_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_varint(&proplist, identifier, 139123999);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\tif(proplist){\n\t\tCU_ASSERT_EQUAL(proplist->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(proplist->value.varint, 139123999);\n\t\tCU_ASSERT_PTR_NULL(proplist->next);\n\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n}\n\n\nstatic void single_add_binary_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_binary(&proplist, identifier, \"test\", 4);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\tif(proplist){\n\t\tCU_ASSERT_EQUAL(proplist->identifier, identifier);\n\t\tCU_ASSERT_EQUAL(proplist->value.bin.len, 4);\n\t\tCU_ASSERT_NSTRING_EQUAL(proplist->value.bin.v, \"test\", 4);\n\t\tCU_ASSERT_PTR_NULL(proplist->next);\n\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n}\n\n\nstatic void single_add_string_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_string(&proplist, identifier, \"string\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\tif(proplist){\n\t\tCU_ASSERT_EQUAL(proplist->identifier, identifier);\n\t\tCU_ASSERT_STRING_EQUAL(proplist->value.s.v, \"string\");\n\t\tCU_ASSERT_EQUAL(proplist->value.s.len, strlen(\"string\"));\n\t\tCU_ASSERT_PTR_NULL(proplist->next);\n\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n}\n\n\nstatic void single_add_string_pair_helper(int identifier)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_string_pair(&proplist, identifier, \"key\", \"value\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\tif(proplist){\n\t\tCU_ASSERT_EQUAL(proplist->identifier, identifier);\n\t\tCU_ASSERT_STRING_EQUAL(proplist->name.v, \"key\");\n\t\tCU_ASSERT_EQUAL(proplist->name.len, strlen(\"key\"));\n\t\tCU_ASSERT_STRING_EQUAL(proplist->value.s.v, \"value\");\n\t\tCU_ASSERT_EQUAL(proplist->value.s.len, strlen(\"value\"));\n\t\tCU_ASSERT_PTR_NULL(proplist->next);\n\n\t\tmosquitto_property_free_all(&proplist);\n\t}\n}\n\n\nstatic void TEST_add_single_byte(void)\n{\n\tsingle_add_byte_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR);\n\tsingle_add_byte_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION);\n\tsingle_add_byte_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION);\n\tsingle_add_byte_helper(MQTT_PROP_MAXIMUM_QOS);\n\tsingle_add_byte_helper(MQTT_PROP_RETAIN_AVAILABLE);\n\tsingle_add_byte_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE);\n\tsingle_add_byte_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE);\n\tsingle_add_byte_helper(MQTT_PROP_SHARED_SUB_AVAILABLE);\n}\n\n\nstatic void TEST_add_single_int16(void)\n{\n\tsingle_add_int16_helper(MQTT_PROP_SERVER_KEEP_ALIVE);\n\tsingle_add_int16_helper(MQTT_PROP_RECEIVE_MAXIMUM);\n\tsingle_add_int16_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM);\n\tsingle_add_int16_helper(MQTT_PROP_TOPIC_ALIAS);\n}\n\n\nstatic void TEST_add_single_int32(void)\n{\n\tsingle_add_int32_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL);\n\tsingle_add_int32_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL);\n\tsingle_add_int32_helper(MQTT_PROP_WILL_DELAY_INTERVAL);\n\tsingle_add_int32_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE);\n}\n\n\nstatic void TEST_add_single_varint(void)\n{\n\tsingle_add_varint_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER);\n}\n\n\nstatic void TEST_add_single_binary(void)\n{\n\tsingle_add_binary_helper(MQTT_PROP_CORRELATION_DATA);\n\tsingle_add_binary_helper(MQTT_PROP_AUTHENTICATION_DATA);\n}\n\n\nstatic void TEST_add_single_string(void)\n{\n\tsingle_add_string_helper(MQTT_PROP_CONTENT_TYPE);\n\tsingle_add_string_helper(MQTT_PROP_RESPONSE_TOPIC);\n\tsingle_add_string_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER);\n\tsingle_add_string_helper(MQTT_PROP_AUTHENTICATION_METHOD);\n\tsingle_add_string_helper(MQTT_PROP_RESPONSE_INFORMATION);\n\tsingle_add_string_helper(MQTT_PROP_SERVER_REFERENCE);\n\tsingle_add_string_helper(MQTT_PROP_REASON_STRING);\n}\n\n\nstatic void TEST_add_single_string_pair(void)\n{\n\tsingle_add_string_pair_helper(MQTT_PROP_USER_PROPERTY);\n}\n\n\n/* ========================================================================\n * ADD ALL PROPERTIES FOR A COMMAND\n * ======================================================================== */\n\n\nstatic void TEST_add_all_connect(void)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_int32(&proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_string(&proplist, MQTT_PROP_AUTHENTICATION_METHOD, \"basic\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, \"password\", strlen(\"password\"));\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_int16(&proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, \"user-agent\", \"mosquitto/test\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_int32(&proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\n\tcheck_count(proplist, 9);\n\n\tmosquitto_property_free_all(&proplist);\n}\n\n\nstatic void TEST_add_all_connack(void)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\n\trc = mosquitto_property_add_int32(&proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_string(&proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, \"clientid\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_int16(&proplist, MQTT_PROP_SERVER_KEEP_ALIVE, 900);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_string(&proplist, MQTT_PROP_AUTHENTICATION_METHOD, \"basic\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, \"password\", strlen(\"password\"));\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_string(&proplist, MQTT_PROP_RESPONSE_INFORMATION, \"response\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_string(&proplist, MQTT_PROP_SERVER_REFERENCE, \"localhost\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_string(&proplist, MQTT_PROP_REASON_STRING, \"reason\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_int16(&proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_MAXIMUM_QOS, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_RETAIN_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, \"user-agent\", \"mosquitto/test\");\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_int32(&proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\n\tcheck_count(proplist, 17);\n\n\tmosquitto_property_free_all(&proplist);\n}\n\n\nstatic void TEST_check_length(void)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\tunsigned int len;\n\tunsigned int varbytes;\n\tunsigned int i;\n\n\tlen = mosquitto_property_get_remaining_length(proplist);\n\tCU_ASSERT_EQUAL(len, 1);\n\n\tfor(i=1; i<10000; i++){\n\t\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\t\tif(proplist){\n\t\t\tlen = mosquitto_property_get_remaining_length(proplist);\n\t\t\tif(i < 64){\n\t\t\t\tvarbytes = 1;\n\t\t\t}else if(i < 8192){\n\t\t\t\tvarbytes = 2;\n\t\t\t}else{\n\t\t\t\tvarbytes = 3;\n\t\t\t}\n\t\t\tCU_ASSERT_EQUAL(len, varbytes+2*i);\n\t\t}else{\n\t\t\tbreak;\n\t\t}\n\t}\n\tmosquitto_property_free_all(&proplist);\n}\n\n\nstatic void TEST_remove_single(void)\n{\n\tmosquitto_property *proplist = NULL, *property;\n\tint rc;\n\tunsigned int len;\n\n\tlen = mosquitto_property_get_remaining_length(proplist);\n\tCU_ASSERT_EQUAL(len, 1);\n\n\tfor(int i=1; i<10; i++){\n\t\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\t}\n\tcheck_count(proplist, 9);\n\n\t/* Remove end item */\n\tproperty = proplist;\n\twhile(property && property->next){\n\t\tproperty = property->next;\n\t}\n\n\trc = mosquitto_property_remove(&proplist, property);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tmosquitto_property_free_all(&property);\n\n\tcheck_count(proplist, 8);\n\n\t/* Remove middle item */\n\tproperty = proplist;\n\tfor(int i=0; i<4; i++){\n\t\tproperty = property->next;\n\t}\n\trc = mosquitto_property_remove(&proplist, property);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tmosquitto_property_free_all(&property);\n\n\tcheck_count(proplist, 7);\n\n\t/* Remove front item */\n\tproperty = proplist;\n\trc = mosquitto_property_remove(&proplist, property);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_EQUAL(proplist, property);\n\tmosquitto_property_free_all(&property);\n\n\tcheck_count(proplist, 6);\n\n\tmosquitto_property_free_all(&proplist);\n}\n\n\nstatic void TEST_remove_all(void)\n{\n\tmosquitto_property *proplist = NULL, *property;\n\tint rc;\n\n\tfor(int i=0; i<100; i++){\n\t\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\t}\n\n\tfor(int i=0; i<100; i++){\n\t\tproperty = proplist;\n\t\trc = mosquitto_property_remove(&proplist, property);\n\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t\tmosquitto_property_free_all(&property);\n\t\tcheck_count(proplist, 100-i-1);\n\t}\n\tCU_ASSERT_PTR_NULL(proplist);\n\tmosquitto_property_free_all(&proplist);\n}\n\n\nstatic void TEST_remove_non_existent(void)\n{\n\tmosquitto_property *proplist = NULL, *property = NULL;\n\tint rc;\n\tunsigned int len;\n\n\tlen = mosquitto_property_get_remaining_length(proplist);\n\tCU_ASSERT_EQUAL(len, 1);\n\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\tif(proplist){\n\t\trc = mosquitto_property_add_byte(&property, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t\tCU_ASSERT_PTR_NOT_NULL(property);\n\t\tif(property){\n\t\t\trc = mosquitto_property_remove(&proplist, property);\n\t\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_NOT_FOUND);\n\t\t}\n\t}\n\tmosquitto_property_free_all(&proplist);\n\tmosquitto_property_free_all(&property);\n}\n\n\nstatic void TEST_remove_invalid(void)\n{\n\tmosquitto_property *proplist = NULL;\n\tint rc;\n\tunsigned int len;\n\n\trc = mosquitto_property_remove(NULL, NULL);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\n\tlen = mosquitto_property_get_remaining_length(proplist);\n\tCU_ASSERT_EQUAL(len, 1);\n\n\trc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(proplist);\n\tif(proplist){\n\t\trc = mosquitto_property_remove(&proplist, NULL);\n\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\t\trc = mosquitto_property_remove(NULL, proplist);\n\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\t}\n\tmosquitto_property_free_all(&proplist);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_property_add_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Property add\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit Property add test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Add nothing, check length\", TEST_check_length)\n\t\t\t|| !CU_add_test(test_suite, \"Add bad byte\", TEST_add_bad_byte)\n\t\t\t|| !CU_add_test(test_suite, \"Add bad int16\", TEST_add_bad_int16)\n\t\t\t|| !CU_add_test(test_suite, \"Add bad int32\", TEST_add_bad_int32)\n\t\t\t|| !CU_add_test(test_suite, \"Add bad varint\", TEST_add_bad_varint)\n\t\t\t|| !CU_add_test(test_suite, \"Add bad binary\", TEST_add_bad_binary)\n\t\t\t|| !CU_add_test(test_suite, \"Add bad string\", TEST_add_bad_string)\n\t\t\t|| !CU_add_test(test_suite, \"Add bad string pair\", TEST_add_bad_string_pair)\n\t\t\t|| !CU_add_test(test_suite, \"Add single byte\", TEST_add_single_byte)\n\t\t\t|| !CU_add_test(test_suite, \"Add single int16\", TEST_add_single_int16)\n\t\t\t|| !CU_add_test(test_suite, \"Add single int32\", TEST_add_single_int32)\n\t\t\t|| !CU_add_test(test_suite, \"Add single varint\", TEST_add_single_varint)\n\t\t\t|| !CU_add_test(test_suite, \"Add single binary\", TEST_add_single_binary)\n\t\t\t|| !CU_add_test(test_suite, \"Add single string\", TEST_add_single_string)\n\t\t\t|| !CU_add_test(test_suite, \"Add single string pair\", TEST_add_single_string_pair)\n\t\t\t|| !CU_add_test(test_suite, \"Add all CONNECT\", TEST_add_all_connect)\n\t\t\t|| !CU_add_test(test_suite, \"Add all CONNACK\", TEST_add_all_connack)\n\t\t\t|| !CU_add_test(test_suite, \"Remove single\", TEST_remove_single)\n\t\t\t|| !CU_add_test(test_suite, \"Remove all\", TEST_remove_all)\n\t\t\t|| !CU_add_test(test_suite, \"Remove non-existent\", TEST_remove_non_existent)\n\t\t\t|| !CU_add_test(test_suite, \"Remove invalid\", TEST_remove_invalid)\n\t\t\t){\n\n\t\tprintf(\"Error adding Property Add CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/libcommon/property_value.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"mosquitto/mqtt_protocol.h\"\n#include \"property_common.h\"\n\n\nstatic void TEST_value_byte_success(void)\n{\n\tmosquitto_property *property = NULL;\n\tuint8_t value, value_set = 1;\n\tint rc;\n\n\trc = mosquitto_property_add_byte(&property, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, value_set);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tvalue = mosquitto_property_byte_value(property);\n\t\tCU_ASSERT_EQUAL(value, value_set);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_byte_fail(void)\n{\n\tmosquitto_property *property = NULL;\n\tuint8_t value, value_set = 1;\n\tint rc;\n\n\trc = mosquitto_property_add_int16(&property, MQTT_PROP_RECEIVE_MAXIMUM, value_set);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tvalue = mosquitto_property_byte_value(property);\n\t\tCU_ASSERT_EQUAL(value, 0);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_int16_success(void)\n{\n\tmosquitto_property *property = NULL;\n\tuint16_t value, value_set = 65535;\n\tint rc;\n\n\trc = mosquitto_property_add_int16(&property, MQTT_PROP_RECEIVE_MAXIMUM, value_set);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tvalue = mosquitto_property_int16_value(property);\n\t\tCU_ASSERT_EQUAL(value, value_set);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_int16_fail(void)\n{\n\tmosquitto_property *property = NULL;\n\tuint16_t value, value_set = 65535;\n\tint rc;\n\n\trc = mosquitto_property_add_int32(&property, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, value_set);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tvalue = mosquitto_property_int16_value(property);\n\t\tCU_ASSERT_EQUAL(value, 0);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_int32_success(void)\n{\n\tmosquitto_property *property = NULL;\n\tuint32_t value, value_set = 123456;\n\tint rc;\n\n\trc = mosquitto_property_add_int32(&property, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, value_set);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tvalue = mosquitto_property_int32_value(property);\n\t\tCU_ASSERT_EQUAL(value, value_set);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_int32_fail(void)\n{\n\tmosquitto_property *property = NULL;\n\tuint32_t value, value_set = 123456;\n\tint rc;\n\n\trc = mosquitto_property_add_varint(&property, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, value_set);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tvalue = mosquitto_property_int32_value(property);\n\t\tCU_ASSERT_EQUAL(value, 0);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_varint_success(void)\n{\n\tmosquitto_property *property = NULL;\n\tuint32_t value, value_set = 654321;\n\tint rc;\n\n\trc = mosquitto_property_add_varint(&property, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, value_set);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tvalue = mosquitto_property_varint_value(property);\n\t\tCU_ASSERT_EQUAL(value, value_set);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_varint_fail(void)\n{\n\tmosquitto_property *property = NULL;\n\tuint32_t value;\n\tint rc;\n\n\trc = mosquitto_property_add_int16(&property, MQTT_PROP_RECEIVE_MAXIMUM, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tvalue = mosquitto_property_varint_value(property);\n\t\tCU_ASSERT_EQUAL(value, 0);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_binary_success(void)\n{\n\tmosquitto_property *property = NULL;\n\tuint8_t value_set[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};\n\tconst uint8_t *value;\n\tuint16_t len;\n\tint rc;\n\n\trc = mosquitto_property_add_binary(&property, MQTT_PROP_AUTHENTICATION_DATA, value_set, sizeof(value_set));\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tlen = mosquitto_property_binary_value_length(property);\n\t\tvalue = mosquitto_property_binary_value(property);\n\t\tCU_ASSERT_EQUAL(len, sizeof(value_set));\n\t\tCU_ASSERT_NSTRING_EQUAL(value, value_set, sizeof(value_set));\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_binary_fail(void)\n{\n\tmosquitto_property *property = NULL;\n\tconst uint8_t *value;\n\tuint16_t len;\n\tint rc;\n\n\trc = mosquitto_property_add_int16(&property, MQTT_PROP_RECEIVE_MAXIMUM, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tlen = mosquitto_property_binary_value_length(property);\n\t\tvalue = mosquitto_property_binary_value(property);\n\t\tCU_ASSERT_EQUAL(len, 0);\n\t\tCU_ASSERT_PTR_NULL(value);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_string_success(void)\n{\n\tmosquitto_property *property = NULL;\n\tchar value_set[] = \"test\";\n\tconst char *value;\n\tuint16_t len;\n\tint rc;\n\n\trc = mosquitto_property_add_string(&property, MQTT_PROP_AUTHENTICATION_METHOD, value_set);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tlen = mosquitto_property_string_value_length(property);\n\t\tvalue = mosquitto_property_string_value(property);\n\t\tCU_ASSERT_EQUAL(len, strlen(value_set));\n\t\tCU_ASSERT_NSTRING_EQUAL(value, value_set, strlen(value_set));\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_string_fail(void)\n{\n\tmosquitto_property *property = NULL;\n\tconst char *value;\n\tuint16_t len;\n\tint rc;\n\n\trc = mosquitto_property_add_int16(&property, MQTT_PROP_RECEIVE_MAXIMUM, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tlen = mosquitto_property_string_value_length(property);\n\t\tvalue = mosquitto_property_string_value(property);\n\t\tCU_ASSERT_EQUAL(len, 0);\n\t\tCU_ASSERT_PTR_NULL(value);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_string_pair_success(void)\n{\n\tmosquitto_property *property = NULL;\n\tchar value_set[] = \"value\";\n\tconst char *value;\n\tuint16_t len;\n\tchar name_set[] = \"name\";\n\tconst char *name;\n\tint rc;\n\n\trc = mosquitto_property_add_string_pair(&property, MQTT_PROP_USER_PROPERTY, name_set, value_set);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tlen = mosquitto_property_string_value_length(property);\n\t\tvalue = mosquitto_property_string_value(property);\n\t\tCU_ASSERT_EQUAL(len, strlen(value_set));\n\t\tCU_ASSERT_NSTRING_EQUAL(value, value_set, strlen(value_set));\n\n\t\tlen = mosquitto_property_string_name_length(property);\n\t\tname = mosquitto_property_string_name(property);\n\t\tCU_ASSERT_EQUAL(len, strlen(name_set));\n\t\tCU_ASSERT_NSTRING_EQUAL(name, name_set, strlen(name_set));\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\nstatic void TEST_value_string_pair_fail(void)\n{\n\tmosquitto_property *property = NULL;\n\tconst char *value;\n\tuint16_t len;\n\tconst char *name;\n\tint rc;\n\n\trc = mosquitto_property_add_int16(&property, MQTT_PROP_RECEIVE_MAXIMUM, 1);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_PTR_NOT_NULL(property);\n\tif(property){\n\t\tlen = mosquitto_property_string_value_length(property);\n\t\tvalue = mosquitto_property_string_value(property);\n\t\tCU_ASSERT_EQUAL(len, 0);\n\t\tCU_ASSERT_PTR_NULL(value);\n\n\t\tlen = mosquitto_property_string_name_length(property);\n\t\tname = mosquitto_property_string_name(property);\n\t\tCU_ASSERT_EQUAL(len, 0);\n\t\tCU_ASSERT_PTR_NULL(name);\n\n\t\tmosquitto_property_free_all(&property);\n\t}\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_property_value_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Property value\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit Property value test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Byte value success\", TEST_value_byte_success)\n\t\t\t|| !CU_add_test(test_suite, \"Int16 value success\", TEST_value_int16_success)\n\t\t\t|| !CU_add_test(test_suite, \"Int32 value success\", TEST_value_int32_success)\n\t\t\t|| !CU_add_test(test_suite, \"Varint value success\", TEST_value_varint_success)\n\t\t\t|| !CU_add_test(test_suite, \"Binary value success\", TEST_value_binary_success)\n\t\t\t|| !CU_add_test(test_suite, \"String value success\", TEST_value_string_success)\n\t\t\t|| !CU_add_test(test_suite, \"String pair value success\", TEST_value_string_pair_success)\n\t\t\t|| !CU_add_test(test_suite, \"Byte value fail\", TEST_value_byte_fail)\n\t\t\t|| !CU_add_test(test_suite, \"Int16 value fail\", TEST_value_int16_fail)\n\t\t\t|| !CU_add_test(test_suite, \"Int32 value fail\", TEST_value_int32_fail)\n\t\t\t|| !CU_add_test(test_suite, \"Varint value fail\", TEST_value_varint_fail)\n\t\t\t|| !CU_add_test(test_suite, \"Binary value fail\", TEST_value_binary_fail)\n\t\t\t|| !CU_add_test(test_suite, \"String value fail\", TEST_value_string_fail)\n\t\t\t|| !CU_add_test(test_suite, \"String pair value fail\", TEST_value_string_pair_fail)\n\t\t\t){\n\n\t\tprintf(\"Error adding Property Value CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/libcommon/strings_test.c",
    "content": "/* Tests for int to string functions. */\n\n#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"mosquitto.h\"\n\nstruct prop_id {\n\tconst char *name;\n\tint proptype;\n};\n\n\nstatic void TEST_string_to_property_info(void)\n{\n\tconst struct prop_id checks[50] = {\n\t\t{ NULL, 0 },\n\t\t{ \"payload-format-indicator\", MQTT_PROP_TYPE_BYTE },\n\t\t{ \"message-expiry-interval\", MQTT_PROP_TYPE_INT32 },\n\t\t{ \"content-type\", MQTT_PROP_TYPE_STRING },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ \"response-topic\", MQTT_PROP_TYPE_STRING },\n\t\t{ \"correlation-data\", MQTT_PROP_TYPE_BINARY },\n\t\t{ NULL, 0 },\n\t\t{ \"subscription-identifier\", MQTT_PROP_TYPE_VARINT },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ \"session-expiry-interval\", MQTT_PROP_TYPE_INT32 },\n\t\t{ \"assigned-client-identifier\", MQTT_PROP_TYPE_STRING },\n\t\t{ \"server-keep-alive\", MQTT_PROP_TYPE_INT16 },\n\t\t{ NULL, 0 },\n\t\t{ \"authentication-method\", MQTT_PROP_TYPE_STRING },\n\t\t{ \"authentication-data\", MQTT_PROP_TYPE_BINARY },\n\t\t{ \"request-problem-information\", MQTT_PROP_TYPE_BYTE },\n\t\t{ \"will-delay-interval\", MQTT_PROP_TYPE_INT32 },\n\t\t{ \"request-response-information\", MQTT_PROP_TYPE_BYTE },\n\t\t{ \"response-information\", MQTT_PROP_TYPE_STRING },\n\t\t{ NULL, 0 },\n\t\t{ \"server-reference\", MQTT_PROP_TYPE_STRING },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ \"reason-string\", MQTT_PROP_TYPE_STRING },\n\t\t{ NULL, 0 },\n\t\t{ \"receive-maximum\", MQTT_PROP_TYPE_INT16 },\n\t\t{ \"topic-alias-maximum\", MQTT_PROP_TYPE_INT16 },\n\t\t{ \"topic-alias\", MQTT_PROP_TYPE_INT16 },\n\t\t{ \"maximum-qos\", MQTT_PROP_TYPE_BYTE },\n\t\t{ \"retain-available\", MQTT_PROP_TYPE_BYTE },\n\t\t{ \"user-property\", MQTT_PROP_TYPE_STRING_PAIR },\n\t\t{ \"maximum-packet-size\", MQTT_PROP_TYPE_INT32 },\n\t\t{ \"wildcard-subscription-available\", MQTT_PROP_TYPE_BYTE },\n\t\t{ \"subscription-identifier-available\", MQTT_PROP_TYPE_BYTE },\n\t\t{ \"shared-subscription-available\", MQTT_PROP_TYPE_BYTE },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t\t{ NULL, 0 },\n\t};\n\n\tfor(int i=0; i<50; i++){\n\t\tint rc, identifier, proptype;\n\t\trc = mosquitto_string_to_property_info(checks[i].name, &identifier, &proptype);\n\t\tif(checks[i].name == NULL){\n\t\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\t\t}else{\n\t\t\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\t\t\tCU_ASSERT_EQUAL(identifier, i);\n\t\t\tCU_ASSERT_EQUAL(proptype, checks[i].proptype);\n\t\t}\n\t}\n}\n\n\nstatic void TEST_mosquitto_strerror(void)\n{\n\tconst char *str;\n\tint used[] = {\n\t\t-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,\n\t\t12, /* 13, */ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 31, 32,\n\t\t33,\n\t\t128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,\n\t\t143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,\n\t\t158, 159, 160, 161, 162\n\t};\n\n\t/* Iterate over all possible errors, checking we have a place holder for all\n\t * unused errors, and that all used errors do not have place holder text. */\n\tfor(int err=-256; err<256; err++){\n\t\tstr = mosquitto_strerror(err);\n\t\tCU_ASSERT_PTR_NOT_NULL(str);\n\t\tif(str){\n\t\t\tbool is_used = false;\n\t\t\tfor(size_t i=0; i<sizeof(used)/sizeof(int); i++){\n\t\t\t\tif(err == used[i]){\n\t\t\t\t\tis_used = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst char *errstr;\n\t\t\tif(err >= 128){\n\t\t\t\terrstr = \"Unknown reason\";\n\t\t\t}else{\n\t\t\t\terrstr = \"Unknown error\";\n\t\t\t}\n\t\t\tif(is_used){\n\t\t\t\tCU_ASSERT_STRING_NOT_EQUAL(str, errstr);\n\t\t\t\tif(!strcmp(str, errstr)){\n\t\t\t\t\tprintf(\"%d: %s (!=)\\n\", err, str);\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tCU_ASSERT_STRING_EQUAL(str, errstr);\n\t\t\t\tif(strcmp(str, errstr)){\n\t\t\t\t\tprintf(\"%d: %s (==)\\n\", err, str);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void TEST_mosquitto_connack_string(void)\n{\n\tconst char *str;\n\tuint8_t used[] = {0, 1, 2, 3, 4, 5};\n\n\t/* Iterate over all possible codes, checking we have a place holder for all\n\t * unused codes, and that all used codes do not have place holder text. */\n\tfor(int code=0; code<256; code++){\n\t\tstr = mosquitto_connack_string(code);\n\t\tCU_ASSERT_PTR_NOT_NULL(str);\n\t\tif(str){\n\t\t\tbool is_used = false;\n\t\t\tfor(size_t i=0; i<sizeof(used); i++){\n\t\t\t\tif(code == used[i]){\n\t\t\t\t\tis_used = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(is_used){\n\t\t\t\tCU_ASSERT_STRING_NOT_EQUAL(str, \"Connection Refused: unknown reason\");\n\t\t\t\tif(!strcmp(str, \"Connection Refused: unknown reason.\")){\n\t\t\t\t\tprintf(\"%d: %s\\n\", code, str);\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tCU_ASSERT_STRING_EQUAL(str, \"Connection Refused: unknown reason\");\n\t\t\t\tif(strcmp(str, \"Connection Refused: unknown reason\")){\n\t\t\t\t\tprintf(\"%d: %s\\n\", code, str);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void TEST_mosquitto_reason_string(void)\n{\n\tconst char *str;\n\tuint8_t used[] = {\n\t\t0, 1, 2, 4, 16, 17, 24, 25, 128, 129, 130, 131, 132, 133,\n\t\t134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,\n\t\t148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,\n\t\t162\n\t};\n\n\t/* Iterate over all possible codes, checking we have a place holder for all\n\t * unused codes, and that all used codes do not have place holder text. */\n\tfor(int code=0; code<256; code++){\n\t\tstr = mosquitto_reason_string(code);\n\t\tCU_ASSERT_PTR_NOT_NULL(str);\n\t\tif(str){\n\t\t\tbool is_used = false;\n\t\t\tfor(size_t i=0; i<sizeof(used); i++){\n\t\t\t\tif(code == used[i]){\n\t\t\t\t\tis_used = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(is_used){\n\t\t\t\tCU_ASSERT_STRING_NOT_EQUAL(str, \"Unknown reason\");\n\t\t\t\tif(!strcmp(str, \"Unknown reason\")){\n\t\t\t\t\tprintf(\"%d: %s\\n\", code, str);\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tCU_ASSERT_STRING_EQUAL(str, \"Unknown reason\");\n\t\t\t\tif(strcmp(str, \"Unknown reason\")){\n\t\t\t\t\tprintf(\"%d: %s\\n\", code, str);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void TEST_mosquitto_string_to_command(void)\n{\n\tint rc, cmd;\n\n\trc = mosquitto_string_to_command(\"CONNECT\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_CONNECT);\n\n\trc = mosquitto_string_to_command(\"CONNACK\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_CONNACK);\n\n\trc = mosquitto_string_to_command(\"PUBLISH\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_PUBLISH);\n\n\trc = mosquitto_string_to_command(\"PUBACK\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_PUBACK);\n\n\trc = mosquitto_string_to_command(\"PUBREC\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_PUBREC);\n\n\trc = mosquitto_string_to_command(\"PUBREL\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_PUBREL);\n\n\trc = mosquitto_string_to_command(\"PUBCOMP\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_PUBCOMP);\n\n\trc = mosquitto_string_to_command(\"SUBSCRIBE\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_SUBSCRIBE);\n\n\trc = mosquitto_string_to_command(\"SUBACK\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_SUBACK);\n\n\trc = mosquitto_string_to_command(\"UNSUBSCRIBE\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_UNSUBSCRIBE);\n\n\trc = mosquitto_string_to_command(\"UNSUBACK\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_UNSUBACK);\n\n\trc = mosquitto_string_to_command(\"DISCONNECT\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_DISCONNECT);\n\n\trc = mosquitto_string_to_command(\"AUTH\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_AUTH);\n\n\trc = mosquitto_string_to_command(\"WILL\", &cmd);\n\tCU_ASSERT_EQUAL(rc, 0);\n\tCU_ASSERT_EQUAL(cmd, CMD_WILL);\n\n\trc = mosquitto_string_to_command(\"CONNACT\", &cmd);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(cmd, 0);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_strings_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Strings\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"mosquitto_strerror\", TEST_mosquitto_strerror)\n\t\t\t|| !CU_add_test(test_suite, \"mosquitto_connack_string\", TEST_mosquitto_connack_string)\n\t\t\t|| !CU_add_test(test_suite, \"mosquitto_reason_string\", TEST_mosquitto_reason_string)\n\t\t\t|| !CU_add_test(test_suite, \"mosquitto_string_to_command\", TEST_mosquitto_string_to_command)\n\t\t\t|| !CU_add_test(test_suite, \"mosquitto_string_to_property_info\", TEST_string_to_property_info)\n\t\t\t){\n\n\t\tprintf(\"Error adding Strings CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/libcommon/test.c",
    "content": "#include \"config.h\"\n#include <stdio.h>\n\n#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\nint init_base64_tests(void);\nint init_file_tests(void);\nint init_property_add_tests(void);\nint init_property_value_tests(void);\nint init_strings_tests(void);\nint init_topic_tests(void);\nint init_trim_tests(void);\nint init_utf8_tests(void);\n\n\nint main(int argc, char *argv[])\n{\n\tunsigned int fails;\n\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tif(CU_initialize_registry() != CUE_SUCCESS){\n\t\tprintf(\"Error initializing CUnit registry.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n#ifdef WITH_TLS\n\t\t\t|| init_base64_tests()\n#endif\n\t\t\t|| init_file_tests()\n\t\t\t|| init_property_add_tests()\n\t\t\t|| init_property_value_tests()\n\t\t\t|| init_strings_tests()\n\t\t\t|| init_topic_tests()\n\t\t\t|| init_trim_tests()\n\t\t\t|| init_utf8_tests()\n\t\t\t){\n\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tCU_basic_set_mode(CU_BRM_NORMAL);\n\tCU_basic_run_tests();\n\tfails = CU_get_number_of_failures();\n\tCU_cleanup_registry();\n\n\treturn (int)fails;\n}\n\n"
  },
  {
    "path": "test/unit/libcommon/topic_test.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include <mosquitto.h>\n\nstruct topic_test {\n\tconst char *topic_filter;\n\tconst char *topic;\n\tconst char *clientid;\n\tconst char *username;\n\tint rc;\n\tbool match;\n};\n\n\nstatic void match_helper(const char *sub, const char *topic)\n{\n\tint rc;\n\tbool match;\n\n\trc = mosquitto_topic_matches_sub(sub, topic, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\tif(match == false){\n\t\tprintf(\"1: %s:%s\\n\", sub, topic);\n\t}\n\n\trc = mosquitto_topic_matches_sub2(sub, strlen(sub), topic, strlen(topic), &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\tif(match == false){\n\t\tprintf(\"2: %s:%s\\n\", sub, topic);\n\t}\n}\n\n\nstatic void no_match_helper(int rc_expected, const char *sub, const char *topic)\n{\n\tint rc;\n\tbool match;\n\n\trc = mosquitto_topic_matches_sub(sub, topic, &match);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tif(rc != rc_expected){\n\t\tprintf(\"%d:%d %s:%s\\n\", rc, rc_expected, sub, topic);\n\t}\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub2(sub, strlen(sub), topic, strlen(topic), &match);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tif(rc != rc_expected){\n\t\tprintf(\"%d:%d %s:%s\\n\", rc, rc_expected, sub, topic);\n\t}\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\n/* ========================================================================\n * EMPTY INPUT\n * ======================================================================== */\n\n\nstatic void TEST_empty_input(void)\n{\n\tint rc;\n\tbool match;\n\n\trc = mosquitto_topic_matches_sub(\"sub\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub(NULL, \"topic\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub(NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub(\"sub\", \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub(\"\", \"topic\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub(\"\", \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub2(\"sub\", 3, NULL, 0, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub2(NULL, 0, \"topic\", 5, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub2(NULL, 0, NULL, 0, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub2(\"sub\", 3, \"\", 0, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub2(\"\", 0, \"topic\", 5, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub2(\"\", 0, \"\", 0, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_topic_pattern_empty_input(void)\n{\n\tint rc;\n\tbool match;\n\n\trc = mosquitto_topic_matches_sub_with_pattern(NULL, NULL, NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"sub\", NULL, NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(NULL, \"topic\", NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(NULL, NULL, \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(NULL, NULL, NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"sub\", \"\", \"\", \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"\", \"topic\", \"\", \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"\", \"\", \"clientid\", \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"\", \"\", \"\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%c\", \"topic\", NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u\", \"topic\", NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%c\", \"\", \"\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u\", \"\", NULL, \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c/test\", \"test//test\", \"\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%u/test\", \"test//test\", NULL, \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_acl_pattern_empty_input(void)\n{\n\tint rc;\n\tbool match;\n\n\trc = mosquitto_sub_matches_acl_with_pattern(NULL, NULL, NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"acl\", NULL, NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(NULL, \"sub\", NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(NULL, NULL, \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(NULL, NULL, NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"acl\", \"\", \"\", \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"\", \"sub\", \"\", \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"\", \"\", \"clientid\", \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"\", \"\", \"\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c\", \"sub\", NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u\", \"sub\", NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c\", \"\", \"\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u\", \"\", NULL, \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c/test\", \"test//test\", \"\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%u/test\", \"test//test\", NULL, \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_sub_match_empty_input(void)\n{\n\tint rc;\n\tbool match;\n\n\trc = mosquitto_sub_matches_acl(\"sub\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl(NULL, \"topic\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl(NULL, NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl(\"sub\", \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl(\"\", \"topic\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl(\"\", \"\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\n/* ========================================================================\n * VALID MATCHING AND NON-MATCHING\n * ======================================================================== */\n\n\nstatic void TEST_valid_matching(void)\n{\n\tmatch_helper(\"foo/#\", \"foo/\");\n\tmatch_helper(\"foo/#\", \"foo\");\n\tmatch_helper(\"foo//bar\", \"foo//bar\");\n\tmatch_helper(\"foo//+\", \"foo//bar\");\n\tmatch_helper(\"foo/+/+/baz\", \"foo///baz\");\n\tmatch_helper(\"foo/bar/+\", \"foo/bar/\");\n\tmatch_helper(\"foo/bar\", \"foo/bar\");\n\tmatch_helper(\"foo/+\", \"foo/bar\");\n\tmatch_helper(\"foo/+/baz\", \"foo/bar/baz\");\n\tmatch_helper(\"A/B/+/#\", \"A/B/B/C\");\n\tmatch_helper(\"foo/+/#\", \"foo/bar/baz\");\n\tmatch_helper(\"foo/+/#\", \"foo/bar\");\n\tmatch_helper(\"#\", \"foo/bar/baz\");\n\tmatch_helper(\"#\", \"foo/bar/baz\");\n\tmatch_helper(\"#\", \"/foo/bar\");\n\tmatch_helper(\"/#\", \"/foo/bar\");\n}\n\n\nstatic void TEST_invalid_but_matching(void)\n{\n\t/* Matching here is \"naive treatment of the wildcards would produce a\n\t * match\". They shouldn't really match, they should fail. */\n\tno_match_helper(MOSQ_ERR_INVAL, \"+foo\", \"+foo\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"fo+o\", \"fo+o\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo+\", \"foo+\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"+foo/bar\", \"+foo/bar\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo+/bar\", \"foo+/bar\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/+bar\", \"foo/+bar\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/bar+\", \"foo/bar+\");\n\n\tno_match_helper(MOSQ_ERR_INVAL, \"+foo\", \"afoo\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"fo+o\", \"foao\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo+\", \"fooa\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"+foo/bar\", \"afoo/bar\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo+/bar\", \"fooa/bar\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/+bar\", \"foo/abar\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/bar+\", \"foo/bara\");\n\n\tno_match_helper(MOSQ_ERR_INVAL, \"#foo\", \"#foo\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"fo#o\", \"fo#o\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo#\", \"foo#\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"#foo/bar\", \"#foo/bar\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo#/bar\", \"foo#/bar\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/#bar\", \"foo/#bar\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/bar#\", \"foo/bar#\");\n\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo+\", \"fooa\");\n\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/+\", \"foo/+\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/#\", \"foo/+\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/+\", \"foo/bar/+\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/#\", \"foo/bar/+\");\n\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/+\", \"foo/#\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/#\", \"foo/#\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/+\", \"foo/bar/#\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/#\", \"foo/bar/#\");\n}\n\n\nstatic void TEST_valid_no_matching(void)\n{\n\tno_match_helper(MOSQ_ERR_SUCCESS, \"test/6/#\", \"test/3\");\n\n\tno_match_helper(MOSQ_ERR_SUCCESS, \"foo/bar\", \"foo\");\n\tno_match_helper(MOSQ_ERR_SUCCESS, \"foo/+\", \"foo/bar/baz\");\n\tno_match_helper(MOSQ_ERR_SUCCESS, \"foo/+/baz\", \"foo/bar/bar\");\n\n\tno_match_helper(MOSQ_ERR_SUCCESS, \"foo/+/#\", \"fo2/bar/baz\");\n\n\tno_match_helper(MOSQ_ERR_SUCCESS, \"/#\", \"foo/bar\");\n\n\tno_match_helper(MOSQ_ERR_SUCCESS, \"#\", \"$SYS/bar\");\n\tno_match_helper(MOSQ_ERR_SUCCESS, \"$BOB/bar\", \"$SYS/bar\");\n}\n\n\nstatic void TEST_invalid(void)\n{\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo#\", \"foo\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"fo#o/\", \"foo\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo#\", \"fooa\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo+\", \"foo\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/#a\", \"foo\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"#a\", \"foo\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"foo/#abc\", \"foo\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"#abc\", \"foo\");\n\tno_match_helper(MOSQ_ERR_INVAL, \"/#a\", \"foo/bar\");\n}\n\n\n/* ========================================================================\n * TOPIC MATCHES SUB PATTERNS\n * ======================================================================== */\n\n\nstatic void TEST_topic_pattern_clientid(void)\n{\n\tint rc;\n\tbool match;\n\n\t/* Sole pattern */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%c\", \"clientid\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%c\", \"clientid\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern at beginning */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%c/test\", \"clientid/test\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%c/test\", \"clientid/test\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern at end */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c\", \"test/clientid\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c\", \"test/clientid\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern in middle */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c/test\", \"test/clientid/test\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c/test\", \"test/clientid/test\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Repeated pattern */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c/%c/test\", \"test/clientid/clientid/test\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c/%c/test\", \"test/clientid/clientid/test\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Not a pattern */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%count\", \"test/clientid\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_topic_pattern_username(void)\n{\n\tint rc;\n\tbool match;\n\n\t/* Sole pattern */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u\", \"username\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u\", \"username\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern at beginning */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u/test\", \"username/test\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u/test\", \"username/test\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern at end */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%u\", \"test/username\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%u\", \"test/username\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern in middle */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%u/test\", \"test/username/test\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%u/test\", \"test/username/test\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Repeated pattern */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%u/%u/test\", \"test/username/username/test\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%u/%u/test\", \"test/username/username/test\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Not a pattern */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%username\", \"test/username\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_topic_pattern_both(void)\n{\n\tint rc;\n\tbool match;\n\n\t/* Sole pattern */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u/%c\", \"username/clientid\", \"clientid\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u/%c\", \"username/clientid\", \"clientid\", \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u/%c\", \"username/clientid\", \"nomatch\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u/%c\", \"username/clientid\", \"nomatch\", \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern in middle */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c/%u/test\", \"test/clientid/username/test\", \"clientid\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c/%u/test\", \"test/clientid/username/test\", \"clientid\", \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c/%u/test\", \"test/clientid/username/test\", \"nomatch\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%c/%u/test\", \"test/clientid/username/test\", \"nomatch\", \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Repeated pattern */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%u/%c/%c/%u/test\", \"test/username/clientid/clientid/username/test\", \"clientid\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\t/* Not a pattern */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/%username/%client\", \"test/username/clientid\", \"clientid\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Not a pattern */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"test/a%u/a%c\", \"test/ausername/aclientid\", \"clientid\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_topic_pattern_wildcard(void)\n{\n\tint rc;\n\tbool match;\n\n\t/* Malicious */\n\t/* ========= */\n\n\t/* / in client id */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%c\", \"clientid/test\", \"clientid/test\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* / in username */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%u\", \"username/test\", NULL, \"username/test\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* + in client id */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"%c\", \"clientid\", \"+\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* + in username */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"username/%u/+\", \"username/test/+\", NULL, \"+\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Valid */\n\t/* ========= */\n\n\t/* Ends in + */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"clientid/%c/+\", \"clientid/test/topic\", \"test\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"clientid/%c/+\", \"clientid/test/topic\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"username/%u/+\", \"username/test/topic\", NULL, \"test\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"username/%u/+\", \"username/test/topic\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Ends in # */\n\trc = mosquitto_topic_matches_sub_with_pattern(\"clientid/%c/#\", \"clientid/test/topic\", \"test\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"clientid/%c/#\", \"clientid/test/topic\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"username/%u/#\", \"username/test/topic\", NULL, \"test\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"username/%u/#\", \"username/test/topic\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"clientid/%c/#\", \"clientid/test\", \"test\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"clientid/%c/#\", \"clientid/test\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"pattern/%u/#\", \"pattern/username\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_topic_matches_sub_with_pattern(\"username/%u/#\", \"username/test\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_topic_pattern_substring_beginning(void)\n{\n\tconst struct topic_test tests[] = {\n\t\t/* topic part matching substring of client id */\n\t\t{\"%c/#\", \"g/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c/#\", \"gu/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c/#\", \"gue/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c/#\", \"gues/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c/#\", \"guest/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, true},\n\t\t{\"%c/#\", \"guestt/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"%c/#\", \"geest/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\n\t\t/* topic part matching substring of client id */\n\t\t{\"%u/#\", \"g/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u/#\", \"gu/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u/#\", \"gue/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u/#\", \"gues/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u/#\", \"guest/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, true},\n\t\t{\"%u/#\", \"guestt/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"%u/#\", \"geest/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t};\n\n\tfor(size_t i=0; i<sizeof(tests)/sizeof(struct topic_test); i++){\n\t\tbool match;\n\t\tint rc = mosquitto_topic_matches_sub_with_pattern(\n\t\t\t\ttests[i].topic_filter, tests[i].topic,\n\t\t\t\ttests[i].clientid, tests[i].username,\n\t\t\t\t&match);\n\t\tCU_ASSERT_EQUAL(rc, tests[i].rc);\n\t\tCU_ASSERT_EQUAL(match, tests[i].match);\n\t}\n}\n\n\nstatic void TEST_topic_pattern_substring_middle(void)\n{\n\tconst struct topic_test tests[] = {\n\t\t/* topic part matching substring of client id */\n\t\t{\"clients/%c/#\", \"clients/g/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c/#\", \"clients/gu/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c/#\", \"clients/gue/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c/#\", \"clients/gues/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c/#\", \"clients/guest/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, true},\n\t\t{\"clients/%c/#\", \"clients/guestt/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"clients/%c/#\", \"clients/geest/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\n\t\t/* topic part matching substring of client id */\n\t\t{\"clients/%u/#\", \"clients/g/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u/#\", \"clients/gu/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u/#\", \"clients/gue/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u/#\", \"clients/gues/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u/#\", \"clients/guest/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, true},\n\t\t{\"clients/%u/#\", \"clients/guestt/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"clients/%u/#\", \"clients/geest/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t};\n\n\tfor(size_t i=0; i<sizeof(tests)/sizeof(struct topic_test); i++){\n\t\tbool match;\n\t\tint rc = mosquitto_topic_matches_sub_with_pattern(\n\t\t\t\ttests[i].topic_filter, tests[i].topic,\n\t\t\t\ttests[i].clientid, tests[i].username,\n\t\t\t\t&match);\n\t\tCU_ASSERT_EQUAL(rc, tests[i].rc);\n\t\tCU_ASSERT_EQUAL(match, tests[i].match);\n\t}\n}\n\n\nstatic void TEST_topic_pattern_substring_end(void)\n{\n\tconst struct topic_test tests[] = {\n\t\t/* topic part matching substring of client id */\n\t\t{\"clients/%c\", \"clients/g\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c\", \"clients/gu\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c\", \"clients/gue\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c\", \"clients/gues\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c\", \"clients/guest\", \"guest\", NULL, MOSQ_ERR_SUCCESS, true},\n\t\t{\"clients/%c\", \"clients/guestt\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"clients/%c\", \"clients/geest\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\n\t\t/* topic part matching substring of client id */\n\t\t{\"clients/%u\", \"clients/g\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u\", \"clients/gu\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u\", \"clients/gue\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u\", \"clients/gues\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u\", \"clients/guest\", NULL, \"guest\", MOSQ_ERR_SUCCESS, true},\n\t\t{\"clients/%u\", \"clients/guestt\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"clients/%u\", \"clients/geest\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t};\n\n\tfor(size_t i=0; i<sizeof(tests)/sizeof(struct topic_test); i++){\n\t\tbool match;\n\t\tint rc = mosquitto_topic_matches_sub_with_pattern(\n\t\t\t\ttests[i].topic_filter, tests[i].topic,\n\t\t\t\ttests[i].clientid, tests[i].username,\n\t\t\t\t&match);\n\t\tCU_ASSERT_EQUAL(rc, tests[i].rc);\n\t\tCU_ASSERT_EQUAL(match, tests[i].match);\n\t}\n}\n\n\nstatic void TEST_topic_pattern_substring_only(void)\n{\n\tconst struct topic_test tests[] = {\n\t\t/* topic part matching substring of client id */\n\t\t{\"%c\", \"g\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c\", \"gu\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c\", \"gue\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c\", \"gues\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c\", \"guest\", \"guest\", NULL, MOSQ_ERR_SUCCESS, true},\n\t\t{\"%c\", \"guestt\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"%c\", \"geest\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\n\t\t/* topic part matching substring of client id */\n\t\t{\"%u\", \"g\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u\", \"gu\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u\", \"gue\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u\", \"gues\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u\", \"guest\", NULL, \"guest\", MOSQ_ERR_SUCCESS, true},\n\t\t{\"%u\", \"guestt\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"%u\", \"geest\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t};\n\n\tfor(size_t i=0; i<sizeof(tests)/sizeof(struct topic_test); i++){\n\t\tbool match;\n\t\tint rc = mosquitto_topic_matches_sub_with_pattern(\n\t\t\t\ttests[i].topic_filter, tests[i].topic,\n\t\t\t\ttests[i].clientid, tests[i].username,\n\t\t\t\t&match);\n\t\tCU_ASSERT_EQUAL(rc, tests[i].rc);\n\t\tCU_ASSERT_EQUAL(match, tests[i].match);\n\t}\n}\n\n\n/* ========================================================================\n * SUB MATCHES ACL PATTERNS\n * ======================================================================== */\n\n\nstatic void TEST_acl_pattern_clientid(void)\n{\n\tint rc;\n\tbool match;\n\n\t/* Sole pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c\", \"clientid\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c\", \"clientid\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern at beginning */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c/test\", \"clientid/test\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c/test\", \"clientid/test\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern at end */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c\", \"test/clientid\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c\", \"test/clientid\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern in middle */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c/test\", \"test/clientid/test\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c/test\", \"test/clientid/test\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Repeated pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c/%c/test\", \"test/clientid/clientid/test\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c/%c/test\", \"test/clientid/clientid/test\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Not a pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%count\", \"test/clientid\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Now repeated, with wildcards: */\n\n\t/* Pattern at beginning */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c/test/+\", \"clientid/test/+\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c/test/+\", \"clientid/test/+\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c/test/#\", \"clientid/test/+\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c/test/#\", \"clientid/test/+\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern at end */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%c\", \"+/test/clientid\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%c\", \"+/test/clientid\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern in middle */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c/+/test\", \"test/clientid/+/test\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c/+/test\", \"test/clientid/+/test\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Repeated pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c/%c/test/+\", \"test/clientid/clientid/test/test\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%c/%c/test/+\", \"test/clientid/clientid/test/test\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Not a pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%count\", \"+/test/clientid\", \"clientid\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_acl_pattern_username(void)\n{\n\tint rc;\n\tbool match;\n\n\t/* Sole pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u\", \"username\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u\", \"username\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern at beginning */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u/test\", \"username/test\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u/test\", \"username/test\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern at end */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%u\", \"test/username\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%u\", \"test/username\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern in middle */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%u/test\", \"test/username/test\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%u/test\", \"test/username/test\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Repeated pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%u/%u/test\", \"test/username/username/test\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%u/%u/test\", \"test/username/username/test\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Not a pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%username\", \"test/username\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Now repeat with wildcards: */\n\n\t/* Pattern at beginning */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u/test/+\", \"username/test/+\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u/test/+\", \"username/test/+\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u/#\", \"username/test/+\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u/#\", \"username/test/+\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern at end */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%u\", \"+/test/username\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%u\", \"+/test/username\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern in middle */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/%u/test\", \"test/username/test\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/%u/test\", \"test/username/test\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Repeated pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%u/%u/test\", \"+/test/username/username/test\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%u/%u/test\", \"+/test/username/username/test\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Not a pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%username/+\", \"+/test/username/+\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_acl_pattern_both(void)\n{\n\tint rc;\n\tbool match;\n\n\t/* Sole pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u/%c\", \"username/clientid\", \"clientid\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u/%c\", \"username/clientid\", \"clientid\", \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u/%c\", \"username/clientid\", \"nomatch\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u/%c\", \"username/clientid\", \"nomatch\", \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Pattern in middle */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%c/%u/#\", \"+/test/clientid/username/test\", \"clientid\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%c/%u/#\", \"+/test/clientid/username/test\", \"clientid\", \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%c/%u/test\", \"a/test/clientid/username/test\", \"nomatch\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/test/%c/%u/test\", \"a/test/clientid/username/test\", \"nomatch\", \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Repeated pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%u/%c/%c/%u/#\", \"test/username/clientid/clientid/username/#\", \"clientid\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\t/* Not a pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/%username/+/%client\", \"test/username/a/clientid\", \"clientid\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Not a pattern */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"test/a%u/+/a%c\", \"test/ausername/a/aclientid\", \"clientid\", \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_acl_pattern_wildcard(void)\n{\n\tint rc;\n\tbool match;\n\n\t/* Malicious */\n\t/* ========= */\n\n\t/* / in client id */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c\", \"clientid/test\", \"clientid/test\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c\", \"/\", \"/\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* / in username */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u\", \"username/test\", NULL, \"username/test\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u\", \"/\", NULL, \"/\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* + in client id */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c\", \"clientid\", \"+\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c\", \"+\", \"+\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* + in username */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"username/%u/+\", \"username/test/+\", NULL, \"+\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"username/%u/+\", \"username/+/+\", NULL, \"+\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"username/%u/+\", \"username/+\", NULL, \"+/a\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* # in client id */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%c\", \"#\", \"#\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* # in username */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"%u\", \"#\", NULL, \"#\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Valid */\n\t/* ========= */\n\n\t/* Ends in + */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"clientid/%c/+\", \"clientid/test/topic\", \"test\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"clientid/%c/+\", \"clientid/test/topic\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"username/%u/+\", \"username/test/topic\", NULL, \"test\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"username/%u/+\", \"username/test/topic\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\t/* Ends in # */\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/clientid/%c/#\", \"+/clientid/test/topic\", \"test\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/clientid/%c/#\", \"+/clientid/test/topic\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/username/%u/#\", \"+/username/test/topic\", NULL, \"test\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/username/%u/#\", \"+/username/test/topic\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/clientid/%c/#\", \"+/clientid/test\", \"test\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/clientid/%c/#\", \"+/clientid/test\", \"nomatch\", NULL, &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/pattern/%u/#\", \"+/pattern/username\", NULL, \"username\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl_with_pattern(\"+/username/%u/#\", \"+/username/test\", NULL, \"nomatch\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_acl_pattern_wildcard_wildcard(void)\n{\n\tint rc;\n\tbool match;\n\n\trc = mosquitto_sub_matches_acl(\"$SYS/#\", \"$SYS/broker/#\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl(\"$SYS/#\", \"$SYS/+/#\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl(\"$SYS/#\", \"$SYS\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, true);\n\n\trc = mosquitto_sub_matches_acl(\"$SYS/+\", \"$SYS/#\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl(\"$SYS/+/a\", \"$SYS/a/+\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl(\"$SYS/+\", \"$SYS/+/a\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl(\"$SYS/broker/uptime\", \"$SYS/broker/#\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n\n\trc = mosquitto_sub_matches_acl(\"#\", \"$SYS/broker/#\", &match);\n\tCU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);\n\tCU_ASSERT_EQUAL(match, false);\n}\n\n\nstatic void TEST_acl_pattern_substring_beginning(void)\n{\n\tconst struct topic_test tests[] = {\n\t\t/* topic part matching substring of client id */\n\t\t{\"%c/#\", \"g/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c/#\", \"gu/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c/#\", \"gue/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c/#\", \"gues/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c/#\", \"guest/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, true},\n\t\t{\"%c/#\", \"guestt/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"%c/#\", \"geest/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\n\t\t/* topic part matching substring of client id */\n\t\t{\"%u/#\", \"g/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u/#\", \"gu/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u/#\", \"gue/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u/#\", \"gues/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u/#\", \"guest/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, true},\n\t\t{\"%u/#\", \"guestt/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"%u/#\", \"geest/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t};\n\n\tfor(size_t i=0; i<sizeof(tests)/sizeof(struct topic_test); i++){\n\t\tbool match;\n\t\tint rc = mosquitto_sub_matches_acl_with_pattern(\n\t\t\t\ttests[i].topic_filter, tests[i].topic,\n\t\t\t\ttests[i].clientid, tests[i].username,\n\t\t\t\t&match);\n\t\tCU_ASSERT_EQUAL(rc, tests[i].rc);\n\t\tCU_ASSERT_EQUAL(match, tests[i].match);\n\t}\n}\n\n\nstatic void TEST_acl_pattern_substring_middle(void)\n{\n\tconst struct topic_test tests[] = {\n\t\t/* topic part matching substring of client id */\n\t\t{\"clients/%c/#\", \"clients/g/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c/#\", \"clients/gu/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c/#\", \"clients/gue/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c/#\", \"clients/gues/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c/#\", \"clients/guest/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, true},\n\t\t{\"clients/%c/#\", \"clients/guestt/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"clients/%c/#\", \"clients/geest/test\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\n\t\t/* topic part matching substring of client id */\n\t\t{\"clients/%u/#\", \"clients/g/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u/#\", \"clients/gu/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u/#\", \"clients/gue/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u/#\", \"clients/gues/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u/#\", \"clients/guest/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, true},\n\t\t{\"clients/%u/#\", \"clients/guestt/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"clients/%u/#\", \"clients/geest/test\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t};\n\n\tfor(size_t i=0; i<sizeof(tests)/sizeof(struct topic_test); i++){\n\t\tbool match;\n\t\tint rc = mosquitto_sub_matches_acl_with_pattern(\n\t\t\t\ttests[i].topic_filter, tests[i].topic,\n\t\t\t\ttests[i].clientid, tests[i].username,\n\t\t\t\t&match);\n\t\tCU_ASSERT_EQUAL(rc, tests[i].rc);\n\t\tCU_ASSERT_EQUAL(match, tests[i].match);\n\t}\n}\n\n\nstatic void TEST_acl_pattern_substring_end(void)\n{\n\tconst struct topic_test tests[] = {\n\t\t/* topic part matching substring of client id */\n\t\t{\"clients/%c\", \"clients/g\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c\", \"clients/gu\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c\", \"clients/gue\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c\", \"clients/gues\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%c\", \"clients/guest\", \"guest\", NULL, MOSQ_ERR_SUCCESS, true},\n\t\t{\"clients/%c\", \"clients/guestt\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"clients/%c\", \"clients/geest\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\n\t\t/* topic part matching substring of client id */\n\t\t{\"clients/%u\", \"clients/g\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u\", \"clients/gu\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u\", \"clients/gue\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u\", \"clients/gues\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"clients/%u\", \"clients/guest\", NULL, \"guest\", MOSQ_ERR_SUCCESS, true},\n\t\t{\"clients/%u\", \"clients/guestt\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"clients/%u\", \"clients/geest\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t};\n\n\tfor(size_t i=0; i<sizeof(tests)/sizeof(struct topic_test); i++){\n\t\tbool match;\n\t\tint rc = mosquitto_sub_matches_acl_with_pattern(\n\t\t\t\ttests[i].topic_filter, tests[i].topic,\n\t\t\t\ttests[i].clientid, tests[i].username,\n\t\t\t\t&match);\n\t\tCU_ASSERT_EQUAL(rc, tests[i].rc);\n\t\tCU_ASSERT_EQUAL(match, tests[i].match);\n\t}\n}\n\n\nstatic void TEST_acl_pattern_substring_only(void)\n{\n\tconst struct topic_test tests[] = {\n\t\t/* topic part matching substring of client id */\n\t\t{\"%c\", \"g\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c\", \"gu\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c\", \"gue\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c\", \"gues\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t{\"%c\", \"guest\", \"guest\", NULL, MOSQ_ERR_SUCCESS, true},\n\t\t{\"%c\", \"guestt\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"%c\", \"geest\", \"guest\", NULL, MOSQ_ERR_SUCCESS, false},\n\n\t\t/* topic part matching substring of client id */\n\t\t{\"%u\", \"g\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u\", \"gu\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u\", \"gue\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u\", \"gues\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t{\"%u\", \"guest\", NULL, \"guest\", MOSQ_ERR_SUCCESS, true},\n\t\t{\"%u\", \"guestt\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t\t/* topic part partial matching substring of client id */\n\t\t{\"%u\", \"geest\", NULL, \"guest\", MOSQ_ERR_SUCCESS, false},\n\t};\n\n\tfor(size_t i=0; i<sizeof(tests)/sizeof(struct topic_test); i++){\n\t\tbool match;\n\t\tint rc = mosquitto_sub_matches_acl_with_pattern(\n\t\t\t\ttests[i].topic_filter, tests[i].topic,\n\t\t\t\ttests[i].clientid, tests[i].username,\n\t\t\t\t&match);\n\t\tCU_ASSERT_EQUAL(rc, tests[i].rc);\n\t\tCU_ASSERT_EQUAL(match, tests[i].match);\n\t}\n}\n\n\n/* ========================================================================\n * PUB TOPIC CHECK\n * ======================================================================== */\n\n\nstatic void pub_topic_helper(const char *topic, int rc_expected)\n{\n\tint rc;\n\n\trc = mosquitto_pub_topic_check(topic);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\n\trc = mosquitto_pub_topic_check2(topic, strlen(topic));\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n}\n\n\nstatic void TEST_pub_topic_valid(void)\n{\n\tpub_topic_helper(\"pub/topic\", MOSQ_ERR_SUCCESS);\n\tpub_topic_helper(\"pub//topic\", MOSQ_ERR_SUCCESS);\n\tpub_topic_helper(\"pub/ /topic\", MOSQ_ERR_SUCCESS);\n}\n\n\nstatic void TEST_pub_topic_invalid(void)\n{\n\tpub_topic_helper(\"+pub/topic\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"pub+/topic\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"pub/+topic\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"pub/topic+\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"pub/topic/+\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"#pub/topic\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"pub#/topic\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"pub/#topic\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"pub/topic#\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"pub/topic/#\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"+/pub/topic\", MOSQ_ERR_INVAL);\n\tpub_topic_helper(\"//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////a\", MOSQ_ERR_INVAL);\n}\n\n\n/* ========================================================================\n * SUB TOPIC CHECK\n * ======================================================================== */\n\n\nstatic void sub_topic_helper(const char *topic, int rc_expected)\n{\n\tint rc;\n\n\trc = mosquitto_sub_topic_check(topic);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\n\trc = mosquitto_sub_topic_check2(topic, strlen(topic));\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n}\n\n\nstatic void TEST_sub_topic_valid(void)\n{\n\tsub_topic_helper(\"sub/topic\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"sub//topic\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"sub/ /topic\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"sub/+/topic\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"+/+/+\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"+\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"sub/topic/#\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"sub//topic/#\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"sub/ /topic/#\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"sub/+/topic/#\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"+/+/+/#\", MOSQ_ERR_SUCCESS);\n\tsub_topic_helper(\"#\", MOSQ_ERR_SUCCESS);\n}\n\n\nstatic void TEST_sub_topic_invalid(void)\n{\n\tsub_topic_helper(\"+sub/topic\", MOSQ_ERR_INVAL);\n\tsub_topic_helper(\"sub+/topic\", MOSQ_ERR_INVAL);\n\tsub_topic_helper(\"sub/+topic\", MOSQ_ERR_INVAL);\n\tsub_topic_helper(\"sub/topic+\", MOSQ_ERR_INVAL);\n\tsub_topic_helper(\"#sub/topic\", MOSQ_ERR_INVAL);\n\tsub_topic_helper(\"sub#/topic\", MOSQ_ERR_INVAL);\n\tsub_topic_helper(\"sub/#topic\", MOSQ_ERR_INVAL);\n\tsub_topic_helper(\"sub/topic#\", MOSQ_ERR_INVAL);\n\tsub_topic_helper(\"#/sub/topic\", MOSQ_ERR_INVAL);\n\tsub_topic_helper(\"//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////a\", MOSQ_ERR_INVAL);\n}\n\n\n/* ========================================================================\n * SUB MATCHES ACL\n * ======================================================================== */\n\n\nstatic void sub_match_test(const char *acl, const char *sub, bool expected, int rc_expected)\n{\n\tbool result;\n\tint rc;\n\n\trc = mosquitto_sub_matches_acl(acl, sub, &result);\n\tCU_ASSERT_EQUAL(rc, rc_expected);\n\tCU_ASSERT_EQUAL(result, expected);\n}\n\n\nstatic void TEST_sub_match_acl(void)\n{\n\tsub_match_test(\"foo/+/bar\", \"foo/#\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"foo/+/ba℞/#\", \"foo/baz/ba℞\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"foo/+/ba℞/#\", \"foo/baz/ba℞/+\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"foo/+/ba℞/#\", \"foo/baz/ba℞/#\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"foo/+/ba℞/#\", \"foo/baz/+/#\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"/+//#\", \"/foo///#\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"#\", \"$SYS/uptime\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"$SYS/#\", \"$SYS/uptime\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"$SYS/+/#\", \"$SYS/uptime\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"$SYS/+/#\", \"$SYS/broker/uptime\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"#\", \"#\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"#\", \"+\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"/#\", \"+\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"/#\", \"/+\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"/+\", \"#\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"/+\", \"+\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"+/+\", \"topic/topic\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"+/+\", \"topic/topic/\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"+\", \"#\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"+\", \"+\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"a/b/c/d/e\", \"a/b/c/d/e\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"a/b/ /d/e\", \"a/b/c/d/e\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"a/b/c/d/e\", \"a/b/c/d/+\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"a/b/c/d/+\", \"a/b/c/d/e\", true, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"a/b/c/d/\", \"a/b/c/d/+\", false, MOSQ_ERR_SUCCESS);\n\tsub_match_test(\"a/b/c/d/+\", \"a/b/c/d/\", true, MOSQ_ERR_SUCCESS);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_topic_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"Util topic\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit util topic test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Matching: Empty input\", TEST_empty_input)\n\t\t\t|| !CU_add_test(test_suite, \"Matching: Valid matching\", TEST_valid_matching)\n\t\t\t|| !CU_add_test(test_suite, \"Matching: Valid no matching\", TEST_valid_no_matching)\n\t\t\t|| !CU_add_test(test_suite, \"Matching: Invalid but matching\", TEST_invalid_but_matching)\n\t\t\t|| !CU_add_test(test_suite, \"Matching: Invalid\", TEST_invalid)\n\t\t\t|| !CU_add_test(test_suite, \"Pub topic: Valid\", TEST_pub_topic_valid)\n\t\t\t|| !CU_add_test(test_suite, \"Pub topic: Invalid\", TEST_pub_topic_invalid)\n\t\t\t|| !CU_add_test(test_suite, \"Sub topic: Valid\", TEST_sub_topic_valid)\n\t\t\t|| !CU_add_test(test_suite, \"Sub topic: Invalid\", TEST_sub_topic_invalid)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern topic: Empty input\", TEST_topic_pattern_empty_input)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern topic: clientid\", TEST_topic_pattern_clientid)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern topic: username\", TEST_topic_pattern_username)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern topic: both\", TEST_topic_pattern_both)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern topic: wildcard\", TEST_topic_pattern_wildcard)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern topic: substring beginning\", TEST_topic_pattern_substring_beginning)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern topic: substring middle\", TEST_topic_pattern_substring_middle)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern topic: substring end\", TEST_topic_pattern_substring_end)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern topic: substring only\", TEST_topic_pattern_substring_only)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern acl: Empty input\", TEST_acl_pattern_empty_input)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern acl: clientid\", TEST_acl_pattern_clientid)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern acl: username\", TEST_acl_pattern_username)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern acl: both\", TEST_acl_pattern_both)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern acl: wildcard\", TEST_acl_pattern_wildcard)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern acl: wildcard vs wildcard\", TEST_acl_pattern_wildcard_wildcard)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern acl: substring\", TEST_acl_pattern_substring_beginning)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern acl: substring\", TEST_acl_pattern_substring_middle)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern acl: substring\", TEST_acl_pattern_substring_end)\n\t\t\t|| !CU_add_test(test_suite, \"Pattern acl: substring\", TEST_acl_pattern_substring_only)\n\t\t\t|| !CU_add_test(test_suite, \"Sub matching: Empty input\", TEST_sub_match_empty_input)\n\t\t\t|| !CU_add_test(test_suite, \"Sub matching: normal\", TEST_sub_match_acl)\n\t\t\t){\n\n\t\tprintf(\"Error adding util topic CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/libcommon/trim_test.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include <mosquitto.h>\n\n\nstatic void rtrim_helper(const char *expected, char *buf)\n{\n\tchar *res;\n\n\tres = mosquitto_trimblanks(buf);\n\tCU_ASSERT_PTR_NOT_NULL(res);\n\tif(res){\n\t\tCU_ASSERT_EQUAL(strlen(buf), strlen(res));\n\t\tCU_ASSERT_STRING_EQUAL(res, expected);\n\t\tCU_ASSERT_PTR_EQUAL(res, buf);\n\t}\n}\n\n\nstatic void ltrim_helper(const char *expected, char *buf)\n{\n\tchar *res;\n\n\tres = mosquitto_trimblanks(buf);\n\tCU_ASSERT_PTR_NOT_NULL(res);\n\tif(res){\n\t\tCU_ASSERT_EQUAL(strlen(expected), strlen(res));\n\t\tCU_ASSERT_STRING_EQUAL(res, expected);\n\t}\n}\n\n\nstatic void TEST_null_input(void)\n{\n\tchar *res;\n\n\tres = mosquitto_trimblanks(NULL);\n\tCU_ASSERT_PTR_NULL(res);\n}\n\n\nstatic void TEST_empty_input(void)\n{\n\tchar buf[10];\n\tchar *res;\n\n\tmemset(buf, 0, sizeof(buf));\n\tres = mosquitto_trimblanks(buf);\n\tCU_ASSERT_PTR_NOT_NULL(res);\n\tif(res){\n\t\tCU_ASSERT_STRING_EQUAL(res, \"\");\n\t}\n}\n\n\nstatic void TEST_no_blanks(void)\n{\n\tchar buf[10] = \"noblanks\";\n\n\trtrim_helper(\"noblanks\", buf);\n}\n\n\nstatic void TEST_rtrim(void)\n{\n\tchar buf1[20] = \"spaces \";\n\tchar buf2[20] = \"spaces  \";\n\tchar buf3[20] = \"spaces   \";\n\tchar buf4[20] = \"spaces    \";\n\tchar buf5[20] = \"tabs\\t\";\n\tchar buf6[20] = \"tabs\\t\\t\";\n\tchar buf7[20] = \"tabs\\t\\t\\t\";\n\tchar buf8[20] = \"tabs\\t\\t\\t\\t\";\n\tchar buf9[20] = \"mixed \\t\";\n\tchar buf10[20] = \"mixed\\t \";\n\tchar buf11[20] = \"mixed\\t\\t \";\n\tchar buf12[20] = \"mixed \\t \\t \";\n\n\trtrim_helper(\"spaces\", buf1);\n\trtrim_helper(\"spaces\", buf2);\n\trtrim_helper(\"spaces\", buf3);\n\trtrim_helper(\"spaces\", buf4);\n\trtrim_helper(\"tabs\", buf5);\n\trtrim_helper(\"tabs\", buf6);\n\trtrim_helper(\"tabs\", buf7);\n\trtrim_helper(\"tabs\", buf8);\n\trtrim_helper(\"mixed\", buf9);\n\trtrim_helper(\"mixed\", buf10);\n\trtrim_helper(\"mixed\", buf11);\n\trtrim_helper(\"mixed\", buf12);\n}\n\n\nstatic void TEST_ltrim(void)\n{\n\tchar buf1[20] = \" spaces\";\n\tchar buf2[20] = \"  spaces\";\n\tchar buf3[20] = \"   spaces\";\n\tchar buf4[20] = \"    spaces\";\n\tchar buf5[20] = \"\\ttabs\";\n\tchar buf6[20] = \"\\t\\ttabs\";\n\tchar buf7[20] = \"\\t\\t\\ttabs\";\n\tchar buf8[20] = \"\\t\\t\\t\\ttabs\";\n\tchar buf9[20] = \"\\t mixed\";\n\tchar buf10[20] = \" \\tmixed\";\n\tchar buf11[20] = \" \\t\\tmixed\";\n\tchar buf12[20] = \"\\t \\t mixed\";\n\n\tltrim_helper(\"spaces\", buf1);\n\tltrim_helper(\"spaces\", buf2);\n\tltrim_helper(\"spaces\", buf3);\n\tltrim_helper(\"spaces\", buf4);\n\tltrim_helper(\"tabs\", buf5);\n\tltrim_helper(\"tabs\", buf6);\n\tltrim_helper(\"tabs\", buf7);\n\tltrim_helper(\"tabs\", buf8);\n\tltrim_helper(\"mixed\", buf9);\n\tltrim_helper(\"mixed\", buf10);\n\tltrim_helper(\"mixed\", buf11);\n\tltrim_helper(\"mixed\", buf12);\n}\n\n\nstatic void TEST_btrim(void)\n{\n\tchar buf1[20] = \" spaces \";\n\tchar buf2[20] = \"  spaces  \";\n\tchar buf3[20] = \"   spaces   \";\n\tchar buf4[20] = \"    spaces    \";\n\tchar buf5[20] = \"\\ttabs\\t\";\n\tchar buf6[20] = \"\\t\\ttabs\\t\\t\";\n\tchar buf7[20] = \"\\t\\t\\ttabs\\t\\t\\t\";\n\tchar buf8[20] = \"\\t\\t\\t\\ttabs\\t\\t\\t\\t\";\n\tchar buf9[20] = \"\\t mixed \\t\";\n\tchar buf10[20] = \" \\tmixed\\t \";\n\tchar buf11[20] = \" \\t\\tmixed\\t\\t \";\n\tchar buf12[20] = \"\\t \\t mixed \\t \\t \";\n\n\tltrim_helper(\"spaces\", buf1);\n\tltrim_helper(\"spaces\", buf2);\n\tltrim_helper(\"spaces\", buf3);\n\tltrim_helper(\"spaces\", buf4);\n\tltrim_helper(\"tabs\", buf5);\n\tltrim_helper(\"tabs\", buf6);\n\tltrim_helper(\"tabs\", buf7);\n\tltrim_helper(\"tabs\", buf8);\n\tltrim_helper(\"mixed\", buf9);\n\tltrim_helper(\"mixed\", buf10);\n\tltrim_helper(\"mixed\", buf11);\n\tltrim_helper(\"mixed\", buf12);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_trim_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"String trim\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit string trim test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"Null input\", TEST_null_input)\n\t\t\t|| !CU_add_test(test_suite, \"Empty input\", TEST_empty_input)\n\t\t\t|| !CU_add_test(test_suite, \"No blanks\", TEST_no_blanks)\n\t\t\t|| !CU_add_test(test_suite, \"Right trim\", TEST_rtrim)\n\t\t\t|| !CU_add_test(test_suite, \"Left trim\", TEST_ltrim)\n\t\t\t|| !CU_add_test(test_suite, \"Both trim\", TEST_btrim)\n\t\t\t){\n\n\t\tprintf(\"Error adding string trim CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/libcommon/utf8.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#include \"mosquitto.h\"\n\n\n/* Test data taken from\n * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt but modified for\n * updated standard (no 5, 6 byte lengths) */\n\n\nstatic void utf8_helper_len(const char *text, int len, int expected)\n{\n\tint result;\n\n\tresult = mosquitto_validate_utf8(text, len);\n\tCU_ASSERT_EQUAL(result, expected);\n}\n\n\nstatic void utf8_helper(const char *text, int expected)\n{\n\tutf8_helper_len(text, (int)strlen(text), expected);\n}\n\n\nstatic void TEST_utf8_empty(void)\n{\n\tutf8_helper_len(NULL, 0, MOSQ_ERR_INVAL);\n}\n\n\nstatic void TEST_utf8_valid(void)\n{\n\t/* 1  Some correct UTF-8 text */\n\tutf8_helper(\"\", MOSQ_ERR_SUCCESS);\n\tutf8_helper(\"You should see the Greek word 'kosme':       \\\"\\xCE\\xBA\\xE1\\xBD\\xB9\\xCF\\x83\\xCE\\xBC\\xCE\\xB5\\\"\", MOSQ_ERR_SUCCESS);\n}\n\n\nstatic void TEST_utf8_truncated(void)\n{\n\tuint8_t buf[4];\n\n\t/* As per boundary condition tests, but less one character */\n\tbuf[0] = 0xC2; buf[1] = 0;\n\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\n\tbuf[0] = 0xE0; buf[1] = 0xA0; buf[2] = 0;\n\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\n\tbuf[0] = 0xF0; buf[1] = 0x90; buf[2] = 0x80; buf[3] = 0;\n\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n}\n\n\nstatic void TEST_utf8_boundary_conditions(void)\n{\n\t/* 2  Boundary condition test cases */\n\t/* 2.1  First possible sequence of a certain length */\n\tutf8_helper_len(\"2.1.1  1 byte  (U-00000000):        \\\"\\x00\\\"\", 39, MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"2.1.2  2 bytes (U-00000080):        \\\"\\xC2\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8); /* control char */\n\tutf8_helper(\"2.1.3  3 bytes (U-00000800):        \\\"\\xE0\\xA0\\x80\\\"\", MOSQ_ERR_SUCCESS);\n\tutf8_helper(\"2.1.4  4 bytes (U-00010000):        \\\"\\xF0\\x90\\x80\\x80\\\"\", MOSQ_ERR_SUCCESS);\n\n\t/* 2.2  Last possible sequence of a certain length */\n\n\tutf8_helper(\"2.2.1  1 byte  (U-0000007F):        \\\"\\x7F\\\"\", MOSQ_ERR_MALFORMED_UTF8); /* control char */\n\tutf8_helper(\"2.2.2  2 bytes (U-000007FF):        \\\"\\xDF\\xBF\\\"\", MOSQ_ERR_SUCCESS);\n\t/* Non character */\n\tutf8_helper(\"2.2.3  3 bytes (U-0000FFFF):        \\\"\\xEF\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\t/* Non character */\n\tutf8_helper(\"2.2.4  4 bytes (U-0010FFFF):        \\\"\\xF7\\xBF\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* 2.3  Other boundary conditions */\n\n\tutf8_helper(\"2.3.1  U-0000D7FF = ed 9f bf = \\\"\\xED\\x9F\\xBF\\\"\", MOSQ_ERR_SUCCESS);\n\tutf8_helper(\"2.3.2  U-0000E000 = ee 80 80 = \\\"\\xEE\\x80\\x80\\\"\", MOSQ_ERR_SUCCESS);\n\tutf8_helper(\"2.3.3  U-0000FFFD = ef bf bd = \\\"\\xEF\\xBF\\xBD\\\"\", MOSQ_ERR_SUCCESS);\n\t/* Non character */\n\tutf8_helper(\"2.3.4  U-0010FFFF = f4 8f bf bf = \\\"\\xF4\\x8F\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\t/* This used to be valid in pre-2003 utf-8 */\n\tutf8_helper(\"2.3.5  U-00110000 = f4 90 80 80 = \\\"\\xF4\\x90\\x80\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n}\n\n\nstatic void TEST_utf8_malformed_sequences(void)\n{\n\tuint8_t buf[100];\n\tint i;\n\t/* 3  Malformed sequences */\n\t/* 3.1  Unexpected continuation bytes */\n\tutf8_helper(\"3.1.1  First continuation byte 0x80: \\\"\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.1.2  Last  continuation byte 0xbf: \\\"\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.1.3  2 continuation bytes: \\\"\\x80\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.1.4  3 continuation bytes: \\\"\\x80\\xBF\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.1.5  4 continuation bytes: \\\"\\x80\\xBF\\x80\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.1.6  5 continuation bytes: \\\"\\x80\\xBF\\x80\\xBF\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.1.7  6 continuation bytes: \\\"\\x80\\xBF\\x80\\xBF\\x80\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.1.8  7 continuation bytes: \\\"\\x80\\xBF\\x80\\xBF\\x80\\xBF\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* 3.1.9  Sequence of all 64 possible continuation bytes (0x80-0xbf): */\n\tmemset(buf, 0, sizeof(buf));\n\tfor(i=0x80; i<0x90; i++){\n\t\tbuf[i-0x80] = (uint8_t)i;\n\t}\n\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\tmemset(buf, 0, sizeof(buf));\n\tfor(i=0x90; i<0xa0; i++){\n\t\tbuf[i-0x90] = (uint8_t)i;\n\t}\n\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\n\tfor(i=0x80; i<0xA0; i++){\n\t\tbuf[0] = (uint8_t)i;\n\t\tbuf[1] = 0;\n\t\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\t}\n\n\tfor(i=0xA0; i<0xC0; i++){\n\t\tbuf[0] = (uint8_t)i;\n\t\tbuf[1] = 0;\n\t\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\t}\n\n\t/* 3.2  Lonely start characters */\n\n\t/* 3.2.1  All 32 first bytes of 2-byte sequences (0xc0-0xdf),\n\t   each followed by a space character: */\n\tfor(i=0xC0; i<0xE0; i++){\n\t\tbuf[0] = (uint8_t)i;\n\t\tbuf[1] = ' ';\n\t\tbuf[2] = 0;\n\t\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\t}\n\n\t/* 3.2.2  All 16 first bytes of 3-byte sequences (0xe0-0xef),\n\t   each followed by a space character: */\n\tfor(i=0xe0; i<0xf0; i++){\n\t\tbuf[0] = (uint8_t)i;\n\t\tbuf[1] = ' ';\n\t\tbuf[2] = 0;\n\t\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\t}\n\n\t/* 3.2.3  All 8 first bytes of 4-byte sequences (0xf0-0xf7),\n\t   each followed by a space character: */\n\tfor(i=0xF0; i<0xF8; i++){\n\t\tbuf[0] = (uint8_t)i;\n\t\tbuf[1] = ' ';\n\t\tbuf[2] = 0;\n\t\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\t}\n\n\t/* 3.2.4  All 4 first bytes of 5-byte sequences (0xf8-0xfb),\n\t   each followed by a space character: */\n\tfor(i=0xF8; i<0xFC; i++){\n\t\tbuf[0] = (uint8_t)i;\n\t\tbuf[1] = ' ';\n\t\tbuf[2] = 0;\n\t\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\t}\n\n\t/* 3.2.5  All 2 first bytes of 6-byte sequences (0xfc-0xfd),\n\t   each followed by a space character: */\n\tfor(i=0xFC; i<0xFE; i++){\n\t\tbuf[0] = (uint8_t)i;\n\t\tbuf[1] = ' ';\n\t\tbuf[2] = 0;\n\t\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\t}\n\n\t/* 3.3  Sequences with last continuation byte missing\n\n\tAll bytes of an incomplete sequence should be signalled as a single\n\tmalformed sequence, i.e., you should see only a single replacement\n\tcharacter in each of the next 10 tests. (Characters as in section 2) */\n\n\tutf8_helper(\"3.3.1  2-byte sequence with last byte missing (U+0000):     \\\"\\xC0\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.3.2  3-byte sequence with last byte missing (U+0000):     \\\"\\xE0\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.3.3  4-byte sequence with last byte missing (U+0000):     \\\"\\xF0\\x80\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.3.4  5-byte sequence with last byte missing (U+0000):     \\\"\\xF8\\x80\\x80\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.3.5  6-byte sequence with last byte missing (U+0000):     \\\"\\xFC\\x80\\x80\\x80\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.3.6  2-byte sequence with last byte missing (U-000007FF): \\\"\\xDF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.3.7  3-byte sequence with last byte missing (U-0000FFFF): \\\"\\xEF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.3.8  4-byte sequence with last byte missing (U-001FFFFF): \\\"\\xF7\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.3.9  5-byte sequence with last byte missing (U-03FFFFFF): \\\"\\xFB\\xBF\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \\\"\\xFD\\xBF\\xBF\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* 3.4  Concatenation of incomplete sequences\n\n\t\tAll the 10 sequences of 3.3 concatenated, you should see 10 malformed\n\t\tsequences being signalled:*/\n\n\tutf8_helper(\"\\\"\\xC0\\xE0\\x80\\xF0\\x80\\x80\\xF8\\x80\\x80\\x80\\xFC\\x80\\x80\\x80\\x80\\xDF\\xEF\\xBF\\xF7\\xBF\\xBF\\xFB\\xBF\\xBF\\xBF\\xFD\\xBF\\xBF\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* 3.5  Impossible bytes\n\n\t\tThe following two bytes cannot appear in a correct UTF-8 string */\n\n\tutf8_helper(\"3.5.1  fe = \\\"\\xFE\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.5.2  ff = \\\"\\xFF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"3.5.3  fe fe ff ff = \\\"\\xFE\\xFE\\xFF\\xFF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n}\n\n\nstatic void TEST_utf8_overlong_encoding(void)\n{\n\t/* 4  Overlong sequences\n\n\t\tThe following sequences are not malformed according to the letter of\n\t\tthe Unicode 2.0 standard. However, they are longer then necessary and\n\t\ta correct UTF-8 encoder is not allowed to produce them. A \"safe UTF-8\n\t\tdecoder\" should reject them just like malformed sequences for two\n\t\treasons: (1) It helps to debug applications if overlong sequences are\n\t\tnot treated as valid representations of characters, because this helps\n\t\tto spot problems more quickly. (2) Overlong sequences provide\n\t\talternative representations of characters, that could maliciously be\n\t\tused to bypass filters that check only for ASCII characters. For\n\t\tinstance, a 2-byte encoded line feed (LF) would not be caught by a\n\t\tline counter that counts only 0x0a bytes, but it would still be\n\t\tprocessed as a line feed by an unsafe UTF-8 decoder later in the\n\t\tpipeline. From a security point of view, ASCII compatibility of UTF-8\n\t\tsequences means also, that ASCII characters are *only* allowed to be\n\t\trepresented by ASCII bytes in the range 0x00-0x7f. To ensure this\n\t\taspect of ASCII compatibility, use only \"safe UTF-8 decoders\" that\n\t\treject overlong UTF-8 sequences for which a shorter encoding exists. */\n\n\t/* 4.1  Examples of an overlong ASCII character\n\n\t\tWith a safe UTF-8 decoder, all of the following five overlong\n\t\trepresentations of the ASCII character slash (\"/\") should be rejected\n\t\tlike a malformed UTF-8 sequence, for instance by substituting it with\n\t\ta replacement character. If you see a slash below, you do not have a\n\t\tsafe UTF-8 decoder! */\n\n\tutf8_helper(\"4.1.1 U+002F = c0 af             = \\\"\\xC0\\xAF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.1.2 U+002F = e0 80 af          = \\\"\\xE0\\x80\\xAF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.1.3 U+002F = f0 80 80 af       = \\\"\\xF0\\x80\\x80\\xAF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.1.4 U+002F = f8 80 80 80 af    = \\\"\\xF8\\x80\\x80\\x80\\xAF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.1.5 U+002F = fc 80 80 80 80 af = \\\"\\xFC\\x80\\x80\\x80\\x80\\xAF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* 4.2  Maximum overlong sequences\n\n\t\tBelow you see the highest Unicode value that is still resulting in an\n\t\toverlong sequence if represented with the given number of bytes. This\n\t\tis a boundary test for safe UTF-8 decoders. All five characters should\n\t\tbe rejected like malformed UTF-8 sequences. */\n\n\tutf8_helper(\"4.2.1  U-0000007F = c1 bf             = \\\"\\xC1\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.2.2  U-000007FF = e0 9f bf          = \\\"\\xE0\\x9F\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.2.3  U-0000FFFF = f0 8f bf bf       = \\\"\\xF0\\x8F\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.2.4  U-001FFFFF = f8 87 bf bf bf    = \\\"\\xF8\\x87\\xBF\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.2.5  U-03FFFFFF = fc 83 bf bf bf bf = \\\"\\xFC\\x83\\xBF\\xBF\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* 4.3  Overlong representation of the NUL character\n\n\t\tThe following five sequences should also be rejected like malformed\n\t\tUTF-8 sequences and should not be treated like the ASCII NUL\n\t\tcharacter. */\n\n\tutf8_helper(\"4.3.1  U+0000 = c0 80             = \\\"\\xC0\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.3.2  U+0000 = e0 80 80          = \\\"\\xE0\\x80\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.3.3  U+0000 = f0 80 80 80       = \\\"\\xF0\\x80\\x80\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.3.4  U+0000 = f8 80 80 80 80    = \\\"\\xF8\\x80\\x80\\x80\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"4.3.5  U+0000 = fc 80 80 80 80 80 = \\\"\\xFC\\x80\\x80\\x80\\x80\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n}\n\n\nstatic void TEST_utf8_illegal_code_positions(void)\n{\n\t/* 5  Illegal code positions\n\n\t\tThe following UTF-8 sequences should be rejected like malformed\n\t\tsequences, because they never represent valid ISO 10646 characters and\n\t\ta UTF-8 decoder that accepts them might introduce security problems\n\t\tcomparable to overlong UTF-8 sequences. */\n\n\t/* 5.1 Single UTF-16 surrogates */\n\n\tutf8_helper(\"5.1.1  U+D800 = ed a0 80 = \\\"\\xED\\xA0\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.1.2  U+DB7F = ed ad bf = \\\"\\xED\\xAD\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.1.3  U+DB80 = ed ae 80 = \\\"\\xED\\xAE\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.1.4  U+DBFF = ed af bf = \\\"\\xED\\xAF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.1.5  U+DC00 = ed b0 80 = \\\"\\xED\\xB0\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.1.6  U+DF80 = ed be 80 = \\\"\\xED\\xBE\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.1.7  U+DFFF = ed bf bf = \\\"\\xED\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* 5.2 Paired UTF-16 surrogates */\n\n\tutf8_helper(\"5.2.1  U+D800 U+DC00 = ed a0 80 ed b0 80 = \\\"\\xED\\xA0\\x80\\xED\\xB0\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.2.2  U+D800 U+DFFF = ed a0 80 ed bf bf = \\\"\\xED\\xA0\\x80\\xED\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.2.3  U+DB7F U+DC00 = ed ad bf ed b0 80 = \\\"\\xED\\xAD\\xBF\\xED\\xB0\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.2.4  U+DB7F U+DFFF = ed ad bf ed bf bf = \\\"\\xED\\xAD\\xBF\\xED\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.2.5  U+DB80 U+DC00 = ed ae 80 ed b0 80 = \\\"\\xED\\xAE\\x80\\xED\\xB0\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.2.6  U+DB80 U+DFFF = ed ae 80 ed bf bf = \\\"\\xED\\xAE\\x80\\xED\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.2.7  U+DBFF U+DC00 = ed af bf ed b0 80 = \\\"\\xED\\xAF\\xBF\\xED\\xB0\\x80\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.2.8  U+DBFF U+DFFF = ed af bf ed bf bf = \\\"\\xED\\xAF\\xBF\\xED\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* 5.3 Noncharacter code positions\n\n\t\tThe following \"noncharacters\" are \"reserved for internal use\" by\n\t\tapplications, and according to older versions of the Unicode Standard\n\t\t\"should never be interchanged\". Unicode Corrigendum #9 dropped the\n\t\tlatter restriction. Nevertheless, their presence in incoming UTF-8 data\n\t\tcan remain a potential security risk, depending on what use is made of\n\t\tthese codes subsequently. Examples of such internal use:\n\n\t\t- Some file APIs with 16-bit characters may use the integer value -1\n\t\t= U+FFFF to signal an end-of-file (EOF) or error condition.\n\n\t\t- In some UTF-16 receivers, code point U+FFFE might trigger a\n\t\tbyte-swap operation (to convert between UTF-16LE and UTF-16BE).\n\n\t\tWith such internal use of noncharacters, it may be desirable and safer\n\t\tto block those code points in UTF-8 decoders, as they should never\n\t\toccur legitimately in incoming UTF-8 data, and could trigger unsafe\n\t\tbehaviour in subsequent processing.\n\n\t\tParticularly problematic noncharacters in 16-bit applications: */\n\tutf8_helper(\"5.3.1  U+FFFE = ef bf be = \\\"\\xEF\\xBF\\xBE\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"5.3.2  U+FFFF = ef bf bf = \\\"\\xEF\\xBF\\xBF\\\"\", MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* Other noncharacters: */\n\n\t/* FIXME - these need splitting up into separate tests. */\n\tutf8_helper(\"\\xEF\\xB7\\x90\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x91\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x92\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x93\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x94\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x95\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x96\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x97\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x98\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x99\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x9A\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x9B\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x9C\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x9D\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x9E\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\x9F\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xA0\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xA1\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xA2\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xA3\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xA4\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xA5\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xA6\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xA7\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xA8\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xA9\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xAA\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xAB\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xAC\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xAD\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xAE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xEF\\xB7\\xAF\", MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* 5.3.4  U+nFFFE U+nFFFF (for n = 1..10) */\n\n\tutf8_helper(\"\\xF0\\x9F\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF0\\x9F\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF0\\xAF\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF0\\xAF\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF0\\xBF\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF0\\xBF\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF1\\x8F\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF1\\x8F\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF1\\x9F\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF1\\x9F\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF1\\xAF\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF1\\xAF\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF1\\xBF\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF1\\xBF\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF2\\x8F\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF2\\x8F\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF2\\x9F\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF2\\x9F\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF2\\xAF\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF2\\xAF\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF2\\xBF\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF2\\xBF\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF3\\x8F\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF3\\x8F\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF3\\x9F\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF3\\x9F\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF3\\xAF\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF3\\xAF\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF3\\xBF\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF3\\xBF\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF4\\x8F\\xBF\\xBE\", MOSQ_ERR_MALFORMED_UTF8);\n\tutf8_helper(\"\\xF4\\x8F\\xBF\\xBF\", MOSQ_ERR_MALFORMED_UTF8);\n}\n\n\nstatic void TEST_utf8_control_characters(void)\n{\n\tuint8_t buf[10];\n\tint i;\n\n\t/* U+0001 to U+001F are single byte control characters */\n\tfor(i=0x01; i<0x20; i++){\n\t\tbuf[0] = (uint8_t)i;\n\t\tbuf[1] = '\\0';\n\t\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\t}\n\n\t/* U+007F is a single byte control character */\n\tbuf[0] = 0x7F;\n\tbuf[1] = '\\0';\n\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\n\t/* U+0080 to U+009F are two byte control characters */\n\tfor(i=0x80; i<0xA0; i++){\n\t\tbuf[0] = 0xC2;\n\t\tbuf[1] = (uint8_t)i;\n\t\tbuf[2] = '\\0';\n\t\tutf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8);\n\t}\n\n}\n\n\nstatic void TEST_utf8_mqtt_1_5_4_2(void)\n{\n\tuint8_t buf[10] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '\\0'};\n\n\tutf8_helper_len((char *)buf, 9, MOSQ_ERR_SUCCESS);\n\n\tbuf[3] = '\\0';\n\tutf8_helper_len((char *)buf, 9, MOSQ_ERR_MALFORMED_UTF8);\n}\n\n\nstatic void TEST_utf8_mqtt_1_5_4_3(void)\n{\n\tuint8_t buf[10] = {'a', 'b', 0xEF, 0xBB, 0xBF, 'f', 'g', 'h', 'i', '\\0'};\n\n\tutf8_helper_len((char *)buf, 9, MOSQ_ERR_SUCCESS);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint init_utf8_tests(void)\n{\n\tCU_pSuite test_suite = NULL;\n\n\ttest_suite = CU_add_suite(\"UTF-8\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit test suite.\\n\");\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 empty\", TEST_utf8_empty)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 valid\", TEST_utf8_valid)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 truncated\", TEST_utf8_truncated)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 boundary conditions\", TEST_utf8_boundary_conditions)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 malformed sequences\", TEST_utf8_malformed_sequences)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 overlong encoding\", TEST_utf8_overlong_encoding)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 illegal code positions\", TEST_utf8_illegal_code_positions)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 control characters\", TEST_utf8_control_characters)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 MQTT-1.5.4-2\", TEST_utf8_mqtt_1_5_4_2)\n\t\t\t|| !CU_add_test(test_suite, \"UTF-8 MQTT-1.5.4-3\", TEST_utf8_mqtt_1_5_4_3)\n\t\t\t){\n\n\t\tprintf(\"Error adding UTF-8 CUnit tests.\\n\");\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/unit/tls_stubs.c",
    "content": "#include \"config.h\"\n\n#include <time.h>\n#include <logging_mosq.h>\n\nint tls_ex_index_mosq;\n\nstruct mosquitto_db {\n\n};\n\n\nint log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...)\n{\n\tUNUSED(mosq);\n\tUNUSED(priority);\n\tUNUSED(fmt);\n\n\treturn 0;\n}\n\n\ntime_t mosquitto_time(void)\n{\n\treturn 123;\n}\n\n\nint net__socket_close(struct mosquitto_db *db, struct mosquitto *mosq)\n{\n\tUNUSED(db);\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n\nint send__pingreq(struct mosquitto *mosq)\n{\n\tUNUSED(mosq);\n\n\treturn MOSQ_ERR_SUCCESS;\n}\n\n"
  },
  {
    "path": "test/unit/tls_test.c",
    "content": "#include <CUnit/CUnit.h>\n#include <CUnit/Basic.h>\n\n#define WITH_TLS\n\n#include \"tls_mosq.c\"\n\n\n//static int mosquitto__cmp_hostname_wildcard(char *certname, const char *hostname)\n\n\nvoid hostname_cmp_helper(char *certname, const char *hostname, int expected)\n{\n\tint rc = mosquitto__cmp_hostname_wildcard(certname, hostname);\n\tCU_ASSERT_EQUAL(rc, expected);\n\tif(rc != expected){\n\t\tprintf(\"%d || %d\\n\", rc, expected);\n\t}\n}\n\n\nvoid TEST_tls_hostname_compare_null(void)\n{\n\thostname_cmp_helper(NULL, \"localhost\", 1);\n\thostname_cmp_helper(\"localhost\", NULL, 1);\n\thostname_cmp_helper(NULL, NULL, 1);\n}\n\n\nvoid TEST_tls_hostname_compare_simple(void)\n{\n\thostname_cmp_helper(\"localhost\", \"localhost\", 0);\n\thostname_cmp_helper(\"localhost\", \"localhose\", 15);\n}\n\n\nvoid TEST_tls_hostname_compare_bad_wildcard_format(void)\n{\n\thostname_cmp_helper(\"**localhost\", \"localhost\", 1);\n\thostname_cmp_helper(\"*,localhost\", \"localhost\", 1);\n\thostname_cmp_helper(\"*.\", \"localhost\", 1);\n}\n\n\nvoid TEST_tls_hostname_compare_invalid_wildcard(void)\n{\n\thostname_cmp_helper(\"*.com\", \"example.com\", 1);\n\thostname_cmp_helper(\"*.com\", \"example.org\", 1);\n\thostname_cmp_helper(\"*.org\", \"example.org\", 1);\n}\n\n\nvoid TEST_tls_hostname_compare_good_wildcard(void)\n{\n\thostname_cmp_helper(\"*.example.com\", \"test.example.com\", 0);\n\thostname_cmp_helper(\"*.example.com\", \"test.example.org\", -12);\n\thostname_cmp_helper(\"*.example.org\", \"test.example.org\", 0);\n}\n\n\n/* ========================================================================\n * TEST SUITE SETUP\n * ======================================================================== */\n\n\nint main(int argc, char *argv[])\n{\n\tCU_pSuite test_suite = NULL;\n\tunsigned int fails;\n\n\tUNUSED(argc);\n\tUNUSED(argv);\n\n\tif(CU_initialize_registry() != CUE_SUCCESS){\n\t\tprintf(\"Error initializing CUnit registry.\\n\");\n\t\treturn 1;\n\t}\n\n\ttest_suite = CU_add_suite(\"Subs\", NULL, NULL);\n\tif(!test_suite){\n\t\tprintf(\"Error adding CUnit TLS test suite.\\n\");\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tif(0\n\t\t\t|| !CU_add_test(test_suite, \"TLS hostname compare null\", TEST_tls_hostname_compare_null)\n\t\t\t|| !CU_add_test(test_suite, \"TLS hostname compare simple\", TEST_tls_hostname_compare_simple)\n\t\t\t|| !CU_add_test(test_suite, \"TLS hostname compare bad wildcard format\", TEST_tls_hostname_compare_bad_wildcard_format)\n\t\t\t|| !CU_add_test(test_suite, \"TLS hostname compare invalid wildcard\", TEST_tls_hostname_compare_invalid_wildcard)\n\t\t\t|| !CU_add_test(test_suite, \"TLS hostname compare good wildcard\", TEST_tls_hostname_compare_good_wildcard)\n\t\t\t){\n\n\t\tprintf(\"Error adding TLS CUnit tests.\\n\");\n\t\tCU_cleanup_registry();\n\t\treturn 1;\n\t}\n\n\tCU_basic_set_mode(CU_BRM_VERBOSE);\n\tCU_basic_run_tests();\n\tfails = CU_get_number_of_failures();\n\tCU_cleanup_registry();\n\n\treturn (int)fails;\n}\n"
  },
  {
    "path": "vcpkg.json",
    "content": "{\n\t\"name\": \"mosquitto\",\n\t\"version-string\": \"2.1.2\",\n\t\"dependencies\": [\n\t\t\"argon2\",\n\t\t\"cjson\",\n\t\t\"libmicrohttpd\",\n\t\t\"openssl\",\n\t\t\"pthreads\",\n\t\t\"sqlite3\"\n\t]\n}\n"
  },
  {
    "path": "www/README.md",
    "content": "This is the mosquitto website, it can be built with `nikola`:\n\n`nikola build`"
  },
  {
    "path": "www/conf.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom __future__ import unicode_literals\nimport time\n\n# !! This is the configuration of Nikola. !! #\n# !!  You should edit it to your liking.  !! #\n\n\n# ! Some settings can be different in different languages.\n# ! A comment stating (translatable) is used to denote those.\n# ! There are two ways to specify a translatable setting:\n# ! (a) BLOG_TITLE = \"My Blog\"\n# ! (b) BLOG_TITLE = {\"en\": \"My Blog\", \"es\": \"Mi Blog\"}\n# ! Option (a) is used when you don't want that setting translated.\n# ! Option (b) is used for settings that are different in different languages.\n\n\n# Data about this site\nBLOG_AUTHOR = \"Mosquitto Project\"  # (translatable)\nBLOG_TITLE = \"Eclipse Mosquitto\"  # (translatable)\n# This is the main URL for your site. It will be used\n# in a prominent link. Don't forget the protocol (http/https)!\nSITE_URL = \"https://mosquitto.org/\"\n# This is the URL where Nikola's output will be deployed.\n# If not set, defaults to SITE_URL\n# BASE_URL = \"https://example.com/\"\nBLOG_EMAIL = \"roger@atchoo.org\"\nBLOG_DESCRIPTION = \"An open source MQTT server\"  # (translatable)\n\n\n# What is the default language?\nDEFAULT_LANG = \"en\"\n\n# What other languages do you have?\n# The format is {\"translationcode\" : \"path/to/translation\" }\n# the path will be used as a prefix for the generated pages location\nTRANSLATIONS = {\n    DEFAULT_LANG: \"\",\n    # Example for another language:\n    # \"es\": \"./es\",\n}\n\n# What will translated input files be named like?\n\n# If you have a page something.rst, then something.pl.rst will be considered\n# its Polish translation.\n#     (in the above example: path == \"something\", ext == \"rst\", lang == \"pl\")\n# this pattern is also used for metadata:\n#     something.meta -> something.pl.meta\n\nTRANSLATIONS_PATTERN = \"{path}.{lang}.{ext}\"\n\n# Links for the sidebar / navigation bar.  (translatable)\n# This is a dict.  The keys are languages, and values are tuples.\n#\n# For regular links:\n#     ('https://getnikola.com/', 'Nikola Homepage')\n#\n# For submenus:\n#     (\n#         (\n#             ('https://apple.com/', 'Apple'),\n#             ('https://orange.com/', 'Orange'),\n#         ),\n#         'Fruits'\n#     )\n#\n# WARNING: Support for submenus is theme-dependent.\n#          Only one level of submenus is supported.\n# WARNING: Some themes, including the default Bootstrap 3 theme,\n#          may present issues if the menu is too large.\n#          (in bootstrap3, the navbar can grow too large and cover contents.)\n# WARNING: If you link to directories, make sure to follow\n#          ``STRIP_INDEXES``.  If it’s set to ``True``, end your links\n#          with a ``/``, otherwise end them with ``/index.html`` — or\n#          else they won’t be highlighted when active.\n\nNAVIGATION_LINKS = {\n    DEFAULT_LANG: (\n        (\"/\", \"Home\"),\n        #(\"/about/\", \"About\"),\n        (\"/blog/\", \"Blog\"),\n        (\"/download/\", \"Download\"),\n        #(\"/development/\", \"Development\"),\n        #(\"/community/\", \"Community\"),\n        #(\"/sponsoring/\", \"Sponsoring\"),\n        (\n            (\n                (\"/documentation/\", \"All\"),\n                (\"/roadmap/\", \"Roadmap\"),\n                (\"/api/\", \"API\"),\n                (\"/man/libmosquitto-3.html\", \"libmosquitto\"),\n                (\"/man/mosquitto-8.html\", \"mosquitto\"),\n                (\"/man/mosquitto-conf-5.html\", \"mosquitto.conf\"),\n                (\"/man/mosquitto_ctrl-1.html\", \"mosquitto_ctrl\"),\n                (\"/man/mosquitto_ctrl_dynsec-1.html\", \"mosquitto_ctrl_dynsec\"),\n                (\"/man/mosquitto_ctrl_shell-1.html\", \"mosquitto_ctrl_shell\"),\n                (\"/man/mosquitto_passwd-1.html\", \"mosquitto_passwd\"),\n                (\"/man/mosquitto_pub-1.html\", \"mosquitto_pub\"),\n                (\"/man/mosquitto_rr-1.html\", \"mosquitto_rr\"),\n                (\"/man/mosquitto_signal-1.html\", \"mosquitto_signal\"),\n                (\"/man/mosquitto_sub-1.html\", \"mosquitto_sub\"),\n                (\"/man/mosquitto-tls-7.html\", \"mosquitto-tls\"),\n                (\"/man/mqtt-7.html\", \"mqtt\"),\n            ), \"Documentation\",\n        )\n    ),\n}\n\n# Name of the theme to use.\nTHEME = \"mosquitto\"\n#THEME = \"bootstrap3\"\n\n# Primary color of your theme. This will be used to customize your theme and\n# auto-generate related colors in POSTS_SECTION_COLORS. Must be a HEX value.\nTHEME_COLOR = '#3c5280'\n\n# POSTS and PAGES contains (wildcard, destination, template) tuples.\n# (translatable)\n#\n# The wildcard is used to generate a list of source files\n# (whatever/thing.rst, for example).\n#\n# That fragment could have an associated metadata file (whatever/thing.meta),\n# and optionally translated files (example for Spanish, with code \"es\"):\n#     whatever/thing.es.rst and whatever/thing.es.meta\n#\n#     This assumes you use the default TRANSLATIONS_PATTERN.\n#\n# From those files, a set of HTML fragment files will be generated:\n# cache/whatever/thing.html (and maybe cache/whatever/thing.html.es)\n#\n# These files are combined with the template to produce rendered\n# pages, which will be placed at\n# output/TRANSLATIONS[lang]/destination/pagename.html\n#\n# where \"pagename\" is the \"slug\" specified in the metadata file.\n# The page might also be placed in /destination/pagename/index.html\n# if PRETTY_URLS are enabled.\n#\n# The difference between POSTS and PAGES is that POSTS are added\n# to feeds, indexes, tag lists and archives and are considered part\n# of a blog, while PAGES are just independent HTML pages.\n#\n# Finally, note that destination can be translated, i.e. you can\n# specify a different translation folder per language. Example:\n#     PAGES = (\n#         (\"pages/*.rst\", {\"en\": \"pages\", \"de\": \"seiten\"}, \"story.tmpl\"),\n#         (\"pages/*.md\", {\"en\": \"pages\", \"de\": \"seiten\"}, \"story.tmpl\"),\n#     )\n\nPOSTS = (\n    (\"posts/*.rst\", \"blog\", \"post.tmpl\"),\n    (\"posts/*.txt\", \"blog\", \"post.tmpl\"),\n    (\"posts/*.html\", \"blog\", \"post.tmpl\"),\n    (\"posts/*.md\", \"blog\", \"post.tmpl\"),\n)\nPAGES = (\n    (\"pages/*.rst\", \"\", \"story.tmpl\"),\n    (\"pages/*.txt\", \"\", \"story.tmpl\"),\n    (\"pages/*.html\", \"\", \"story.tmpl\"),\n    (\"pages/*.md\", \"\", \"story.tmpl\"),\n    (\"pages/documentation/*.md\", \"\", \"story.tmpl\"),\n    (\"man/*.xml\", \"man\", \"story.tmpl\"),\n)\n\n\n# Below this point, everything is optional\n\n# Post's dates are considered in UTC by default, if you want to use\n# another time zone, please set TIMEZONE to match. Check the available\n# list from Wikipedia:\n# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\n# (e.g. 'Europe/Zurich')\n# Also, if you want to use a different time zone in some of your posts,\n# you can use the ISO 8601/RFC 3339 format (ex. 2012-03-30T23:00:00+02:00)\nTIMEZONE = \"Europe/London\"\n\n# If you want to use ISO 8601 (also valid RFC 3339) throughout Nikola\n# (especially in new_post), set this to True.\n# Note that this does not affect DATE_FORMAT.\n# FORCE_ISO8601 = False\n\n# Date format used to display post dates. (translatable)\n# (str used by datetime.datetime.strftime)\n# DATE_FORMAT = '%Y-%m-%d %H:%M'\n\n# Date format used to display post dates, if local dates are used. (translatable)\n# (str used by moment.js)\n# JS_DATE_FORMAT = 'YYYY-MM-DD HH:mm'\n\n# Date fanciness.\n#\n# 0 = using DATE_FORMAT and TIMEZONE\n# 1 = using JS_DATE_FORMAT and local user time (via moment.js)\n# 2 = using a string like “2 days ago”\n#\n# Your theme must support it, bootstrap and bootstrap3 already do.\n# DATE_FANCINESS = 0\n\n# While Nikola can select a sensible locale for each language,\n# sometimes explicit control can come handy.\n# In this file we express locales in the string form that\n# python's locales will accept in your OS, by example\n# \"en_US.utf8\" in Unix-like OS, \"English_United States\" in Windows.\n# LOCALES = dict mapping language --> explicit locale for the languages\n# in TRANSLATIONS. You can omit one or more keys.\n# LOCALE_FALLBACK = locale to use when an explicit locale is unavailable\n# LOCALE_DEFAULT = locale to use for languages not mentioned in LOCALES; if\n# not set the default Nikola mapping is used.\n\n# LOCALES = {}\n# LOCALE_FALLBACK = None\n# LOCALE_DEFAULT = None\n\n# One or more folders containing files to be copied as-is into the output.\n# The format is a dictionary of {source: relative destination}.\n# Default is:\n# FILES_FOLDERS = {'files': ''}\n# Which means copy 'files' into 'output'\n\n# One or more folders containing code listings to be processed and published on\n# the site. The format is a dictionary of {source: relative destination}.\n# Default is:\n# LISTINGS_FOLDERS = {'listings': 'listings'}\n# Which means process listings from 'listings' into 'output/listings'\n\n# A mapping of languages to file-extensions that represent that language.\n# Feel free to add or delete extensions to any list, but don't add any new\n# compilers unless you write the interface for it yourself.\n#\n# 'rest' is reStructuredText\n# 'markdown' is MarkDown\n# 'html' assumes the file is HTML and just copies it\nCOMPILERS = {\n    \"rest\": ('.rst', '.txt'),\n    \"markdown\": ('.md', '.mdown', '.markdown'),\n    \"textile\": ('.textile',),\n    \"txt2tags\": ('.t2t',),\n    \"bbcode\": ('.bb',),\n    \"wiki\": ('.wiki',),\n    \"ipynb\": ('.ipynb',),\n    \"html\": ('.html', '.htm'),\n    # PHP files are rendered the usual way (i.e. with the full templates).\n    # The resulting files have .php extensions, making it possible to run\n    # them without reconfiguring your server to recognize them.\n    \"php\": ('.php',),\n    # Pandoc detects the input from the source filename\n    # but is disabled by default as it would conflict\n    # with many of the others.\n    \"docbookmanpage\": ('.xml',),\n}\n\n# Create by default posts in one file format?\n# Set to False for two-file posts, with separate metadata.\n# ONE_FILE_POSTS = True\n\n# Use date-based path when creating posts?\n# Can be enabled on a per-post basis with `nikola new_post -d`.\n# The setting is ignored when creating pages (`-d` still works).\nNEW_POST_DATE_PATH = True\n\n# What format to use when creating posts with date paths?\n# Default is '%Y/%m/%d', other possibilities include '%Y' or '%Y/%m'.\nNEW_POST_DATE_PATH_FORMAT = '%Y/%m'\n\n# If this is set to True, the DEFAULT_LANG version will be displayed for\n# untranslated posts.\n# If this is set to False, then posts that are not translated to a language\n# LANG will not be visible at all in the pages in that language.\n# Formerly known as HIDE_UNTRANSLATED_POSTS (inverse)\n# SHOW_UNTRANSLATED_POSTS = True\n\n# Nikola supports logo display.  If you have one, you can put the URL here.\n# Final output is <img src=\"LOGO_URL\" id=\"logo\" alt=\"BLOG_TITLE\">.\n# The URL may be relative to the site root.\n# LOGO_URL = ''\n\n# If you want to hide the title of your website (for example, if your logo\n# already contains the text), set this to False.\nSHOW_BLOG_TITLE = False\n\n# Writes tag cloud data in form of tag_cloud_data.json.\n# Warning: this option will change its default value to False in v8!\nWRITE_TAG_CLOUD = True\n\n# Generate pages for each section. The site must have at least two sections\n# for this option to take effect. It wouldn't build for just one section.\n#POSTS_SECTIONS = True\n\n# Setting this to False generates a list page instead of an index. Indexes\n# are the default and will apply GENERATE_ATOM if set.\n# POSTS_SECTIONS_ARE_INDEXES = True\n\n# Final locations are:\n# output / TRANSLATION[lang] / SECTION_PATH / SECTION_NAME / index.html (list of posts for a section)\n# output / TRANSLATION[lang] / SECTION_PATH / SECTION_NAME / rss.xml (RSS feed for a section)\n# (translatable)\n# SECTION_PATH = \"\"\n\n# Each post and section page will have an associated color that can be used\n# to style them with a recognizable color detail across your site. A color\n# is assigned to  each section based on shifting the hue of your THEME_COLOR\n# at least 7.5 % while leaving the lightness and saturation untouched in the\n# HUSL colorspace. You can overwrite colors by assigning them colors in HEX.\n# POSTS_SECTION_COLORS = {\n#     DEFAULT_LANG: {\n#         'posts':  '#49b11bf',\n#         'reviews':   '#ffe200',\n#     },\n# }\n\n# Associate a description with a section. For use in meta description on\n# section index pages or elsewhere in themes.\n# POSTS_SECTION_DESCRIPTIONS = {\n#     DEFAULT_LANG: {\n#         'how-to': 'Learn how-to things properly with these amazing tutorials.',\n#     },\n# }\n\n# Sections are determined by their output directory as set in POSTS by default,\n# but can alternatively be determined from file metadata instead.\n# POSTS_SECTION_FROM_META = False\n\n# Names are determined from the output directory name automatically or the\n# metadata label. Unless overwritten below, names will use title cased and\n# hyphens replaced by spaces.\n# POSTS_SECTION_NAME = {\n#    DEFAULT_LANG: {\n#        'posts': 'Blog Posts',\n#        'uncategorized': 'Odds and Ends',\n#    },\n# }\n\n# Titles for per-section index pages. Can be either one string where \"{name}\"\n# is substituted or the POSTS_SECTION_NAME, or a dict of sections. Note\n# that the INDEX_PAGES option is also applied to section page titles.\n# POSTS_SECTION_TITLE = {\n#     DEFAULT_LANG: {\n#         'how-to': 'How-to and Tutorials',\n#     },\n# }\n\n# Paths for different autogenerated bits. These are combined with the\n# translation paths.\n\n# Final locations are:\n# output / TRANSLATION[lang] / TAG_PATH / index.html (list of tags)\n# output / TRANSLATION[lang] / TAG_PATH / tag.html (list of posts for a tag)\n# output / TRANSLATION[lang] / TAG_PATH / tag.xml (RSS feed for a tag)\n# (translatable)\nTAG_PATH = \"blog/categories\"\n\n# By default, the list of tags is stored in\n#     output / TRANSLATION[lang] / TAG_PATH / index.html\n# (see explanation for TAG_PATH). This location can be changed to\n#     output / TRANSLATION[lang] / TAGS_INDEX_PATH\n# with an arbitrary relative path TAGS_INDEX_PATH.\n# (translatable)\n# TAGS_INDEX_PATH = \"tags.html\"\n\n# If TAG_PAGES_ARE_INDEXES is set to True, each tag's page will contain\n# the posts themselves. If set to False, it will be just a list of links.\n# TAG_PAGES_ARE_INDEXES = False\n\n# Set descriptions for tag pages to make them more interesting. The\n# default is no description. The value is used in the meta description\n# and displayed underneath the tag list or index page’s title.\n# TAG_PAGES_DESCRIPTIONS = {\n#    DEFAULT_LANG: {\n#        \"blogging\": \"Meta-blog posts about blogging about blogging.\",\n#        \"open source\": \"My contributions to my many, varied, ever-changing, and eternal libre software projects.\"\n#    },\n# }\n\n# Set special titles for tag pages. The default is \"Posts about TAG\".\n# TAG_PAGES_TITLES = {\n#    DEFAULT_LANG: {\n#        \"blogging\": \"Meta-posts about blogging\",\n#        \"open source\": \"Posts about open source software\"\n#    },\n# }\n\n# If you do not want to display a tag publicly, you can mark it as hidden.\n# The tag will not be displayed on the tag list page, the tag cloud and posts.\n# Tag pages will still be generated.\nHIDDEN_TAGS = ['mathjax']\n\n# Only include tags on the tag list/overview page if there are at least\n# TAGLIST_MINIMUM_POSTS number of posts or more with every tag. Every tag\n# page is still generated, linked from posts, and included in the sitemap.\n# However, more obscure tags can be hidden from the tag index page.\n# TAGLIST_MINIMUM_POSTS = 1\n\n# Final locations are:\n# output / TRANSLATION[lang] / CATEGORY_PATH / index.html (list of categories)\n# output / TRANSLATION[lang] / CATEGORY_PATH / CATEGORY_PREFIX category.html (list of posts for a category)\n# output / TRANSLATION[lang] / CATEGORY_PATH / CATEGORY_PREFIX category.xml (RSS feed for a category)\n# (translatable)\n# CATEGORY_PATH = \"categories\"\n# CATEGORY_PREFIX = \"cat_\"\n\n# By default, the list of categories is stored in\n#     output / TRANSLATION[lang] / CATEGORY_PATH / index.html\n# (see explanation for CATEGORY_PATH). This location can be changed to\n#     output / TRANSLATION[lang] / CATEGORIES_INDEX_PATH\n# with an arbitrary relative path CATEGORIES_INDEX_PATH.\n# (translatable)\n# CATEGORIES_INDEX_PATH = \"categories.html\"\n\n# If CATEGORY_ALLOW_HIERARCHIES is set to True, categories can be organized in\n# hierarchies. For a post, the whole path in the hierarchy must be specified,\n# using a forward slash ('/') to separate paths. Use a backslash ('\\') to escape\n# a forward slash or a backslash (i.e. '\\//\\\\' is a path specifying the\n# subcategory called '\\' of the top-level category called '/').\nCATEGORY_ALLOW_HIERARCHIES = False\n# If CATEGORY_OUTPUT_FLAT_HIERARCHY is set to True, the output written to output\n# contains only the name of the leaf category and not the whole path.\nCATEGORY_OUTPUT_FLAT_HIERARCHY = False\n\n# If CATEGORY_PAGES_ARE_INDEXES is set to True, each category's page will contain\n# the posts themselves. If set to False, it will be just a list of links.\n# CATEGORY_PAGES_ARE_INDEXES = False\n\n# Set descriptions for category pages to make them more interesting. The\n# default is no description. The value is used in the meta description\n# and displayed underneath the category list or index page’s title.\n# CATEGORY_PAGES_DESCRIPTIONS = {\n#    DEFAULT_LANG: {\n#        \"blogging\": \"Meta-blog posts about blogging about blogging.\",\n#        \"open source\": \"My contributions to my many, varied, ever-changing, and eternal libre software projects.\"\n#    },\n# }\n\n# Set special titles for category pages. The default is \"Posts about CATEGORY\".\n# CATEGORY_PAGES_TITLES = {\n#    DEFAULT_LANG: {\n#        \"blogging\": \"Meta-posts about blogging\",\n#        \"open source\": \"Posts about open source software\"\n#    },\n# }\n\n# If you do not want to display a category publicly, you can mark it as hidden.\n# The category will not be displayed on the category list page.\n# Category pages will still be generated.\nHIDDEN_CATEGORIES = []\n\n# If ENABLE_AUTHOR_PAGES is set to True and there is more than one\n# author, author pages are generated.\n# ENABLE_AUTHOR_PAGES = True\n\n# Path to author pages. Final locations are:\n# output / TRANSLATION[lang] / AUTHOR_PATH / index.html (list of authors)\n# output / TRANSLATION[lang] / AUTHOR_PATH / author.html (list of posts by an author)\n# output / TRANSLATION[lang] / AUTHOR_PATH / author.xml (RSS feed for an author)\n# (translatable)\nAUTHOR_PATH = \"blog/authors\"\n\n# If AUTHOR_PAGES_ARE_INDEXES is set to True, each author's page will contain\n# the posts themselves. If set to False, it will be just a list of links.\n# AUTHOR_PAGES_ARE_INDEXES = False\n\n# Set descriptions for author pages to make them more interesting. The\n# default is no description. The value is used in the meta description\n# and displayed underneath the author list or index page’s title.\n# AUTHOR_PAGES_DESCRIPTIONS = {\n#    DEFAULT_LANG: {\n#        \"Juanjo Conti\": \"Python coder and writer.\",\n#        \"Roberto Alsina\": \"Nikola father.\"\n#    },\n# }\n\n\n# If you do not want to display an author publicly, you can mark it as hidden.\n# The author will not be displayed on the author list page and posts.\n# Tag pages will still be generated.\nHIDDEN_AUTHORS = ['Guest']\n\n# Final location for the main blog page and sibling paginated pages is\n# output / TRANSLATION[lang] / INDEX_PATH / index-*.html\n# (translatable)\nINDEX_PATH = \"blog\"\n\n# Optional HTML that displayed on “main” blog index.html files.\n# May be used for a greeting. (translatable)\nFRONT_INDEX_HEADER = {\n    DEFAULT_LANG: ''\n}\n\n# Create per-month archives instead of per-year\n# CREATE_MONTHLY_ARCHIVE = False\n# Create one large archive instead of per-year\n# CREATE_SINGLE_ARCHIVE = False\n# Create year, month, and day archives each with a (long) list of posts\n# (overrides both CREATE_MONTHLY_ARCHIVE and CREATE_SINGLE_ARCHIVE)\n# CREATE_FULL_ARCHIVES = False\n# If monthly archives or full archives are created, adds also one archive per day\n# CREATE_DAILY_ARCHIVE = False\n# Create previous, up, next navigation links for archives\n# CREATE_ARCHIVE_NAVIGATION = False\n# Final locations for the archives are:\n# output / TRANSLATION[lang] / ARCHIVE_PATH / ARCHIVE_FILENAME\n# output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / index.html\n# output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / MONTH / index.html\n# output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / MONTH / DAY / index.html\n# ARCHIVE_PATH = \"\"\n# ARCHIVE_FILENAME = \"archive.html\"\n\n# If ARCHIVES_ARE_INDEXES is set to True, each archive page which contains a list\n# of posts will contain the posts themselves. If set to False, it will be just a\n# list of links.\n# ARCHIVES_ARE_INDEXES = False\n\n# URLs to other posts/pages can take 3 forms:\n# rel_path: a relative URL to the current page/post (default)\n# full_path: a URL with the full path from the root\n# absolute: a complete URL (that includes the SITE_URL)\n# URL_TYPE = 'rel_path'\n\n# If USE_BASE_TAG is True, then all HTML files will include\n# something like <base href=http://foo.var.com/baz/bat> to help\n# the browser resolve relative links.\n# Most people don’t need this tag; major websites don’t use it. Use\n# only if you know what you’re doing. If this is True, your website\n# will not be fully usable by manually opening .html files in your web\n# browser (`nikola serve` or `nikola auto` is mandatory). Also, if you\n# have mirrors of your site, they will point to SITE_URL everywhere.\nUSE_BASE_TAG = False\n\n# Final location for the blog main RSS feed is:\n# output / TRANSLATION[lang] / RSS_PATH / rss.xml\n# (translatable)\n# RSS_PATH = \"\"\n\n# Slug the Tag URL. Easier for users to type, special characters are\n# often removed or replaced as well.\n# SLUG_TAG_PATH = True\n\n# Slug the Author URL. Easier for users to type, special characters are\n# often removed or replaced as well.\n# SLUG_AUTHOR_PATH = True\n\n# A list of redirection tuples, [(\"foo/from.html\", \"/bar/to.html\")].\n#\n# A HTML file will be created in output/foo/from.html that redirects\n# to the \"/bar/to.html\" URL. notice that the \"from\" side MUST be a\n# relative URL.\n#\n# If you don't need any of these, just set to []\nREDIRECTIONS = [ \\\n    [\"2009/12/version-0-2-released/index.html\", \"/blog/2009/12/version-0-2-released\"], \\\n    [\"2009/12/version-0-3-released/index.html\", \"/blog/2009/12/version-0-3-released\"], \\\n    [\"2010/01/mailing-list-irc/index.html\", \"/blog/2010/01/mailing-list-irc\"], \\\n    [\"2010/01/version-0-4-1-released/index.html\", \"/blog/2010/01/version-0-4-1-released\"], \\\n    [\"2010/01/version-0-4-released/index.html\", \"/blog/2010/01/version-0-4-released\"], \\\n    [\"2010/02/version-0-4-2-released/index.html\", \"/blog/2010/02/version-0-4-2-released\"], \\\n    [\"2010/03/google-powermeter/index.html\", \"/blog/2010/03/google-powermeter\"], \\\n    [\"2010/03/upgrading-to-0-5-1/index.html\", \"/blog/2010/03/upgrading-to-0-5-1\"], \\\n    [\"2010/03/version-0-5-1-released/index.html\", \"/blog/2010/03/version-0-5-1-released\"], \\\n    [\"2010/03/version-0-5-2-released/index.html\", \"/blog/2010/03/version-0-5-2-released\"], \\\n    [\"2010/03/version-0-5-3-released/index.html\", \"/blog/2010/03/version-0-5-3-released\"], \\\n    [\"2010/03/version-0-5-4-released/index.html\", \"/blog/2010/03/version-0-5-4-released\"], \\\n    [\"2010/04/help-wanted-rpm-packaging/index.html\", \"/blog/2010/04/help-wanted-rpm-packaging\"], \\\n    [\"2010/04/mind-control-mqtt/index.html\", \"/blog/2010/04/mind-control-mqtt\"], \\\n    [\"2010/04/oggcamp/index.html\", \"/blog/2010/04/oggcamp\"], \\\n    [\"2010/05/fedora-packages-available/index.html\", \"/blog/2010/05/fedora-packages-available\"], \\\n    [\"2010/05/gentoo-ebuilds-available/index.html\", \"/blog/2010/05/gentoo-ebuilds-available\"], \\\n    [\"2010/05/mosquitto-org/index.html\", \"/blog/2010/05/mosquitto-org\"], \\\n    [\"2010/05/mqtt-push-on-android/index.html\", \"/blog/2010/05/mqtt-push-on-android\"], \\\n    [\"2010/05/mqtt-wiki/index.html\", \"/blog/2010/05/mqtt-wiki\"], \\\n    [\"2010/05/version-0-6-1-released/index.html\", \"/blog/2010/05/version-0-6-1-released\"], \\\n    [\"2010/05/version-0-6-released/index.html\", \"/blog/2010/05/version-0-6-released\"], \\\n    [\"2010/06/automation-has-the-oven-warmed-up-yet/index.html\", \"/blog/2010/06/automation-has-the-oven-warmed-up-yet\"], \\\n    [\"2010/06/google-powermeter-step-by-step/index.html\", \"/blog/2010/06/google-powermeter-step-by-step\"], \\\n    [\"2010/06/mosquitto-0-7rc1/index.html\", \"/blog/2010/06/mosquitto-0-7rc1\"], \\\n    [\"2010/06/version-0-7-released/index.html\", \"/blog/2010/06/version-0-7-released\"], \\\n    [\"2010/07/mosquitto-on-opensuse-11-3/index.html\", \"/blog/2010/07/mosquitto-on-opensuse-11-3\"], \\\n    [\"2010/07/mqtt-client-library/index.html\", \"/blog/2010/07/mqtt-client-library\"], \\\n    [\"2010/08/compiling-mosquitto-on-mac-os-x/index.html\", \"/blog/2010/08/compiling-mosquitto-on-mac-os-x\"], \\\n    [\"2010/08/mosquitto-running-on-mac-os-x/index.html\", \"/blog/2010/08/mosquitto-running-on-mac-os-x\"], \\\n    [\"2010/08/mqtt-v3-1/index.html\", \"/blog/2010/08/mqtt-v3-1\"], \\\n    [\"2010/08/version-0-8-1-released/index.html\", \"/blog/2010/08/version-0-8-1-released\"], \\\n    [\"2010/08/version-0-8-2/index.html\", \"/blog/2010/08/version-0-8-2\"], \\\n    [\"2010/08/version-0-8-released/index.html\", \"/blog/2010/08/version-0-8-released\"], \\\n    [\"2010/09/debian-packages/index.html\", \"/blog/2010/09/debian-packages\"], \\\n    [\"2010/09/mqtt-with-php/index.html\", \"/blog/2010/09/mqtt-with-php\"], \\\n    [\"2010/10/man-page-translations/index.html\", \"/blog/2010/10/man-page-translations\"], \\\n    [\"2010/10/one-year-old/index.html\", \"/blog/2010/10/one-year-old\"], \\\n    [\"2010/10/version-0-8-3-released/index.html\", \"/blog/2010/10/version-0-8-3-released\"], \\\n    [\"2010/11/distro-packaging/index.html\", \"/blog/2010/11/distro-packaging\"], \\\n    [\"2010/11/mosquitto-0-9test2/index.html\", \"/blog/2010/11/mosquitto-0-9test2\"], \\\n    [\"2010/11/version-0-9-released/index.html\", \"/blog/2010/11/version-0-9-released\"], \\\n    [\"2010/12/version-0-9-1-released/index.html\", \"/blog/2010/12/version-0-9-1-released\"], \\\n    [\"2011/01/mosquitto-for-slackware/index.html\", \"/blog/2011/01/mosquitto-for-slackware\"], \\\n    [\"2011/01/mqtt-news/index.html\", \"/blog/2011/01/mqtt-news\"], \\\n    [\"2011/02/lightweight-messaging-and-linux/index.html\", \"/blog/2011/02/lightweight-messaging-and-linux\"], \\\n    [\"2011/02/mosquitto-on-maemo/index.html\", \"/blog/2011/02/mosquitto-on-maemo\"], \\\n    [\"2011/02/mqtt-on-android/index.html\", \"/blog/2011/02/mqtt-on-android\"], \\\n    [\"2011/02/version-0-9-2-released/index.html\", \"/blog/2011/02/version-0-9-2-released\"], \\\n    [\"2011/03/api-documentation/index.html\", \"/blog/2011/03/api-documentation\"], \\\n    [\"2011/03/mosquitto-in-mac-homebrew/index.html\", \"/blog/2011/03/mosquitto-in-mac-homebrew\"], \\\n    [\"2011/03/version-0-9-3-released/index.html\", \"/blog/2011/03/version-0-9-3-released\"], \\\n    [\"2011/04/version-0-10-released/index.html\", \"/blog/2011/04/version-0-10-released\"], \\\n    [\"2011/05/mqtt-ontology/index.html\", \"/blog/2011/05/mqtt-ontology\"], \\\n    [\"2011/05/version-0-10-1-released/index.html\", \"/blog/2011/05/version-0-10-1-released\"], \\\n    [\"2011/06/nanode-a-cheap-networked-arduino-clone/index.html\", \"/blog/2011/06/nanode-a-cheap-networked-arduino-clone\"], \\\n    [\"2011/06/version-0-10-2-released/index.html\", \"/blog/2011/06/version-0-10-2-released\"], \\\n    [\"2011/06/version-0-11-1-released/index.html\", \"/blog/2011/06/version-0-11-1-released\"], \\\n    [\"2011/06/version-0-11-2-released/index.html\", \"/blog/2011/06/version-0-11-2-released\"], \\\n    [\"2011/06/version-0-11-released/index.html\", \"/blog/2011/06/version-0-11-released\"], \\\n    [\"2011/07/debian-and-ubuntu-packaging/index.html\", \"/blog/2011/07/debian-and-ubuntu-packaging\"], \\\n    [\"2011/07/lua-mqtt-client/index.html\", \"/blog/2011/07/lua-mqtt-client\"], \\\n    [\"2011/07/mosquitto-on-qnx/index.html\", \"/blog/2011/07/mosquitto-on-qnx\"], \\\n    [\"2011/07/version-0-11-3-released/index.html\", \"/blog/2011/07/version-0-11-3-released\"], \\\n    [\"2011/07/version-0-12-released/index.html\", \"/blog/2011/07/version-0-12-released\"], \\\n    [\"2011/07/wireshark-mqtt-decoder/index.html\", \"/blog/2011/07/wireshark-mqtt-decoder\"], \\\n    [\"2011/08/arch-linux-package/index.html\", \"/blog/2011/08/arch-linux-package\"], \\\n    [\"2011/08/facebook-using-mqtt/index.html\", \"/blog/2011/08/facebook-using-mqtt\"], \\\n    [\"2011/08/mosquitto-on-openwrt/index.html\", \"/blog/2011/08/mosquitto-on-openwrt\"], \\\n    [\"2011/08/mqtt-standardisation/index.html\", \"/blog/2011/08/mqtt-standardisation\"], \\\n    [\"2011/09/version-0-13-released/index.html\", \"/blog/2011/09/version-0-13-released\"], \\\n    [\"2011/10/mqtt-power-usage-on-android/index.html\", \"/blog/2011/10/mqtt-power-usage-on-android\"], \\\n    [\"2011/10/two/index.html\", \"/blog/2011/10/two\"], \\\n    [\"2011/11/android-mqtt-example-project/index.html\", \"/blog/2011/11/android-mqtt-example-project\"], \\\n    [\"2011/11/ibm-java-and-c-clients-to-be-open-source/index.html\", \"/blog/2011/11/ibm-java-and-c-clients-to-be-open-source\"], \\\n    [\"2011/11/new-linux-repositories/index.html\", \"/blog/2011/11/new-linux-repositories\"], \\\n    [\"2011/11/version-0-14-1-released/index.html\", \"/blog/2011/11/version-0-14-1-released\"], \\\n    [\"2011/11/version-0-14-2-released/index.html\", \"/blog/2011/11/version-0-14-2-released\"], \\\n    [\"2011/11/version-0-14-released/index.html\", \"/blog/2011/11/version-0-14-released\"], \\\n    [\"2011/12/mqtt-on-nanode/index.html\", \"/blog/2011/12/mqtt-on-nanode\"], \\\n    [\"2011/12/version-0-14-3-released/index.html\", \"/blog/2011/12/version-0-14-3-released\"], \\\n    [\"2012/01/challenge-web-based-mqtt-graphing/index.html\", \"/blog/2012/01/challenge-web-based-mqtt-graphing\"], \\\n    [\"2012/01/do-you-use-mqtt/index.html\", \"/blog/2012/01/do-you-use-mqtt\"], \\\n    [\"2012/01/mosquitto-test-server/index.html\", \"/blog/2012/01/mosquitto-test-server\"], \\\n    [\"2012/01/version-0-14-4-released/index.html\", \"/blog/2012/01/version-0-14-4-released\"], \\\n    [\"2012/02/mqtt2pachube/index.html\", \"/blog/2012/02/mqtt2pachube\"], \\\n    [\"2012/02/version-0-15-released/index.html\", \"/blog/2012/02/version-0-15-released\"], \\\n    [\"2012/03/quick-start-guide-for-mqtt-with-pachube/index.html\", \"/blog/2012/03/quick-start-guide-for-mqtt-with-pachube\"], \\\n    [\"2012/03/upcoming-incompatible-library-changes/index.html\", \"/blog/2012/03/upcoming-incompatible-library-changes\"], \\\n    [\"2012/05/python-client-module-available-for-testing/index.html\", \"/blog/2012/05/python-client-module-available-for-testing\"], \\\n    [\"2012/06/ipv6-on-test-server/index.html\", \"/blog/2012/06/ipv6-on-test-server\"], \\\n    [\"2012/06/ssl-support-on-test-server/index.html\", \"/blog/2012/06/ssl-support-on-test-server\"], \\\n    [\"2012/07/upcoming-release/index.html\", \"/blog/2012/07/upcoming-release\"], \\\n    [\"2012/08/baby/index.html\", \"/blog/2012/08/baby\"], \\\n    [\"2012/08/bugfix-coming-soon/index.html\", \"/blog/2012/08/bugfix-coming-soon\"], \\\n    [\"2012/08/version-1-0-1-released/index.html\", \"/blog/2012/08/version-1-0-1-released\"], \\\n    [\"2012/08/version-1-0-2-released/index.html\", \"/blog/2012/08/version-1-0-2-released\"], \\\n    [\"2012/08/version-1-0-released/index.html\", \"/blog/2012/08/version-1-0-released\"], \\\n    [\"2012/09/updating-password-files/index.html\", \"/blog/2012/09/updating-password-files\"], \\\n    [\"2012/09/version-1-0-3-released/index.html\", \"/blog/2012/09/version-1-0-3-released\"], \\\n    [\"2012/10/version-1-0-4-released/index.html\", \"/blog/2012/10/version-1-0-4-released\"], \\\n    [\"2012/11/making-mosquitto-packages-for-debian-yourself/index.html\", \"/blog/2012/11/making-mosquitto-packages-for-debian-yourself\"], \\\n    [\"2012/11/version-1-0-5-released/index.html\", \"/blog/2012/11/version-1-0-5-released\"], \\\n    [\"2012/12/libmosquitto-go-bindings/index.html\", \"/blog/2012/12/libmosquitto-go-bindings\"], \\\n    [\"2012/12/version-1-1-released/index.html\", \"/blog/2012/12/version-1-1-released\"], \\\n    [\"2013/01/mosquitto-debian-repository/index.html\", \"/blog/2013/01/mosquitto-debian-repository\"], \\\n    [\"2013/01/version-1-1-1-released/index.html\", \"/blog/2013/01/version-1-1-1-released\"], \\\n    [\"2013/01/version-1-1-2-released/index.html\", \"/blog/2013/01/version-1-1-2-released\"], \\\n    [\"2013/02/mqtt-standardisation-oasis-call-for-participation/index.html\", \"/blog/2013/02/mqtt-standardisation-oasis-call-for-participation\"], \\\n    [\"2013/02/version-1-1-3-released/index.html\", \"/blog/2013/02/version-1-1-3-released\"], \\\n    [\"2013/04/some-interesting-mqtt-things/index.html\", \"/blog/2013/04/some-interesting-mqtt-things\"], \\\n    [\"2013/05/mosquitto-javascript-client-deprecated/index.html\", \"/blog/2013/05/mosquitto-javascript-client-deprecated\"], \\\n    [\"2013/07/authentication-plugins/index.html\", \"/blog/2013/07/authentication-plugins\"], \\\n    [\"2013/07/version-1-2-near-complete/index.html\", \"/blog/2013/07/version-1-2-near-complete\"], \\\n    [\"2013/08/mosquitto-on-fedora/index.html\", \"/blog/2013/08/mosquitto-on-fedora\"], \\\n    [\"2013/08/mqtt-watchdir/index.html\", \"/blog/2013/08/mqtt-watchdir\"], \\\n    [\"2013/08/version-1-2-released/index.html\", \"/blog/2013/08/version-1-2-released\"], \\\n    [\"2013/09/version-1-2-1-released/index.html\", \"/blog/2013/09/version-1-2-1-released\"], \\\n    [\"2013/10/version-1-2-2-released/index.html\", \"/blog/2013/10/version-1-2-2-released\"], \\\n    [\"2013/12/paho-mqtt-python-client/index.html\", \"/blog/2013/12/paho-mqtt-python-client\"], \\\n    [\"2013/12/version-1-2-3-released/index.html\", \"/blog/2013/12/version-1-2-3-released\"], \\\n    [\"2014/03/version-1-3-1-released/index.html\", \"/blog/2014/03/version-1-3-1-released\"], \\\n    [\"2014/03/version-1-3-released/index.html\", \"/blog/2014/03/version-1-3-released\"], \\\n    [\"2014/05/new-arrival/index.html\", \"/blog/2014/05/new-arrival\"], \\\n    [\"2014/07/version-1-3-2-released/index.html\", \"/blog/2014/07/version-1-3-2-released\"], \\\n    [\"2014/08/version-1-3-3-released/index.html\", \"/blog/2014/08/version-1-3-3-released\"], \\\n    [\"2014/08/version-1-3-4-released/index.html\", \"/blog/2014/08/version-1-3-4-released\"], \\\n    [\"2014/10/mosquitto-and-poodle/index.html\", \"/blog/2014/10/mosquitto-and-poodle\"], \\\n    [\"2014/10/unintended-change-of-behaviour-in-1-3-4/index.html\", \"/blog/2014/10/unintended-change-of-behaviour-in-1-3-4\"], \\\n    [\"2014/10/version-1-3-5-released/index.html\", \"/blog/2014/10/version-1-3-5-released\"], \\\n    [\"2015/01/seeking-sponsorship/index.html\", \"/blog/2015/01/seeking-sponsorship\"], \\\n    [\"2015/02/version-1-4-released/index.html\", \"/blog/2015/02/version-1-4-released\"], \\\n    [\"2015/04/version-1-4-1-released/index.html\", \"/blog/2015/04/version-1-4-1-released\"], \\\n    [\"2015/05/mosquitto-and-current-unreleased-libwebsockets-branch/index.html\", \"/blog/2015/05/mosquitto-and-current-unreleased-libwebsockets-branch\"], \\\n    [\"2015/05/version-1-4-2-released/index.html\", \"/blog/2015/05/version-1-4-2-released\"], \\\n    [\"2015/08/version-1-4-3-released/index.html\", \"/blog/2015/08/version-1-4-3-released\"], \\\n    [\"2015/09/version-1-4-4-released/index.html\", \"/blog/2015/09/version-1-4-4-released\"], \\\n    [\"2015/11/version-1-4-5-released/index.html\", \"/blog/2015/11/version-1-4-5-released\"], \\\n    [\"2015/12/using-lets-encrypt-certificates-with-mosquitto/index.html\", \"/blog/2015/12/using-lets-encrypt-certificates-with-mosquitto\"], \\\n    [\"2015/12/version-1-4-7-released/index.html\", \"/blog/2015/12/version-1-4-7-released\"], \\\n    [\"2016/01/test6-mosquitto-org/index.html\", \"/blog/2016/01/test6-mosquitto-org\"], \\\n    [\"2016/02/version-1-4-8-released/index.html\", \"/blog/2016/02/version-1-4-8-released\"], \\\n    [\"2016/03/logo-contest-results-for-shortlisting/index.html\", \"/blog/2016/03/logo-contest-results-for-shortlisting\"], \\\n    [\"2016/03/logo-contest/index.html\", \"/blog/2016/03/logo-contest\"], \\\n    [\"2016/03/repository-moved-to-github/index.html\", \"/blog/2016/03/repository-moved-to-github\"], \\\n    [\"2016/05/stickers/index.html\", \"/blog/2016/05/stickers\"], \\\n    [\"2016/06/version-1-4-9-released/index.html\", \"/blog/2016/06/version-1-4-9-released\"], \\\n    [\"2016/08/mqtt-v5-draft-features/index.html\", \"/blog/2016/08/mqtt-v5-draft-features\"], \\\n    [\"2016/08/version-1-4-10-released/index.html\", \"/blog/2016/08/version-1-4-10-released\"], \\\n    [\"2016/12/pre-christmas-update/index.html\", \"/blog/2016/12/pre-christmas-update\"], \\\n    [\"2017/02/version-1-4-11-released/index.html\", \"/blog/2017/02/version-1-4-11-released\"], \\\n    [\"2017/03/for-the-final-time/index.html\", \"/blog/2017/03/for-the-final-time\"], \\\n    [\"2017/05/security-advisory-cve-2017-7650/index.html\", \"/blog/2017/05/security-advisory-cve-2017-7650\"], \\\n    [\"2017/06/citing-eclipse-mosquitto/index.html\", \"/blog/2017/06/citing-eclipse-mosquitto\"], \\\n    [\"2017/06/security-advisory-cve-2017-9868/index.html\", \"/blog/2017/06/security-advisory-cve-2017-9868\"], \\\n    [\"2017/07/version-1-4-13-released/index.html\", \"/blog/2017/07/version-1-4-13-released\"], \\\n    [\"2017/07/version-1-4-14-released/index.html\", \"/blog/2017/07/version-1-4-14-released\"], \\\n    [\"2018/01/mosquitto-debian-repo-key-updated/index.html\", \"/blog/2018/01/mosquitto-debian-repo-key-updated\"] \\\n    ]\n\n\n## Presets of commands to execute to deploy. Can be anything, for\n# example, you may use rsync:\n# \"rsync -rav --delete output/ joe@my.site:/srv/www/site\"\n# And then do a backup, or run `nikola ping` from the `ping`\n# plugin (`nikola plugin -i ping`).  Or run `nikola check -l`.\n# You may also want to use github_deploy (see below).\n# You can define multiple presets and specify them as arguments\n# to `nikola deploy`.  If no arguments are specified, a preset\n# named `default` will be executed.  You can use as many presets\n# in a `nikola deploy` command as you like.\n# DEPLOY_COMMANDS = {\n#     'default': [\n#         \"rsync -rav --delete output/ joe@my.site:/srv/www/site\",\n#     ]\n# }\n\n# github_deploy configuration\n# For more details, read the manual:\n# https://getnikola.com/handbook.html#deploying-to-github\n# You will need to configure the deployment branch on GitHub.\nGITHUB_SOURCE_BRANCH = 'src'\nGITHUB_DEPLOY_BRANCH = 'master'\n\n# The name of the remote where you wish to push to, using github_deploy.\nGITHUB_REMOTE_NAME = 'origin'\n\n# Whether or not github_deploy should commit to the source branch automatically\n# before deploying.\nGITHUB_COMMIT_SOURCE = True\n\n# Where the output site should be located\n# If you don't use an absolute path, it will be considered as relative\n# to the location of conf.py\nOUTPUT_FOLDER = '/home/mosqorg/site/mosquitto.org'\n\n# where the \"cache\" of partial generated content should be located\n# default: 'cache'\n# CACHE_FOLDER = 'cache'\n\n# Filters to apply to the output.\n# A directory where the keys are either: a file extensions, or\n# a tuple of file extensions.\n#\n# And the value is a list of commands to be applied in order.\n#\n# Each command must be either:\n#\n# A string containing a '%s' which will\n# be replaced with a filename. The command *must* produce output\n# in place.\n#\n# Or:\n#\n# A python callable, which will be called with the filename as\n# argument.\n#\n# By default, only .php files uses filters to inject PHP into\n# Nikola’s templates. All other filters must be enabled through FILTERS.\n#\n# Many filters are shipped with Nikola. A list is available in the manual:\n# <https://getnikola.com/handbook.html#post-processing-filters>\n#\n# from nikola import filters\n# FILTERS = {\n#    \".html\": [filters.typogrify],\n#    \".js\": [filters.closure_compiler],\n#    \".jpg\": [\"jpegoptim --strip-all -m75 -v %s\"],\n# }\n\n# Executable for the \"yui_compressor\" filter (defaults to 'yui-compressor').\n# YUI_COMPRESSOR_EXECUTABLE = 'yui-compressor'\n\n# Executable for the \"closure_compiler\" filter (defaults to 'closure-compiler').\n# CLOSURE_COMPILER_EXECUTABLE = 'closure-compiler'\n\n# Executable for the \"optipng\" filter (defaults to 'optipng').\n# OPTIPNG_EXECUTABLE = 'optipng'\n\n# Executable for the \"jpegoptim\" filter (defaults to 'jpegoptim').\n# JPEGOPTIM_EXECUTABLE = 'jpegoptim'\n\n# Executable for the \"html_tidy_withconfig\", \"html_tidy_nowrap\",\n# \"html_tidy_wrap\", \"html_tidy_wrap_attr\" and \"html_tidy_mini\" filters\n# (defaults to 'tidy5').\n# HTML_TIDY_EXECUTABLE = 'tidy5'\n\n\n\n# Expert setting! Create a gzipped copy of each generated file. Cheap server-\n# side optimization for very high traffic sites or low memory servers.\n# GZIP_FILES = False\n# File extensions that will be compressed\n# GZIP_EXTENSIONS = ('.txt', '.htm', '.html', '.css', '.js', '.json', '.atom', '.xml')\n# Use an external gzip command? None means no.\n# Example: GZIP_COMMAND = \"pigz -k {filename}\"\n# GZIP_COMMAND = None\n# Make sure the server does not return a \"Accept-Ranges: bytes\" header for\n# files compressed by this option! OR make sure that a ranged request does not\n# return partial content of another representation for these resources. Do not\n# use this feature if you do not understand what this means.\n\n# Compiler to process LESS files.\n# LESS_COMPILER = 'lessc'\n\n# A list of options to pass to the LESS compiler.\n# Final command is: LESS_COMPILER LESS_OPTIONS file.less\n# LESS_OPTIONS = []\n\n# Compiler to process Sass files.\n# SASS_COMPILER = 'sass'\n\n# A list of options to pass to the Sass compiler.\n# Final command is: SASS_COMPILER SASS_OPTIONS file.s(a|c)ss\n# SASS_OPTIONS = []\n\n# #############################################################################\n# Image Gallery Options\n# #############################################################################\n\n# One or more folders containing galleries. The format is a dictionary of\n# {\"source\": \"relative_destination\"}, where galleries are looked for in\n# \"source/\" and the results will be located in\n# \"OUTPUT_PATH/relative_destination/gallery_name\"\n# Default is:\n# GALLERY_FOLDERS = {\"galleries\": \"galleries\"}\n# More gallery options:\n# THUMBNAIL_SIZE = 180\n# MAX_IMAGE_SIZE = 1280\n# USE_FILENAME_AS_TITLE = True\n# EXTRA_IMAGE_EXTENSIONS = []\n#\n# If set to False, it will sort by filename instead. Defaults to True\n# GALLERY_SORT_BY_DATE = True\n\n# If set to True, EXIF data will be copied when an image is thumbnailed or\n# resized. (See also EXIF_WHITELIST)\n# PRESERVE_EXIF_DATA = False\n\n# If you have enabled PRESERVE_EXIF_DATA, this option lets you choose EXIF\n# fields you want to keep in images. (See also PRESERVE_EXIF_DATA)\n#\n# For a full list of field names, please see here:\n# http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf\n#\n# This is a dictionary of lists. Each key in the dictionary is the\n# name of a IDF, and each list item is a field you want to preserve.\n# If you have a IDF with only a '*' item, *EVERY* item in it will be\n# preserved. If you don't want to preserve anything in a IDF, remove it\n# from the setting. By default, no EXIF information is kept.\n# Setting the whitelist to anything other than {} implies\n# PRESERVE_EXIF_DATA is set to True\n# To preserve ALL EXIF data, set EXIF_WHITELIST to {\"*\": \"*\"}\n\n# EXIF_WHITELIST = {}\n\n# Some examples of EXIF_WHITELIST settings:\n\n# Basic image information:\n# EXIF_WHITELIST['0th'] = [\n#    \"Orientation\",\n#    \"XResolution\",\n#    \"YResolution\",\n# ]\n\n# If you want to keep GPS data in the images:\n# EXIF_WHITELIST['GPS'] = [\"*\"]\n\n# Embedded thumbnail information:\n# EXIF_WHITELIST['1st'] = [\"*\"]\n\n# Folders containing images to be used in normal posts or pages.\n# IMAGE_FOLDERS is a dictionary of the form {\"source\": \"destination\"},\n# where \"source\" is the folder containing the images to be published, and\n# \"destination\" is the folder under OUTPUT_PATH containing the images copied\n# to the site. Thumbnail images will be created there as well.\n\n# To reference the images in your posts, include a leading slash in the path.\n# For example, if IMAGE_FOLDERS = {'images': 'images'}, write\n#\n#   .. image:: /images/tesla.jpg\n#\n# See the Nikola Handbook for details (in the “Embedding Images” and\n# “Thumbnails” sections)\n\n# Images will be scaled down according to IMAGE_THUMBNAIL_SIZE and MAX_IMAGE_SIZE\n# options, but will have to be referenced manually to be visible on the site\n# (the thumbnail has ``.thumbnail`` added before the file extension by default,\n# but a different naming template can be configured with IMAGE_THUMBNAIL_FORMAT).\n\nIMAGE_FOLDERS = {'images': 'images'}\n# IMAGE_THUMBNAIL_SIZE = 400\n# IMAGE_THUMBNAIL_FORMAT = '{name}.thumbnail{ext}'\n\n# #############################################################################\n# HTML fragments and diverse things that are used by the templates\n# #############################################################################\n\n# Data about post-per-page indexes.\n# INDEXES_PAGES defaults to ' old posts, page %d' or ' page %d' (translated),\n# depending on the value of INDEXES_PAGES_MAIN.\n#\n# (translatable) If the following is empty, defaults to BLOG_TITLE:\n# INDEXES_TITLE = \"\"\n#\n# (translatable) If the following is empty, defaults to ' [old posts,] page %d' (see above):\n# INDEXES_PAGES = \"\"\n#\n# If the following is True, INDEXES_PAGES is also displayed on the main (the\n# newest) index page (index.html):\n# INDEXES_PAGES_MAIN = False\n#\n# If the following is True, index-1.html has the oldest posts, index-2.html the\n# second-oldest posts, etc., and index.html has the newest posts. This ensures\n# that all posts on index-x.html will forever stay on that page, now matter how\n# many new posts are added.\n# If False, index-1.html has the second-newest posts, index-2.html the third-newest,\n# and index-n.html the oldest posts. When this is active, old posts can be moved\n# to other index pages when new posts are added.\n# INDEXES_STATIC = True\n#\n# (translatable) If PRETTY_URLS is set to True, this setting will be used to create\n# prettier URLs for index pages, such as page/2/index.html instead of index-2.html.\n# Valid values for this settings are:\n#   * False,\n#   * a list or tuple, specifying the path to be generated,\n#   * a dictionary mapping languages to lists or tuples.\n# Every list or tuple must consist of strings which are used to combine the path;\n# for example:\n#     ['page', '{number}', '{index_file}']\n# The replacements\n#     {number}     --> (logical) page number;\n#     {old_number} --> the page number inserted into index-n.html before (zero for\n#                      the main page);\n#     {index_file} --> value of option INDEX_FILE\n# are made.\n# Note that in case INDEXES_PAGES_MAIN is set to True, a redirection will be created\n# for the full URL with the page number of the main page to the normal (shorter) main\n# page URL.\n# INDEXES_PRETTY_PAGE_URL = False\n#\n# If the following is true, a page range navigation will be inserted to indices.\n# Please note that this will undo the effect of INDEXES_STATIC, as all index pages\n# must be recreated whenever the number of pages changes.\n# SHOW_INDEX_PAGE_NAVIGATION = False\n\n# If the following is True, a meta name=\"generator\" tag is added to pages. The\n# generator tag is used to specify the software used to generate the page\n# (it promotes Nikola).\n# META_GENERATOR_TAG = True\n\n# Color scheme to be used for code blocks. If your theme provides\n# \"assets/css/code.css\" this is ignored. Leave empty to disable.\n# Can be any of:\n# algol, algol_nu, autumn, borland, bw, colorful, default, emacs, friendly,\n# fruity, igor, lovelace, manni, monokai, murphy, native, paraiso-dark,\n# paraiso-light, pastie, perldoc, rrt, tango, trac, vim, vs, xcode\n# This list MAY be incomplete since pygments adds styles every now and then.\n# Check with list(pygments.styles.get_all_styles()) in an interpreter.\n# CODE_COLOR_SCHEME = 'default'\n\n# FAVICONS contains (name, file, size) tuples.\n# Used to create favicon link like this:\n# <link rel=\"name\" href=\"file\" sizes=\"size\"/>\nFAVICONS = (\n    (\"icon\", \"/favicon-16x16.png\", \"16x16\"),\n    (\"icon\", \"/favicon-32x32.png\", \"32x32\"),\n )\n\n# Show teasers (instead of full posts) in indexes? Defaults to False.\n# INDEX_TEASERS = False\n\n# HTML fragments with the Read more... links.\n# The following tags exist and are replaced for you:\n# {link}                        A link to the full post page.\n# {read_more}                   The string “Read more” in the current language.\n# {reading_time}                An estimate of how long it will take to read the post.\n# {remaining_reading_time}      An estimate of how long it will take to read the post, sans the teaser.\n# {min_remaining_read}          The string “{remaining_reading_time} min remaining to read” in the current language.\n# {paragraph_count}             The amount of paragraphs in the post.\n# {remaining_paragraph_count}   The amount of paragraphs in the post, sans the teaser.\n# {post_title}                  The title of the post.\n# {{                            A literal { (U+007B LEFT CURLY BRACKET)\n# }}                            A literal } (U+007D RIGHT CURLY BRACKET)\n\n# 'Read more...' for the index page, if INDEX_TEASERS is True (translatable)\nINDEX_READ_MORE_LINK = '<p class=\"more\"><a href=\"{link}\">{read_more}…</a></p>'\n# 'Read more...' for the feeds, if FEED_TEASERS is True (translatable)\nFEED_READ_MORE_LINK = '<p><a href=\"{link}\">{read_more}…</a> ({min_remaining_read})</p>'\n\n# Append a URL query to the FEED_READ_MORE_LINK in Atom and RSS feeds. Advanced\n# option used for traffic source tracking.\n# Minimum example for use with Piwik: \"pk_campaign=feed\"\n# The following tags exist and are replaced for you:\n# {feedRelUri}                  A relative link to the feed.\n# {feedFormat}                  The name of the syndication format.\n# Example using replacement for use with Google Analytics:\n# \"utm_source={feedRelUri}&utm_medium=nikola_feed&utm_campaign={feedFormat}_feed\"\nFEED_LINKS_APPEND_QUERY = False\n\n# A HTML fragment describing the license, for the sidebar.\n# (translatable)\nLICENSE = \"\"\n# I recommend using the Creative Commons' wizard:\n# https://creativecommons.org/choose/\n# LICENSE = \"\"\"\n# <a rel=\"license\" href=\"https://creativecommons.org/licenses/by-nc-sa/4.0/\">\n# <img alt=\"Creative Commons License BY-NC-SA\"\n# style=\"border-width:0; margin-bottom:12px;\"\n# src=\"https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png\"></a>\"\"\"\n\n# A small copyright notice for the page footer (in HTML).\n# (translatable)\nCONTENT_FOOTER = 'Contents &copy; {date}         <a href=\"mailto:{email}\">{author}</a> - Powered by         <a href=\"https://getnikola.com\" rel=\"nofollow\">Nikola</a>         {license}'\n\n# Things that will be passed to CONTENT_FOOTER.format().  This is done\n# for translatability, as dicts are not formattable.  Nikola will\n# intelligently format the setting properly.\n# The setting takes a dict. The keys are languages. The values are\n# tuples of tuples of positional arguments and dicts of keyword arguments\n# to format().  For example, {'en': (('Hello'), {'target': 'World'})}\n# results in CONTENT_FOOTER['en'].format('Hello', target='World').\n# WARNING: If you do not use multiple languages with CONTENT_FOOTER, this\n#          still needs to be a dict of this format.  (it can be empty if you\n#          do not need formatting)\n# (translatable)\nCONTENT_FOOTER_FORMATS = {\n    DEFAULT_LANG: (\n        (),\n        {\n            \"email\": BLOG_EMAIL,\n            \"author\": BLOG_AUTHOR,\n            \"date\": time.gmtime().tm_year,\n            \"license\": LICENSE\n        }\n    )\n}\n\n# A simple copyright tag for inclusion in RSS feeds that works just\n# like CONTENT_FOOTER and CONTENT_FOOTER_FORMATS\nRSS_COPYRIGHT = 'Contents © {date} <a href=\"mailto:{email}\">{author}</a> {license}'\nRSS_COPYRIGHT_PLAIN = 'Contents © {date} {author} {license}'\nRSS_COPYRIGHT_FORMATS = CONTENT_FOOTER_FORMATS\n\n# To use comments, you can choose between different third party comment\n# systems.  The following comment systems are supported by Nikola:\n#   disqus, facebook, googleplus, intensedebate, isso, livefyre, muut\n# You can leave this option blank to disable comments.\nCOMMENT_SYSTEM = \"\"\n# And you also need to add your COMMENT_SYSTEM_ID which\n# depends on what comment system you use. The default is\n# \"nikolademo\" which is a test account for Disqus. More information\n# is in the manual.\n#COMMENT_SYSTEM_ID = \"mosquitto\"\n\n# Enable annotations using annotateit.org?\n# If set to False, you can still enable them for individual posts and pages\n# setting the \"annotations\" metadata.\n# If set to True, you can disable them for individual posts and pages using\n# the \"noannotations\" metadata.\n# ANNOTATIONS = False\n\n# Create index.html for page folders?\n# WARNING: if a page would conflict with the index file (usually\n#          caused by setting slug to `index`), the PAGE_INDEX\n#          will not be generated for that directory.\n# PAGE_INDEX = False\n# Enable comments on pages (i.e. not posts)?\n# COMMENTS_IN_PAGES = False\n# Enable comments on picture gallery pages?\n# COMMENTS_IN_GALLERIES = False\n\n# What file should be used for directory indexes?\n# Defaults to index.html\n# Common other alternatives: default.html for IIS, index.php\n# INDEX_FILE = \"index.html\"\n\n# If a link ends in /index.html,  drop the index.html part.\n# http://mysite/foo/bar/index.html => http://mysite/foo/bar/\n# (Uses the INDEX_FILE setting, so if that is, say, default.html,\n# it will instead /foo/default.html => /foo)\n# (Note: This was briefly STRIP_INDEX_HTML in v 5.4.3 and 5.4.4)\nSTRIP_INDEXES = True\n\n# Should the sitemap list directories which only include other directories\n# and no files.\n# Default to True\n# If this is False\n# e.g. /2012 includes only /01, /02, /03, /04, ...: don't add it to the sitemap\n# if /2012 includes any files (including index.html)... add it to the sitemap\n# SITEMAP_INCLUDE_FILELESS_DIRS = True\n\n# List of files relative to the server root (!) that will be asked to be excluded\n# from indexing and other robotic spidering. * is supported. Will only be effective\n# if SITE_URL points to server root. The list is used to exclude resources from\n# /robots.txt and /sitemap.xml, and to inform search engines about /sitemapindex.xml.\n# ROBOTS_EXCLUSIONS = [\"/archive.html\", \"/category/*.html\"]\n\n# Instead of putting files in <slug>.html, put them in <slug>/index.html.\n# No web server configuration is required. Also enables STRIP_INDEXES.\n# This can be disabled on a per-page/post basis by adding\n#    .. pretty_url: False\n# to the metadata.\nPRETTY_URLS = True\n\n# If True, publish future dated posts right away instead of scheduling them.\n# Defaults to False.\n# FUTURE_IS_NOW = False\n\n# If True, future dated posts are allowed in deployed output\n# Only the individual posts are published/deployed; not in indexes/sitemap\n# Generally, you want FUTURE_IS_NOW and DEPLOY_FUTURE to be the same value.\n# DEPLOY_FUTURE = False\n# If False, draft posts will not be deployed\n# DEPLOY_DRAFTS = True\n\n# Allows scheduling of posts using the rule specified here (new_post -s)\n# Specify an iCal Recurrence Rule: http://www.kanzaki.com/docs/ical/rrule.html\n# SCHEDULE_RULE = ''\n# If True, use the scheduling rule to all posts by default\n# SCHEDULE_ALL = False\n\n# Do you want a add a Mathjax config file?\n# MATHJAX_CONFIG = \"\"\n\n# If you want support for the $.$ syntax (which may conflict with running\n# text!), just use this config:\n# MATHJAX_CONFIG = \"\"\"\n# <script type=\"text/x-mathjax-config\">\n# MathJax.Hub.Config({\n#     tex2jax: {\n#         inlineMath: [ ['$','$'], [\"\\\\\\(\",\"\\\\\\)\"] ],\n#         displayMath: [ ['$$','$$'], [\"\\\\\\[\",\"\\\\\\]\"] ],\n#         processEscapes: true\n#     },\n#     displayAlign: 'center', // Change this to 'left' if you want left-aligned equations.\n#     \"HTML-CSS\": {\n#         styles: {'.MathJax_Display': {\"margin\": 0}}\n#     }\n# });\n# </script>\n# \"\"\"\n\n# Want to use KaTeX instead of MathJax? While KaTeX may not support every\n# feature yet, it's faster and the output looks better.\n# USE_KATEX = False\n\n# KaTeX auto-render settings. If you want support for the $.$ syntax (which may\n# conflict with running text!), just use this config:\n# KATEX_AUTO_RENDER = \"\"\"\n# delimiters: [\n#     {left: \"$$\", right: \"$$\", display: true},\n#     {left: \"\\\\\\[\", right: \"\\\\\\]\", display: true},\n#     {left: \"$\", right: \"$\", display: false},\n#     {left: \"\\\\\\(\", right: \"\\\\\\)\", display: false}\n# ]\n# \"\"\"\n\n# Do you want to customize the nbconversion of your IPython notebook?\n# IPYNB_CONFIG = {}\n# With the following example configuration you can use a custom jinja template\n# called `toggle.tpl` which has to be located in your site/blog main folder:\n# IPYNB_CONFIG = {'Exporter':{'template_file': 'toggle'}}\n\n# What Markdown extensions to enable?\n# You will also get gist, nikola and podcast because those are\n# done in the code, hope you don't mind ;-)\n# Note: most Nikola-specific extensions are done via the Nikola plugin system,\n#       with the MarkdownExtension class and should not be added here.\n# The default is ['fenced_code', 'codehilite']\n#MARKDOWN_EXTENSIONS = ['fenced_code', 'codehilite', 'extra', 'toc']\n\nMARKDOWN_EXTENSIONS = ['markdown.extensions.fenced_code', 'markdown.extensions.codehilite', 'markdown.extensions.extra', 'markdown.extensions.toc']\n\n# Options to be passed to markdown extensions (See https://python-markdown.github.io/reference/)\n# Default is {} (no config at all)\nMARKDOWN_EXTENSION_CONFIGS = {\n    DEFAULT_LANG: {\n        'markdown.extensions.toc':{\n            'toc_depth':2\n        }\n    }\n}\n\n\n# Extra options to pass to the pandoc command.\n# by default, it's empty, is a list of strings, for example\n# ['-F', 'pandoc-citeproc', '--bibliography=/Users/foo/references.bib']\n# Pandoc does not demote headers by default.  To enable this, you can use, for example\n# ['--base-header-level=2']\n# PANDOC_OPTIONS = []\n\n# Social buttons. This is sample code for AddThis (which was the default for a\n# long time). Insert anything you want here, or even make it empty (which is\n# the default right now)\n# (translatable)\n# SOCIAL_BUTTONS_CODE = \"\"\"\n# <!-- Social buttons -->\n# <div id=\"addthisbox\" class=\"addthis_toolbox addthis_peekaboo_style addthis_default_style addthis_label_style addthis_32x32_style\">\n# <a class=\"addthis_button_more\">Share</a>\n# <ul><li><a class=\"addthis_button_facebook\"></a>\n# <li><a class=\"addthis_button_google_plusone_share\"></a>\n# <li><a class=\"addthis_button_linkedin\"></a>\n# <li><a class=\"addthis_button_twitter\"></a>\n# </ul>\n# </div>\n# <script src=\"https://s7.addthis.com/js/300/addthis_widget.js#pubid=ra-4f7088a56bb93798\"></script>\n# <!-- End of social buttons -->\n# \"\"\"\n\n# Show link to source for the posts?\n# Formerly known as HIDE_SOURCELINK (inverse)\nSHOW_SOURCELINK = False\n# Copy the source files for your pages?\n# Setting it to False implies SHOW_SOURCELINK = False\nCOPY_SOURCES = False\n\n# Modify the number of Post per Index Page\n# Defaults to 10\n# INDEX_DISPLAY_POST_COUNT = 10\n\n# By default, Nikola generates RSS files for the website and for tags, and\n# links to it.  Set this to False to disable everything RSS-related.\n# GENERATE_RSS = True\n\n# By default, Nikola does not generates Atom files for indexes and links to\n# them. Generate Atom for tags by setting TAG_PAGES_ARE_INDEXES to True.\n# Atom feeds are built based on INDEX_DISPLAY_POST_COUNT and not FEED_LENGTH\n# Switch between plain-text summaries and full HTML content using the\n# FEED_TEASER option. FEED_LINKS_APPEND_QUERY is also respected. Atom feeds\n# are generated even for old indexes and have pagination link relations\n# between each other. Old Atom feeds with no changes are marked as archived.\n# GENERATE_ATOM = False\n\n# Only include teasers in Atom and RSS feeds. Disabling include the full\n# content. Defaults to True.\n# FEED_TEASERS = True\n\n# Strip HTML from Atom and RSS feed summaries and content. Defaults to False.\n# FEED_PLAIN = False\n\n# Number of posts in Atom and RSS feeds.\n# FEED_LENGTH = 10\n\n# Include preview image as a <figure><img></figure> at the top of the entry.\n# Requires FEED_PLAIN = False. If the preview image is found in the content,\n# it will not be included again. Image will be included as-is, aim to optimize\n# the image source for Feedly, Apple News, Flipboard, and other popular clients.\n# FEED_PREVIEWIMAGE = True\n\n# RSS_LINK is a HTML fragment to link the RSS or Atom feeds. If set to None,\n# the base.tmpl will use the feed Nikola generates. However, you may want to\n# change it for a FeedBurner feed or something else.\n# RSS_LINK = None\n\n# A search form to search this site, for the sidebar. You can use a Google\n# custom search (https://www.google.com/cse/)\n# Or a DuckDuckGo search: https://duckduckgo.com/search_box.html\n# Default is no search form.\n# (translatable)\n# SEARCH_FORM = \"\"\n#\n# This search form works for any site and looks good in the \"site\" theme where\n# it appears on the navigation bar:\n#\n# SEARCH_FORM = \"\"\"\n# <!-- DuckDuckGo custom search -->\n# <form method=\"get\" id=\"search\" action=\"https://duckduckgo.com/\"\n#  class=\"navbar-form pull-left\">\n# <input type=\"hidden\" name=\"sites\" value=\"%s\">\n# <input type=\"hidden\" name=\"k8\" value=\"#444444\">\n# <input type=\"hidden\" name=\"k9\" value=\"#D51920\">\n# <input type=\"hidden\" name=\"kt\" value=\"h\">\n# <input type=\"text\" name=\"q\" maxlength=\"255\"\n#  placeholder=\"Search&hellip;\" class=\"span2\" style=\"margin-top: 4px;\">\n# <input type=\"submit\" value=\"DuckDuckGo Search\" style=\"visibility: hidden;\">\n# </form>\n# <!-- End of custom search -->\n# \"\"\" % SITE_URL\n#\n# If you prefer a Google search form, here's an example that should just work:\n# SEARCH_FORM = \"\"\"\n# <!-- Google custom search -->\n# <form method=\"get\" action=\"https://www.google.com/search\" class=\"navbar-form navbar-right\" role=\"search\">\n# <div class=\"form-group\">\n# <input type=\"text\" name=\"q\" class=\"form-control\" placeholder=\"Search\">\n# </div>\n# <button type=\"submit\" class=\"btn btn-primary\">\n#   <span class=\"glyphicon glyphicon-search\"></span>\n# </button>\n# <input type=\"hidden\" name=\"sitesearch\" value=\"%s\">\n# </form>\n# <!-- End of custom search -->\n# \"\"\" % SITE_URL\n\n# Use content distribution networks for jQuery, twitter-bootstrap css and js,\n# and html5shiv (for older versions of Internet Explorer)\n# If this is True, jQuery and html5shiv are served from the Google CDN and\n# Bootstrap is served from BootstrapCDN (provided by MaxCDN)\n# Set this to False if you want to host your site without requiring access to\n# external resources.\n# USE_CDN = False\n\n# Check for USE_CDN compatibility.\n# If you are using custom themes, have configured the CSS properly and are\n# receiving warnings about incompatibility but believe they are incorrect, you\n# can set this to False.\n# USE_CDN_WARNING = True\n\n# Extra things you want in the pages HEAD tag. This will be added right\n# before </head>\n# (translatable)\n# EXTRA_HEAD_DATA = \"\"\n# Google Analytics or whatever else you use. Added to the bottom of <body>\n# in the default template (base.tmpl).\n# (translatable)\n# BODY_END = \"\"\n\n# The possibility to extract metadata from the filename by using a\n# regular expression.\n# To make it work you need to name parts of your regular expression.\n# The following names will be used to extract metadata:\n# - title\n# - slug\n# - date\n# - tags\n# - link\n# - description\n#\n# An example re is the following:\n# '.*\\/(?P<date>\\d{4}-\\d{2}-\\d{2})-(?P<slug>.*)-(?P<title>.*)\\.rst'\n# (Note the '.*\\/' in the beginning -- matches source paths relative to conf.py)\n# FILE_METADATA_REGEXP = None\n\n# If you hate \"Filenames with Capital Letters and Spaces.md\", you should\n# set this to true.\nFILE_METADATA_UNSLUGIFY_TITLES = True\n\n# Additional metadata that is added to a post when creating a new_post\n# ADDITIONAL_METADATA = {}\n\n# Nikola supports Open Graph Protocol data for enhancing link sharing and\n# discoverability of your site on Facebook, Google+, and other services.\n# Open Graph is enabled by default.\n# USE_OPEN_GRAPH = True\n\n# Nikola supports Twitter Card summaries, but they are disabled by default.\n# They make it possible for you to attach media to Tweets that link\n# to your content.\n#\n# IMPORTANT:\n# Please note, that you need to opt-in for using Twitter Cards!\n# To do this please visit https://cards-dev.twitter.com/validator\n#\n# Uncomment and modify to following lines to match your accounts.\n# Images displayed come from the `previewimage` meta tag.\n# You can specify the card type by using the `card` parameter in TWITTER_CARD.\n# TWITTER_CARD = {\n#     # 'use_twitter_cards': True,  # enable Twitter Cards\n#     # 'card': 'summary',          # Card type, you can also use 'summary_large_image',\n#                                   # see https://dev.twitter.com/cards/types\n#     # 'site': '@website',         # twitter nick for the website\n#     # 'creator': '@username',     # Username for the content creator / author.\n# }\n\n# If webassets is installed, bundle JS and CSS into single files to make\n# site loading faster in a HTTP/1.1 environment but is not recommended for\n# HTTP/2.0 when caching is used. Defaults to True.\nUSE_BUNDLES = False\n\n# Plugins you don't want to use. Be careful :-)\n# DISABLED_PLUGINS = [\"render_galleries\"]\n\n# Special settings to disable only parts of the indexes plugin (to allow RSS\n# but no blog indexes, or to allow blog indexes and Atom but no site-wide RSS).\n# Use with care.\n# DISABLE_INDEXES_PLUGIN_INDEX_AND_ATOM_FEED = False\n# DISABLE_INDEXES_PLUGIN_RSS_FEED = False\n\n# Add the absolute paths to directories containing plugins to use them.\n# For example, the `plugins` directory of your clone of the Nikola plugins\n# repository.\nEXTRA_PLUGINS_DIRS = ['plugins']\n\n# Add the absolute paths to directories containing themes to use them.\n# For example, the `v7` directory of your clone of the Nikola themes\n# repository.\n# EXTRA_THEMES_DIRS = []\n\n# List of regular expressions, links matching them will always be considered\n# valid by \"nikola check -l\"\n# LINK_CHECK_WHITELIST = []\n\n# If set to True, enable optional hyphenation in your posts (requires pyphen)\n# Enabling hyphenation has been shown to break math support in some cases,\n# use with caution.\n# HYPHENATE = False\n\n# The <hN> tags in HTML generated by certain compilers (reST/Markdown)\n# will be demoted by that much (1 → h1 will become h2 and so on)\n# This was a hidden feature of the Markdown and reST compilers in the\n# past.  Useful especially if your post titles are in <h1> tags too, for\n# example.\n# (defaults to 1.)\n# DEMOTE_HEADERS = 1\n\n# Docutils, by default, will perform a transform in your documents\n# extracting unique titles at the top of your document and turning\n# them into metadata. This surprises a lot of people, and setting\n# this option to True will prevent it.\n# NO_DOCUTILS_TITLE_TRANSFORM = False\n\n# If you don’t like slugified file names ([a-z0-9] and a literal dash),\n# and would prefer to use all the characters your file system allows.\n# USE WITH CARE!  This is also not guaranteed to be perfect, and may\n# sometimes crash Nikola, your web server, or eat your cat.\n# USE_SLUGIFY = True\n\n# Templates will use those filters, along with the defaults.\n# Consult your engine's documentation on filters if you need help defining\n# those.\n# TEMPLATE_FILTERS = {}\n\n# Put in global_context things you want available on all your templates.\n# It can be anything, data, functions, modules, etc.\nGLOBAL_CONTEXT = {}\n\n# Add functions here and they will be called with template\n# GLOBAL_CONTEXT as parameter when the template is about to be\n# rendered\nGLOBAL_CONTEXT_FILLER = []\n"
  },
  {
    "path": "www/files/manifest.json",
    "content": "{\n    \"name\": \"Mosquitto\",\n    \"icons\": [\n        {\n            \"src\": \"/android-chrome-192x192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"/android-chrome-512x512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\"\n        }\n    ],\n    \"theme_color\": \"#ffffff\",\n    \"background_color\": \"#ffffff\",\n    \"display\": \"standalone\"\n}"
  },
  {
    "path": "www/files/stickers/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n\t<title>Eclipse Mosquitto&trade; Sticker Generator</title>\n</head>\n\n<body style=\"background-color:#dddddd\">\n\n\n\n<div style=\"width:500px; text-align:center; margin: 0 auto;\">\n\t<h1>Eclipse Mosquitto&trade; Sticker Generator</h1>\n\n\t<p>This page used to allow you to create a book of square stickers to buy\n\tfrom <a href=\"http://moo.com/\">Moo</a>. Unfortunately they have\n\tdiscontinued their API, so if you want Mosquitto stickers you can use the\n\timages and create your own sticker book manually.</p>\n\t<p>Click the logos to choose which to include, then click submit to be\n\ttaken to the Moo site with a stickerbook ready in your basket. If you\n\tchoose both logos you will get 45 of each, otherwise you will get 90 of the\n\tone you choose.</p>\n\n\t<span style=\"margin: 0 auto; width: 200px; text-align: center\">\n\t\t<img id=\"mono\" src=\"mosquitto-mono.png\" width=\"100\" height=\"100\">\n\t\t<img id=\"colour\" src=\"mosquitto-colour.png\" width=\"100\" height=\"100\">\n\t</span>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "www/pages/documentation/authentication-methods.md",
    "content": "<!--\n.. title: Authentication methods\n.. slug: authentication-methods\n.. date: 2021-02-05 14:25:28 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIt is important to configure authentication on your Mosquitto instance, so\nunauthorised clients cannot connect.\n\nIn Mosquitto 2.0 and up, you must choose your authentication options explicitly\nbefore clients can connect. In earlier versions the default is to allow clients\nto connect without authentication.\n\nThere are three choices for authentication: password files, authentication\nplugins, and unauthorised/anonymous access. It is possible to use a combination\nof all three choices.\n\nIt is possible to have different listeners use different authentication methods\nby setting `per_listener_settings true` in your configuration file.\n\nAs well as authentication you should also consider some form of access control\nto determine what clients can access which topics.\n\n## Password files\n\nPassword files are a simple mechanism of storing usernames and passwords in a\nsingle file. They are good if you have a relatively small number of fairly\nstatic users.\n\nIf you make changes to the password file you must trigger the broker to reload\nthe file by sending a SIGHUP message:\n\n```\nkill -HUP <process id of mosquitto>\n```\n\n### Creating a password file\n\nTo create a password file, use the `mosquitto_passwd` utility, use the line\nbelow. You will be asked for the password. Note that `-c` means an existing\nfile will be overwritten:\n\n```\nmosquitto_passwd -c <password file> <username>\n```\n\nTo add more users to an existing password file, or to change the password for\nan existing user, leave out the `-c` argument:\n\n```\nmosquitto_passwd <password file> <username>\n```\n\nTo remove a user from a password file:\n\n```\nmosquitto_passwd -D <password file> <username>\n```\n\nYou can also add/update a username and password in a single line, but be aware\nthat this means the password is visible on the command line and in any command\nhistory:\n\n```\nmosquitto_passwd <password file> <username> <password>\n```\n\n### Configuring the broker\n\nTo start using your password file you must add the `password_file` option to\nyour configuration file:\n\n```\npassword_file <path to the configuration file>\n```\n\nThe password file must be able to be read by whatever user Mosquitto is running\nas. On Linux/POSIX systems this will typically be the `mosquitto` user, and\n`/etc/mosquitto/password_file` is a good place for the file itself.\n\nIf you are using the `per_listener_settings true` option to have separate\nsecurity settings per listener, you must place the password file option *after*\nthe listener it is for:\n\n```\nlistener 1883\npassword_file /etc/mosquitto/password_file\n```\n\n## Authentication plugins\n\nIf you want more control over authentication of your users than is offered by a\npassword file, then an authentication plugin may be suitable for you. The\nfeatures offered depend on which plugin you use.\n\n### Configuring the plugin\n\nConfiguring a plugin varies depending on the version of Mosquitto plugin\ninterface the plugin was written for, either version 2.0 and up, or 1.6.x and\nearlier.\n\nFor 1.6.x and below, use the `auth_plugin` option. These plugins are also\nsupported by version 2.0:\n\n```\nlistener 1883\nauth_plugin <path to plugin>\n```\n\nSome plugins require extra configuration which will be described in their\ndocumentation.\n\nFor 2.0 and up, use the `plugin` option:\n\n```\nlistener 1883\nplugin <path to plugin>\n```\n\n### Available plugins\n\n* [Dynamic security](https://mosquitto.org/documentation/dynamic-security/),\n  for 2.0 and up only, provided by the Mosquitto project to give flexible\n  in-broker clients, groups, and roles that can be administered remotely.\n* [mosquitto-go-auth](https://github.com/iegomez/mosquitto-go-auth), which\n  offers the use of a variety of backends to store user data, such as mysql,\n  jwt, or redis.\n\n\n## Unauthenticated access\n\nTo configure unauthenticated access, use the `allow_anonymous` option:\n\n```\nlistener 1883\nallow_anonymous true\n```\n\nIt is valid to allow anonmous and authenticated access on the same broker. In\nparticular the dynamic security plugin allows you to assign different rights to\nanonymous users than to authenticated users, which may be useful for read-only\naccess to data for example.\n"
  },
  {
    "path": "www/pages/documentation/dynamic-security.md",
    "content": "<!--\n.. title: Dynamic Security Plugin\n.. slug: dynamic-security\n.. date: 2020-11-15 09:25:28 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n\n[TOC]\n\n## Introduction\n\nThe Dynamic Security plugin is a Mosquitto plugin which provides role based\nauthentication and access control features that can be updated whilst the broker\nis running, using a special topic based API.\n\nIt is supported since Mosquitto 2.0, and should be available in all\ninstallations, but will not be activated by default.\n\n\n## Concepts\n\nThis section describes the concepts of how the plugin operates. If you want to\nfind out how to use the plugin features, look in the [Installation] section below.\n\nThe plugin allows you to create three main objects, `clients`, `groups`, and `roles`.\n\nThis document will use the term `clients` to mean the clients defined in the\nplugin, and `devices` or `users` to mean the MQTT clients that connect to the\nbroker.\n\n---\n\n### Clients\n\nWhen you want a device or user to be able to connect and authenticate to the\nbroker, you create a client. Each client has the following attributes:\n\n#### Username\n\nThe client username maps to the username provided in the CONNECT packet when a\ndevice connects. The username is unique across the plugin, so attempting to\ncreate a client with a duplicate username will result in an error. The username\nacts as the primary key if you want to change anything about the client.\n\n#### Password\n\nThe client password maps to the password provided in the CONNECT packet when a\ndevice connects. The password may be unset when a client is created, this will\nmean that devices will be unable to connect as the corresponding client.\n\nThe password can be updated at any point, but only by a client with the correct\naccess. Devices typically cannot update their own passwords.\n\n#### Client ID\n\nThe client id maps to the client id provided in the CONNECT packet when a\ndevice connects. This is an optional attribute.\n\nIf the client id is empty or not provided, then any device can connect with the\nusername for this client regardless of its client id. This means that multiple\ndevices could connect with the same credentials at the same time, but sharing\ncredentials between devices is not recommended.\n\nIf the client id is set, then a device can only connect as this client if the\ntriple of username, password, and client id all match those in the client.\n\n#### Groups\n\nA client can be a member of any number of groups.\n\n#### Roles\n\nA client can be assigned to any number of roles. A role gives the client access\nto different topics.\n\n#### Text name\n\nThis is an optional text field to give a human friendly name to this client.\n\n#### Text description\n\nThis is an optional text field to give a human friendly description to this\nclient.\n\n#### Disabled\n\nA client can be set to be enabled/disabled at any point. Disabling a client\nmeans that any devices currently connected using the credentials for that\nclient will be disconnected and unable to reconnect.\n\n---\n\n### Groups\n\nMultiple clients can be placed in a group. Groups can have roles assigned to\nthem, so using groups is appropriate where you have a number of clients that\nneed to have the same access.\n\nGroups have the following attributes:\n\n#### Group name\n\nThe group name is the primary name for the group. It is used when modifying the\ngroup in any way, such as adding a client or a role.\n\n#### Roles\n\nA group can be assigned to any number of roles. A role gives the group access\nto different topics.\n\n#### Text name\n\nThis is an optional text field to give a human friendly name to this group.\n\n#### Text description\n\nThis is an optional text field to give a human friendly description to this\ngroup.\n\n---\n\n### Roles\n\nRoles contain multiple access control lists (ACLs), and can be assigned to clients and/or groups.\n\nRoles have the following attributes:\n\n#### Role name\n\nThe role name is the primary name for the role. It is used when modifying the\nrole in any way, such as adding an ACL.\n\n#### Access Control Lists\n\nACLs are the feature which allows access to topics to be controlled. Checks are\nmade on different events as they happen: `publishClientSend`,\n`publishClientReceive`, `subscribe`, and `unsubscribe`. The `publishClientSend`\nevent occurs when a device sends a PUBLISH message to the broker, i.e. \"is the\ndevice allowed to publish to this topic\". The `publishClientReceive` event\noccurs when a device is due to receive a PUBLISH message from the broker, i.e.\nit has a valid subscription and a matching message has been published to the\nbroker. The `subscribe` event occurs in response to a device sending a\nSUBSCRIBE message, and the `unsubscribe` event occurs in response to a device\nsending an UNSUBSCRIBE packet.\n\nThe default behaviour of the different events can be set to allow or deny\naccess. The default behaviour applies if no matching ACL is found.\n\nThe default behaviour for the different events when the plugin has first been\nconfigured is:\n\n* `publishClientSend`: deny\n* `publishClientReceive`: allow\n* `subscribe`: deny\n* `unsubscribe`: allow\n\nThere is some overlap between `publishClientReceive` and `subscribe`. In most\ncases, using a `subscribe` ACL is sufficient to provide the control you need,\nhowever by combining the two types it is possible to e.g. allow subscriptions\nto a wildcard topic like `topic/#`, but deny access for device to receive\nmessages on a specific topic within that hierarchy like 'topic/secret'.\n\nThe different events have ACL types associated with them, and it is these ACLs\nthat you will add to your roles. Each ACL has a `topic`, a `priority`, and can\nbe set to `allow` or `deny`.\n\nThe `publishClientSend` and `publishClientReceive` ACL types map directly to\nthe events of the same name. The topic can contain wildcards, so allowing send\naccess to `topic/#` will allow devices to publish to all topics in the\n`topic/#` hierarchy, including `topic`.\n\nThe `subscribe` and `unsubscribe` events have two ACL types each:\n`subscribeLiteral`, `subscribePattern`, `unsubscribeLiteral`, and\n`unsubscribePattern`.\n\nThe `*Literal` ACL types make a literal comparison between the topic filter\nprovided for the ACL and the topic filter provided in the SUBSCRIBE or\nUNSUBSCRIBE message. This means that setting a `subscribeLiteral` ACL with\ntopic filter `#` to deny would prevent matching devices from subscribing the\nthe `#` topic filter only, but still allow them to subscribe to `topic/#`, for\nexample.\n\nThe `*Pattern` ACL types allow or deny access based on a wildcard comparison of\nthe ACL topic filter and the topic provided in the SUBSCRIBE or UNSUBSCRIBE\nmessage. This means that setting a `subscribePattern` ACL with topic filter `#`\nto deny would prevent matching devices from subscribing to any topic at all.\n\n#### ACL pattern substitution\n\nThe `publishClientSend`, `publishClientReceive`, `subscribePattern`, and\n`unsubscribePattern` ACL types can make use of pattern substitution. This means\nthat the strings `%c` and `%u` will be replaced with the client id and username\nof the client being checked, respectively. The pattern strings must be the only\nitem in that level of hierarchy, so the ACL `topic/%count` will not be\nconsidered as a pattern.\n\nFor example, with an ACL of `room/%c/temperature`, a client connecting with\nclient id `kitchen` would be allowed to use the topic\n`room/kitchen/temperature` only.\n\nIf a client does not have a username, a pattern that includes `%u` will always\nfail to match against that client.\n\n#### Text name\n\nThis is an optional text field to give a human friendly name to this role.\n\n#### Text description\n\nThis is an optional text field to give a human friendly description to this\nrole.\n\n---\n\n### Priorities\n\nIf you are working with more than one role per client or group, or more than\none group per client, then it is crucial to understand how roles and ACLs are\napplied.\n\nThe order in which checks are made is determined in part by the `priority` of\ngroups, roles and ACLs. Each client group has a priority, each client role and\ngroup role has a priority, and each ACL within a role has a priority. If not\nset explicitly, priorities will default to -1. Priority has a maximum of 100000.\n\nFor each of the group, role, and ACL objects, checks are made in priority order\nfrom the highest numerical value to the lowest numerical value. If two objects\nof the same type have the same priority, then they will be checked in\nlexicographical order according to the username/groupname/rolename, but it is\nadvised to use unique priorities per object type.\n\nWhen an event occurs that needs an ACL check, the ACLs for that ACL type are\nchecked in order until there is a matching ACL for the topic in question.\n\nWithin each role that is checked, the ACLs are checked in priority order. If\nACLs have identical priority, they are evaluated in the order shown in the\n`getRole` command.\n\nThe roles assigned to a client are checked first, in priority order.\nEach client group is checked in priority order, with all of the roles in a\ngroup being checked in priority order before the next group is checked.\n\nAs an example, let us assume we have the following client, groups, and roles:\n\nClient: `sensor`\nGroups: `temperature` (priority 2), `humidity` (priority 1)\nRoles: `hallway`\n\nGroup: `temperature`\nRoles: `input` (priority 5), `output` (priority 1)\n\nGroup: `humidity`\nRoles: `humidity`\n\nRole: `hallway`\nACLs: `Z` (priority 3), `A` (priority 1)\n\nRole: `input`\nACLs: `Z` (priority 3), `A` (priority 3)\n\nRole: `output`\nACLs: `Z` (priority 3), `A` (priority 1)\n\nRole: `humidity`\nACLs: `Z` (priority 3), `A` (priority 1)\n\nWe are also assuming we are only looking at single ACL type.\n\nIf our client `sensor` triggers an ACL check, the ACLs will be checked in this\norder, and the first matching ACL will be used to allow/reject the event:\n\n1. sensor/hallway Z\n2. sensor/hallway A\n3. temperature/input A (alphabetical sort)\n4. temperature/input Z (alphabetical sort)\n5. temperature/output Z\n6. temperature/output A\n7. humidity/humidity Z\n8. humidity/humidity A\n\nThis is provided as an example that covers all combinations of roles, it is\nrecommended to use as simple a setup as possible for your situation.\n\n### Anonymous access\n\nAll of the documentation so far assumes that you do not allow anonymous\nunauthenticated access - meaning devices or users that connect without a\nusername.\n\nYou may wish to allow anonymous access, but still make use of the dynamic\nsecurity plugin, and this is supported through the automatic anonymous group.\nIf allowed, anything connecting without a username will be assigned to a group\nthat you define. By assigning roles to that group, you can control what\nanonymous devices can access.\n\n## Installation\n\nTo use the Dynamic Security plugin, it must be configured in the broker and an\ninitial plugin configuration must be generated.\n\nTo configure the broker, add the following to your configuration file.\n\nLinux/BSD:\n```\nplugin path/to/mosquitto_dynamic_security.so\nplugin_opt_config_file path/to/dynamic-security.json\n```\n\nWindows:\n```\nplugin path\\to\\mosquitto_dynamic_security.dll\nplugin_opt_config_file path\\to\\dynamic-security.json\n```\n\nOn Linux you would expect the plugin library to be installed to\n`/usr/lib/x86_64-linux-gnu/mosquitto_dynamic_security.so` or a similar path,\nbut this will vary depending on the particular distribution and hardware in\nuse.\n\nIt is recommended to use `per_listener_settings false` with this plugin, so all\nlisteners use the same authentication and access control.\n\nThe `dynamic-security.json` file is where the plugin configuration will be\nstored. This file will be updated each time you make client/group/role changes,\nduring normal operation the configuration stays in memory.\n\n### Generating the configuration file - 2.1 onwards\n\nTo generate your initial configuration file there are a few choices. In version\n2.0.x, you must use the `mosquitto_ctrl` utility as described below. From\nversion 2.1 onwards, if the configuration file does not exist, the plugin will\nattempt to generate a default configuration file with some sensible defaults.\n\nThe roles created are:\n\n* `broker-admin` - grants access to administer general broker settings\n* `client` - read/write access to the full application topic hierarchy '#'\n* `dynsec-admin` - grants access to administer clients/groups/roles\n* `super-admin` - grants access to administer any `$CONTROL` APIs\n* `sys-notify` - allow bridges to publish connection state messages\n* `sys-observe` - allow read only access to the $SYS/# topic hierarchy\n* `topic-observe` - allow read only access to the full application topic hierarchy '#'\n\nThe groups created are:\n\n* `unauthenticated` - automatic group that anonymous/unauthenticated clients\n  are placed in, if anonymous access is allowed.\n\nThe initial users can be generated in three different ways, as described below.\n\n#### Initialisation file\n\nCreate a text file with a single line. This line will be used as the password\nfor the `admin` user, which will have access to administer the dynamic security\nplugin.\n\nSet the configuration option to trigger the use of this file:\n```\nplugin_opt_password_init_file path/to/init-file\n```\n\nOnce the initial run of the broker has been done, the init file can be deleted.\n\nThis method is well suited to use with e.g. docker secrets inside a container.\n\n#### Environment variable\n\nSet the `MOSQUITTO_DYNSEC_PASSWORD` environment variable to a string text and\nit will be used as the password for the `admin` user, which will have access to\nadminister the dynamic security plugin.\n\n#### Default\n\nIf neither `plugin_opt_password_init_file` nor `MOSQUITTO_DYNSEC_PASSWORD` are\nset, then the plugin will generate random passwords and store them in *plain\ntext* at `<plugin_opt_config_file>.pw`, for example `dynamic-security.json.pw`.\nThis file should be deleted once the passwords are known.\n\nTwo users will be created, `admin`, which will have access to administer the\ndynamic security plugin, and `democlient`, which will have read/write access to\nthe application topic hierarchy `#`.\n\n### Generating the configuration file - 2.0 onwards\n\nTo generate an initial file using the `mosquitto_ctrl` utility:\n\n```\nmosquitto_ctrl dynsec init path/to/dynamic-security.json admin-user\n```\n\nChoose your own `admin-user` username. You will be asked for a password for the\nclient. This user will be assigned the `admin` role, which has the following\naccess:\n\n* publishClientSend: `$CONTROL/dynamic-security/#` - this allows the client to\n  control the Dynamic security plugin.\n* publishClientReceive: `$CONTROL/dynamic-security/#` - this allows the client\n  to receive information from the plugin. This is not necessary in the default\n  configuration, but is included in case the default behaviour for\n  `publishClientReceive` is set to `deny`.\n* subscribePattern: `$CONTROL/dynamic-security/#` - this allows the client to\n  receive information from the plugin.\n* publishClientReceive: `$SYS/#` - this allows the client to see the broker\n  metrics.\n* subscribePattern: `$SYS/#` - this allows the client to see the broker\n  metrics.\n* publishClientReceive: `#` - this allows the client to examine the messages\n  being published by other clients.\n* subscribePattern: `#` - this allows the client to examine the messages\n  being published by other clients.\n* unsubscribePattern: `#` - this allows the client to undo previous\n  subscriptions. This is not necessary in the default configuration, but is\n  included in case the default behaviour for `unsubscribe` is set to `deny`.\n\nThe admin user does not have access to publish to normal application topics in\nthe `#` hierarchy by default. You are strongly encouraged to keep the admin\nuser purely for administering the plugin, and create other clients for your\napplication.\n\n## Usage\n\nAll control of the plugin after initial installation is through the MQTT topic\nAPI at `$CONTROL/dynamic-security/v1`. This allows integrations to be built,\nbut isn't the best choice for people to use directly. The `mosquitto_ctrl`\ncommand provided with Mosquitto implements support for the dynamic security\nplugin API, as described below. Other options include the [Management Center\nfor Mosquitto](https://docs.cedalo.com/latest/) which is an open source web\nbased tool for controlling the plugin and other features. The Management Center\nis not part of the Mosquitto project.\n\n### Using mosquitto_ctrl with a running broker\n\nThe initial configuration is the only time that `mosquitto_ctrl` does not\nconnect to a broker to carry out the configuration. All other commands require\na connection to a broker, and hence a username, password, and whatever else is\nrequired for that particular connection. It is strongly recommended that your\nbroker connection uses encryption so that your configuration, including new\npasswords, is not transmitted in plain text.\n\nThe connection options must be given before the `dynsec` part of the command\nline:\n```\nmosquitto_ctrl <connection options> dynsec <command> ...\n```\n\nFor example:\n\n```\nmosquitto_ctrl -u admin -h localhost dynsec <command> ...\n```\n\nIt is possible to provide the admin password on the command line using `-P\npassword`, but this is not recommended. If you do not provide a password,\nmosquitto_ctrl will ask you to enter the password when it is needed.\n\n### Using an options file\n\nFor convenience, mosquitto_ctrl can load an options file which contains a list\nof options it should use. This means you can set the encryption options, host,\nadmin username and any other options once and not have to add them to the\ncommand line every time.\n\nmosquitto_ctrl will try to load a configuration file from a default location.\nFor Windows this is at `%USER_PROFILE%\\mosquitto_ctrl`. For other systems,\nit will try `$XDG_CONFIG_HOME/mosquitto_ctrl` or\n`$HOME/.config/mosquitto_ctrl`.\n\nYou may override this behaviour by manually specifying an options file with\n`-o <path to options file>`.\n\nThe options file should contain a list of options, one per line, exactly as\nthey would be provided on the command line. For example:\n\n```\n--cafile /path/to/my/CA.crt\n--cert /path/to/my/client.crt\n--key /path/to/my/client.key\n-u admin\n-h mosquitto.example.com\n\n```\n\n### mosquitto_ctrl options\n\n* `-A address` : Bind the outgoing connection to a local ip address/hostname.\n  Use this argument if you need to restrict network communication to a\n  particular interface.\n* `--cafile path-to-ca.crt` : Define the path to a file containing PEM encoded\n  CA certificates that are trusted. Used to enable SSL communication.  See also\n  `--capath`\n* `--capath` : Define the path to a directory containing PEM encoded CA\n  certificates that are trusted. Used to enable SSL communication. For\n  `--capath` to work correctly, the certificate files must have \".crt\" as the\n  file ending and you must run `openssl rehash <path to capath>` each time you\n  add/remove a certificate. See also `--cafile`.\n* `--cert path-to-client.crt` : Define the path to a file containing a PEM\n  encoded certificate for this client, if required by the server. See also\n  `--key`.\n* `--ciphers` : An openssl compatible list of TLS ciphers to support in the\n  client. See ciphers(1) for more information.\n* `-d` : Enable debug messages.\n* `--help` : Display usage information.\n* `-h hostname` : Specify the host to connect to. Defaults to localhost.\n* `-i client-id` : The id to use for this client. If not given, a client id\n  will be generated depending on the MQTT version being used. For v3.1.1/v3.1,\n  the client generates a client id in the format mosq-XXXXXXXXXXXXXXXXXX, where\n  the X are replaced with random alphanumeric characters.  For v5.0, the client\n  sends a zero length client id, and the server will generate a client id for\n  the client.\n* `--insecure` : When using certificate based encryption, this option disables\n  verification of the server hostname in the server certificate. This can be\n  useful when testing initial server configurations but makes it possible for a\n  malicious third party to impersonate your server through DNS spoofing, for\n  example. Use this option in testing only. If you need to resort to using this\n  option in a production environment, your setup is at fault and there is no\n  point using encryption.\n* `--key path-to-client.key` : Define the path to a file containing a PEM\n  encoded private key for this client, if required by the server. See also\n  `--cert`.\n* `-L url` : Specify specify user, password, hostname, port and topic at once\n  as a URL. The URL must be in the form:\n  `mqtt(s)://[username[:password]@]host[:port]`. If the scheme is mqtt:// then\n  the port defaults to 1883. If the scheme is mqtts:// then the port defaults\n  to 8883.\n* `--nodelay` : Disable Nagle's algorithm for the socket. This means that\n  latency of sent messages is reduced, which is particularly noticeable for\n  small, reasonably infrequent messages. Using this option may result in more\n  packets being sent than would normally be necessary.\n* `-p port` : Connect to the port specified. If not given, the default of 1883\n  for plain MQTT or 8883 for MQTT over TLS will be used.\n* `-P password` : Provide a password to be used for authenticating with the\n  broker. Using this argument without also specifying a username is invalid\n  when using MQTT v3.1 or v3.1.1. See also the `-u` option.\n* `--proxy proxy-url` : Specify a SOCKS5 proxy to connect through. \"None\" and\n  \"username\" authentication types are supported. The socks-url must be of the\n  form `socks5h://[username[:password]@]host[:port]`. The protocol prefix\n  socks5h means that hostnames are resolved by the proxy. The symbols %25, %3A\n  and %40 are URL decoded into %, : and @ respectively, if present in the\n  username or password.  If username is not given, then no authentication is\n  attempted. If the port is not given, then the default of 1080 is used.\n* `--psk key` : Provide the hexadecimal (no leading 0x) pre-shared-key matching\n  the one used on the broker to use TLS-PSK encryption support.\n  `--psk-identity` must also be provided to enable TLS-PSK.\n* `--psk-identity identify` : The client identity to use with TLS-PSK support.\n  This may be used instead of a username if the broker is configured to do so.\n* `-q qos` : Specify the quality of service to use for messages, from 0, 1 and\n  2. Defaults to 1.\n* `--quiet` :  If this argument is given, no runtime errors will be printed.\n  This excludes any error messages given in case of invalid user input (e.g.\n  using `-p` without a port).\n* `--tls-version version` : Choose which TLS protocol version to use when\n  communicating with the broker. Valid options are tlsv1.3 and tlsv1.2.\n  The default value is tlsv1.2. Must match the protocol version used\n  by the broker.\n* `-u username` : Provide a username to be used for authenticating with the\n  broker. See also the `-P` argument.\n* `--unix path` : Connect to a broker through a local unix domain socket\n  instead of a TCP socket. This is a replacement for `-h` and `-L`. For\n  example: `mosquitto_ctrl --unix /tmp/mosquitto.sock ...`.\n* `-V protocol-version` : Specify which version of the MQTT protocol should be\n  used when connecting to the remote broker. Can be `5`, `311`, `31`, or the\n  more verbose `mqttv5`, `mqttv311`, or `mqttv31`. Defaults to `311`.\n\n\n## Configuring default access\n\nThe initial configuration sets the default ACL type behaviours to:\n\n* `publishClientSend`: deny\n* `publishClientReceive`: allow\n* `subscribe`: deny\n* `unsubscribe`: allow\n\nIf you wish to change these, use `mosquitto_ctrl`.\n\n```\nmosquitto_ctrl <options> dynsec setDefaultACLAccess publishClientSend deny\nmosquitto_ctrl <options> dynsec setDefaultACLAccess publishClientReceive deny\nmosquitto_ctrl <options> dynsec setDefaultACLAccess subscribe deny\nmosquitto_ctrl <options> dynsec setDefaultACLAccess unsubscribe deny\n```\n\nYou can examine the current default access with the `getDefaultACLAccess` command:\n\n```\nmosquitto_ctrl <options> dynsec getDefaultACLAccess unsubscribe\n```\n\n## Creating and modifying clients\n\nTo create a new client:\n\n```\nmosquitto_ctrl <options> dynsec createClient <username>\n```\n\nThis creates a client which does not have a client id associated with it. You\nwill be asked for the password for the new client before you are asked for the\nadmin user password. Pay attention to the messages on the command line.\n\n```\nmosquitto_ctrl <options> dynsec createClient <username> -i <client id>\n```\nThis creates a client which has a client id associated with it.\n\n\nTo delete a client (clients connected with these credentials will be\ndisconnected from the broker):\n\n```\nmosquitto_ctrl <options> dynsec deleteClient <username>\n```\n\nTo disable a client (clients connected with these credentials will be\ndisconnected from the broker):\n\n```\nmosquitto_ctrl <options> dynsec disableClient <username>\n```\n\nTo enable a client (clients will be able to use these credentials to log in\nagain):\n\n```\nmosquitto_ctrl <options> dynsec enableClient <username>\n```\n\nTo set a client password:\n\n```\nmosquitto_ctrl <options> dynsec setClientPassword <username>\nmosquitto_ctrl <options> dynsec setClientPassword <username> <password>\n```\n\nTo add/remove a role to/from a client:\n\n```\nmosquitto_ctrl <options> dynsec addClientRole <username> <rolename> <priority>\nmosquitto_ctrl <options> dynsec removeClientRole <username> <rolename>\n```\n\nTo get information on a client:\n\n```\nmosquitto_ctrl <options> dynsec getClient <username>\n```\n\nTo list all clients:\n\n```\nmosquitto_ctrl <options> dynsec listClients\n```\n\nThis gives an output that is a list of client usernames:\n\n```\nclient1\nclient2\n```\n\nThe `modifyClient` command also exists in the topic API, but is not currently available in `mosquitto_ctrl`.\n\n\n## Creating and modifying groups\n\nTo create a new group:\n\n```\nmosquitto_ctrl <options> dynsec createGroup <groupname>\n```\n\nTo delete a group:\n\n```\nmosquitto_ctrl <options> dynsec deleteGroup <groupname>\n```\n\nTo add/remove a client to/from a group:\n\n```\nmosquitto_ctrl <options> dynsec addGroupClient <groupname> <username> <priority>\nmosquitto_ctrl <options> dynsec removeGroupClient <groupname> <username>\n```\n\nIn this case the `priority` refers to the priority of the group within the\nclient's list of groups.\n\nTo add/remove a role to/from a group:\n\n```\nmosquitto_ctrl <options> dynsec addGroupRole <groupname> <rolename> <priority>\nmosquitto_ctrl <options> dynsec removeGroupRole <groupname> <rolename>\n```\n\nTo set/get the group that anonymous devices are assigned to:\n\n```\nmosquitto_ctrl <options> dynsec setAnonymousGroup <groupname>\nmosquitto_ctrl <options> dynsec getAnonymousGroup\n```\n\nTo get information on a group:\n\n```\nmosquitto_ctrl <options> dynsec getGroup <groupname>\n```\n\nTo list all groups:\n\n```\nmosquitto_ctrl <options> dynsec listGroups\n```\n\nThe `modifyGroup` command also exists in the topic API, but is not currently\navailable in `mosquitto_ctrl`.\n\n## Creating and modifying roles\n\nTo create a new role:\n\n```\nmosquitto_ctrl <options> dynsec createRole <rolename>\n```\n\nTo delete a role:\n\n```\nmosquitto_ctrl <options> dynsec deleteRole <rolename>\n```\n\nTo add an ACL to a role:\n```\nmosquitto_ctrl <options> dynsec addRoleACL <rolename> <acltype> <topic filter> allow|deny <priority>\n```\nWhere `acltype` is one of `publishClientSend`, `publishClientReceive`,\n`subscribeLiteral`, `subscribePattern`, `unsubscribeLiteral`, and\n`unsubscribePattern`.\n\nFor example:\n\n```\nmosquitto_ctrl <options> dynsec addRoleACL <rolename> publishClientSend client/topic allow 5\n```\n\nTo remove an ACL from a role using the topic filter as the key:\n```\nmosquitto_ctrl <options> dynsec removeRoleACL <rolename> <acltype> <topic filter>\n```\nFor example:\n\n```\nmosquitto_ctrl <options> dynsec removeRoleACL <rolename> publishClientSend client/topic\n```\n\nTo get information on a role:\n\n```\nmosquitto_ctrl <options> dynsec getRole <rolename>\n```\n\nTo list all roles:\n\n```\nmosquitto_ctrl <options> dynsec listRoles\n```\n\nThe `modifyRole` command also exists in the topic API, but is not currently\navailable in `mosquitto_ctrl`.\n"
  },
  {
    "path": "www/pages/documentation/listeners/haproxy.md",
    "content": "<!--\n.. title: Using Mosquitto with HAProxy\n.. slug: haproxy\n.. date: 2024-04-14 22:32:58 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n\n[TOC]\n\n## Introduction\n\nUsing Mosquitto as an entirely standalone broker works very well, but sometimes\nyou may wish to place it behind a load balancer / proxy such as [HAProxy]. This\nprovides certain advantages such as carrying out TLS termination in the proxy\nto reduce load on the broker. It does have one disadvantage which is that only\nthe proxy IP address is found in the broker logs - the client IP addresses are\nnot seen. Since Mosquitto 2.1, this can be fixed using the PROXY protocol v2\nsupport, which can be enabled using the `enable_proxy_protocol 2` option. This\nis the recommended mode when using HAProxy. The PROXY protocol v1 is also\nsupported with `enable_proxy_protocol 1`. This version of the protocol has a\nreduced feature set, particularly around sending on TLS related information,\nhowever it is more widely supported than v2.\n\nThis document describes some different ways you can combine Mosquitto and\nHAProxy. It is not a complete guide to HAProxy.\n\n**Important:** Enabling PROXY protocol support requires that the broker itself\nis not directly accessible on its network port. All communication must go\nthrough the broker. If a client is able to connect to the broker directly, it\nis trivial to spoof connection information and this is especially important\nwhen using client certificates for mutual TLS on HAProxy. In that case, the\ncontents of the PROXY header can directly indicate whether a client is allowed\nto connect so it must be protected.\n\nIt may be desirable to use a firewall to restrict access to the broker port.\n\n## General setup\n\nAll examples presented will be of the `haproxy.cfg` file, typically located at\n`/etc/haproxy/haproxy.cfg` on a native Linux installation.\n\nThe first part of the config file contains the `global` and `defaults` sections\nwhich are going to be common to all of the examples and not repeated.\n\n### Global section\n\nThis is a fairly standard global section, presented without comment.\n\n```\nglobal\n        log /dev/log    local0\n        log /dev/log    local1 notice\n        user haproxy\n        group haproxy\n        daemon\n\n        # Default SSL material locations\n        ca-base /etc/ssl/certs\n        crt-base /etc/ssl/private\n\n        # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate\n        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384\n        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256\n        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets\n```\n\n### Defaults section\n\n```\ndefaults\n        mode    tcp\n        timeout connect 5000\n        timeout client  60000\n        timeout server  60000\n```\n\n* `mode tcp` - set TCP mode rather than HTTP mode by default. MQTT is not HTTP.\n* `timeout connect 5000` - the timeout allowed for a client to connect, in\n  milliseconds.\n* `timeout client 60000` - if a client does not communicate in this interval,\n  the connection will be closed by HAProxy.\n* `timeout server 60000` - if the broker does not communicate in this interval,\n  the connection will be closed by HAProxy.\n\nThe client and server timeout intervals should be chosen based on the intervals\nyou expect to have communication occurring in your clients. If your\ncommunication is typically very sparse, the timeout should be chosen based on\nthe keepalive interval you are using, otherwise the quiet clients will be\ndisconnected before they have chance to send a keepalive request.\n\n## Direct pass through, without PROXY protocol\n\nThe most basic approach is to pass connections directly through HAProxy to\nMosquitto without being modified. This means that if an unencrypted MQTT\nconnection is made, it will pass throughas an unencrypted connection, and if an\nencrypted MQTT connection is made it will pass through as an encrypted MQTT\nconnection. Likewise, websockets connections are passed through unaffected.\n\nCreate a frontend, which is where HAProxy will listen for connections, and a\nbackend which is where the broker that HAProxy will connect to is defined.\n\n```\nfrontend mqtt_frontend\n         bind *:1883\n         default_backend mqtt_backend\n\nbackend mqtt_backend\n        server server1 127.0.0.1:1884 check on-marked-down shutdown-sessions\n```\n\nIn this case, HAProxy is listening on all interfaces on port 1883 and will\nattempt to connect to the broker on address 127.0.0.1 on port 1884. In other\nwords, HAProxy and Mosquitto are running on the same instance. This is not\nrequired.\n\nThe broker configuration could be with an encrypted or unencrypted listener:\n\nUnencrypted:\n```\nlistener 1884\n# Further listener settings\n```\n\nEncrypted:\n```\nlistener 1884\ncertfile <path/to/server.crt>\nkeyfile <path/to/server.key>\n# Further listener settings\n```\n\n## TLS termination with server certificate only, without PROXY protocol\n\nPlace your server certificate and private key in `/etc/haproxy/certs/`.\n\n```\nfrontend mqtts_frontend\n         bind 0.0.0.0:8883 ssl crt /etc/haproxy/certs/\n\nbackend mqtt_backend\n        server server1 127.0.0.1:1883 check on-marked-down shutdown-sessions\n```\n\nThe broker configuration should declare an unencrypted listener:\n\n```\nlistener 1883\n# Further listener settings\n```\n\n## TLS termination with mutual TLS - client and server certificates, without PROXY protocol\n\nIn addition to the server certificate, you must also provide the CA certificate\nthat will sign the client certificates, ask for verification of the client\ncertificate and make the certificate required.\n\n```\nfrontend mqtts_frontend\n         bind *:8883 ssl crt /etc/haproxy/certs/ verify required ca-file /etc/haproxy/client-ca.crt\n         default_backend mqtt_backend\n\nbackend mqtt_backend\n        server server1 127.0.0.1:1883 check on-marked-down shutdown-sessions\n```\n\nThe broker configuration should declare an unencrypted listener:\n\n```\nlistener 1883\n# Further listener settings\n```\n\n## Direct pass through, with PROXY protocol v2\n\nTo enable PROXY protocol v2 on HAProxy, add the `send-proxy-v2` option to the backend.\n```\nfrontend mqtt_frontend\n         bind *:1883\n         default_backend mqtt_backend\n\nbackend mqtt_backend\n        server server1 127.0.0.1:1884 check on-marked-down shutdown-sessions send-proxy-v2\n```\n\nThe broker configuration should declare an unencrypted listener and enable\nPROXY protocol v2 support. It is not possible to have direct pass through\nwith encrypted connections on the broker.\n```\nlistener 1883\nenable_proxy_protocol 2\n# Further listener settings\n```\n\n\n## Direct pass through, with PROXY protocol v1\n\nTo enable PROXY protocol v1 on HAProxy, add the `send-proxy` option to the backend.\n```\nfrontend mqtt_frontend\n         bind *:1883\n         default_backend mqtt_backend\n\nbackend mqtt_backend\n        server server1 127.0.0.1:1884 check on-marked-down shutdown-sessions send-proxy\n```\n\nThe broker configuration should declare an unencrypted listener and enable\nPROXY protocol v1 support. It is not possible to have direct pass through\nwith encrypted connections on the broker.\n```\nlistener 1883\nenable_proxy_protocol 1\n# Further listener settings\n```\n\n\n## TLS termination with server certificate only, with PROXY protocol v2\n\nFor TLS connections, use `send-proxy-v2-ssl` instead of `send-proxy-v2`. This\nensures that TLS information is added to the PROXY header.\n```\nfrontend mqtts_frontend\n         bind *:8883 ssl crt /etc/haproxy/certs/\n         default_backend mqtt_backend\n\nbackend mqtt_backend\n        server server1 127.0.0.1:1883 check on-marked-down shutdown-sessions send-proxy-v2-ssl\n```\n\nOn the broker side, use `proxy_protocol_v2_require_tls true` to ensure that\nonly connections that were made using TLS are accepted on the broker. No other\nTLS configuration is required.\n```\nlistener 1883\nenable_proxy_protocol 2\nproxy_protocol_v2_require_tls true\n```\n\n## TLS termination with mutual TLS - client and server certificate, with PROXY protocol v2\n\nFor TLS connections, use `send-proxy-v2-ssl` instead of `send-proxy-v2`. This\nensures that TLS information is added to the PROXY header.\n\n```\nfrontend mqtts_frontend\n         bind *:8883 ssl crt /etc/haproxy/certs/ verify required ca-file /etc/haproxy/client-ca.crt\n         default_backend mqtt_backend\n\nbackend mqtt_backend\n        server server1 127.0.0.1:1883 check on-marked-down shutdown-sessions send-proxy-v2-ssl\n```\n\nThe broker configuration uses `require_certificate true` to indicate that\nthe broker should check the PROXY protocol header for the valid certificate\nresult. No other TLS configuration is required.\n```\nlistener 1883\nenable_proxy_protocol 2\nproxy_protocol_v2_require_tls true\nrequire_certificate true\n```\n\n\n## TLS termination with mutual TLS - client and server certificate, with username and PROXY protocol v2\n\nThe Mosquitto option `use_identity_as_username true` can be used with the PROXY\nprotocol support. This requires that the `send-proxy-v2-ssl-cn` option is used\non HAProxy.\n\nIt is not possible to use `use_subject_as_username` with the PROXY protocol.\n\n```\nfrontend mqtts_frontend\n         bind *:8883 ssl crt /etc/haproxy/certs/ verify required ca-file /etc/haproxy/client-ca.crt\n         default_backend mqtt_backend\n\nbackend mqtt_backend\n        server server1 127.0.0.1:1883 check on-marked-down shutdown-sessions send-proxy-v2-ssl-cn\n```\n\nThe broker configuration uses `require_certificate` and\n`use_identity_as_username`. No other TLS configuration is required.\n```\nlistener 1883\nenable_proxy_protocol 2\nproxy_protocol_v2_require_tls true\nrequire_certificate true\nuse_identity_as_username true\n```\n\n\n[HAProxy]:https://www.haproxy.org/\n"
  },
  {
    "path": "www/pages/documentation/listeners/per_listener_settings.md",
    "content": "<!--\n.. title: Replacing the per_listener_settings option\n.. slug: per-listener-settings\n.. date: 2026-01-29 09:00:00 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n\n[TOC]\n\n## Introduction\n\nThe `per_listener_settings` option was introduced in version 1.5 as a way to\nallow security options such as `allow_anonymous` and `password_file` to be\napplied on a per-listener basis, rather than globally as was the only option\nbefore. It was however a poorly thought out idea that has lead to a great deal\nof confusion, and since version 2.1 is deprecated. It will be removed in\nversion 3.0.\n\nThis document sets out how to remove the use of this option whilst keeping the\nsame functionality in your configuration. These changes require version 2.1.\n\n## Authentication\n\n* Replace the `acl_file` option with the [mosquitto_acl_file](/documentation/plugins/acl-file/) plugin.\n* Replace the `password_file` option with the [mosquitto_password_file](/documentation/plugins/password-file/) plugin.\n* Replace the use of `allow_anonymous` with the listener specific\n`listener_allow_anonymous`. If `listener_allow_anonymous` is set for a\nlistener, this overrides any value set by `allow_anonymous`.\n* Replace `auto_id_prefix` with `listener_auto_id_prefix`.\n`allow_zero_length_clientid` has no replacement.\n\n## Plugins\n\nPrior to 2.1, plugins could be loaded with the `plugin` or `global_plugin`\noptions, where `plugin` would be applied to all plugins or a single plugin,\ndepending on the `per_listener_setting` value, and `global_plugin` would always\napply to all listeners.\n\n`global_plugin` should still be used to load a plugin across all listener.\n\nTo use a plugin on some listeners only, use `plugin_load`, which loads a plugin\ninto the broker, and `plugin_use` which applies it to a listener.\n\nFor example:\n\n```\nplugin_load dynsec /usr/lib/mosquitto_dynamic_security.so\nplugin_opt_config_file /mosquitto/data/dynamic-security.json\n\nlistener 1883\nplugin_use dynsec\n\nlistener 1884\nlistener_allow_anonymous true\n\nlistener 1885\nplugin_use dynsec\n```\n\nThis configuration loads the dynamic-security plugin and uses it with the\nlisteners on ports 1883 and 1885. The listener on port 1884 does not have the\nplugin applied, and also allows anonymous connections (so is very insecure,\nanything connecting to that port can publish/subscribe to anything).\n\n"
  },
  {
    "path": "www/pages/documentation/migrating-to-2-0.md",
    "content": "<!--\n.. title: Migrating from 1.x to 2.0\n.. slug: migrating-to-2-0\n.. date: 2020-12-03 12:25:28 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n[TOC]\n\n## Introduction\n\nMosquitto 2.0 introduces a number of changes to the behaviour of the broker\nwhich new users need to be aware of, and which this document explains.\n\nIf you are packaging Mosquitto for distribution, see the Packaging and\nDistribution section.\n\nIf you are a plugin author, see the Plugins section.\n\n## Listener behaviour changes\n\nThe way in which Mosquitto configures listeners has been changed to help\nencourage end users to take an informed choice about security, rather than just\nrelying on the previously very forgiving defaults.\n\n### Listener without configuration\n\nWhen Mosquitto is run without a configuration file, or without configuring any\nlisteners, it will now bind to the loopback interfaces 127.0.0.1 and/or ::1.\nThis means that only connections from the local host will be possible. This\nmode allows automated or manual testing on a local machine without the need for\na configuration file. In this mode only, anonymous/unauthenticated users are\nallowed by default.\n\nThis applies to you if you run your broker in a similar way to one of these\nexamples:\n\n* `mosquitto`\n* `mosquitto -p 1883`.\n\nIf you use this mode and wish to have clients connect from a remote machine,\nthen you will need to use a configuration file:\n\n```\nlistener 1883\n# Note that this will not allow anonymous access by default.\n```\n\nThis configuration binds the listener for port 1883 to the `0.0.0.0` or `::`\ninterface by default, i.e. allows connections on all interfaces. It is still\npossible to bind to a specific interface manually, e.g. `listener 1883\n192.168.1.1`.\n\n### Authentication requires configuration\n\nAll listeners now require authentication to be configured. This is with the\nexception of the case where no listener configuration is provided and hence the\nlistener is bound to the loopback interface, as described above.\n\nThis means that `allow_anonymous` now defaults to false. If you currently have\na broker running that has a listener configured in the configuration file, but\nhas no other authentication configured and no explicit `allow_anonymous`\nsetting, then your clients will be unable to connect after upgrading to\nMosquitto 2.0.\n\nThere are three choices :\n\n* Configure the in-built `password_file` and `acl_file` options for\n  authentication.\n* Use an authentication plugin, such as the new [dynamic-security plugin], or\n  the third party [mosquitto-go-auth plugin].\n* Set `allow_anonymous true` - this should be done only if you have a specific\n  need for unauthenticated clients.\n\n### Listener TLS protocol version changes\n\nThe listener `tls_version` option now defines the *minimum* TLS protocol version to\nbe used, rather than the exact version. For example, setting `tls_version\ntlsv1.2` would allow both TLS v1.2 and TLS v1.3.\n\nSupport for TLS v1.0 has been disabled.\n\n### Mixing configuration files with -p\n\nIf you configure a listener in your configuration file *and* use e.g. `-p 1883`\non the command line at the same time, you will need to add all listeners to the\nconfiguration file because this behaviour is no longer supported - the port\nprovided on the command line will be ignored.\n\nIf you have a configuration file like this:\n```\nlistener 1883\n# ...\n```\n\nAnd run Mosquitto like this: `mosquitto -c mosquitto.conf -p 1884`\n\nThen you should instead run Mosquitto as `mosquitto -c mosquitto.conf`, and use\na configuration file with both listeners in:\n```\nlistener 1883\n# ...\nlistener 1884\n# ...\n```\n\n## Use of root/privileged user\n\nIn versions prior to 2.0, if Mosquitto was run as root it would load TLS\ncertificates, start listeners, and start logging, before dropping to the\nunprivileged mosquitto user.\n\nThis behaviour has changed. In 2.0, Mosquitto load the configuration file and\nimmediately drop to the configured unprivileged user, which defaults to\n`mosquitto`. If the `mosquitto` or manually configured user is not available,\nthe broker will attempt to drop to the `nobody` user.\n\nThis means that the only files Mosquitto will access as root are the\nconfiguration files, and hence any other files that Mosquitto needs to access\nor write must be accessible by the unprivileged user.\n\nIn particular those people using TLS certificates from Lets Encrypt will need\nto do something to allow Mosquitto to access those certificates. An example\ndeploy renewal hook script to help with this is at\n[misc/letsencrypt/mosquitto-copy.sh].\n\nIt is still possible to force Mosquitto to run as root, but this is strongly\nrecommended against.\n\n## Other behaviour\n\nThe `pid_file` option will now always attempt to write a pid file,\nregardless of whether the `-d` argument is used when running the broker.\n\nThe `max_queued_messages` option has been increased from 100 to 1000 by\ndefault, and now also applies to QoS 0 messages, when a client is connected.\n\n\n## Packaging and Distribution\n\nThe components that Mosquitto provides can be categorised as follows:\n\n### Client libraries\n\nC/C++ libraries for creating MQTT clients.\n\n* lib/libmosquitto.so.1\n* lib/cpp/libmosquittopp.so.1\n* include/mosquitto.h\n* include/mqtt_protocol.h\n\n### Clients\n\nGeneral purpose command line MQTT clients. These depend upon libmosquitto.so.1.\n\n* client/mosquitto_pub\n* client/mosquitto_sub\n* client/mosquitto_rr\n\n### Broker\n\nThe main offering of the project, the Mosquitto broker, plus associated\nutilities and plugins.\n\n* apps/mosquitto_ctrl/mosquitto_ctrl\n* apps/mosquitto_passwd/mosquitto_passwd\n* plugins/dynamic-security/mosquitto_dynamic_security.so\n* src/mosquitto\n* include/mosquitto_broker.h\n* include/mosquitto_plugin.h\n\nChanges:\n* The mosquitto_passwd utility has changed location.\n* The mosquitto_ctrl utility has been added.\n* The mosquitto_dynamic_security plugin has been added, this is a Mosquitto\n  specific shared library.\n* The mosquitto_ctrl utility requires libmosquitto.so.1.\n* Plugin developers would expect to have the header files from libmosquitto\n  available, as well as those from the broker..\n\n### Dependencies\n\nmosquitto_ctrl and mosquitto_dynamic_security.so require the cJSON library. If\nit is not detected or desired, those features can be disabled.\n\n## Plugins\n\nMosquitto 2.0 introduces a new plugin interface which should be simpler to\ndevelop for, and is more easily extendable. A separate document will describe\nthe new plugin interface. If you have an existing plugin that followed the\nguidelines in `mosquitto_plugin.h`, then it will continue to work with\nMosquitto 2.0 *unless* it is compiled using the Mosquitto 2.0 header files,\nwhich contained a mistake in the documentation.\n\nTo modify your plugin so that it will work on both of Mosquitto 1.6 and\nMosquitto 2.0, you should change your instance of\n`mosquitto_auth_plugin_version` so that it returns the version of the plugin\ninterface that you support, i.e. `4`.\n\n[misc/letsencrypt/mosquitto-copy.sh]:https://github.com/eclipse/mosquitto/tree/master/misc/letsencrypt\n[dynamic-security plugin]:/documentation/dynamic-security/\n[mosquitto-go-auth plugin]:https://github.com/iegomez/mosquitto-go-auth\n"
  },
  {
    "path": "www/pages/documentation/persistence/sqlite.md",
    "content": "<!--\n.. title: Sqlite Persistence\n.. slug: sqlite\n.. date: 2026-01-30 09:00:00 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n## Introduction\n\nAvailable since version 2.1.\n\nThis plugin provides a replacement for the traditional mosquitto persistence\nnormally enabled with `persistence true`.\n\nThis plugin should be preferred when you are interested in persistence, because\nit saves changes to disk as they are made, where as the traditional persistence\nonly takes periodic snapshots.\n\nNote that it is not possible to run both the traditional persistence and the\nsqlite persistence plugin at the same time.\n\n\n## Usage\n\nThe plugin requires minimal configuration.\n\nThe database is stored at the location specified by the `persistence_location`\noption and is named `mosquitto.sqlite3. As an alternative, the file can be\nspecified directly using the `plugin_opt_db_file` option.\n\nThe `plugin_opt_sync` option can be set to `extra`, `full`, `normal`, or `off`,\nwith a default of `normal`. This option controls how hard sqlite works to\nensure data is on the disk before continuing. This is better described by\n[sqlite themselves](https://www.sqlite.org/pragma.html#pragma_synchronous).\n\nThe `plugin_opt_page_size` option sets the database page size, as described\n[here](https://www.sqlite.org/pragma.html#pragma_page_size).\n\nThe `plugin_opt_flush_period` option is a positive integer number of seconds,\ndefaulting to 5, that the plugin will batch database updates over in order to\nimprove performance.\n\n# Config\n\nWindows:\n```\npersistence_location <path to save mosquitto.sqlite3>\nglobal_plugin C:\\Program Files\\Mosquitto\\mosquitto_persist_sqlite.dll\n```\n\nOther:\n```\npersistence_location <path to save mosquitto.sqlite3>\nglobal_plugin /path/to/mosquitto_persist_sqlite.so\n```\n\n# Migration\n\nA [script](https://raw.githubusercontent.com/eclipse-mosquitto/mosquitto/refs/heads/master/plugins/persist-sqlite/migrate_to_persist_sqlite.py)\nis available to help migrate from the traditional persistence.\n"
  },
  {
    "path": "www/pages/documentation/plugins/acl-file.md",
    "content": "<!--\n.. title: ACL file Plugin\n.. slug: acl-file\n.. date: 2026-01-30 09:00:00 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n## Introduction\n\nAvailable since version 2.1.\n\nThis plugin provides the same functionality as the `acl_file` option, and\nshould be the preferred way of using an ACL file.\n\nThe [dynamic-security plugin](/documentation/dynamic-security/) provides a more\npowerful approach to authentication and authorisation.\n\n## Usage\n\nControl access to topics on the broker using an access control list file. If\nthis parameter is defined then only the topics listed will have access.  If the\nfirst character of a line of the ACL file is a `#` it is treated as a comment.\n\nTopic access is added with lines of the format:\n\n```\ntopic [read|write|readwrite|deny] <topic>\n```\n\nThe access type is controlled using `read`, `write`, `readwrite` or `deny`.\nThis parameter is optional (unless `<topic>` contains a space character) - if\nnot given then the access is read/write.  `<topic>` can contain the `+` or `#` \nwildcards as in subscriptions.\n\nThe `deny` option can used to explicitly deny access to a topic that would\notherwise be granted by a broader read/write/readwrite statement. Any `deny`\ntopics are handled before topics that grant read/write access.\n\nThe first set of topics are applied to anonymous clients, assuming anonymous\naccess is allowed. User specific topic ACLs are added after a user line as\nfollows:\n\n```\nuser <username>\n```\n\nThe username referred to here is the same as provided in the CONNECT packet. It\nis not the clientid.\n\nIf is also possible to define ACLs based on pattern substitution within the\ntopic.\n\n```\npattern [read|write|readwrite] <topic>\n```\n\nThe patterns available for substitution are:\n\n* %c to match the client id of the client\n* %u to match the username of the client\n\nThe substitution pattern must be the only text for that level of hierarchy.\n\nThe form is the same as for the topic keyword, but using pattern as the\nkeyword.\n\nPattern ACLs apply to all users even if the `user` keyword has previously\nbeen given.\n\nIf using bridges with usernames and ACLs, connection messages can be allowed\nwith the following pattern:\n\n```\npattern write $SYS/broker/connection/%c/state\n```\n\n\nExample:\n\n```\npattern write sensor/%u/data\n```\n\n# Config\n\nWindows:\n```\nglobal_plugin C:\\Program Files\\Mosquitto\\mosquitto_acl_file.dll\nplugin_opt_acl_file <my acl file path>\n```\n\nOther:\n```\nglobal_plugin /path/to/mosquitto_acl_file.so\nplugin_opt_acl_file <my acl file path>\n```\n"
  },
  {
    "path": "www/pages/documentation/plugins/password-file.md",
    "content": "<!--\n.. title: Password file Plugin\n.. slug: password-file\n.. date: 2026-01-30 09:00:00 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n## Introduction\n\nAvailable since version 2.1.\n\nThis plugin provides the same functionality as the `password_file` option, and\nshould be the preferred way of using an password file.\n\nThe [dynamic-security plugin](/documentation/dynamic-security/) provides a more\npowerful approach to authentication and authorisation.\n\n## Usage\n\nGenerate password files using the [mosquitto_passwd](/man/mosquitto_passwd-1.html) utility.\n\n# Config\n\nWindows:\n```\nglobal_plugin C:\\Program Files\\Mosquitto\\mosquitto_password_file.dll\nplugin_opt_password_file <my password file path>\n```\n\nOther:\n```\nglobal_plugin /path/to/mosquitto_password_file.so\nplugin_opt_password_file <my password file path>\n```\n"
  },
  {
    "path": "www/pages/documentation/plugins/sparkplug-aware.md",
    "content": "<!--\n.. title: Sparkplug Aware Plugin\n.. slug: sparkplug-aware\n.. date: 2026-01-30 09:00:00 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nAvailable since version 2.1.\n\nThe [Sparkplug protocol](https://sparkplug.eclipse.org/) provides a unified way\nto manage topics, device lifetime, and payload format. It is typically intended\nfor use in Industrial Internet of Things applications, such as in factories.\n\nThe Sparkplug specification makes certain requirements on clients and brokers.\nFor brokers there are two levels of conformance: Sparkplug Compliant and\nSparkplug Aware.\n\nAny MQTT broker that conforms to the MQTT v3.1.1 or v5.0 protocol meets the\nrequirements to be Sparkplug Compliant.\n\nA Sparkplug Aware broker also needs to monitor birth messages from Sparkplug\nnodes and devices, and republish them to the appropriate topic within\n`$sparkplug/certificates/spBv1.0/`\n\nLoading this plugin makes provides Sparkplug Aware support for Mosquitto.\n\n## Config\n\nWindows:\n```\nglobal_plugin C:\\Program Files\\Mosquitto\\mosquitto_sparkplug_aware.dll\n```\n\nOther:\n```\nglobal_plugin /path/to/mosquitto_sparkplug_aware.so\n```\n"
  },
  {
    "path": "www/pages/documentation/using-the-snap.md",
    "content": "<!--\n.. title: Using the snap package\n.. slug: using-the-snap\n.. date: 2020-06-14 09:25:28 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nOn Linux systems that have snap support, Mosquitto can be installed from the\ngraphical software installer, or with `snap install mosquitto`.\n\nAfter installing the Mosquitto snap, the Mosquitto broker will be running with\nthe default configuration, which means it is listening for connections on port\n1883 on the local computer only. If you want to allow connections from other\ncomputers you must configure a listener and an [authentication method].\n\nTo test the broker, you can use the `mosquitto_pub` and `mosquitto_sub` command\nline utilities, which are also provided in the snap. `mosquitto_pub` allows you\nto publish messages to an MQTT broker, and `mosquitto_sub` allows you to\nsubscribe to messages from an MQTT broker. Both tools have a large number of\noptions to control how they are used and as such are useful for a wide variety\nof tasks. In this case, we will just use them for some simple testing.\n\nTo subscribe to all messages being published to the MQTT broker on the\n`snap/example` topic, use the following command. If your MQTT broker is not\nrunning on the same machine as `mosquitto_sub`, you will need to change the\n`localhost` argument to match your MQTT broker host or IP address.\n\n```\nmosquitto_sub -h localhost -t 'snap/example' -v\n```\n\nThe `-t snap/example` option sets the topic to subscribe to, and can be\nprovided multiple times. The `-v` option means to print both the topic of the\nmessage as well as its payload.\n\nNow to publish a message to the same topic, use the very similar `mosquitto_pub`\ncommand:\n\n```\nmosquitto_pub -h localhost -t 'snap/example' -m 'Hello from mosquitto_pub'\n```\n\nIn this case the `-m` option provides the message payload to be published. If\neverything works as planned, you should see `mosquitto_sub` print\n\n```\nsnap/example Hello from mosquitto_pub\n```\n\nThis is of course a very simple example, but it does allow testing of the\nbroker operation. Other things you may wish to try are subscribing to wildcard\ntopics that include `#` or `+`, or subscribing to the `$SYS/#` topic to see\ninformation the broker is publishing about itself. Beware that the command line\ntreats `#` as a special character, and `$SYS` will be expanded as a environment\nvariable if you do not surround them with single quotes.\n\nOnce you have finished your testing, you will want to configure your broker to\nhave encrypted connections and use authentication, possibly configuring\nbridges, which allow different brokers to share topics, or many other options.\n\nTo do this, you need to provide a new configuration file. The snap provides an\nexample configuration file at\n`/var/snap/mosquitto/common/mosquitto_example.conf`. This file contains all of\nthe broker configuration, in a similar manner to the man page. To create your\nown configuration, copy the example file to\n`/var/snap/mosquitto/common/mosquitto.conf` and edit according to your needs.\n\nAny additional files required by the configuration, such as TLS certificates\nand keys, must also be placed in `/var/snap/mosquitto/common/` - in new folders\nif wanted. This directory is the only place accessible by Mosquitto when\nrunning as a snap.\n\nStarting and stopping the broker service can be done with the snap command:\n\n```\nsnap start mosquitto\nsnap stop mosquitto\n```\n\nOr via systemd:\n\n```\nsystemctl start snap.mosquitto.mosquitto\nsystemctl stop snap.mosquitto.mosquitto\n```\n\nAll other aspects of running Mosquitto are the same as with any other\ninstallation methods.\n\n## Client configuration files\n\nIf you use the mosquitto_pub, mosquitto_rr, or mosquitto_sub configuration\nfiles they should be placed in `$HOME/snap/mosquitto/current/.config`:\n\n* `$HOME/snap/mosquitto/current/.config/mosquitto_pub`\n* `$HOME/snap/mosquitto/current/.config/mosquitto_rr`\n* `$HOME/snap/mosquitto/current/.config/mosquitto_sub`\n\n[authentication method]:/documentation/authentication-methods\n"
  },
  {
    "path": "www/pages/documentation.md",
    "content": "<!--\n.. title: Documentation\n.. slug: documentation\n.. date: 2020-07-06 17:25:28 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n# Man pages\n\n* [mosquitto] - running the Mosquitto broker\n* [mosquitto.conf] - the Mosquitto broker configuration file\n* [mosquitto_ctrl] - command line utility for managing Mosquitto broker configuration\n* [mosquitto_ctrl_dynsec] - `mosquitto_ctrl` batch mode for the dynamic-security plugin\n* [mosquitto_ctrl_shell] - `mosquitto_ctrl` interactive shell mode (recommended)\n* [mosquitto_passwd] - command line utility for generating Mosquitto password files\n* [mosquitto_pub] - command line utility for publishing messages to a broker\n* [mosquitto_rr] - command line utility for simple request/response with a broker\n* [mosquitto_signal] - command line utility for sending signals to a broker, most useful on Windows\n* [mosquitto_sub] - command line utility for subscribing to topics on a broker\n* [mosquitto-tls] - brief cheat sheet for creating x509 certificates\n* [mqtt] - description of MQTT features\n\n# Listeners\n\n* [Using Mosquitto with HAProxy] - using Mosquitto with HAProxy with or without TLS termination.\n* [Replacing the per_listener_settings option](/documentation/listeners/per_listener_settings/)\n\n# Persistence\n\n* [Sqlite](/documentation/persistence/sqlite/)\n\n# Plugins\n\n* [ACL file] - replacement for the `acl_file` option.\n* [Password file] - replacement for the `password_file` option.\n* [Dynamic Security] - details of using the Dynamic Security authentication and access control plugin.\n* [Sparkplug Aware] - make Mosquitto fully compliant with the Sparkplug protocol.\n\n# Other\n\n* [Authentication methods] - details on the different authentication options available.\n* [Using the snap package] - specific instructions on installing and configuring the Mosquitto snap package.\n* [Migrating from 1.x to 2.0] - details of changes needed to migrate to version 2.0.\n\n# libmosquitto API\n\n* [libmosquitto API documentation]\n\n# Third party\n\nThese are some Mosquitto documentation hosted by third parties.\n\n* [Steve's internet guide] - a broad range of documentation and examples\n  covering Mosquitto and the Paho Python client, amongst others.\n* [docs.cedalo.com] - includes documentation for both Mosquitto and Eclipse\n  Streamsheets\n\n[mosquitto]:/man/mosquitto-8.html\n[mosquitto.conf]:/man/mosquitto-conf-5.html\n[mosquitto_ctrl]:/man/mosquitto_ctrl-1.html\n[mosquitto_ctrl_dynsec]:/man/mosquitto_ctrl_dynsec-1.html\n[mosquitto_ctrl_shell]:/man/mosquitto_ctrl_shell-1.html\n[mosquitto_passwd]:/man/mosquitto_passwd-1.html\n[mosquitto_pub]:/man/mosquitto_pub-1.html\n[mosquitto_rr]:/man/mosquitto_rr-1.html\n[mosquitto_signal]:/man/mosquitto_signal-1.html\n[mosquitto_sub]:/man/mosquitto_sub-1.html\n[mosquitto-tls]:/man/mosquitto-tls-7.html\n[mqtt]:/man/mqtt-7.html\n\n\n[libmosquitto API documentation]:/api/\n\n[Authentication methods]:/documentation/authentication-methods/\n[Using the snap package]:/documentation/using-the-snap/\n[Dynamic Security]:/documentation/dynamic-security/\n[ACL file]:/documentation/plugins/acl-file/\n[Password file]:/documentation/plugins/password-file/\n[Sparkplug Aware]:/documentation/plugins/sparkplug-aware/\n[Using Mosquitto with HAProxy]:/documentation/listeners/haproxy/\n[Migrating from 1.x to 2.0]:/documentation/migrating-to-2-0/\n\n[Steve's internet guide]: http://www.steves-internet-guide.com/\n[docs.cedalo.com]: https://docs.cedalo.com/\n"
  },
  {
    "path": "www/pages/download.md",
    "content": "<!--\n.. title: Download\n.. slug: download\n.. date: 2021-10-27 16:37:38 UTC+1\n.. tags: tag\n.. category: category\n.. link: link\n.. description:\n.. type: text\n-->\n\n# Source\n\n* [mosquitto-2.1.2.tar.gz](https://mosquitto.org/files/source/mosquitto-2.1.2.tar.gz) ([GPG signature](https://mosquitto.org/files/source/mosquitto-2.1.2.tar.gz.asc))\n* [Git source code repository](https://github.com/eclipse-mosquitto/mosquitto) (github.com)\n\nOlder downloads are available at [https://mosquitto.org/files/](../files/)\n\n# Binary Installation\n\nThe binary packages listed below are supported by the Mosquitto project. In many\ncases Mosquitto is also available directly from official Linux/BSD\ndistributions.\n\n## Windows\n\n* [mosquitto-2.1.2-install-windows-x64.exe](https://mosquitto.org/files/binary/win64/mosquitto-2.1.2-install-windows-x64.exe)\n* [mosquitto-2.1.2-install-windows-x86.exe](https://mosquitto.org/files/binary/win32/mosquitto-2.1.2-install-windows-x86.exe)\n\nOlder installers can be found at [https://mosquitto.org/files/binary/](https://mosquitto.org/files/binary/).\n\nSee also README-windows.md after installing.\n\n## Mac\nMosquitto can be installed from the homebrew project. See\n[brew.sh](https://brew.sh/) and then use `brew install mosquitto`\n\n## Linux distributions with snap support\n\n* `snap install mosquitto`\n\n## Debian\n* Mosquitto is now in Debian proper. There will be a short delay between a new\n  release and it appearing in Debian as part of the normal Debian procedures.\n  The tracker for the package is at <https://tracker.debian.org/pkg/mosquitto>.\n* There are also Debian repositories provided by the mosquitto project, as\n  described at <https://mosquitto.org/2013/01/mosquitto-debian-repository>\n\n## Raspberry Pi\nMosquitto is available through the main repository.\n\nThere are also Debian repositories provided by the mosquitto project, as\ndescribed at <https://mosquitto.org/2013/01/mosquitto-debian-repository/>\n\n## Ubuntu\nMosquitto is available in the Ubuntu repositories so you can install as with\nany other package. If you are on an earlier version of Ubuntu or want a more\nrecent version of mosquitto, add the [mosquitto-dev\nPPA](https://launchpad.net/%7Emosquitto-dev/+archive/mosquitto-ppa/) to your\nrepositories list - see the link for details. mosquitto can then be installed\nfrom your package manager.\n\n* `sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa`\n* `sudo apt-get update`\n"
  },
  {
    "path": "www/pages/index.html",
    "content": "<!--\n.. title: Eclipse Mosquitto\n.. slug: index\n.. date: 2018-01-08 15:39:57 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n<section class=\"section is-small\">\n  <div class=\"container\">\n\t<p>Eclipse Mosquitto is an open source (EPL/EDL licensed) message broker that\n\t\timplements the MQTT protocol versions 5.0, 3.1.1 and 3.1. Mosquitto\n\t\tis lightweight and is suitable for use on all devices from low\n\t\tpower single board computers to full servers.</p>\n\n\t\t<p>The MQTT protocol provides a lightweight method of carrying\n\t\tout messaging using a publish/subscribe model. This makes it\n\t\tsuitable for Internet of Things messaging such as with low\n\t\tpower sensors or mobile devices such as phones, embedded\n\t\tcomputers or microcontrollers.</p>\n\n\t\t<p>The Mosquitto project also provides a C library for\n\t\timplementing MQTT clients, and the very popular mosquitto_pub\n\t\tand mosquitto_sub command line MQTT clients.</p>\n\n\t\t<p>Mosquitto is part of the <a href=\"https://eclipse.org/\">Eclipse\n\t\tFoundation</a>, and is an <a href=\"https://iot.eclipse.org/\">iot.eclipse.org</a>\n\t\tproject. The development is driven by <a href=\"https://cedalo.com/\">Cedalo</a>.</p>\n  </div>\n</section>\n<hr/>\n\n<section class=\"section is-small\">\n\t<div class=\"container\">\n\t\t<div class=\"columns\">\n\t\t\t<div class=\"column column-justify\">\n\t\t\t\t<h2 style=\"text-align: center\">Download and Security</h2>\n\t\t\t\t<p>Mosquitto is highly portable and available for a wide range of\n\t\t\t\tplatforms. Go to the dedicated <a href=\"/download\">download\n\t\t\t\tpage</a> to find the source or binaries for your platform.</p>\n\t\t\t\t<p>Read the <a href=\"/ChangeLog.txt\">Change Log</a> to find out\n\t\t\t\tabout recent releases.</p>\n\n\t\t\t\t<p>Use the <a href=\"/security\">security</a> page to find out\n\t\t\t\thow to report vulnerabilities or responses to past security\n\t\t\t\tissues.</p>\n\t\t\t</div>\n\n\t\t\t<div class=\"column column-justify\">\n\t\t\t\t<h2 style=\"text-align: center\">Test</h2>\n\t\t\t\t<p>You can have your own instance of Mosquitto running in\n\t\t\t\tminutes, but to make testing even easier, the Mosquitto Project\n\t\t\t\truns a test server at\n\t\t\t\t<a href=\"https://test.mosquitto.org/\">test.mosquitto.org</a> where\n\t\t\t\tyou can test your clients in a variety of ways: plain MQTT,\n\t\t\t\tMQTT over TLS, MQTT over TLS (with\n\t\t\t\t<a href=\"https://test.mosquitto.org/ssl/\">client certificate</a>),\n\t\t\t\tMQTT over WebSockets and MQTT over WebSockets with TLS.</p>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div class=\"columns\">\n\t\t\t<div class=\"column column-justify\">\n\t\t\t\t<h2 style=\"text-align: center\">Community</h2>\n\t\t\t\t<ul>\n\t\t\t\t<li>Report bugs or submit changes on the <a href=\"https://github.com/eclipse/mosquitto\">Github repository</a></li>\n\t\t\t\t<li>Talk to other users on the <a href=\"https://dev.eclipse.org/mailman/listinfo/mosquitto-dev\">Mosquitto mailing list</a>\n\t\t\t\tor on <a href=\"https://eclipse-iot-wg.slack.com/join/shared_invite/zt-d8zil9s0-NF5UHh92Odf3AbonspswHA#/\">Slack</a>.</li>\n\t\t\t\t<li>Get help from the <a href=\"https://forum.cedalo.com/\">forums</a>.</li>\n\t\t\t\t<li><a href=\"/2017/06/citing-eclipse-mosquitto/\">Cite Mosquitto</a> in your academic work.</li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\n\t\t\t<div class=\"column column-justify\">\n\t\t\t\t<h2 style=\"text-align: center\">Support</h2>\n\t\t\t\t<p>Support is always available from the community channels on a\n\t\t\t\tbest effort basis. If you require commercial support,\n\t\t\t\t<a href=\"https://cedalo.com/\">Cedalo</a> can offer support for hosted\n\t\t\t\tor on-premise instances, consulting on the use of Mosquitto,\n\t\t\t\tand custom development to your needs.</p>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div class=\"columns\">\n\t\t\t<div class=\"column column-justify\">\n\t\t\t\t<h2 style=\"text-align: center\">Related Projects</h2>\n\t\t\t\t<p><a href=\"https://eclipse.org/paho/\">Paho</a> provides MQTT\n\t\t\t\tclient library implementations in a wide variety of\n\t\t\t\tlanguages.</p>\n\t\t\t\t<p><a href=\"https://eclipse.org/streamsheets/\">Streamsheets</a> is an\n\t\t\t\teasy to use web based real time spreadsheet interface that can\n\t\t\t\tbe used to process incoming data from a variety of sources,\n\t\t\t\tsuch as MQTT, OPC-UA, and REST. Developers and non-developers\n\t\t\t\tcan use Streamsheets to control processes and build dashboards,\n\t\t\t\tfor example. Mosquitto is a core component of Streamsheets.</p>\n\t\t\t</div>\n\t\t\t<div class=\"column column-justify\">\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</section>\n"
  },
  {
    "path": "www/pages/roadmap.md",
    "content": "<!--\n.. title: Roadmap\n.. slug: roadmap\n.. date: 2018-11-09 10:53:50 UTC\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n# Roadmap\n\n## Version 1.6\n\nThe next minor release. The focus of this release is on providing support for\nversion 5 of the MQTT protocol.\n\nThis release will provide a feature complete implementation, but does not\nrepresent the final interface for all features. In particular, functions are\nbeing added to libmosquitto to provide support for MQTT 5 features, but these\nwill be consolidated with the API changes planned for version 2.0.\n\n### Deprecation notices\n\n#### libmosquittopp\n\nlibmosquittopp, the C++ wrapper around libmosquitto is now deprecated and will\nbe removed in the next major release (2.0). The wrapper came about by an\nexternal request and at the time it was created there were no other C++\nsolutions for MQTT. This has changed in the past years and this wrapper\nprovides no benefit over true C++ libraries or using the pure C libmosquitto.\n\n#### libmosquitto API changes\n\nThe Mosquitto project has maintained API and ABI compatibility in libmosquitto\nsince version 1.0, and has dealt with the introduction of new specification\nfeatures by adding new functions which duplicate the behaviour of existing\nfunctions, but with additional arguments to support the new features.\nParticularly with regards to adding support for MQTT version 5, this has lead\nto a proliferation of functions which offer small variations on a theme.\n\nThe libmosquitto functions listed below (which includes some new functions\nincluded in 1.6) are going to be updated for version 2.0. Functions not listed\nhere should still be considered at risk of being updated.\n\n* mosquitto\\_will\\_set\n* mosquitto\\_connect\\*\n* mosquitto\\_reconnect\\*\n* mosquitto\\_disconnect\n* mosquitto\\_publish\\*\n* mosquitto\\_subscribe\\*\n* mosquitto\\_unsubscribe\\*\n* mosquitto\\_loop\\*\n* mosquitto\\_\\*\\_callback\\_set\n* All callbacks\n* mosquitto\\_\\*\\_topic\\_check\\*\n\n\n## Version 2.0\n\nThis is the next major release and includes breaking changes. Other features\nplanned include:\n\n## Disk persistence improvements\n\nA new disk persistence interface will be created to allow persistence to occur\nimmediately, rather than periodically. This will allow queued messages for\ndisconnected clients to be removed from memory, and reduce the periodic pause\ncaused when writing the persistence file.\n\n## Breaking changes\n\n### libmosquitto\n\nThe libmosquitto API is being consolidated to better support the new MQTT 5\nfeatures whilst reducing the number of function variants.\n\n### libmosquittopp\n\nThe C++ wrapper around libmosquitto will be removed in this release.\n"
  },
  {
    "path": "www/pages/security.md",
    "content": "<!--\n.. title: Security\n.. slug: security\n.. date: 2021-04-03 11:55:50 UTC+1\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n# Reporting security vulnerabilities\n\nIf you think you have found a security vulnerability in Mosquitto, please\nfollow the steps on [Eclipse Security] page to report it.\n\n# Past vulnerabilities\n\nListed with most recent first. Further information on security related issues\ncan be found in the [security category].\n\n* August 2023: [CVE-2023-0809]: Fix excessive memory being allocated based on\n  malicious initial packets that are not CONNECT packets. Affecting versions\n  **1.5.0** to **2.0.15**. Fixed in **2.0.16**.\n* August 2023: [CVE-2023-3592]: Fix memory leak when clients send v5 CONNECT\n  packets with a will message that contains invalid property types. Affecting\n  version **1.6.0** to **2.0.15** Fixed in **2.0.16**.\n* August 2023: [CVE-2023-28366]: Clients sending unacknowledged QoS 2 messages\n  with duplicate message ids cause a memory leak. Affecting versions **1.3.2**\n  to **2.0.15** inclusive, fixed in **2.0.16**.\n* August 2022: Deleting the anonymous group in the dynamic security plugin\n  could lead to a crash. Affecting versions **2.0.0** to **2.0.14** inclusive,\n  fixed in **2.0.15**.\n* August 2021: [CVE-2021-34434] Affecting versions **2.0.0** to **2.0.11**\n  inclusive, fixed in **2.0.12**.\n* April 2021: [CVE-2021-28166] Affecting versions **2.0.0** to **2.0.9**\n  inclusive, fixed in **2.0.10**.\n* December 2020: Running mosquitto_passwd with the following arguments only\n  `mosquitto_passwd -b password_file username password` would cause the\n  username to be used as the password. Affecting versions **2.0.0** to\n  **2.0.2** inclusive, fixed in **2.0.3**.\n* September 2019: [CVE-2019-11779]. Affecting versions **1.5** to **1.6.5**\n  inclusive, fixed in **1.6.6** and **1.5.9**. More details at [version-166-released].\n* September 2019: [CVE-2019-11778]. Affecting versions **1.6** to **1.6.4**\n  inclusive, fixed in **1.6.5**. More details at [version-166-released].\n* April 2019: No CVE assigned. Affecting versions **1.6** and **1.6.1**,\n  fixed in **1.6.2**. More details at [version-162-released].\n* December 2018: [CVE-2018-20145]. Affecting versions **1.5** to **1.5.4**\n  inclusive, fixed in **1.5.5.**. More details at [version-155-released].\n* November 2018: No CVE assigned. Affecting versions **1.4** to **1.5.3**\n  inclusive, fixed in **1.5.4**. More details at [version-154-released].\n* September 2018: [CVE-2018-12543] affecting versions **1.5** to **1.5.2**\n  inclusive, fixed in **1.5.3**.\n* April 2018: [CVE-2017-7655] affecting versions **1.0** to **1.4.15**\n  inclusive, fixed in **1.5**.\n* April 2018: [CVE-2017-7654] affecting versions **1.0** to **1.4.15**\n  inclusive, fixed in **1.5**.\n  [security-advisory-cve-2017-7653-cve-2017-7654].\n* April 2018: [CVE-2017-7653] affecting versions **1.0** to **1.4.15**\n  inclusive, fixed in **1.5**.\n* February 2018: [CVE-2017-7651] affecting versions **0.15** to **1.4.14**\n  inclusive, fixed in **1.4.15**. More details at\n  [security-advisory-cve-2017-7651-cve-2017-7652].\n* February 2018: [CVE-2017-7652] affecting versions **1.0** to **1.4.14**\n  inclusive, fixed in **1.4.15**. More details at\n  [security-advisory-cve-2017-7651-cve-2017-7652].\n* June 2017: [CVE-2017-9868] affecting versions **0.15** to **1.4.12**\n  inclusive, fixed in **1.4.13**. More details at\n  [security-advisory-cve-2017-9868].\n* May 2017: [CVE-2017-7650] affecting versions **0.15** to **1.4.11**\n  inclusive, fixed in **1.4.12**. More details at\n  [security-advisory-cve-2017-7650].\n\n[version-166-released]: /blog/2019/09/version-1-6-6-released/\n[version-162-released]: /blog/2019/04/version-1-6-2-released/\n[version-155-released]: /blog/2018/11/version-155-released/\n[version-154-released]: /blog/2018/11/version-154-released/\n[security-advisory-cve-2018-12543]: /blog/2018/09/security-advisory-cve-2018-12543/\n[security-advisory-cve-2017-7651-cve-2017-7652]: /blog/2018/02/security-advisory-cve-2017-7651-cve-2017-7652/\n[security-advisory-cve-2017-7650]: /blog/2017/05/security-advisory-cve-2017-7650/\n[security-advisory-cve-2017-9868]: /blog/2017/06/security-advisory-cve-2017-9868/\n\n[Eclipse Security]: https://www.eclipse.org/security/\n[security category]: /blog/categories/security/\n\n[CVE-2021-34434]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-34434\n[CVE-2021-28166]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-28166\n[CVE-2019-11779]: https://nvd.nist.gov/vuln/detail/CVE-2019-11779\n[CVE-2019-11778]: https://nvd.nist.gov/vuln/detail/CVE-2019-11778\n[CVE-2018-20145]: https://nvd.nist.gov/vuln/detail/CVE-2018-20145\n[CVE-2018-12543]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12543\n[CVE-2017-9868]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9868\n[CVE-2017-7655]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7655\n[CVE-2017-7654]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7654\n[CVE-2017-7653]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7653\n[CVE-2017-7652]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7652\n[CVE-2017-7651]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7651\n[CVE-2017-7650]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7650\n"
  },
  {
    "path": "www/plugins/__init__.py",
    "content": "# Plugin modules go here."
  },
  {
    "path": "www/plugins/docbookmanpage/docbookmanpage.plugin",
    "content": "[Core]\nName = docbookmanpage\nModule = docbookmanpage\n\n[Nikola]\nPluginCategory = PageCompiler\n\n[Documentation]\nAuthor = Roger Light (asciidoc code by Roberto Alsina)\nVersion = 0.4\nWebsite = https://github.com/ralight/nikola-docbook-manpage\nDescription = Compile Docbook manpages into html, based on asciidoc plugin.\n"
  },
  {
    "path": "www/plugins/docbookmanpage/docbookmanpage.py",
    "content": "# -*- coding: utf-8 -*-\n\n# Copyright © 2012-2014 Roberto Alsina and others.\n\n# Permission is hereby granted, free of charge, to any\n# person obtaining a copy of this software and associated\n# documentation files (the \"Software\"), to deal in the\n# Software without restriction, including without limitation\n# the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the\n# Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice\n# shall be included in all copies or substantial portions of\n# the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\n# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\n# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\"\"\"Implementation of compile_html based on asciidoc.\n\nYou will need, of course, to install asciidoc\n\n\"\"\"\n\nimport codecs\nimport os\nimport subprocess\n\nfrom nikola.plugin_categories import PageCompiler\nfrom nikola.utils import makedirs, req_missing, write_metadata\n\ntry:\n    from collections import OrderedDict\nexcept ImportError:\n    OrderedDict = dict  # NOQA\n\n\nclass CompileDocbookManpage(PageCompiler):\n    \"\"\"Compile docbookmanpage into HTML.\"\"\"\n\n    name = \"docbookmanpage\"\n    demote_headers = True\n\n    def compile(self, source, dest, is_two_file=True, post=None, lang=None):\n        \"\"\"Compile the source file into HTML and save as dest.\"\"\"\n        makedirs(os.path.dirname(dest))\n        if \"common/\" in source and post is not None:\n            post.write_depfile(dest, [], post, lang)\n            with open(dest, \"wt\") as f:\n                pass\n            return []\n        binary = self.site.config.get('XSLTPROC_BINARY', 'xsltproc')\n        xslpath = os.path.join(os.path.split(__file__)[0], 'html.xsl')\n        try:\n            source = os.path.abspath(source)\n            dest = os.path.abspath(dest)\n            xslpath = os.path.abspath(xslpath)\n            subprocess.check_call((binary, '--xinclude', '-o', dest, xslpath, source))\n            if post is None:\n                if shortcode_deps:\n                    self.logger.error(\n                        \"Cannot save dependencies for post {0} (post unknown)\",\n                        source)\n        except OSError as e:\n            print(e)\n            req_missing(['xsltproc'], 'build this site (compile with xsltproc)', python=False)\n\n    def create_post(self, path, content=None, onefile=False, is_page=False, **kw):\n        \"\"\"Create post file with optional metadata.\"\"\"\n        metadata = OrderedDict()\n        metadata.update(self.default_metadata)\n        metadata.update(kw)\n        makedirs(os.path.dirname(path))\n        if not content.endswith('\\n'):\n            content += '\\n'\n        with codecs.open(path, \"wb+\", \"utf8\") as fd:\n            if onefile:\n                fd.write(\"////\\n\")\n                fd.write(write_metadata(metadata))\n                fd.write(\"////\\n\")\n            fd.write(content)\n"
  },
  {
    "path": "www/plugins/docbookmanpage/html.xsl",
    "content": "<!-- Set parameters for manpage xsl -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n\t<xsl:import href=\"/usr/share/xml/docbook/stylesheet/docbook-xsl/html/docbook.xsl\"/>\n\t<xsl:output encoding=\"utf-8\" indent=\"yes\"/>\n\t<xsl:param name=\"html.stylesheet\">man.css</xsl:param>\n\t<!-- Generate ansi style function synopses. -->\n\t<xsl:param name=\"man.funcsynopsis.style\">ansi</xsl:param>\n\t<xsl:param name=\"funcsynopsis.style\">ansi</xsl:param>\n\t<xsl:param name=\"man-funcprototype-style\">ansi</xsl:param>\n\t<xsl:param name=\"make.clean.html\" select=\"1\"></xsl:param>\n\t<xsl:param name=\"make.valid.html\" select=\"1\"></xsl:param>\n\t<xsl:param name=\"html.cleanup\" select=\"1\"></xsl:param>\n\t<xsl:param name=\"docbook.css.source\"></xsl:param>\n\t<xsl:param name=\"css.decoration\" select=\"1\"></xsl:param>\n\t<xsl:param name=\"variablelist.as.table\" select=\"0\"></xsl:param>\n\t<xsl:param name=\"variablelist.term.break.after\">1</xsl:param>\n\t<xsl:param name=\"citerefentry.link\" select=\"1\"></xsl:param>\n</xsl:stylesheet>\n"
  },
  {
    "path": "www/posts/2009/12/version-0-2-released.md",
    "content": "<!--\n.. title: Version 0.2 released\n.. slug: version-0-2-released\n.. date: 2009-12-04 10:49:06\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 0.2 released - please see the [change log](/ChangeLog.txt).\n"
  },
  {
    "path": "www/posts/2009/12/version-0-3-released.md",
    "content": "<!--\n.. title: Version 0.3 released\n.. slug: version-0-3-released\n.. date: 2009-12-17 10:52:55\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n * Added logging support.\n * Now restarts much more quickly even when the network socket was in use.\n * Can now be configured to run on multiple network ports and restricted to\n   specific network addresses.\n * Added host access control in the form of tcp-wrappers support.\n\nSee the [change log](/ChangeLog.txt) for full details.\n\nWild card support in topics is coming in the next version.\n"
  },
  {
    "path": "www/posts/2010/01/mailing-list-irc.md",
    "content": "<!--\n.. title: Mailing list / irc\n.. slug: mailing-list-irc\n.. date: 2010-01-11 10:18:10\n.. tags: Support\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nWe've created some new support channels for mosquitto - a mailing list and an\nirc channel. Although they are both intended for providing support for\nmosquitto itself, general discussion of anything to do with mqtt is strongly\nencouraged. We want to reduce the barrier to getting started and provide a\nplace where people can share their experiences.\n\nThe mailing list is at <http://launchpad.net/~mqtt-users>\n\nThe irc channel is #mqtt on the [freenode network]\n\n[freenode network]: http://freenode.net/\n"
  },
  {
    "path": "www/posts/2010/01/version-0-4-1-released.md",
    "content": "<!--\n.. title: Version 0.4.1 released\n.. slug: version-0-4-1-released\n.. date: 2010-01-12 22:10:13\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release:\n\n * Fix regex used for finding retained messages to send on new subscription.\n"
  },
  {
    "path": "www/posts/2010/01/version-0-4-released.md",
    "content": "<!--\n.. title: Version 0.4 released\n.. slug: version-0-4-released\n.. date: 2010-01-05 10:54:37\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n * Added support for wildcard subscriptions using + and #.\n * All network operations are now non-blocking and can cope with partial packets, meaning that networking should be a lot more reliable.\n * Total messsages/bytes sent/received are now available in $SYS.\n * Improved logging information - use client ip address and id instead of socket number.\n * Keepalive==0 is now correctly treated as \"never disconnect\".\n * Default logging destination no longer includes \"topics\" to prevent possible error logging to the db before it is initialised.\n * Periodic $SYS messages can now be disabled.\n\nSee the [changelog] for full details.\n\n[changelog]: /ChangeLog.txt\n"
  },
  {
    "path": "www/posts/2010/02/version-0-4-2-released.md",
    "content": "<!--\n.. title: Version 0.4.2 released\n.. slug: version-0-4-2-released\n.. date: 2010-02-06 14:50:27\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Fix segfault on client connect with invalid protocol name/version.\n\nGet it at the [download page].\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/03/google-powermeter.md",
    "content": "<!--\n.. title: Google powermeter\n.. slug: google-powermeter\n.. date: 2010-03-31 17:37:08\n.. tags: Applications\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nA popular use for mqtt brokers seems to be coupling them with a [CurrentCost]\n(or similar) energy monitor to then log energy data and produce pretty (and\nuseful!) graphs.\n\nGoogle recently opened up their PowerMeter API which looks to provide very nice\ngraphing of energy data. They are working with utility companies directly with\nin home monitors, but it's also possible to use it as an individual.\n\nToby Evans got to the bottom of registering a device (see his [explanatory blog\npost]) which just leaves getting data to Google.\n\nIf you're already logging energy data to an MQTT broker, it's as simple as\nadding another subscriber to send the data to Google. You could use the\nmosquitto_sub client and a script I wrote for posting to google,\n[google_powermeter_update_mqtt.pl] like so:\n\n```\nmosquitto_sub -t sensors/cc128/ch1 | google_powermeter_update_mqtt.pl\n```\n\nThis assumes that the data appearing on the sensors/cc128/ch1 topic is in the\nformat `&lt;unix timestamp&gt; &lt;power reading in Watts&gt;`.\n\nIf you're not logging your energy data to a broker, you should probably\nconsider doing so :) There is another script [google_powermeter_update.pl]\nwhich may be more suitable and can be used as:\n\n```\ngoogle_powermeter_update.pl &lt;unix timestamp&gt; &lt;power in Watts&gt;\n```\n\nBoth of the scripts need your user details adding and should be easy to modify\nto match your own particular need. They also assume you're using a single\ncumulative variable with your device and will need modifying if you're using\nmore than one variable or aren't using cumulative variables.\n\nFor reference, I use the script [cc128.pl] to read data from my CurrentCost\nCC128 (Envi).\n\n# Update:\n\nGoogle has a limit of 6 API requests per hour, so the above description will\nonly work for a short while (the 6 request limit doesn't appear to be a hard\nlimit when you first exceed it, but becomes increasingly stricter). I'm now\nlogging my CC128 data to a mysql database and sending batch updates every 15\nminutes with [google_powermeter_update_mysql.pl].\n\n[CurrentCost]: http://currentcost.com/\n[explanatory blog post]: http://2cheap2meter.blogspot.com/2010/03/setting-up-google-powermeter.html\n[google_powermeter_update_mqtt.pl]: /files/perl/google_powermeter_update_mqtt.pl\n[google_powermeter_update.pl]: /files/perl/google_powermeter_update.pl\n[cc128.pl]: /files/perl/cc128.pl\">cc128.pl\n[google_powermeter_update_mysql.pl]: /files/perl/google_powermeter_update_mysql.pl\n"
  },
  {
    "path": "www/posts/2010/03/upgrading-to-0-5-1.md",
    "content": "<!--\n.. title: Upgrading to 0.5.1\n.. slug: upgrading-to-0-5-1\n.. date: 2010-03-01 09:48:41\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nWhen upgrading to 0.5.1 from 0.4 or higher, there is an important change in the\nlocation of the sqlite3-pcre library used. On Linux, the expected location of\nthis library has changed from /usr/lib/sqlite3-pcre.so to\n/usr/lib/sqlite3/pcre.so. This is because the library is an extension\nspecifically for sqlite3, not a general use shared library.\n\nIf you installed sqlite3-pcre manually, or are not using Ubuntu, you should\neither modify the `ext_sqlite3_regex` option in /etc/mosquitto.conf to point to\nyour library path, or move the library to the new location.\n\nIf you are using Ubuntu and have installed mosquitto from the launchpad ppa,\nthis will largely be taken care of. However, due to a mistake in the packaging\nof sqlite3-pcre, you must first remove sqlite3-pcre with your package manager\nand then reinstall it before updating mosquitto. You will only ever need to do\nthis once.\n\nSorry for the inconvenience caused by this change. If you have any problem or\nquestions, feel free to get in touch on the [mqtt users mailing list].\n\n[mqtt users mailing list]: https://launchpad.net/~mqtt-users\n"
  },
  {
    "path": "www/posts/2010/03/version-0-5-1-released.md",
    "content": "<!--\n.. title: Version 0.5.1 released\n.. slug: version-0-5-1-released\n.. date: 2010-03-01 09:26:28\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis announcement summarises the changes in both 0.5 and 0.5.1.\n\nThe interesting changes:\n\n * Add mosquitto_sub and mosquitto_pub, simple clients for subscribe/publish.\n * Change persistence behaviour. The database is now stored in memory even if\n   persistence is enabled. It is written to disk when mosquitto exits and also\n   at periodic intervals as defined by the new `autosave_interval` option. This\n   makes persistence more suitable when being used on devices with a limited\n   number of writes, such as flash.\n * Default sqlite3-pcre path on Linux is now /usr/lib/sqlite3/pcre.so to match\n   new sqlite3-pcre packages.\n\nThe less interesting/bug fixes:\n\n * No longer store QoS=0 messages for disconnected clients that do not have\n   clean start set.\n * Rename `msg_timeout` option to `retry_interval` for better rsmb\n   compatibility.\n * The writing of the persistence database may be forced by sending mosquitto\n   the SIGUSR1 signal.\n * Clients that do not send CONNECT as their first command are now\n   disconnected.\n * Boolean configuration values may now be specified with true/false as well as\n   1/0.\n * Log message on CONNECT with invalid protocol or protocol version.\n * Add man pages for clients.\n * Add general man page on mqtt.\n * Root privileges are now dropped only after attempting to write a pid file\n   (if configured). This means that the pid file can be written to /var/run/\n   directly and should fix bug #523183.\n"
  },
  {
    "path": "www/posts/2010/03/version-0-5-2-released.md",
    "content": "<!--\n.. title: Version 0.5.2 released\n.. slug: version-0-5-2-released\n.. date: 2010-03-02 16:29:22\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release; it is recommended that you upgrade to this version:\n\n * Always update last backup time, so that the backup doesn't run every time\n   through the main loop once `autosave_interval` has been reached.\n * Report $SYS/broker/uptime in the same format as rsmb.\n * Make mandatory options obvious in usage output and man page of\n   mosquitto_pub. Fixes bug [#529990].\n * Treat subscriptions with a trailing slash correctly. This should fix bugs\n   [#530099] and [#530369].\n\nMosquitto is now also available for Linux x86 statically compiled against\nsqlite3, which makes it usable on older distributions such as Ubuntu Hardy that\nare still supported but do not have a sufficiently new version of sqlite3. To\ndownload this package, go to the [download page].\n\n[#529990]: https://bugs.launchpad.net/mosquitto/+bug/529990\n[#530099]: https://bugs.launchpad.net/mosquitto/+bug/530099\n[#530369]: https://bugs.launchpad.net/mosquitto/+bug/530369\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/03/version-0-5-3-released.md",
    "content": "<!--\n.. title: Version 0.5.3 released\n.. slug: version-0-5-3-released\n.. date: 2010-03-03 22:03:26\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Will messages are now only sent when a client disconnects  unexpectedly.\n * Fix all incoming topics/subscriptions that start with a / or  contain\n   multiple / in a row (//). This should finally fix bug [#530099].\n * Do actually disconnect client when it sends an empty  subscription/topic\n   string.\n * Add missing $SYS/broker/clients/total to man page.\n\n[#530099]: https://bugs.launchpad.net/mosquitto/+bug/530099\n"
  },
  {
    "path": "www/posts/2010/03/version-0-5-4-released.md",
    "content": "<!--\n.. title: Version 0.5.4 released\n.. slug: version-0-5-4-released\n.. date: 2010-03-14 18:28:15\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Fix memory allocation in `mqtt3_fix_sub_topic()` ([bug #531861]).\n * Remove accidental limit of 100 client connections.\n * Fix mosquitto_pub handling of messages with QoS&gt;0 ([bug #537061]).\n\n[bug #531861]: https://bugs.launchpad.net/mosquitto/+bug/531861\n[bug #537061]: https://bugs.launchpad.net/mosquitto/+bug/537061\n"
  },
  {
    "path": "www/posts/2010/04/help-wanted-rpm-packaging.md",
    "content": "<!--\n.. title: Help wanted – RPM packaging\n.. slug: help-wanted-rpm-packaging\n.. date: 2010-04-26 16:59:30\n.. tags: Packaging,Support\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI'm currently working on the finishing touches of mosquitto 0.6 and will\nhopefully be releasing it some time this week in time for oggcamp. Mosquitto is\npretty usable now so I'm keen on making it as easy as possible for people to\nget and use. The ultimate goal is of course to get it into the major Linux\ndistros so it appears in the normal package repositories. Until then, other\nsolutions are possible. I can provide Windows executables and have a PPA to\nsupport Ubuntu Linux users, but don't have anything for rpm based distros.\n\nCan you help? I'm quite happy using the opensuse build service to build and\nhost the final packages, but the creation of the rpm build script isn't\nsomething I know how to do at the moment. Given the amount of time I've spent\non the Debian style packaging, I thought I'd ask for help with rpms! :)\n\nIf you've got familiarity with rpm and would like to help, please [get in\ntouch]. If you aren't familiar with creating rpms but want a reason to learn,\nthat would suit me fine as well.\n\nThanks in advance!\n\n[get in touch]: /support\n"
  },
  {
    "path": "www/posts/2010/04/mind-control-mqtt.md",
    "content": "<!--\n.. title: Mind control MQTT\n.. slug: mind-control-mqtt\n.. date: 2010-04-14 13:06:03\n.. tags: Applications\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIf you're in the UK, you may be interested in watching the Wednesday 21st April\nepisode of \"Bang Goes the Theory\". IBM employees [Nicholas O'Leary] and [Kevin\nBrown] will be in one of the segments, on controlling remote control cars with\ntheir minds - all with MQTT under the covers!\n\n * [BBC programme link]\n\n[Nicholas O'Leary]: http://twitter.com/knolleary\n[Kevin Brown]: http://twitter.com/kevinxbrown\n[BBC programme link]: https://www.bbc.co.uk/programmes/b00s5fvq\n"
  },
  {
    "path": "www/posts/2010/04/oggcamp.md",
    "content": "<!--\n.. title: Oggcamp\n.. slug: oggcamp\n.. date: 2010-04-26 16:45:01\n.. tags: Events\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI'm going to be at [oggcamp] this coming weekend. If you're there, try and find\nme and say hello! I'm planning a talk on mosquitto/mqtt with Andy\nStanford-Clark, so should be relatively easy to find.\n\n[oggcamp]: http://oggcamp.org/\n"
  },
  {
    "path": "www/posts/2010/05/fedora-packages-available.md",
    "content": "<!--\n.. title: Fedora packages available\n.. slug: fedora-packages-available\n.. date: 2010-05-14 15:43:10\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThanks to help from Chris Procter, there are now rpm packages available for\nFedora Linux. As these are the first rpm packages, there may be problems so\nplease report back if you find any.\n\nThere are details on where to get the packages on the [download page].\n\nUsers of other rpm based distributions are currently out of luck - the versions\nof sqlite that they provide are typically either too old or else don't have\nsupport for some required features compiled in.\n\nThanks to everybody else who got in touch about rpms as well!\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/05/gentoo-ebuilds-available.md",
    "content": "<!--\n.. title: Gentoo ebuilds available\n.. slug: gentoo-ebuilds-available\n.. date: 2010-05-18 16:48:11\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThanks to Neil Bothwick, there are now some Gentoo ebuilds available for\nmosquitto and sqlite3-pcre. You can grab them from the links below - hopefully\nthey'll be integrated in the near future.\n\n * <http://bugs.gentoo.org/show_bug.cgi?id=320159>\n * <http://bugs.gentoo.org/show_bug.cgi?id=320153>\n"
  },
  {
    "path": "www/posts/2010/05/mosquitto-org.md",
    "content": "<!--\n.. title: mosquitto.org\n.. slug: mosquitto-org\n.. date: 2010-05-31 17:48:08\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI'm pleased to announce that the mosquitto website is now available at\n<http://mosquitto.org/> so please update your bookmarks! I'll be updating the\nlinks here in a few days when the change has propagated.\n"
  },
  {
    "path": "www/posts/2010/05/mqtt-push-on-android.md",
    "content": "<!--\n.. title: MQTT Push on Android\n.. slug: mqtt-push-on-android\n.. date: 2010-05-16 22:43:34\n.. tags: Applications, Obsolete\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIf you want to use MQTT for push in Android apps, you'll probably want to head\nover to Anton L's blog post [How to Implement Push Notifications for Android].\nHe has a sample Android app that uses the IBM Java library to implement push\nnotifications using MQTT, as well as a web page solution to demo pushing\nnotifications to your phone.\n\n[How to Implement Push Notifications for Android]: http://tokudu.com/2010/how-to-implement-push-notifications-for-android/\n"
  },
  {
    "path": "www/posts/2010/05/mqtt-wiki.md",
    "content": "<!--\n.. title: MQTT Wiki\n.. slug: mqtt-wiki\n.. date: 2010-05-17 23:35:23\n.. tags: Applications,Support\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nA new wiki has been created, devoted to MQTT. If you want to share what you're\ndoing with MQTT and how to do it, or want to find any of that out, head over to\n<http://mqtt.org/wiki>.\n"
  },
  {
    "path": "www/posts/2010/05/version-0-6-1-released.md",
    "content": "<!--\n.. title: Version 0.6.1 released\n.. slug: version-0-6-1-released\n.. date: 2010-05-06 12:44:06\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis fixes an important bug that didn't get caught for 0.6 that can prevent old\ndatabase versions being upgraded.\n\nFrom 0.7 onwards, mosquitto will be using release candidates to ensure this\nkind of problem doesn't occur in the future.\n"
  },
  {
    "path": "www/posts/2010/05/version-0-6-released.md",
    "content": "<!--\n.. title: Version 0.6 released\n.. slug: version-0-6-released\n.. date: 2010-05-05 22:57:20\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a new features release. It offers quite a bit of change over the\nprevious version. More details of the new features can be found in the\n[man pages].\n\nThe substantial changes are:\n\n * Basic support for connecting multiple MQTT brokers together (bridging).\n * mosquitto_sub can now subscribe to multiple topics (limited to a global\n   QoS).\n * mosquitto_pub can now send a file as a message.\n * mosquitto_pub can now read all of stdin and send it as a message.\n * mosquitto_pub can now read stdin and send each line as a message.\n * Implement a more efficient database design, so that only one copy of each\n   message is held in the database, rather than one per subscribed client.\n * Add support for automatic upgrading of the mosquitto DB from v1 to v2.\n * If a retained message is received with a zero length payload, the retained\n   message for that topic is deleted.\n * Implement the `max_inflight_messages` and `max_queued_messages` features in\n   the broker.\n\nThe less visible features and bug fixes are as follows:\n\n * Add support for disabling `clean session` for the sub client.\n * mosquitto will now correctly run VACUUM on the persistent database on exit.\n * Add the `store_cleanup_interval` config option for dealing with the internal\n   message store.\n * Add `persistence_file` config option to allow changing the filename of the\n   persistence database. This allows multiple mosquitto DBs to be stored in the\n   same location whilst keeping `persistence_location` compatible with rsmb.\n * Don't store QoS=0 messages for disconnected clients. Fixes bug #572608. This\n   wasn't correctly fixed in version 0.5.\n * Don't disconnect clients if they send a PUBLISH with zero length payload\n   (bug #573610).\n * Send through zero length messages.\n * Produce a warning on unsupported rsmb options instead of quitting.\n * Describe clean session flag in the mqtt man page.\n\nGet it from the [download page]. Windows and Ubuntu binaries will follow along\nshortly.\n\n[man pages]: /documentation\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/06/automation-has-the-oven-warmed-up-yet.md",
    "content": "<!--\n.. title: Automation: Has the oven warmed up yet?\n.. slug: automation-has-the-oven-warmed-up-yet\n.. date: 2010-06-07 14:55:54\n.. tags: Automation\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIn the Bang Goes the Theory episode [The Human Power Station] a family of four\npeople had their electricity supplied by a large group of cyclists on cycles\nhooked up to generators, the point being to highlight the amount of energy that\nis used and wasted on a daily basis. There's a video on [youtube].\n\nOne example that seemed to waste a lot of energy was cooking roast dinner. The\noven was turned on to preheat (this is often the first instruction in a\nrecipe), but it wasn't actually used until a significant time later, wasting a\nlot of energy. An example which may be more common is turning the oven on to\npreheat before cooking frozen food (which requires no preparation), then\nforgetting to check to see if it has preheated.\n\nThis used to happen to me, but I've solved the problem by using my electricity\nmonitor (I have an electric oven), mqtt and asterisk.\n\nFirst off, we need to monitor electricity usage. I do this with a [CurrentCost]\nCC128 (note that EON customers in the UK can apply to get one of these for free\nin an [Energy Fit] pack) hooked up to a low power computer that is running\nmosquitto. If you're running Linux, you can use [cc128_read.py] or\n[cc128_read.pl] to read the data coming from the monitor and publish it to an\nmqtt broker. A second client, [cc128_parse.pl], takes the data from the monitor\nand republishes it to the broker in a friendlier format - the Unix timestamp of\nthe reading and the power usage, separated by a single space.\n\nTo figure out when the oven has warmed up, I look at the electricity usage with\nthe oven heater on - approximately 2.4kW. If the energy usage drops below this\nvalue, I know that the oven heater has turned off and so the oven has warmed\nup. This is of course slightly simplistic - I'm not actually measuring the\noven, just the electricity usage so if I turned on the kettle at the same time\nit could cause my guess to be incorrect. When CurrentCost produce their\nindividual appliance monitors, I'll be able to be certain of how much\nelectricity just the oven is using.\n\nThis uncertainty means that I only want to turn the oven monitor on when I've\nactually turned the oven on. Looking for an easy way to start the monitor, I\nspotted my house phone - a Siemens C460IP - which is a \"normal\" land line phone\nand an IP phone all in one. I've got Asterisk running on the same server as\nmosquitto, so it's an ideal solution for controlling things. Configuring\nAsterisk is way beyond the scope of this text, so I'm only going to talk about\nthe bits I changed. I added a new extension number 100 which, when called,\nstarts the oven monitor:\n\n```\nexten =&gt; 100,1,Answer()\nexten =&gt; 100,n,System(echo \"/usr/local/bin/oven_pub.pl\" | at now)\nexten =&gt; 100,n,Playback(oven-trigger)\nexten =&gt; 100,n,Hangup\n```\n\nThis answers the call, starts the monitor, plays a sound clip so the person\ncalling knows what has happened and then hangs up. I'm using the \"at\" command\nhere as a simple way of putting the job into the background, thanks to [Ed] for\nthe suggestion. The script [oven_monitor.pl] looks for the electricity usage to\ndrop beneath 2kW, then runs `oven_warmed_up.sh` and exits.\n\nThe final step is to do something in [oven_warmed_up.sh] to give feedback. I\nmake use of asterisk once again and initiate a call to the phone - so when the\noven has warmed up I get a phone call with a message that tells me so. The\noutgoing call is initiated by moving [oven.call] to\n/var/spool/asterisk/outgoing/, as shown in the script.\n\nDo you have any suggestions on how to improve this? Or other ways of using\nasterisk or mqtt like this? Let me know in the comments!\n\n[The Human Power Station]: http://www.bbc.co.uk/programmes/b00p8469\n[youtube]: http://www.youtube.com/watch?v=C93cL_zDVIM\n[CurrentCost]: http://www.currentcost.com/\n[Energy Fit]: http://www.eon-uk.com/media/energyfit.aspx\n[cc128_read.py]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_read.py\n[cc128_read.pl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_read.pl\n[parse.pl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_parse.pl\n[Ed]: http://twitter.com/ribzlike\n[oven_monitor.pl]: /files/examples/oven-asterisk/oven_monitor.pl\n[oven_warmed_up.sh]: /files/examples/oven-asterisk/oven_warmed_up.sh\n[oven.call]: /files/examples/oven-asterisk/oven.call\n"
  },
  {
    "path": "www/posts/2010/06/google-powermeter-step-by-step.attachments.json",
    "content": "{\"54\": {\"wordpress_user_name\": \"roger\", \"title\": \"powermeter-example\", \"date_utc\": \"2010-06-15 13:52:24\", \"files_meta\": [{\"height\": 292, \"width\": 632}, {\"height\": 138, \"size\": \"medium\", \"width\": 300}, {\"height\": 150, \"size\": \"thumbnail\", \"width\": 150}], \"files\": [\"/wp-content/uploads/2010/06/powermeter-example.png\", \"/wp-content/uploads/2010/06/powermeter-example-300x138.png\", \"/wp-content/uploads/2010/06/powermeter-example-150x150.png\"]}}"
  },
  {
    "path": "www/posts/2010/06/google-powermeter-step-by-step.md",
    "content": "<!--\n.. title: Google Powermeter : Step by step\n.. slug: google-powermeter-step-by-step\n.. date: 2010-06-15 14:34:50\n.. tags: cc128,currentcost,google,mysql,powermeter,Applications\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n# Note: Google Powermeter is now defunct but this post will remain here for those interested.\n\nThis is a follow up to my previous [post on using Google Powermeter],\nbut this time I'm going to give a step by step guide to getting your data\nuploaded. The only assumptions are that you have a CurrentCost monitor (note\nthat CurrentCost monitors are often rebadged by electricity suppliers such as\nEON in the UK so check yours) and have already connected it to your computer,\nwant to use MQTT and that you're using Linux, or another Unix operating system.\n\n# Retrieving the data\n\nThe first step is to get the data from the CurrentCost into the MQTT broker.\nThis is straightforward - simply read data from the serial port and send it all\nto the broker. I have scripts to do this with mosquitto in both [perl] and\n[python].\n\nThe data coming from the CurrentCost is in XML format and as well as providing\nthe real time power reading every 6 seconds, will also send historical data\nperiodically. I'm only going to deal with the real time readings here. The next\nstep is to reprocess the incoming data into something more manageable, then\nrepublish it. An example of doing that is the script [cc128_parse.pl], which\nassumes you're only using the main channel from the CurrentCost. If you have\nmultiple monitoring channels, you'll need to modify it to suit.\n\n# Logging the data\n\nGoogle limits the number of times we can send data to 6 per hour, so we have to\nlog the data and then send amalgamated updates. I use mysql for this - I'm\ngoing to assume that you've got it installed and running. Log into the mysql\nconsole using \"mysql -u root\",  \"mysql -u root -p\" if you know the password, or\npossibly \"sudo mysql\". We're now going to create a database and table to hold\nthe powermeter data, then add a user to access and update the data.\n\nTo create the database and table enter the following:\n\n```\nCREATE DATABASE powermeter;\nUSE 'powermeter';\nCREATE TABLE powermeter (\n  `id` INT NOT NULL auto_increment,\n  `timestamp` INT NOT NULL,\n  `temperature` FLOAT NOT NULL DEFAULT 0.0,\n  `ch1` INT NOT NULL DEFAULT 0,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `timestamp` (`timestamp`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;</pre>\n```\n\nNote that there's a column there for the temperature as well.\n\nTo add the user and grant access to the database:\n\n```\nCREATE USER 'powermeter'@'localhost' IDENTIFIED BY '&lt;your password&gt;';\nGRANT ALL ON powermeter.* to 'powermeter'@'localhost';\n```\n\nFinally, you'll need to get data into this database. My script\n[cc128_log_mysql.pl] subscribes to the data from cc128_parse.pl and logs it\ninto the database.  You'll need to edit it to have the correct database\ndetails.\n\nIf you already have your power data published to an MQTT topic, it's quite\nlikely that you won't have it in the same format that I use above. If this is\nthe case, you will need to modify cc128_log_mysql.pl. Assuming your data coming\nin over MQTT is just the power reading, then you can replace this:\n\n```\n@vals = split(/,/, $line);\n$timestamp = @vals[0];\n$temperature = @vals[1];\n$ch1 = @vals[2];\n```\n\nwith this:\n\n```\n$timestamp = time();\n$temperature = 0;\n$ch1 = $line;\n```\n\nYou can of course leave the temperature column out completely if you prefer.\n\n# Registering with Google Powermeter\n\nBefore you can send any data to Google, you need to register your device with\nthem. This would normally be done automatically by your device, but because\nwe're doing things ourselves we need to do it manually. See [2cheap2meter] and\nthe links it provides for more details.\n\nWe first need to decide on a few parameters for our device:\n\n * Manufacturer (e.g. CurrentCost)\n * Device model (e.g. CC128 or Envi)\n * Device id (e.g. Serial number or your own made up string, 1234)\n * Number of channels to log (e.g. 1)\n\nWe can then construct an address which you will paste into your web browser:\n\n```\nhttps://www.google.com/powermeter/device/activate?mfg=CurrentCost&amp;model=CC128&amp;did=1234&amp;dvars=1\n```\n\n`dvars` here is the number of channels (or monitors) that we wish to register.\nIf you have more than one channel logging, change the number accordingly - bear\nin mind that you'll have to modify just about everything else in this post to\nmatch. You will need to remember the values you put here for later.\n\nVisiting that link will take you to the activation page, which you should\ncomplete. After you have done this, you will be presented with authorisation\ninformation for your new device. The piece of information we need is the 32\ncharacter string contained between \"token=\" and \"&amp;path\" (the authorisation\ntoken) as well as the 20 digit number after \"&amp;path=/user/\" (your google\npowermeter id).\n\n# Sending the data\n\nI have a script [google_powermeter_update.pl] that will query the database for\nreadings from the past 15 minutes and then send them. You'll need to edit the\nscript to put the correct database details, power meter id, authorisation token\nand device details. To set it to run every fifteen minutes, I use cron. Either\nadd an entry to your own crontab by running \"crontab -e\" then entering the\nfollowing line:\n\n```\n*/15 * * * * /path/to/google_powermeter_update.pl &gt; /dev/null\n```\n\nOr by creating a file containing the line below and copying it to\n/etc/cron.d/powermeter_update.cron.\n\n```\n*/15 * * * * nobody /path/to/google_powermeter_update.pl &gt; /dev/null\n```\n\nIn both cases, you can change the output redirection from \"/dev/null\" to e.g.\n\"/tmp/powermeter\" to allow you to check any error codes in case of a problem.\n\nNow go to <http://www.google.com/powermeter/site/> to check your data! Here's\nan example of mine:\n\n[![powermeter example](/blog/uploads/2010/06/powermeter-example-300x138.png)](/blog/uploads/2010/06/powermeter-example.png)\n\n# Possible changes\nThe above description and scripts aren't ideal - if you lose your internet\nconnection then data will still be recorded but won't be sent to google. One\npossible change would be to add a column to the database to list whether that\nparticular piece of data had been sent or not, which would allow all data to\neventually be sent and deleted afterwards if desired.\n\nA second way around this would be to make use of the historical data that the\nCurrentCost monitors use. This could also be a way of reducing the need to log\nthings ourselves.\n\n# Conclusion\nI hope this is of use to you - please let me know if you have any problems with\nany of the above steps.\n\n[post on using Google Powermeter]: /blog/2010/03/google-powermeter/\n[perl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_read.pl\n[python]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_read.py\n[cc128_parse.pl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_parse.pl\n[cc128_log_mysql.pl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_log_mysql.pl\n[2cheap2meter]: http://2cheap2meter.blogspot.com/2010/03/setting-up-google-powermeter.html\n[google_powermeter_update.pl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/google_powermeter_update.pl\n"
  },
  {
    "path": "www/posts/2010/06/mosquitto-0-7rc1.md",
    "content": "<!--\n.. title: Mosquitto 0.7rc1\n.. slug: mosquitto-0-7rc1\n.. date: 2010-06-04 18:10:57\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 0.7 release candidate 1 is available for testing. Please give it a\ntry and report back any problems you find. The source code is available at \n<http://mosquitto.org/files/source/rc/> and I'll hopefully have various\nbinaries available soon.\n"
  },
  {
    "path": "www/posts/2010/06/version-0-7-released.md",
    "content": "<!--\n.. title: Version 0.7 released\n.. slug: version-0-7-released\n.. date: 2010-06-15 23:38:52\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a new features release. Note that although the number of changes is\nrelatively small, there is a fairly major change in the network socket handling\n(to allow &gt;1024 clients) , which is one reason this has been treated as a\nseparate release.\n\nChanges:\n\n * Use poll() instead of select() to allow &gt;1024 clients.\n * Implement `max_connections`.\n * Run VACUUM on in-memory database on receiving SIGUSR2.\n * mosquitto_pub can now send null (zero length) messages.\n * Add option to print debug messages in pub and sub clients.\n * hg revision is now exported via $SYS/broker/changeset\n * Add compile time option to disable heap memory tracking.\n\nBug fixes:\n\n * Don't store QoS=0 messages for disconnected clients with subscriptions of QoS&gt;0.\n * accept() all available sockets when new clients are connecting, rather than just one (performance advantage)\n * Send Will when client exceeds keepalive timer and is disconnected.\n * Check to see if a client has a will before sending it.\n * Correctly deal with clients connecting with the same id multiple times.\n * Fix bridge keepalive timeouts and reconnects.\n * Don't attempt to drop root privileges when running on Windows as this isn't well supported (bug #586231).\n\nSource downloads are available at the [download page] Links for binary packages\non Ubuntu and Fedora can be found on the same page.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/07/mosquitto-on-opensuse-11-3.md",
    "content": "<!--\n.. title: Mosquitto on openSUSE 11.3\n.. slug: mosquitto-on-opensuse-11-3\n.. date: 2010-07-12 12:39:25\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe upcoming release of [openSUSE], version 11.3, includes extension support\nfor sqlite3 which means it now has everything required for mosquitto.\n\nI've created packages for this new version of openSUSE and details can be found\non the [download page]. You just need to wait three days until the release of\n11.3!\n\n[openSUSE]: http://www.opensuse.org/\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/07/mqtt-client-library.md",
    "content": "<!--\n.. title: MQTT client library\n.. slug: mqtt-client-library\n.. date: 2010-07-22 00:34:38\n.. tags: Testing\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI have been working on a client library for MQTT for the next release of\nMosquitto. It is now at a stage where it is usable and ready for wider testing.\nThere isn't any documentation yet (!) so it's only available in the source\nrepository at <http://bitbucket.org/oojah/mosquitto>. Use the \"get source\" link\nin the top right corner of the page to  download a snapshot. If you're\ninterested in developing your own open source MQTT clients, it'd be great if\nyou could take a look to make sure the interface is sane before I make a\nrelease!\n\nThe library itself is written in C, with bindings for C++ and Python.\n\nI plan to package it up in a more easy to access form in the not too distant\nfuture, hopefully with some documentation as well.\n\n# Update\nI've put the start of a man page online, which shows an example of using\nlibmosquitto to subscribe to a topic and print the results: [libmosquitto.3].\n\n[libmosquitto.3]: http://mosquitto.org/man/libmosquitto-3.html\n"
  },
  {
    "path": "www/posts/2010/08/compiling-mosquitto-on-mac-os-x.md",
    "content": "<!--\n.. title: Compiling mosquitto on Mac OS X\n.. slug: compiling-mosquitto-on-mac-os-x\n.. date: 2010-08-08 23:44:52\n.. tags: Links\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIn a follow up to his screencast [demoing mosquitto on Mac OS X], Andy Piper\nhas written a blog post detailing how he got compiled mosquitto and, more\nimportantly, the dependencies, on the Mac.\n\nTake a look over at [OS X mosquitto \"bites\"...]\n\n[demoing mosquitto on Mac OS X]: /blog/2010/08/mosquitto-running-on-mac-os-x/\n[OS X mosquitto \"bites\"...]: http://andypiper.co.uk/2010/08/08/os-x-mosquitto-bites/\n"
  },
  {
    "path": "www/posts/2010/08/mosquitto-running-on-mac-os-x.md",
    "content": "<!--\n.. title: Mosquitto running on Mac OS X\n.. slug: mosquitto-running-on-mac-os-x\n.. date: 2010-08-08 09:58:26\n.. tags: demo,mac,video\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nAndy Piper has put together a 2 minute screencast demoing mosquitto running on\nMac OS X and showing publishing and subscribing using the mosquitto command\nline clients and the IBM java gui client.\n\nView the screen cast here: <http://www.youtube.com/watch?v=SP9Vv3Rksm8>.\n\nThanks Andy!\n"
  },
  {
    "path": "www/posts/2010/08/mqtt-v3-1.md",
    "content": "<!--\n.. title: MQTT v3.1\n.. slug: mqtt-v3-1\n.. date: 2010-08-22 00:18:19\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe MQTT v3 spec has been updated to v3.1. The significant change is the\ninclusion of the option to send a username and password as part of the connect\ncommand. The new spec is available at\n<http://www.ibm.com/developerworks/webservices/library/ws-mqtt/index.html>\nand is a lot more readable and clear than the original.\n\nMosquitto will support the v3.1 spec in a future release, along with the\nability to control both broker and topic access by username. In the meantime,\nif you need this functionality, the IBM proprietary [RSMB] broker may be\nsuitable for testing purposes. The RSMB package now also includes an MQTT\nclient library, a simple publish client and a simple subscribe client, just\nlike mosquitto. Be sure to check the license terms before using it!\n\n[RSMB]: http://www.alphaworks.ibm.com/tech/rsmb\n"
  },
  {
    "path": "www/posts/2010/08/version-0-8-1-released.md",
    "content": "<!--\n.. title: Version 0.8.1 released\n.. slug: version-0-8-1-released\n.. date: 2010-08-11 23:47:18\n.. tags: Packaging,Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a minor release. The primary reason for it is the amount of interest in\nthe Python interface to libmosquitto. This release tidies up the Python\ninterface considerably (it is now more \"Pythonic\" and easier to use), and\nsignificantly, brings the promised packages.\n\nThis release also provides a few fixes, including to the packaging and\ninstallation scripts. Unfortunately, it does also include a known bug that was\nfixed prior to release, but accidentally left unmerged. This affects\nmosquitto_pub client when using the -l option (publish line by line input from\nstdin), causing it to exhibit high cpu load. I'll make a new bug fix release in\na few days with this and any other fixes that come up.\n\nThis release also provides improved packaging options. All of the available\noptions are now packaged for Ubuntu, including the libmosquitto0-python\npackage. Because there are now multiple packages, it is possible to provide\nsome mosquitto functionality on distributions where the version of sqlite3 is\ntoo old. The packages available on these systems are listed as \"clients only\":\n\n * Fedora 12, 13 (full support)\n * openSUSE 11.3 (full support)\n * openSUSE 11.1, 11.2 (clients only)\n * Redhat Enterprise Linux 5 (clients only)\n * CentOS 5 (clients only)\n\nDetails are available on the [download page]. Please note that some\ndistributions have different naming schemes, so the Python module can be called\nboth python-mosquitto and libmosquitto0-mosquitto for example.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/08/version-0-8-2.md",
    "content": "<!--\n.. title: Version 0.8.2\n.. slug: version-0-8-2\n.. date: 2010-08-15 19:07:04\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Fix default loop() timeout value in mosquitto.py. Previous value was 0,\n   causing high cpu load.\n * Fix message handling problem in client library when more than one message\n   was in the client queue.\n * Fix the logic used to determine whether a QoS&gt;0 message needs to be\n   retried.\n * Fix the Python sub.py example so that it quits on error.\n\nSee the [download page]. Includes Windows 32-bit binaries for the broker\ncompiled with Cygwin, and the client library and clients compiled natively with\nVisual Studio to allow developing native Windows MQTT clients.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/08/version-0-8-released.md",
    "content": "<!--\n.. title: Version 0.8 released\n.. slug: version-0-8-released\n.. date: 2010-08-08 10:31:17\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is the library release. There are a few bug fixes and changes of behaviour\nfor the mosquitto and the clients, but the significant part of this release is\nthe new mosquitto MQTT client library. The library comes in three flavours: the\nC library, which is the main library, and C++ and Python bindings. If you're\ninterested in helping add bindings for your favourite language, please get in\ntouch.\n\nThe library interface (API) is to be considered experimental, although I\nbelieve the C and C++ APIs to be complete and sane. The Python bindings are a\nnaïve attempt by a C programmer and will definitely be changing in the future\nto something more pythonic. I'd be extremely grateful for help from experienced\npython programmers to this end.\n\nThe documentation of the library is currently ongoing... There is an overview\nof most of the function calls and an example in the [libmosquitto.3] man page,\nbut complete coverage can be found in the mosquitto.h man page. This, combined\nwith the class details in mosquittopp.h can be used to help use the C++\nlibrary. The python module isn't documented due to its extremely changeable\nstate, but there is an example in the python directory.\n\nOther changes:\n\n * Topics starting with a / are treated as distinct to those not starting with\n   a /. For example, /topic/path is different to topic/path. This matches the\n   behaviour of rsmb.\n * Correctly calculate the will QoS on a new client connection (bug #597451).\n * Add \"addresses\" configuration file variable as an alias of \"address\", for\n   better rsmb compatibility.\n * Bridge `clean_session` setting is now false, to give more sensible behaviour\n   and be more compatible with rsmb.\n * Add `cleansession` variable for configuring bridges.\n * Add `keepalive_interval` variable for bridges.\n * Remove default topic subscription for mosquitto_sub because the old\n   behaviour was too confusing.\n * Added a C client library, which the pub and sub clients now use.\n * Added a C++ client library (bound to the C library).\n * Added a Python client library (bound to the C library).\n * Added CMake build scripts to allow the library and clients (not the broker)\n   to be compiled natively on Windows.\n\nGet it from the [download page].\n\nThe change to using a library means that packaging mosquitto for distros  is a\nlot more complex. This is stretching my packaging experience, so  please bear\nwith me on that front! Mosquitto will now likely consist of a number of\ndifferent packages on Ubuntu at least:\n\n * mosquitto (the broker)\n * mosquitto-clients (mosquitto_sub, mosquitto_pub)\n * libmosquitto0 (C library)\n * libmosquitto0-dev (C library development files)\n * libmosquittopp0 (C++ library)\n * libmosquittopp0-dev (C++ library development files)\n * libmosquitto-python (Python binding)\n\n# Update\nI've been getting a few questions about the python interface. This isn't\ncurrently packaged for Ubuntu, but hopefully will be soon. There are basic\npython examples in the downloads at lib/python/sub.py and\nmisc/currentcost/gnome-panel/CurrentCostMQTT.py\n\n[libmosquitto.3]: /man/libmosquitto-3.html\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/09/debian-packages.md",
    "content": "<!--\n.. title: Debian packages\n.. slug: debian-packages\n.. date: 2010-09-27 20:14:50\n.. tags: Packaging,Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI've created some packages for Debian on i386 and amd64. They can be found at\n<http://mosquitto.org/files/binary/debian/>. They are almost identical to the\nUbuntu packages (Debian doesn't use upstart, so there is a different init\nscript), but compiled against Debian testing (Squeeze) instead. This is because\nDebian 5 (Lenny) doesn't include a recent enough version of sqlite3.\n\nPlease let me know if you have any problems with the packages.\n"
  },
  {
    "path": "www/posts/2010/09/mqtt-with-php.md",
    "content": "<!--\n.. title: MQTT with PHP\n.. slug: mqtt-with-php\n.. date: 2010-09-09 10:00:59\n.. tags: Solutions\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nUsing MQTT in PHP has been possible for a long time using the [Simple\nAsynchronous Messaging] MQTT class. Unfortunately this is an imperfect solution\ndue to unclear licensing, some slightly dubious design decisions and bugs.\n\nThankfully, [Andrew Milstead] has started creating an alternative\nimplementation. It is MIT licensed and available on [github]. It's very new, so\nif you have problems check back to see if there have been updates and then let\nAndrew know.\n\n[Simple Asynchronous Messaging]: http://project-sam.awardspace.com/\n[Andrew Milstead]: http://twitter.com/bluerhinos\n[github]: http://github.com/bluerhinos/phpMQTT\n"
  },
  {
    "path": "www/posts/2010/10/man-page-translations.md",
    "content": "<!--\n.. title: Man Page Translations\n.. slug: man-page-translations\n.. date: 2010-10-24 11:15:57\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nSomething that is very much in the back of my mind whilst developing mosquitto\nis that it should have support for language translations. I've been reluctant\nto start this effort until development was a little bit more settled, to avoid\nwasting translators time. I think it's now time to start the ball rolling.\n\nI'm going to approach this in two stages - the man pages and the programs. Man\npages are first. I've imported the translation templates to launchpad, so if\nyour native tongue is anything other than English and you'd like to translate\nthem - please go ahead. I'll be putting any translated man pages (with credits\n:) into the upcoming 0.9 release. I think I've finished making changes but if I\nhaven't, the one most likely to change is for mosquitto.conf\n\nThe translation page can be found at <http://translations.launchpad.net/mosquitto/trunk>\n\nThanks in advance, and please get in touch if you find any strings that don't\nmake sense.\n"
  },
  {
    "path": "www/posts/2010/10/one-year-old.md",
    "content": "<!--\n.. title: One year old!\n.. slug: one-year-old\n.. date: 2010-10-25 09:16:44\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nOn the 25th October 2009, at 21:40:51 (just five hours and forty minutes after\nthe first [oggcamp] ended :), I made the first commit to what would become the\nmosquitto source code repository. Although there was no code committed until\nthe next day, and the first release wasn't until almost six weeks later, I\nconsider this to be when mosquitto was born.  It's been a good year. Thanks to\neverybody who has helped out and been in touch!\n\nI had hoped to release version 0.9 today, but it isn't to be. Nevertheless, I\nhope you'll join me in hoping for an even more successful year ahead. I'm\naiming for a 1.0 release before this time next year with full MQTT 3.1 support,\nfull rsmb feature set (except where inappropriate), IPv6 support, SSL support,\nlanguage translation for the programs and man pages, full API documentation and\nexamples, and whatever I think of in the meantime.\n\nHave a feature you're particularly interested in? Leave a comment! :)\n\n[oggcamp]: http://oggcamp.org/\n"
  },
  {
    "path": "www/posts/2010/10/version-0-8-3-released.md",
    "content": "<!--\n.. title: Version 0.8.3 released\n.. slug: version-0-8-3-released\n.. date: 2010-10-04 21:01:59\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Fix compliance with the MQTT protocol for messages published at QoS 2. This\n   means that messages that time out are dealt with correctly and duplicate\n   messages are also dealt with correctly.\n\nSee the [download page] for the update.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/11/distro-packaging.md",
    "content": "<!--\n.. title: Distro packaging\n.. slug: distro-packaging\n.. date: 2010-11-07 12:02:58\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nStarting with version 0.9, I plan on getting mosquitto packaged in the major\nLinux distributions. By this I mean Debian, Fedora, openSUSE and Ubuntu.  This\nis my understanding of the current state of play of those distributions. Feel\nfree to correct me!\n\n * Debian is currently in freeze for the Squeeze release. Mosquitto will have\n   to go into Squeeze+1, although it can still be uploaded to Unstable.\n * Fedora 14 has just been released, Fedora 15 will have feature freeze on the\n   8th of February.\n * openSUSE 11.4 will have feature freeze at the start of December.\n * Ubuntu 11.04 has its Debian import freeze on the 30th of December and\n   feature freeze on 24th of February.\n\nThe plan is therefore to release 0.9 around the 14th of November and aim to be\npackaged for Debian unstable before 30th of December and openSUSE before the\nstart of December, with packaging for Fedora 15 coming at some point later. If\npackaging for Debian unstable isn't possible before the Ubuntu import freeze,\nthen package for Ubuntu separately.\n\nIf you can help out with the packaging process for any of the above, I'd love\nto hear from you. If your distribution isn't included and you'd like it to be,\nget in touch as well and we'll see what's possible.\n\nFinally, this won't stop me producing Ubuntu PPA or openSUSE build service\npackaged binaries for those that prefer to stay at the cutting\nedge.\n"
  },
  {
    "path": "www/posts/2010/11/mosquitto-0-9test2.md",
    "content": "<!--\n.. title: Mosquitto 0.9~test2\n.. slug: mosquitto-0-9test2\n.. date: 2010-11-04 10:37:49\n.. tags: Testing\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 0.9, which I hope to release mid November, represents the most\nsignificant change to mosquitto to date - the removal of sqlite as an absolute\ndependency. In addition, this removes the dependency on the sqlite3-pcre\nextension and on pcre. This gives a definite performance improvement, reduces\nthe amount of object code that needs loading by around 95%, reduces memory\nusage and also makes it lots easier to compile on more unusual systems.\n\nIt's quite a substantial change though, so I've made a test release to\nhopefully get some external testing. If you could give it a try and report back\nthat'd be great. The source is at <http://mosquitto.org/files/source/test/>.\n(use the highest numbered version available). There are also Ubuntu packages\navailable at the [mosquitto-expt ppa] and binaries for Fedora, Mandriva,  SLES\nand openSUSE at the [openSUSE build service]. If you'd like binaries for other\nsystems, please get in touch.\n\nNote that this is a test release, not a release candidate - there are\ndefinitely things that still need changing. The following list shows the points\nI'm currently aware of:\n\n * Old style sqlite will be imported when the option is compiled in (enabled by\n   default). This import currently only imports retained messages and durable\n   subscriptions, but not queued messages.\n * ~~The `max_inflight_messages` and `max_queued_messages` config options are\n   ignored and no maximum is applied.~~\n * ~~The CMake compilation scripts aren't updated.~~\n\n# Update\nI've uploaded test3 with a python fix, updated CMake scripts and fixed\n`max_inflight_messages` and `max_queued_messages`.\n\n[mosquitto-expt ppa]: https://launchpad.net/~mosquitto-dev/+archive/mosquitto-expt\n[openSUSE build service]: https://build.opensuse.org/project/show?project=home%3Aoojah%3Amqtt_expt\n"
  },
  {
    "path": "www/posts/2010/11/version-0-9-released.md",
    "content": "<!--\n.. title: Version 0.9 released\n.. slug: version-0-9-released\n.. date: 2010-11-15 01:04:47\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a features release. It is probably the most significant change for\nmosquitto so far.\n\nThe important change is the removal of the sqlite dependency, along with the\nassociated pcre and sqlite3-pcre dependencies. This results in better\nperformance both in terms of messages handled per second and memory usage.\nOptional support for importing existing persistent databases in sqlite3 format\nis provided, with the option compiled in by default. This will be set to not be\ncompiled by default in 0.10 and then removed in 0.11.\n\nThis release also provides support for the recently updated MQTT v3.1 spec -\nmost notably offering username/password authentication support. The client\nlibrary and clients have full v3.1 support. The broker is fully compatible with\nv3.1, but doesn't provide any mechanism for controlling username/password\ncontrol. This will come in 0.10.\n\nOne goal of mosquitto is to be a drop in replacement for the IBM rsmb broker.\nAnother goal is to do more than rsmb :) I'm still working on the first goal,\nbut this release helps with the second as mosquitto now has IPv6 support, which\nisn't supported in rsmb.\n\nA detailed list of changes is given below:\n\n * Client and message data is now stored in memory with custom routines rather\n   than a sqlite database. This removes the dependencies on sqlite, pcre and\n   sqlite3-pcre. It also means that the persistent database format has had to\n   be reimplemented in a custom format. Optional support for importing old\n   sqlite databases is provided.\n * Added IPv6 support for mosquitto and the clients.\n * Provide username and password support for the clients and client libraries.\n   This is part of the new MQTT v3.1 spec.\n * The broker supports the username and password connection flags, but will not\n   do anything with the username and password.\n * Python callback functions now optionally take an extra argument which will\n   return the user object passed to the `Mosquitto()` constructor, or the calling\n   python object itself if nothing was given to `Mosquitto()`.\n * Remove the mosquitto command line option `-i interface`.\n * Remove the mosquitto.conf \"interface\" variable.\n * Add support for the listener config variable (replaces the interface\n   variable)\n * Add support for the `bind_address` config variable.\n * Change the port config variable behaviour to match that of rsmb (applies to\n   the default listener only, can be given just once).\n * Fix QoS 2 protocol compliance - stop sending duplicate messages and handle\n   timeouts correctly. Fixes bug #598290.\n * Set retain flag correctly for outgoing messages. It should only be set for\n   messages sent in response to a subscribe command (ie. stale data).\n * Fix bug in returning correct CONNACK result to `on_connect` client callback.\n * Don't send client will if it is disconnected for exceeding its keepalive\n   timer.\n * Fix client library unsubscribe function incorrectly sending a SUBSCRIBE\n   command when it should be UNSUBSCRIBE.\n * Fix `max_inflight_messages` and `max_queued_messages` operation. These\n   parameters now apply only to QoS 1 and 2 messages and are used regardless of\n   the client connection state.\n * mosquitto.conf now installed to /etc/mosquitto/mosquitto.conf instead of\n   /etc/mosquitto.conf. The /etc/mosquitto/ directory will be used for password\n   and access control files in the future.\n * Give the compile time option of using 32-bit integers for the database IDs\n   instead of 64-bit integers. This is useful where htobe64()/be64toh() are not\n   available or for embedded systems for example.\n * The DUP bit is now set correctly when resending PUBREL messages.\n * A port to Windows native has been partially completed. This currently drops\n   a number of features, including the ability to change configuration\n   parameters and persistent storage.\n\nSee the [download page] for the update. Debian and Ubuntu users should note\nthat the package libmosquitto0-python has been renamed python-mosquitto to\ncomply with Debian naming policies. The Debian packages aren't yet ready.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2010/12/version-0-9-1-released.md",
    "content": "<!--\n.. title: Version 0.9.1 released\n.. slug: version-0-9-1-released\n.. date: 2010-12-03 11:04:26\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Add missing code for parsing the `bind_address` configuration option.\n * Fix missing include when compiling with tcp-wrappers support.\n * Add linker version script for C library to control exported functions.\n\nSource code is on the [download page], binary packages will follow on later.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2011/01/mosquitto-for-slackware.md",
    "content": "<!--\n.. title: Mosquitto for Slackware\n.. slug: mosquitto-for-slackware\n.. date: 2011-01-31 09:04:13\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nChris Willing of the University of Queensland has very kindly put together some\nmosquitto packages for Slackware 13.0 and 13.1 as well as instruction on how to\nbuild your own packages.\n\nThe packages and instructions are here:\n<http://www.vislab.uq.edu.au/howto/mqtt/index.html> I've also put the link on\nthe downloads page.\n\nThanks Chris!\n"
  },
  {
    "path": "www/posts/2011/01/mqtt-news.md",
    "content": "<!--\n.. title: MQTT News\n.. slug: mqtt-news\n.. date: 2011-01-12 09:04:06\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nHere are some MQTT updates from out there on the internet:\n\nA new perl client implementation by Mark Hindess\n\n * <https://github.com/beanz/net-mqtt-perl>\n\nA [homebrew] recipe for installing mosquitto on Mac by Adam Rudd\n\n * <https://github.com/mxcl/homebrew/pull/3824>\n\nMQTT implemented for the mbed processor by Yiluin Fan\n\n * <http://ceit.uq.edu.au/content/mbed-client-mqtt-version-10>\n\n[homebrew]: http://brew.sh/\n"
  },
  {
    "path": "www/posts/2011/02/lightweight-messaging-and-linux.md",
    "content": "<!--\n.. title: Lightweight Messaging and Linux\n.. slug: lightweight-messaging-and-linux\n.. date: 2011-02-07 11:40:36\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nAndy Piper was at [Linux Conf Australia] this year and gave a talk on MQTT.\n\nHis blog post Lightweight Messaging and Linux] gives a few details and has a\nlink to the slides. The video can be seen at\n<http://linuxconfau.blip.tv/file/4729456/>\n\n[Linux Conf Australia]: http://linux.conf.au/\n[Lightweight Messaging and Linux]: http://andypiper.co.uk/2011/01/28/lightweight-messaging-and-linux/\n"
  },
  {
    "path": "www/posts/2011/02/mosquitto-on-maemo.md",
    "content": "<!--\n.. title: Mosquitto on Maemo\n.. slug: mosquitto-on-maemo\n.. date: 2011-02-13 22:06:04\n.. tags: Mobile,Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nYuvraaj Kelkar got in touch to say he's packaged up mosquitto and the client\nlibraries for Maemo. If you want to use MQTT on your Maemo device then take a\nlook at the details on <http://talk.maemo.org/showthread.php?t=69604>\n\nThanks Yuvraaj!\n"
  },
  {
    "path": "www/posts/2011/02/mqtt-on-android.md",
    "content": "<!--\n.. title: MQTT on Android\n.. slug: mqtt-on-android\n.. date: 2011-02-01 15:19:37\n.. tags: Mobile\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nDale Lane has written an enormous blog post [Using MQTT in Android Mobile\nApplications in which he talks about a lot of the points you are likely to want\nto consider if you're writing MQTT applications for Android. There's lots of\nuseful information and he even includes a complete source code\nimplementation.\n\n[Using MQTT in Android Mobile Applications]: http://dalelane.co.uk/blog/?p=1599\n"
  },
  {
    "path": "www/posts/2011/02/version-0-9-2-released.md",
    "content": "<!--\n.. title: Version 0.9.2 released\n.. slug: version-0-9-2-released\n.. date: 2011-02-10 00:28:23\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release:\n\n * Only send a single DISCONNECT command when using -l in the pub client.\n * Set QoS=1 on PUBREL commands to meet protocol spec.\n * Don't leak sockets on connection failure in the library.\n * Install man pages when building under cmake.\n * Fix crash bug on malformed CONNECT message.\n * Clients are now rejected if their socket peer name cannot be obtained on\n   connection.\n * Fix a number of potential problems caused when a client with a duplicate id\n   connects.\n * Install mosquitto.conf under cmake.\n\nThanks to Mark Hindess, Joshua Lock, Adam Rudd and Ben Davenport for their\nhelp.\n\nThe source code is available as always on the [download page]. Binaries will\nappear shortly.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2011/03/api-documentation.md",
    "content": "<!--\n.. title: API documentation\n.. slug: api-documentation\n.. date: 2011-03-08 23:36:16\n.. tags: Documentation\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI've rewritten the API documentation for the C library using the [NaturalDocs]\nformat. This covers the whole C library and so should give enough information\nfor anybody using the C++ or Python wrappers as well.\n\nThe documentation generated from mosquitto.h is available at\n<http://mosquitto.org/api/>\n\n[NaturalDocs]: http://www.naturaldocs.org/\n"
  },
  {
    "path": "www/posts/2011/03/mosquitto-in-mac-homebrew.md",
    "content": "<!--\n.. title: Mosquitto in Mac homebrew\n.. slug: mosquitto-in-mac-homebrew\n.. date: 2011-03-27 18:29:39\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThanks to work done by Adam Rudd, mosquitto is now available in the Mac\n[homebrew](https://brew.sh) package manager. Once you've installed homebrew\n(see the link), you can install mosquitto with:\n\n```\nbrew install mosquitto\n```\n"
  },
  {
    "path": "www/posts/2011/03/version-0-9-3-released.md",
    "content": "<!--\n.. title: Version 0.9.3 released\n.. slug: version-0-9-3-released\n.. date: 2011-03-11 17:08:49\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release:\n\n * Set retained message status for QoS 2 messages (bug #726535).\n * Only abort with an error when opening listening sockets if no address family is available, rather than aborting when any address family is not available.\n * Don't clean queued messages when a non clean session client reconnects.\n * Make mosquitto.py compatible with Python &lt;2.6.\n * Fix mosquitto.h header includes for Windows.\n\nPlease see the [download page].\n\nThanks to Joe B, David Monro,  Yuvraaj Kelkar and Colin Jones.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2011/04/version-0-10-released.md",
    "content": "<!--\n.. title: Version 0.10 released\n.. slug: version-0-10-released\n.. date: 2011-04-29 19:05:28\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis release brings the new MQTT v3.1 features to the broker - client\nauthentication and topic access control. See [mosquitto.conf(5)] or the\nincluded example password and ACL files.\n\n * Implement support for the `password_file` option and accompanying\n   authentication requirements in the broker.\n * Implement topic Access Control Lists.\n * `mosquitto_will_set()` and `mosquitto_publish()` now return\n   `MOSQ_ERR_PAYLOAD_SIZE` if the payload is too large (&gt;268,435,455 bytes).\n * Bridge support can now be disabled at compile time.\n * Group together network writes for outgoing packets - don't send single byte\n   writes!\n * Add support for `clientid_prefixes` variable.\n * Add support for the `clientid` config variable for controlling bridge client\n   ids.\n * Remove 32-bit database ID support because htobe64() no longer used.\n * Multiple client subscriptions to the same topic result in only a single\n   subscription. Bug #744077.\n\nPlease see the [download page].\n\nThanks to Adam Rudd, Joshua Lock,  Sang Kyeong Nam and Yuvraaj Kelkar.\n\n[mosquitto.conf(5)]: /man/mosquitto-conf-5.html\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2011/05/mqtt-ontology.md",
    "content": "<!--\n.. title: MQTT Ontology\n.. slug: mqtt-ontology\n.. date: 2011-05-02 23:11:43\n.. tags: Applications,Automation,Solutions\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMark Hindess has written a blog post titled [Home Automation Protocols: MQTT],\nwhere he asks for suggestions on how to go forward making \"a specification for\ntopic usage and semantics\". I think this kind of work is really valuable to\nmake it easy to have different MQTT systems that can interoperate. If you've\ngot any suggestions you can make, please go and leave a comment\nthere.\n\n[Home Automation Protocols: MQTT]: http://www.temporalanomaly.com/blog/2011/05/02/home-automation-protocols:-mqtt\n"
  },
  {
    "path": "www/posts/2011/05/version-0-10-1-released.md",
    "content": "<!--\n.. title: Version 0.10.1 released\n.. slug: version-0-10-1-released\n.. date: 2011-05-12 10:32:11\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release primarily for Windows users.\n\n * Fix Windows compilation.\n * Fix mosquitto.py on Windows - call lib init/cleanup.\n * Don't abort when connecting if given an unknown address type (assuming\n   an IPv4 or IPv6 address is given).\n\nPlease see the [download page].\n\nThanks to Karl Palsson.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2011/06/nanode-a-cheap-networked-arduino-clone.md",
    "content": "<!--\n.. title: Nanode - a cheap networked arduino clone\n.. slug: nanode-a-cheap-networked-arduino-clone\n.. date: 2011-06-08 14:31:49\n.. tags: Applications\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe arduino, the open source microcontroller board, has had MQTT support for a\nlong time in the form of [Nick O'Leary's arduino client]. It does however\nrequire networking support which has traditionally provided by an add on\nshield, which increases the cost of the system.\n\nThe [Nanode] is an arduino compatible board which includes network support and\ncan be built for approximately the same cost as a normal arduino board. It's\nstill a work in progress, but is definitely worth a look if you want to use low\npower MQTT capable sensors/controllers.\n\n[Nick O'Leary's arduino client]: http://knolleary.net/arduino-client-for-mqtt/\n[Nanode]: http://nanode.eu/\n"
  },
  {
    "path": "www/posts/2011/06/version-0-10-2-released.md",
    "content": "<!--\n.. title: Version 0.10.2 released\n.. slug: version-0-10-2-released\n.. date: 2011-06-01 20:20:37\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Don't abort when connecting if the first connection fails. This is important\n   on e.g. Windows 7, where IPV6 is offered as the first choice but may not be\n   available.\n * Deal with long logging messages properly (bug #785882).\n * Fix library compilation on Symbian - no pselect() available.\n * Don't stop processing subscriptions on received messages after a\n   subscription with # matches. (bug #791206).\n\nPlease see the [download page].\n\nThanks again to Karl Palsson and Yuvraaj Kelkar.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2011/06/version-0-11-1-released.md",
    "content": "<!--\n.. title: Version 0.11.1 released\n.. slug: version-0-11-1-released\n.. date: 2011-06-20 19:40:18\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is an important bugfix release. It fixes a buffer overrun that affects\n0.11 only. Users of 0.11 should upgrade immediately.\n\n * Fix buffer overrun when checking for + and # in topics (bug #799688).\n * Pub client now quits if publish fails.\n\nThanks to Karl Palsson.\n"
  },
  {
    "path": "www/posts/2011/06/version-0-11-2-released.md",
    "content": "<!--\n.. title: Version 0.11.2 released\n.. slug: version-0-11-2-released\n.. date: 2011-06-26 21:30:34\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Don't free contexts in mqtt3_context_disconnect() (bug #799688 / #801678).\n * Only free will if present when freeing a client context.\n"
  },
  {
    "path": "www/posts/2011/06/version-0-11-released.md",
    "content": "<!--\n.. title: Version 0.11 released\n.. slug: version-0-11-released\n.. date: 2011-06-19 14:45:43\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is an update with some fairly minor changes and some bug fixes. I had\nplanned on more exciting features but my time has been occupied getting ready\nfor the 25th, when I'm getting married. Those changes will just have to wait\nuntil 0.12!\n\n * Removed all old sqlite code.\n * Remove client id limit in clients.\n * Implemented $SYS/broker/heap/maximum size\n * Implemented $SYS/broker/clients/inactive to show the number of disconnected\n   non-clean session clients.\n * $SYS/broker/heap/current size and maximum size messages now include \"bytes\"\n   to match rsmb message format.\n * Implemented the `retained_persistence` config file option - a synonym of the\n   `persistence` option.\n * Added security_external.c to broker source to make it easier for third\n   parties to add support for their existing username/password and ACL database\n   for security checks. See external_security_checks.txt.\n * $SYS messages are now only republished when their value changes.\n * Windows native broker now responds to command line arguments.\n * Simplify client disconnecting so wills gets sent in all cases (bug #792468).\n * Clients now have a `--quiet` option.\n * The on_disconnect() callback will always be called now, even if the client\n   has disconnected unexpectedly.\n * Always close persistent DB file after restoring.\n * Return error code when exiting the clients.\n * mosquitto_publish() now returns `MOSQ_ERR_INVAL` if the topic contains + or\n   #\n * mosquitto now silently rejects published messages with + or # in the topic.\n * `max_connections` is now a per-listener setting instead of global.\n * Connection count is now reduced when clients disconnect (bug #797983).\n\nThanks to Sebastian Kroll and Karl Palsson.\n"
  },
  {
    "path": "www/posts/2011/07/debian-and-ubuntu-packaging.md",
    "content": "<!--\n.. title: Debian and Ubuntu packaging\n.. slug: debian-and-ubuntu-packaging\n.. date: 2011-07-10 23:16:24\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI'm very pleased to say that Mosquitto is very nearly packaged in Debian and\nUbuntu. In truth, 0.10 is packaged and uploaded for both Debian testing\n(Wheezy) and Ubuntu Oneiric Ocelot, but there is a problem with the config that\nmeans it won't restart properly. That is fixed with the 0.11.3 upload which is\nnow in unstable. That means after 10 days and it will be in Debian testing for\nall to use. I've also submitted a sync request with Ubuntu ([bug #808530])\nto ensure it makes it across. I'll still be maintaining the\nLaunchpad PPA for older versions of Ubuntu.\n\nThanks to the Debian developer Michael Tautschnig for reviewing my package and\ndoing the upload.\n\n[bug #808530]: https://bugs.launchpad.net/ubuntu/+source/mosquitto/+bug/808530\n"
  },
  {
    "path": "www/posts/2011/07/lua-mqtt-client.md",
    "content": "<!--\n.. title: Lua MQTT client\n.. slug: lua-mqtt-client\n.. date: 2011-07-29 07:42:28\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nAndy Gelme [reports] that his Lua MQTT client is ready for use. The downloads,\ninstallation and usage instructions, example code and api information are all\navailable at <https://github.com/geekscape/mqtt_lua>.  I particularly like the\nimage of it running on a PSP.\n\nWell done Andy!\n\nI wonder what the next language to get MQTT support will be?\n\n[reports]: https://twitter.com/#%21/geekscape/status/96710950979256323\n"
  },
  {
    "path": "www/posts/2011/07/mosquitto-on-qnx.md",
    "content": "<!--\n.. title: Mosquitto on QNX\n.. slug: mosquitto-on-qnx\n.. date: 2011-07-08 21:20:54\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nAndrea asked a [question on launchpad] about problems compiling Mosquitto on\nQNX. I've now managed to get an evaluation version of QNX and fix the\ncompilation problems. These fixes will be in 0.12, but you can get them in the\ncurrent snapshot if it's urgent. I've also put compiled binaries in the\n[downloads directory] but they are completely untested, so use at your own\nrisk.\n\nAlthough I've provided these binaries I don't intend to keep doing so for each\nversion of Mosquitto. I will endeavour to fix any other problems that arise in\nthe future though.\n\n[question on launchpad]: https://answers.launchpad.net/mosquitto/+question/164154\n[downloads directory]: http://mosquitto.org/files/binary/qnx/\n"
  },
  {
    "path": "www/posts/2011/07/version-0-11-3-released.md",
    "content": "<!--\n.. title: Version 0.11.3 released\n.. slug: version-0-11-3-released\n.. date: 2011-07-07 13:37:26\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Don't complain and quit if `persistence_file` option is given (bug #802423).\n * Initialise listeners correctly when clients with duplicate client ids\n   connect. Bug #801678.\n * Memory tracking is now disabled for Symbian builds due to lack of malloc.h.\n * Fix memory tracking compilation for kFreeBSD.\n * Python callbacks can now be used with class member functions.\n * Fix persistent database writing of client message chunks which caused errors\n   when restoring (bug #798164)\n\nThanks to Neil Bothwick, Yuvraaj Kelkar, Craig Hollabaugh, Karl Palsson and\nAndy Piper.\n"
  },
  {
    "path": "www/posts/2011/07/version-0-12-released.md",
    "content": "<!--\n.. title: Version 0.12 released\n.. slug: version-0-12-released\n.. date: 2011-07-25 22:23:19\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is an update with some features and bug fixes. The most significant\nchange is configuration reloading support. This will be improved to include\nbridge reloading in the future.\n\n * Reload (most) configuration on SIGHUP.\n * Memory tracking is no longer compiled in the client library.\n * Add `--help` option to mosquitto to display usage.\n * Add `--id-prefix` option to clients to allow easier use with brokers that\n   are using the `clientid_prefix` option.\n * Fix compilation on QNX.\n * Add `-P` as a synonym argument for `--pw` in the clients.\n * Fix python MosquittoMessage payload parameter. This is now returned as a\n   pointer to an array of c_uint8 values so binary data is handled correctly.\n   If a string is needed, use msg.payload_str\n * Fix memory leaks on client authentication.\n * If `password_file` is not defined then clients can now connect even if they \n   use a username/password.\n * Add mosquitto_reconnect() to the client library.\n * Add option for compiling with liberal protocol compliance support (enabled\n   by default).\n * Fix problems with clients reconnecting and old messages remaining in the\n   message store.\n * Display both ip and client id in the log message when a client connects.\n * Change the socket connection message to make it more obvious that it is just\n   a socket connection being made (bug #801135).\n * Fix retained message delivery where a subscription contains a +.\n * Be more lenient when reloading persistent database to reduce errors with\n   empty retained messages.\n"
  },
  {
    "path": "www/posts/2011/07/wireshark-mqtt-decoder.md",
    "content": "<!--\n.. title: Wireshark MQTT decoder\n.. slug: wireshark-mqtt-decoder\n.. date: 2011-07-05 13:23:42\n.. tags: Solutions\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIf you're trying to debug your MQTT connection, you may be interested in\nsomething Karl P has written - an MQTT decoder/dissector for Wireshark. It\ndoesn't have complete protocol support yet, but is a good start.\n\n * <http://false.ekta.is/2011/06/mqtt-dissector-decoder-for-wireshark/>\n"
  },
  {
    "path": "www/posts/2011/08/arch-linux-package.md",
    "content": "<!--\n.. title: Arch Linux package\n.. slug: arch-linux-package\n.. date: 2011-08-16 09:49:18\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nGordon Pearce has packaged Mosquitto on Arch Linux through an Arch User\nRepository. The package details are at\n<http://aur.archlinux.org/packages.php?ID=51571>\n\nThanks Gordon!\n"
  },
  {
    "path": "www/posts/2011/08/facebook-using-mqtt.attachments.json",
    "content": "{\"165\": {\"wordpress_user_name\": \"roger\", \"title\": \"iphone-app\", \"date_utc\": \"2011-08-17 15:40:22\", \"files_meta\": [{\"height\": 768, \"width\": 1024}, {\"height\": 225, \"size\": \"medium\", \"width\": 300}, {\"height\": 150, \"size\": \"thumbnail\", \"width\": 150}], \"files\": [\"/wp-content/uploads/2011/08/image.png\", \"/wp-content/uploads/2011/08/image-300x225.png\", \"/wp-content/uploads/2011/08/image-150x150.png\"]}}"
  },
  {
    "path": "www/posts/2011/08/facebook-using-mqtt.md",
    "content": "<!--\n.. title: Facebook using MQTT\n.. slug: facebook-using-mqtt\n.. date: 2011-08-17 16:43:27\n.. tags: Awesome\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nSomething else that has happened recently is the announcement by Facebook that\nthey're using MQTT in their new [Facebook Messenger app] They've posted some\ndetails in a [facebook-engineering blogpost] and cite the low bandwidth and\nbattery usage as important considerations.\n\nThis is very exciting as an application that is potentially huge and very user\noriented (rather than \"internet of things\" oriented), but the really exciting\nbit is if you use an iPhone under Settings and Licenses (apparently it's quite\nhard to find):\n\n<a href=\"/blog/uploads/2011/08/image.png\"><img class=\"size-medium wp-image-165 aligncenter\" title=\"iphone-app\" src=\"/blog/uploads/2011/08/image-300x225.png\" alt=\"\" width=\"300\" height=\"225\"></a>\n\nThanks to Michael Rowe for getting me the screenshot and Andy Piper for\npestering Michael on my behalf.\n\nYou should note that if you're in the UK, the Facebook Messenger app isn't\ncurrently available.\n\n[Facebook Messenger app]: https://www.facebook.com/mobile/messenger\n[facebook-engineering blogpost]: http://www.facebook.com/notes/facebook-engineering/building-facebook-messenger/10150259350998920\n"
  },
  {
    "path": "www/posts/2011/08/mosquitto-on-openwrt.md",
    "content": "<!--\n.. title: Mosquitto on OpenWrt\n.. slug: mosquitto-on-openwrt\n.. date: 2011-08-15 20:41:21\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThanks to work done by Karl Palsson, Mosquitto is now available on [OpenWrt],\nthe embedded Linux distribution frequently used on wireless routers. This is\nexciting if you want a really low power way of running an MQTT broker. It also\nincludes the mosquitto clients and development libraries.\n\nIt's only in the source tree at the moment, so if you want to install it I\nbelieve you'll have to download everything and compile it yourself.\n\nUpdate:\n\nKarl tells me that if you're running a binary snapshot from trunk then you can do:\n\n```\nopkg update\nopkg install mosquitto mosquitto-client libmosquitto\n```\n\nYou only need to build it yourself if you're running a stable binary.\n\n[OpenWrt]: https://openwrt.org/\n"
  },
  {
    "path": "www/posts/2011/08/mqtt-standardisation.md",
    "content": "<!--\n.. title: MQTT Standardisation\n.. slug: mqtt-standardisation\n.. date: 2011-08-15 20:31:10\n.. tags: MQTT\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIBM have announced that MQTT is to be formally standardised. If you're\ninterested in taking part in the process, there are full details at\n<http://mqtt.org/2011/08/open-invitation-to-join-the-mqtt-standardization-discussion>\n"
  },
  {
    "path": "www/posts/2011/09/version-0-13-released.md",
    "content": "<!--\n.. title: Version 0.13 released\n.. slug: version-0-13-released\n.. date: 2011-09-20 23:13:16\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis release brings some new features and fixes. Although there are no real\n\"killer features\", this release does include some fairly significant updates.\nOf particular note are the fixes to subscription wildcard matching, which now\nmeets the spec in all cases, the Python payload parameter being a Python string\nwhich should make life lots easier for Python developers, the non clean-session\nclient fixes and related persistent database fixes.\n\n * Implement bridge state notification messages.\n * Save client last used mid in persistent database (DB version number bumped).\n * Expose message id in Python MosquittoMessage.\n * It is now possible to set the topic QoS level for bridges.\n * Python MosquittoMessage payload parameter is now a Python string, not a\n   ctypes object which makes it much easier to use.\n * Fix queueing of messages for disconnected clients. The `max_queued_messages`\n   option is now obeyed.\n * C++ library is now in its own namespace, mosquittopp.\n * Add support for adding log message timestamps in the broker.\n * Fix missing mosquitto_username_pw_set() python binding.\n * Fix keepalive timeout for reconnecting non clean-session clients. Prevents\n   immediate disconnection on reconnection.\n * Fix subscription wildcard matching - a subscription of +/+ will now match\n   against /foo\n * Fix subscription wildcard matching - a subscription of foo/# will now match\n * against foo\n * When restoring persistent database, clients should be set to non\n   clean-session or their subscriptions will be immediately removed.\n * Fix SUBACK payload for multiple topic subscriptions.\n * Don't send retained messages when a client subscribes to a topic it is\n   already subscribed to.\n"
  },
  {
    "path": "www/posts/2011/10/mqtt-power-usage-on-android.md",
    "content": "<!--\n.. title: MQTT Power Usage on Android\n.. slug: mqtt-power-usage-on-android\n.. date: 2011-10-16 20:36:03\n.. tags: Mobile\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nStephen Nicholas has carried out some power usage analysis of MQTT on Android.\nDetails are at <http://stephendnicholas.com/archives/219> and the conclusion is\nthat it doesn't use much power.\n"
  },
  {
    "path": "www/posts/2011/10/two.md",
    "content": "<!--\n.. title: Two!\n.. slug: two\n.. date: 2011-10-25 23:46:24\n.. tags: Events\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nToday (just) marks the 2nd birthday of the mosquitto project. In the past year\nmosquitto has undergone pretty substantial changes and improvements. Some\nhighlights from the year:\n\n * Move away from using sqlite to store data in memory and on disk, resulting\n   in a much more compact, better performing and *more elegant* broker\n * Windows native port\n * MQTT 3.1 support\n * Greatly improved Python module\n * Getting really close to being feature complete with respect to RSMB\n * Being packaged in Debian...\n * ... and Ubuntu\n * The mosquitto client code being used by Facebook in their iphone app\n * The numerous bugs reported, bugfixes, suggestions and general interest\n   displayed by people. Thanks everyone!\n\nMosquitto has gone from version 0.8.3 to 0.13 - so what about next year? This\nwill be the year when 1.0 is released. The bar I'm setting is complete RSMB\nfeatures, with the exception of some of the more esoteric ones. At the moment\nthis means there are still some of the bridge features to implement and\ncomplete configuration reloading. I'm also going to have a much improved\nWindows port so there will be no need for a separate Cygwin version. At the\nsame time I'm making a Windows installer and allowing mosquitto to be installed\nas a proper Windows service. This work should all be in 0.14. Another point for\nimprovement is the Python module - it could be more Pythonic than it is now. My\ncurrent plan is to have it throw exceptions rather than return integer error\nvalues but I could do with the help of a Python expert really.\n\nAll in all I think it should be a good year.\n"
  },
  {
    "path": "www/posts/2011/11/android-mqtt-example-project.md",
    "content": "<!--\n.. title: Android MQTT example project\n.. slug: android-mqtt-example-project\n.. date: 2011-11-03 14:54:30\n.. tags: Mobile\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nTo celebrate the news that the IBM Java MQTT client implementation will be\nreleased as open source, I've put together a simple Android example based on\nthe [MQTT service code written by Dale Lane]. I'm a beginner at both Java and\nAndroid, so expect it to be a bit rough.\n\nThe example displays incoming payload text on a text label. It's a complete\nproject that you can build and install on your phone with only a few small\nchanges - search for \"CHANGE ME\" in\nsrc/org/mosquitto/android/mqtt/MQTTDemo.java.\n\nTo get the project working, assuming you've already installed the android sdk,\nfirst get the IBM Java library (see <http://mqtt.org/software>) and put it in\n&lt;project dir&gt;/lib then do the following:\n\n```\nandroid update project -p &lt;path to project&gt;\n# If the update complains about build.xml - delete it and run again\ncd &lt;path to project&gt;\nant debug\nsudo adb start-server\nant installd\n```\n\nI'll not be at all surprised if there are problems in the project due to\ndifferent sdk or tool versions. Please comment if you find a problem.\n\nThe project is available from\n<http://mosquitto.org/files/examples/android-mqtt-example.zip>. Until the IBM\nJava implementation is open source please be aware of the licence attached to\nit.\n\nThanks to Dale for the core Android MQTT service implementation.\n\n[MQTT service code written by Dale Lane]: http://dalelane.co.uk/blog/?p=1599\n"
  },
  {
    "path": "www/posts/2011/11/ibm-java-and-c-clients-to-be-open-source.md",
    "content": "<!--\n.. title: IBM Java and C clients to be open source\n.. slug: ibm-java-and-c-clients-to-be-open-source\n.. date: 2011-11-03 14:49:41\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe web is currently buzzing with the announcement yesterday that IBM and\nEurotech are donating MQTT to the Eclipse Foundation. One part of this is the\nnew [Machine to Machine Working Group], again part of Eclipse. Another much more significant part\nhas been released as part of a new Eclipse open source project [Paho], which is\nin the proposal stage.\n\nThe exciting part is the \"Initial contributions\" section which states \"The\ninitial code contribution to Paho will include  Java and C client-side\nimplementations the MQTT protocol, contributed by IBM. \"\n\nIt looks like the code will be licensed under the EPL (Eclipse Public License).\nThis is particularly exciting because there is currently no solid freely\navailable and usable implementation of MQTT in Java.\n\nWell done everyone at IBM for making this happen. Roll on the end of November!\n\nUpdate:\n\nThis post on mqtt.org explains what the various announcements mean more\nclearly: <http://mqtt.org/2011/11/eclipse-paho-open-source-and-other-news>\n\nUpdate 2:\n\nAndy Piper's blog post covers things even better, along with clearing up some\nof the confusions from the news releases:\n<http://andypiper.co.uk/2011/11/04/mqtt-goes-free-a-personal-qa/>\n\n[Machine to Machine Working Group]: http://wiki.eclipse.org/M2MIWG_charter_draft\n[Paho]: http://www.eclipse.org/proposals/technology.paho/\n"
  },
  {
    "path": "www/posts/2011/11/new-linux-repositories.md",
    "content": "<!--\n.. title: New Linux repositories\n.. slug: new-linux-repositories\n.. date: 2011-11-11 11:20:41\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI've just added some more Linux repositories to the download page for Fedora 16\nand SLE 10, 11 and 11 SP1.\n\nNote that mosquitto-python isn't available on SLE 10.\n\nSee the [download page](/download).\n"
  },
  {
    "path": "www/posts/2011/11/version-0-14-1-released.md",
    "content": "<!--\n.. title: Version 0.14.1 released\n.. slug: version-0-14-1-released\n.. date: 2011-11-21 16:22:01\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Fix Python syntax errors (bug #891673).\n"
  },
  {
    "path": "www/posts/2011/11/version-0-14-2-released.md",
    "content": "<!--\n.. title: Version 0.14.2 released\n.. slug: version-0-14-2-released\n.. date: 2011-11-28 21:20:46\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release:\n\n * Add uninstall target for libs.\n * Don't try to write packet whilst in a callback.\n"
  },
  {
    "path": "www/posts/2011/11/version-0-14-released.md",
    "content": "<!--\n.. title: Version 0.14 released\n.. slug: version-0-14-released\n.. date: 2011-11-16 23:23:03\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a fairly minor feature release. The major changes are the pattern\nmatching ACL support, the support for running directly as a Windows service and\nthe change to the network code to attempt to send packets immediately. The\nWindows binary is now supplied as an installer rather than a zip file.\n\n * Add support for matching ACLs based on client id and username.\n * Add a Windows installer file (NSIS based).\n * Add native support for running the broker as a Windows service. This is the\n   default when installed using the new installer.\n * Fix client count for listeners. When clients disconnect, decrement the\n   count. Allow `max_connections` to work again.\n * Attempt to send all packets immediately upon being queued. This will result\n   in more immediate network communication in many cases.\n * Log IP address when reporting CONNACK packets if the client id isn't yet\n   known.\n * Fix payload length calculation in python `will_set` function.\n * Fix Python publish and `will_set` functions for payload=None.\n * Fix keepalive value being lost when reconnecting a client (bug #880863).\n * Persistence file writing now uses portable file functions, so the Cygwin\n   broker build should no longer be necessary.\n * Duplicate code between the client and broker side has been reduced.\n * Queued messages for clients reconnecting with `clean_session=false` set were\n   not being sent until the next message for that client was received. This has\n   been fixed (bug #890724).\n * Fix subscriptions to # incorrectly matching against topics beginning with /\n"
  },
  {
    "path": "www/posts/2011/12/mqtt-on-nanode.md",
    "content": "<!--\n.. title: MQTT on Nanode\n.. slug: mqtt-on-nanode\n.. date: 2011-12-31 11:07:48\n.. tags: Solutions\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n[Nanode], the popular arduino-with-ethernet board started early in 2011 is\nideal for small MQTT based projects but has so far lacked an implementation of\nMQTT.\n\nNick O'Leary, the author of the original Arduino MQTT client, [has created a\nNanode implementation], but it [isn't quite ready for the public].\n\nNicholas Humfrey has made public some code at\n<https://github.com/njh/NanodeMQTT> that he says [still needs some work] but\nsupports publishing QoS 0 messages of up to 127 bytes long and subscribing to\ntopics with QoS 0.\n\n[Nanode]: http://nanode.eu/\n[has created a Nanode implementation]: https://twitter.com/#!/knolleary/status/151057575775965184\n[isn't quite ready for the public]: https://twitter.com/#!/knolleary/status/151059089881960448\n[still needs some work]: https://twitter.com/#!/njh/status/152913104446038018\n"
  },
  {
    "path": "www/posts/2011/12/version-0-14-3-released.md",
    "content": "<!--\n.. title: Version 0.14.3 released\n.. slug: version-0-14-3-released\n.. date: 2011-12-10 18:32:33\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Fix potential crash when client connects with an invalid CONNECT packet.\n * Fix incorrect invalid socket comparison on Windows.\n * Server shouldn't crash when a message is published to foo/ when a\n   subscription to foo/# exists (bug #901697).\n * SO_REUSEADDR doesn't work the same on Windows, so don't use it.\n * Cygwin builds now support Windows service features.\n * Fix $SYS/broker/bytes/sent reporting.\n"
  },
  {
    "path": "www/posts/2012/01/challenge-web-based-mqtt-graphing.md",
    "content": "<!--\n.. title: Challenge: Web based MQTT graphing\n.. slug: challenge-web-based-mqtt-graphing\n.. date: 2012-01-16 12:21:06\n.. tags: Solutions\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThanks to a data feed courtesy of an IBM broker, [test.mosquitto.org] now\npublishes information on energy generation and demand in the UK (in the energy/\ntopic tree). I think this could be used as a great demonstration for coupling\nMQTT and the web.\n\n# The challenge\nCreate a web based report that takes energy data from the broker over MQTT and\ndisplays it in interesting and useful ways. Alternatively, an Android/iPhone\napp would be ok, but web based is the preferred option.\n\n# The rules\nThere are no rules really. Having said that, I'd be most pleased if the end\nresult was something that other people could learn from. There are bonus points\nfor solutions that work where a web proxy is the only internet access. If you\nwant to use new or unusual technologies that's fine.\n\n# The prize\nI'm afraid there is no tangible prize - I hope you'll be content with your work\nbeing shown here and the respect of your peers.\n\n# Some suggestions\nGoogle charts is definitely worth looking at for generating the actual graphs.\nSome examples of what you might show are:\n\n * Pie chart of generation source\n * Gauge of current mains frequency\n * Historical graph of electricity export amount\n\nI look forward to any and all responses!\n\n[test.mosquitto.org]: http://test.mosquitto.org/\n"
  },
  {
    "path": "www/posts/2012/01/do-you-use-mqtt.md",
    "content": "<!--\n.. title: Do you use MQTT?\n.. slug: do-you-use-mqtt\n.. date: 2012-01-05 22:21:49\n.. tags: Applications\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI saw this in the nanode irc channel:\n\n> I've never seen any real world projects with MQTT... it looks good though.\n\nSo I'm looking for real world projects that use MQTT. If you've got a project\nit'd be great if you could mention it in the comments. A short sentence on what\nit does and how many clients you run on it - really anything you can say. If\nit's a secret please still comment if you can, just be very very vague. If\nyou've got a blog post describing it, link that instead. I'm interested in\neverything from a single temperature sensor reporting to a computer up to\nmillions of mobile users.\n\nThanks!\n"
  },
  {
    "path": "www/posts/2012/01/mosquitto-test-server.md",
    "content": "<!--\n.. title: Mosquitto Test Server\n.. slug: mosquitto-test-server\n.. date: 2012-01-06 21:43:07\n.. tags: Support,Testing\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nA publicly accessible Mosquitto server is now available to use. Details are at\n[test.mosquitto.org]\n\n[test.mosquitto.org]: http:/test.mosquitto.org/\n"
  },
  {
    "path": "www/posts/2012/01/version-0-14-4-released.md",
    "content": "<!--\n.. title: Version 0.14.4 released\n.. slug: version-0-14-4-released\n.. date: 2012-01-07 16:24:14\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release:\n\n * Fix local bridge notification messages.\n * Fix return values for more internal library calls.\n * Fix incorrect out of memory checks in library and broker.\n * Never time out local bridge connections.\n"
  },
  {
    "path": "www/posts/2012/02/mqtt2pachube.md",
    "content": "<!--\n.. title: mqtt2pachube\n.. slug: mqtt2pachube\n.. date: 2012-02-06 22:41:58\n.. tags: Solutions, Obsolete\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI've written a tool to help get data from mqtt to [pachube]. Existing pachube\nlibraries offer good support for updating feeds that have a single datastream\nor updating all feeds in a datastream, but seem to offer limited support for\nupdating an arbitrary datastream on its own. This can make life difficult when\nyour data is coming in from sensors as individual messages.\n\n[mqtt2pachube] allows you to choose what mqtt subscriptions to make and then\nmatch incoming messages by their topics to a pachube feed and datastream id.\n\nAt the moment it is still experimental, but seems to work. It has highlighted a\nshortcoming in the mosquitto client library, so requires version 0.15.90 (ie.\nthe in-progress work for the next release). There is no Windows support for the\nmoment and no binary packages either. If you are interested in giving it a try,\nyou will have to compile it yourself. If you need help, please get in touch.\n\nThere are two examples of feeds created through mqtt2pachube using data from\n[test.mosquitto.org]\n\n * [test.mosquitto.org details]\n * [UK energy data - generation source percentage]\n\n[pachube]: http://pachube.com/\n[mqtt2pachube]: http://bitbucket.org/oojah/mqtt2pachube\n[test.mosquitto.org details]: https://pachube.com/feeds/43810\n[UK energy data - generation source percentage]: https://pachube.com/feeds/47080\n[test.mosquitto.org]: http://test.mosquitto.org/\n"
  },
  {
    "path": "www/posts/2012/02/version-0-15-released.md",
    "content": "<!--\n.. title: Version 0.15 released\n.. slug: version-0-15-released\n.. date: 2012-02-05 09:26:41\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a feature and bugfix release.\n\n * Implement \"once\" and \"lazy\" bridge start types.\n * Add support for $SYS/broker/clients/maximum and $SYS/broker/clients/active\n   topics.\n * Add support for $SYS messages/byte per second received/sent topics.\n * Updated mosquitto man page - $SYS hierarchy and signal support were out of\n   date.\n * Auto generated pub/sub client ids now include the hostname.\n * Tool for dumping persistent DB contents is available in src/db_dump. It\n   isn't installed by default.\n * Enforce topic length checks in client library.\n * Add new return type `MOSQ_ERR_ERRNO` to indicate that the errno variable\n   should be checked for the real error code.\n * Add support for `connection_messages` config option.\n * mosquitto_sub will now refuse to run if the -c option (disable clean\n   session) is given and no client id is provided.\n * mosquitto_pub now gives more useful error messages on invalid input or other\n   error conditions.\n * Fix Python `will_set()` true/True typo.\n * Fix messages to topic `a/b` incorrectly matching on a subscription `a` if\n   another subscription `a/#` exists.\n"
  },
  {
    "path": "www/posts/2012/03/quick-start-guide-for-mqtt-with-pachube.md",
    "content": "<!--\n.. title: Quick start guide for MQTT with Pachube/Cosm/Xively\n.. slug: quick-start-guide-for-mqtt-with-pachube\n.. date: 2012-03-10 10:32:22\n.. tags: Applications,Documentation,Solutions\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nPachube (now Cosm) has recently announced beta support for publishing and\nreceiving data to their service using MQTT. This is great news and something I\nknow that a lot of people have been hoping for. Well done Pachube!\n\nTheir documentation is at\n<https://xively.com/dev/docs/api/communicating/mqtts/> and provides enough\ninformation to get going if you're already familiar with MQTT.\n\nIf you aren't familiar with MQTT, here's a few examples of how you can use the\nnew service.\n\nFirst off, I'm going to use the command line MQTT clients I've created to\npublish and receive data. You can get these clients as part of the [mosquitto\ndownload].\n\n \n# Command Line Examples\n\n## Publishing Data\n\n```\nmosquitto_pub -h api.xively.com\n              -u &lt;your xively api-key&gt;\n              -t /v2/feeds/504.csv\n              -m \"0,29\"\n```\n\nIn this example we're connecting to host api.xively.com, using our xively\napi-key as the username, publishing to feed /v2/feeds/504 using the csv format\nand are updating datastream 0 with the value 29. Another way to achieve the\nsame thing would be to do:\n\n```\nmosquitto_pub -h api.xively.com\n              -u &lt;your xively api-key&gt;\n              -t /v2/feeds/504/datastreams/0.csv\n              -m 29\n```\n\nmosquitto_pub can read data from stdin and publish it, so on Unix type systems the following arrangement is possible:\n\n```\nsensor_read | mosquitto_pub -h api.xively.com\n                            -u &lt;api-key&gt;\n                            -t /v2/feeds/504/datastreams/0.csv\n                            -l\n```\n\nThe `-l` option reads messages from stdin, sending a separate message for each\nline. This means that our imaginary executable sensor_read that is reading data\nfrom a sensor must be printing each reading as a text line.\n\n## Retrieving Data\n\nIn the MQTT world, retrieving data is done through subscriptions:\n\n```\nmosquitto_sub -h api.xively.com\n              -u &lt;api-key&gt;\n              -t /v2/feeds/504/datastreams/0.csv\n```\n\nIn this example, mosquitto_sub will print a text line containing the csv data\nfor datastream 0 of feed 504 every time it is updated.\n\n## Last Will and Testament\n\nThe last will and testament or just \"will\" is a very nice feature of MQTT. When\nyour client connects to the MQTT broker/server, it can give the broker this\nwill, which consists of a topic and a message. If the client is disconnected\nfrom the broker unexpectedly, that is to say without sending a disconnect\nmessage, then the broker publishes the will message on the will topic.\n\nThis provides a very simple mechanism for client connection monitoring. When\nyour client connects it could publish a message \"1\" to a topic. If it also set\na will to send a message \"0\" to the same topic on unexpected disconnect, then\nit would be possible to determine whether that client was connected by\nmonitoring the topic.\n\nIn the context of Xively, the same approach is possible, but using a trigger to\nindicate that the client had disconnected.\n\nThe mosquitto_sub client provides support for wills as shown in the example\nbelow:\n\n```\nmosquitto_sub -h api.xively.com\n              -u &lt;api-key&gt;\n              -t /v2/feeds/504/datastreams/0.csv\n              --will-topic /v2/feeds/12345/datastreams/0.csv\n              --will-payload \"0\"\n```\n\nIn this example, the Xively broker would publish the value \"0\" to datastream 0\nof feed 12345  if mosquitto_sub disconnects unexpectedly. This isn't the most\nuseful example because of the limitations of what mosquitto_sub provides.\n\n# Writing Your Own Clients\nIn practice, to get the full benefit of the advantages that MQTT provides you\nwill probably want to write your own MQTT client to connect to Xively for your\nspecific application. The <http://mqtt.org/software> page lists client\nimplementations for lots of different programming languages including the\nmosquitto client libraries in C/C++, libraries in Java, Python and also device\nspecific implementations for Arduino and other low power devices.\n\n# MQTT Beyond Xively\nThe Xively offering is a slightly restricted MQTT offering. \"Full\" MQTT offers\na bit more scope for doing fun things using topic wildcards for example,\nsomething that wouldn't really make sense for Xively.\n\nThere is an overview of MQTT at [mqtt man page] and examples of some\napplications at <http://mosquitto.org/2012/01/do-you-use-mqtt/>.\n\nIf you'd like to play on an MQTT broker, try looking at [test.mosquitto.org].\n\nIf you want some help there are mailing lists and irc channels listed on\n<http://mqtt.org/get-involved>.\n\n[mosquitto download]: /download\n[mqtt man page]: /man/mqtt-7.html\n[test.mosquitto.org]: http://test.mosquitto.org/\n"
  },
  {
    "path": "www/posts/2012/03/upcoming-incompatible-library-changes.md",
    "content": "<!--\n.. title: Upcoming incompatible library changes\n.. slug: upcoming-incompatible-library-changes\n.. date: 2012-03-06 07:37:31\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 0.16 of the mosquitto client libraries will have some binary\nincompatible changes to their APIs. This means that it is a good time to make\nother changes that are incompatible. If you think any part of the interface\n(see <http://mosquitto.org/api/>) is crazy or could be improved in any way,\nplease get in touch or add a comment below.\n"
  },
  {
    "path": "www/posts/2012/05/python-client-module-available-for-testing.md",
    "content": "<!--\n.. title: Python client module available for testing\n.. slug: python-client-module-available-for-testing\n.. date: 2012-05-07 01:17:57\n.. tags: Solutions,Testing\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nAs part of the ongoing work on mosquitto 0.16, the libmosquitto C client\nlibrary has been ported to Python. It provides complete MQTTv3.1 support and\nwill eventually remove the need for the current Python wrapper around the C\nlibrary and will allow it to be used more easily and in more situations.\n\nThe interface is largely the same as the existing Python wrapper. The\ndifferences are that it uses the current development interface which differs\nslightly from that in 0.15 (see the [Python documentation]), not all of the new\ninterface is implemented - there is no threading support and finally some\ndatatypes may be more Python like (e.g. lists in `on_subscribe()` callback\nrather than an integer).\n\nThe conversion from ~4000 lines C to ~1000 lines Python took just two evenings\nand is now ready for testing. It is available in the 0.16 branch in the\n[bitbucket repository], or as a single file at\n<http://mosquitto.org/files/python/mosquitto.py>\n\nPlease give it a try and report any bugs you find using any of the methods on\nthe [Support page].\n\nPlease note that the new Python module does not currently support Python 3.\n\n[Python documentation]: /documentation/python\n[bitbucket repository]: https://bitbucket.org/oojah/mosquitto/src/b9e04ef2a762/lib/python/mosquitto.py\n[Support page]: /support\n"
  },
  {
    "path": "www/posts/2012/06/ipv6-on-test-server.md",
    "content": "<!--\n.. title: IPv6 on Test Server\n.. slug: ipv6-on-test-server\n.. date: 2012-06-29 09:49:41\n.. tags: Testing\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe public Mosquitto test server, [test.mosquitto.org] has supported IPv6 since\nit was originally put online but the required DNS record was missing. This has\nnow been fixed so once the record has propagated across the internet you should\nbe able to test your IPv6 clients.\n\n[test.mosquitto.org]: http://test.msoquitto.org/\n"
  },
  {
    "path": "www/posts/2012/06/ssl-support-on-test-server.md",
    "content": "<!--\n.. title: SSL support on Test Server\n.. slug: ssl-support-on-test-server\n.. date: 2012-06-30 15:15:57\n.. tags: Testing\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe next version of Mosquitto will provide SSL support for network encryption\nand authentication. This work is still in development, but is sufficiently\nadvanced to make available for testing on [test.mosquitto.org]. In addition to\nthe existing unencrypted access via port 1883, connections are now possible on\nports 8883 and 8884.\n\nPort 8883 provides simple encrypted access. Your client should verify the\nserver certificate using the CA certificate available at\n<http://test.mosquitto.org/ssl/mosquitto.org.crt>\n\nPort 8884 uses the same server certificate, but requires that your client\nprovide a valid certificate signed by the mosquitto.org CA key. If you wish to\nobtain a client certificate for testing purposes, please get in touch.\n\nThe development Python module provides client SSL support. The latest version\nis available at [mosquitto.py] with a simple example at [ssub.py].  You will\nneed to place the mosquitto.org CA certificate linked above in the same\ndirectory. All versions of Python from 2.7 upwards (including Python 3) are\nsupported.\n\nPlease get in touch if you have any problems.\n\nUpdate:\n\nAll clients in the development version now support SSL.\n\n[test.mosquitto.org]: http://test.mosquitto.org/\n[mosquitto.py]: http://test.mosquitto.org/ssl/mosquitto.py\n[ssub.py]: http://test.mosquitto.org/ssl/ssub.py\n"
  },
  {
    "path": "www/posts/2012/07/upcoming-release.md",
    "content": "<!--\n.. title: Upcoming release\n.. slug: upcoming-release\n.. date: 2012-07-21 09:17:21\n.. tags: Testing\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe next release of mosquitto is approaching. There is currently only one\nfeature left on the todo list to complete and I've pencilled in the end of the\nmonth as the release date. The date may slip a week or two after that depending\non any bugs reported.\n\nDespite the development being carried out in the 0.16 branch and the current\nin-development version numbers being 0.15.90, this will be version 1.0 of\nmosquitto. There has been significant API changes (now a lot more sane\nhopefully) which means the client library interface version has been\nincremented, and the number of changes involved in this release far outreach\nany previous release, including SSL support, a pure Python client\nimplementation, a healthy start on tests and an associated improvement in\nprotocol compliance, and threaded client support. I think it is well worthy of\nthe version number.\n\nI am, however, very keen that this be as bug free a release as possible. To\nthis end, if you're a mosquitto user I'd be very appreciative if you'd download\nthe current source code and give it a try. Maybe read through the documentation\nand check it makes sense\n\nThe source for the current version is at either of these links (ignore the\n\"0.16\", that is just the branch name):\n\n * <https://bitbucket.org/oojah/mosquitto/get/0.16.zip>\n * <https://bitbucket.org/oojah/mosquitto/get/0.16.bz2>\n\nIf you want to test but with a minimum amount of effort, please download the\nsource, run \"make test\" and report back any problems. This would be\nparticularly  useful if you are using something other than a\nDebian/Ubuntu/openSUSE based Linux. If you have any problems, bugs can be\nreported at <https://launchpad.net/mosquitto>, by leaving a comment or by\ngetting in touch directly. I'm interested in anything, but would be especially\nkeen to hear from you if you think something to do with the client API needs\nchanging.\n\nThanks in advance!\n"
  },
  {
    "path": "www/posts/2012/08/baby.attachments.json",
    "content": "{\"243\": {\"wordpress_user_name\": \"roger\", \"title\": \"IMAG0006\", \"date_utc\": \"2012-08-22 09:39:33\", \"files_meta\": [{\"height\": 333, \"width\": 500, \"meta\": {\"camera\": \"HTC Wildfire S A510e\", \"created_timestamp\": 1345456796.0, \"focal_length\": 3.53, \"iso\": 165.0}}, {\"height\": 199, \"size\": \"medium\", \"width\": 300}, {\"height\": 150, \"size\": \"thumbnail\", \"width\": 150}], \"files\": [\"/wp-content/uploads/2012/08/IMAG0006.jpg\", \"/wp-content/uploads/2012/08/IMAG0006-300x199.jpg\", \"/wp-content/uploads/2012/08/IMAG0006-150x150.jpg\"]}}"
  },
  {
    "path": "www/posts/2012/08/baby.md",
    "content": "<!--\n.. title: Baby\n.. slug: baby\n.. date: 2012-08-22 10:41:07\n.. tags: Releases,Support\n.. category:\n.. link:\n.. description:\n.. type: text\n.. author: Roger\n-->\n\n[![baby](/blog/uploads/2012/08/IMAG0006-300x199.jpg)](/blog/uploads/2012/08/IMAG0006.jpg)\n\nI've recently become a father, so please don't be offended if I take a while to\nrespond to any mosquitto related queries.\n"
  },
  {
    "path": "www/posts/2012/08/bugfix-coming-soon.md",
    "content": "<!--\n.. title: Bugfix coming soon...\n.. slug: bugfix-coming-soon\n.. date: 2012-08-15 11:13:40\n.. tags: Releases,Testing\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nA few bugs have been identified with the 1.0 release; thanks to everyone who\nhas got in touch about it. They're mostly documentation/build script mistakes\n(see [ChangeLog.txt]), but there is a Python bug that makes it worthwhile\nmaking a quick bugfix release.\n\nI intend to make the release this evening (in around 8 hours from this post),\nso if you have anything you think needs fixing please try and get in touch\nbefore then.\n\n[ChangeLog.txt]: /ChangeLog.txt\n"
  },
  {
    "path": "www/posts/2012/08/version-1-0-1-released.md",
    "content": "<!--\n.. title: Version 1.0.1 released\n.. slug: version-1-0-1-released\n.. date: 2012-08-15 22:39:52\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release. The important changes are fixing the `on_log()`\ncallback in the Python module and the `log_dest` option when running as a\nWindows service. The rest of the fixes are documentation and build script\nfixes.\n\nDownloads are available on the [download page] and include all supported\nbinaries (except for Ubuntu packages which are still waiting to build due to\nLaunchpad maintenance). The Python module has been uploaded to [Python Package\nIndex].\n\n# Broker\n\n * Fix default `log_dest` when running as a Windows service.\n\n# Client library\n \n * Fix incorrect parameters in Python `on_log()` callback call. Fixes bug\n   #1036818.\n\n# Clients\n\n * Clients now don't display TLS/TLS-PSK usage help if they don't support it.\n\n# Build scripts\n\n * Fix TLS-PSK support in the CMake build files.\n * Fix man page installation in the CMake build files.\n * Fix SYSCONFDIR in cmake on \\*nix when installing to /usr. Fixes bug\n   #1036908.\n\n# Documentation\n \n * Fix mqtt/MQTT capitalisation in man pages.\n * Update compiling.txt.\n * Fix incorrect callback docs in mosquitto.py. Fixes bug #1036607.\n * Fix various doc typos and remove obsolete script. Fixes bug #1037088.\n\n[download page]: /download\n[Python Package Index]: http://pypi.python.org/pypi\n"
  },
  {
    "path": "www/posts/2012/08/version-1-0-2-released.md",
    "content": "<!--\n.. title: Version 1.0.2 released\n.. slug: version-1-0-2-released\n.. date: 2012-08-19 06:25:35\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n * If the broker was configured for persistence, a durable client had a\n   subscription to topics in $SYS/# and had messages in its queue when the\n   broker restarted, then the persistent database would have messages missing\n   and so the broker would not restart properly. This has been fixed.\n\n# Library\n\n * Fix threading problem on some systems.\n\n# Tests\n\n * Close socket after 08-ssl-connect-no-auth-wrong-ca.py test to prevent\n * subsequent tests having problems.\n\n# Build scripts\n\n * Install pskfile.example in CMake. Fixes bug #1037504.\n\n# Other\n\n * Fix db_dump parameter printing message store and sub chunks.\n"
  },
  {
    "path": "www/posts/2012/08/version-1-0-released.md",
    "content": "<!--\n.. title: Version 1.0 released\n.. slug: version-1-0-released\n.. date: 2012-08-14 00:12:52\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a feature and bugfix release. This is the most significant release for\nthe mosquitto project so far. It encompasses &gt;20% of the total commits for\nthe project and has an increase in source tarball size of 95%, mostly down to\nthe new bundled tests and new man pages. It introduces lots of new features for\nthe broker and improves the API of the client libraries, although this does\nmean that the libraries are incompatible with previous releases. I apologise\nfor this and hope you'll agree that the changes are worth it.\n\nI've been overwhelmed with the amount of feedback that I've received recently,\nthanks to everyone that has got in touch to let me know where something could\nbe improved. I'd particularly like to thank Nicholas Humfrey for setting me on\nthe continuous integration path.\n\nOn a slightly different note, my wife was expecting our first child two days\nago so it's quite likely I'll be less responsive to support requests for a\nlittle while.\n\n# Significant changes\n\nThese are what I think are the exciting changes for this release.\n\n * SSL/TLS support across the board - the broker, client libraries and pub/sub\n   clients. This provides certificate based network encryption in a very\n   similar manner to SSL in a web browser where the client verifies that the\n   server is valid. It is also possible to use client certificates to\n   authenticate the clients with the server.\n * TLS-PSK support (not on Python). This is \"pre-shared-key\" network encryption\n   and represents a simpler encryption interface than certificate based\n   encryption which makes it much more suitable for embedded/constrained\n   devices.\n * The Python client library is now written in pure Python so is much easier to\n   use. It supports Python 2.6, 2.7 and 3.\\* (no SSL support for 2.6).\n * All client libraries have had their interface overhauled and should now be\n   much saner and straightforward to use.\n * The client libraries have thread support.\n * Passwords files for the broker are stored hashed and salted and a utility\n   for maintaining them has been provided.\n * It is now possible to write access and authentication plugins for the broker\n   for providing custom support for authentication against e.g. a SQL database.\n * Implementation of a good test suite which has lead to improved protocol\n   compliance amongst other bug fixes.\n * Masses of bug fixes.\n\n# Downloads\n\nSource is available on the [download page], the binary packages will follow as\nsoon as possible. Windows and Ubuntu packages are currently available, more to\nfollow.\n\n# Changes\n\nThe complete list of changes is below:\n\n# The broker\n\n * Add SSL/TLS support.\n * Add TLS-PSK support, providing a simpler encryption method for constrained\n   devices.\n * Passwords are now salted+hashed if compiled with WITH_TLS (recommended).\n * Add mosquitto_passwd for handling password files.\n * Add $SYS/broker/publish/messages/{sent|received} to show the number of\n   PUBLISH messages sent/received.\n * Add $SYS/broker/publish/bytes/{sent|received} to show the number of PUBLISH\n   bytes sent/received.\n * Add reload parameter for security init/cleanup functions.\n * Add option for expiring disconnected persistent clients.\n * Add option for queueing of QoS 0 messages when persistent clients are\n   disconnected.\n * Enforce client id limits in the broker (only when WITH_STRICT_PROTOCOL is\n   defined).\n * Fix reloading of log configuration.\n * Add support for `try_private` config option for bridge connections.\n * Add support for `autosave_on_changes` config option.\n * Add support for `include_dir` config option.\n * Add support for topic remapping.\n * Usernames were being lost when a non clean-session client reconnected,\n   potentially causing problems with ACLs. This has been fixed.\n * Significant improvement to memory handling on Windows.\n * Bridges with outgoing topics will now set the retain flag correctly so that\n   messages will be retained on the remote broker.\n * Incoming bridge connections are now detected by checking if bit 8 of the\n   protocol version number is set. This requires support from the remote\n   broker.\n * Add support for `notification_topic` option.\n * Add $SYS/broker/subscriptions/count and $SYS/broker/retained messages/count.\n * Add `restart_timeout` to control the amount of time an automatic bridge will\n   wait before reconnecting.\n * Overlapping subscriptions are now handled properly. Fixes bug #928538.\n * Fix reloading of `persistence_file` and `persistence_location`.\n * Fix broker crash on incorrect protocol number.\n * Fix missing COMPAT_ECONNRESET define on Windows.\n * Clients that had disconnected were not always being detected immediately on\n   Linux. This has been fixed.\n * Don't save $SYS messages to the on-disk persistent db. All $SYS messages\n   should be reconstructed on a restart. This means bridge connection\n   notifications will now be correct on a restart.\n * Fix reloading of bridge clients from the persistent db. This means that\n   outgoing bridged topics should always work.\n * Local bridges are now no longer restricted by local ACLs.\n * Discard publish messages with zero length topics.\n * Drop to \"mosquitto\" user even if no config file specified.\n * Don't incorrectly allow topic access if ACL patterns but no normal ACL rules\n   are defined.\n\n## The client libraries\n\n * Add SSL/TLS support.\n * Add TLS-PSK support, providing a simpler encryption method for constrained\n   devices.\n * Add javascript/websockets client library.\n * Add `struct mosquitto *mosq` parameter for all callbacks in the client\n   library. This is a binary incompatible change so the soversion of the\n   libraries has been incremented. The new parameter should make it easier to\n   use callbacks in practice.\n * Add `mosquitto_want_write()` for use when using own select() loop with\n   `mosquitto_socket()`.\n * Add `mosquitto_connect_async()` to provide a non-blocking connect client call.\n * Add `mosquitto_user_data_set()` to allow user data pointer to be updated.\n * Add \"int rc\" parameter to disconnect callback to indicate whether disconnect\n   was unexpected or the result of calling `mosquitto_disconnect()`.\n * Add `mosquitto_strerror()` for obtaining a string description of error numbers.\n * Add `mosquitto_connack_string()` for obtaining a string description of MQTT\n   connection results.\n * Add `mosquitto_will_clear()` and change `mosquitto_will_set()` to only set the\n   will.\n * Add `mosquitto_sub_topic_tokenise()` and `mosquitto_sub_topic_tokens_free()`\n   utility functions to tokenise a subscription/topic string into a string\n   array.\n * Add `mosquitto_topic_matches_sub()` to check whether a topic matches a\n   subscription.\n * Replaced `mosquitto_log_init()` with `mosquitto_log_callback_set()` to allow\n   clients to decide what to do with log messages.\n * Client will now disconnect itself from the broker if it doesn't receive a\n   PINGRESP in the keepalive period after sending a PINGREQ.\n * Client will now send a PINGREQ if it has not received a message from the\n   broker in keepalive seconds.\n * `mosquitto_new()` will now generate a random client id if the id parameter\n   is NULL.\n * Added `max_packets` to `mosquitto_loop()`, `mosquitto_loop_read()` and\n   `mosquitto_loop_write()` to control the maximum number of packets that are\n   handled per call.\n * Payload parameters are now void * instead of uint8\\_t \\*.\n * The `clean_session` parameter has been moved from `mosquitto_connect()` to\n   `mosquitto_new()` because it is a client parameter rather than a connection\n   parameter.\n * Functions now use int instead of uint\\*\\_t where possible.\n * `mosquitto_new()` now sets errno to indicate failure type.\n * Return `MOSQ_ERR_INVAL` on zero length topic.\n * Fix automatic client id generation on Windows.\n * `mosquitto_loop_misq()` can now return `MOSQ_ERR_NO_CONN`.\n * Compile static library as well as dynamic library with default makefiles.\n * Rename C++ namespace from mosquittopp to mosqpp to remove ambiguity.\n * C++ `lib_init()`, `lib_version()` and `lib_cleanup()` are now in the mosqpp\n   namespace directly, not mosquittopp class members.\n * The Python library is now written in pure Python and so no longer depends on\n   libmosquitto.\n * The Python library includes SSL/TLS support.\n * The Python library should now be compatible with Python 3.\n\n## Other\n\n * Fix db_dump reading of retained messages.\n * Add example of logging all messages to mysql.\n * Add C++ client example.\n * Fix potential buffer overflow in pub/sub clients.\n * Add \"make binary\" target that doesn't make documents.\n * Add `--help` arguments to pub/sub clients.\n * Fix building on Solaris.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2012/09/updating-password-files.md",
    "content": "<!--\n.. title: Updating password files\n.. slug: updating-password-files\n.. date: 2012-09-09 21:41:25\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.0 introduced the use of password files with hashed passwords but\nhad no way to convert from the old plain text password files. This feature will\nbe available in version 1.1 but if it is important to you then you can already\nget the updated code for the mosquitto_passwd utility at\n<https://bitbucket.org/oojah/mosquitto/src/3b8ef11cf687>\n"
  },
  {
    "path": "www/posts/2012/09/version-1-0-3-released.md",
    "content": "<!--\n.. title: Version 1.0.3 released\n.. slug: version-1-0-3-released\n.. date: 2012-09-27 14:06:28\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Fix loading of psk files.\n * Don't return an error when reloading config if an ACL file isn't defined. \n   This was preventing psk files being reloaded.\n * Clarify meaning of $SYS/broker/clients/total in mosquitto(8) man page.\n * Clarify meaning of $SYS/broker/messages/stored in mosquitto(8) man page.\n * Fix non-retained message delivery when subscribing to #.\n * Fix retained message delivery for subs to foo/# with retained messages at\n   foo.\n * Include the filename in password/acl file loading errors.\n\n# Library\n\n * Fix possible AttributeError when `self._sock == None` in Python module.\n * Fix reconnecting after a timeout in Python module.\n * Fix reconnecting when there were outgoing packets in the queue in the Python\n   module.\n * Fix problem with mutex initialisation causing crashes on some Windows\n   installations.\n\nSource is available on the [download page], the binary packages for Windows are\navailable now and Linux builds will be available as soon as the various build\nservers complete their tasks.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2012/10/version-1-0-4-released.md",
    "content": "<!--\n.. title: Version 1.0.4 released\n.. slug: version-1-0-4-released\n.. date: 2012-10-17 23:35:15\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Deal with poll() POLLIN/POLLOUT before POLL[RD]HUP to correctly handle the\n   case where a client sends data and immediately closes its socket.\n\n# Library\n\n * Fix memory leak with messages of QoS=2. Fixes bug #1064981.\n * Fix potential thread synchronisation problem with outgoing packets in the\n   Python module. Fixes bug #1064977.\n\n# Clients\n\n * Fix `mosquitto_sub -l` incorrectly only sending one message per second.\n"
  },
  {
    "path": "www/posts/2012/11/making-mosquitto-packages-for-debian-yourself.md",
    "content": "<!--\n.. title: Making Mosquitto packages for Debian yourself\n.. slug: making-mosquitto-packages-for-debian-yourself\n.. date: 2012-11-27 16:26:41\n.. tags: Packaging,Solutions\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nAs Debian has been in feature freeze since before Mosquitto 1.0 was released,\nit will be a long time until there is an updated version of Mosquitto in\nDebian. It is, however, fairly straightforward to do the packaging yourself.\nHere's how to do that from the command line.\n\nDownload and unpack the mosquitto source tarball:\n\n```\nwget http://mosquitto.org/files/source/mosquitto-1.1.2.tar.gz\ntar -zxf mosquitto-1.1.2.tar.gz\n```\n\nRename the tarball to match Debian requirements:\n\n```\nmv mosquitto-1.1.2.tar.gz mosquitto_1.1.2.orig.tar.gz\n```\n\nThe current mosquitto packaging files are available at\n<https://packages.debian.org/source/stable/mosquitto> - you want the\n.debian.tar.xz.\n\nThe next step is to build the package, but you may find that you need to\ninstall some packages first:\n\n```\nsudo apt-get install build-essential python quilt libwrap0-dev libssl-dev devscripts python-setuptools\n```\n\nTo build the packages do\n\n```\ncd mosquitto-1.1.2/\ndebuild\n```\n\nYou should now have a list of .deb files in the parent directory which you can\ninstall with:\n\n```\nsudo dpkg -i &lt;deb file&gt;\n```\n\nPlease leave comments if you find this useful or have any problems.\n"
  },
  {
    "path": "www/posts/2012/11/version-1-0-5-released.md",
    "content": "<!--\n.. title: Version 1.0.5 released\n.. slug: version-1-0-5-released\n.. date: 2012-11-03 12:29:16\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Fix crash when the broker has `use_identity_as_username` set to true but a\n   client connects without a certificate.\n * mosquitto_passwd should only be installed if `WITH_TLS=yes`.\n\n# Library\n\n * Use symbolic errno values rather than numbers in Python module to avoid\n   cross platform issues (incorrect errno on Mac OS).\n\n# Other\n\n * Build script fixes for FreeBSD.\n"
  },
  {
    "path": "www/posts/2012/12/libmosquitto-go-bindings.md",
    "content": "<!--\n.. title: libmosquitto Go bindings\n.. slug: libmosquitto-go-bindings\n.. date: 2012-12-24 00:18:18\n.. tags: Solutions\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI just discovered that Shane Hanna has written a Go language binding for\nlibmosquitto available at <https://bitbucket.org/shanehanna/mosquitto/>.\nGood work Shane! Note that the readme file states:\n\n> Doesn't expose all of libmosquitto, just what I've needed so far.\n\nso you shouldn't necessarily expect everything to work.\n"
  },
  {
    "path": "www/posts/2012/12/version-1-1-released.md",
    "content": "<!--\n.. title: Version 1.1 released\n.. slug: version-1-1-released\n.. date: 2012-12-19 17:13:18\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a feature and bugfix release.\n\n# Broker\n\n * Add $SYS/broker/messages/dropped\n * Add $SYS/broker/clients/expired\n * Replace $SYS/broker/+/per second/+ with moving average versions published at\n   $SYS/broker/load/#\n * Add $SYS/broker/load/sockets/+ and $SYS/broker/load/connections/+\n * Documentation on password file format has been fixed.\n * Disable SSL compression. This reduces memory usage significantly and removes\n   the possibility of CRIME type attacks.\n * Enable SSL_MODE_RELEASE_BUFFERS mode to reduce SSL memory usage further.\n * Add allow_duplicate_messages option.\n * ACL files can now have comment lines with # as the first character.\n * Display message on startup about which config is being loaded.\n * Fix `max_inflight_messages` and `max_queued_messages` not being applied.\n * Fix documentation error in mosquitto.conf.\n * Ensure that QoS 2 queued messages are sent out in a timely manner.\n * Local bridges now act on `clean_session` correctly.\n * Local bridges with `clean_session==false` now remove unused subscriptions on\n   broker restart.\n * The $SYS/broker/heap/# messages now no longer include \"bytes\" as part of the\n   string for ease of use.\n\n# Client library\n\n * Free memory used by OpenSSL in `mosquitto_lib_cleanup()` where possible.\n * Change WebSocket subprotocol name to mqttv3.1 to make future changes easier\n   and for compatibility with other implementations.\n * `mosquitto_loop_read()` and `mosquitto_loop_write()` now handle errors\n   themselves rather than having `mosquitto_loop()` handle their errors. This\n   makes using them in a separate event loop more straightforward.\n * Add `mosquitto_loop_forever()` / `loop_forever()` function call to make\n   simple clients easier.\n * Disable SSL compression. This reduces memory usage significantly and removes\n   the possibility of CRIME type attacks.\n * Enable SSL_MODE_RELEASE_BUFFERS mode to reduce SSL memory usage further.\n * `mosquitto_tls_set()` will now return an error or raise an exception\n   immediately if the CA certificate or client certificate/key cannot be\n   accessed.\n * Fix potential memory leaks on connection failures.\n * Don't produce return error from `mosquitto_loop()` if a system call is\n   interrupted. This prevents disconnects/reconnects in threaded mode and\n   simplifies non-threaded client handling.\n * Ignore SIGPIPE to prevent unnecessary client quits in threaded mode.\n * Fix document error for `mosquitto_message_retry_set()`.\n * Fix `mosquitto_topic_matches_sub()` for subscriptions with + as the final\n   character. Fixes bug #1085797.\n * Rename all \"obj\" parameters to \"userdata\" for consistency with other\n   libraries.\n * Reset errno before network read/write to ensure EAGAIN isn't mistakenly\n   returned.\n * The message queue length is now tracked and used to determine the maximum\n   number of packets to process at once. This removes the need for the\n   `max_packets` parameter which is now unused.\n * Fix incorrect error value in Python `error_string()` function. Fixes bug #1086777.\n * Reset last message in/out timer in Python module when we send a PINGREQ.\n   Fixes too-early disconnects.\n\n# Clients\n\n * Clients now display their own version number and library version number in\n   their help messages.\n * Fix `mosquitto_pub -l -q 2` disconnecting before all messages were\n   transmitted.\n * Fix potential out-of-bounds array access with client ids. Fixes bug #1083182.\n\n# Other\n\n * mosquitto_passwd can now convert password files with plain text files to\n   hashed versions.\n"
  },
  {
    "path": "www/posts/2013/01/mosquitto-debian-repository.md",
    "content": "<!--\n.. title: Mosquitto Debian repository\n.. slug: mosquitto-debian-repository\n.. date: 2013-01-10 22:43:37\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nOn a previous post I described [how to make mosquitto debian packages]. This\nturned out to be a bit problematic, so I've now put up an experimental debian\nrepository for mosquitto. It includes packages for the i386, amd64, armel and\nraspberry pi (raspbian armhf ) architectures.\n\nIt's worth repeating that this is experimental - there are package changes that\nhaven't been vetted by a Debian developer so it's possible something will\nbreak. I've tested myself and had no problems so far.\n\nTo use the new repository you should first import the repository package\nsigning key:\n\n```\nwget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key\nsudo apt-key add mosquitto-repo.gpg.key\n```\n\nThen make the repository available to apt:\n\n```\ncd /etc/apt/sources.list.d/\n```\n\nThen one of the following, depending on which version of debian you are using:\n\n```\nsudo wget http://repo.mosquitto.org/debian/mosquitto-jessie.list\nsudo wget http://repo.mosquitto.org/debian/mosquitto-stretch.list\nsudo wget http://repo.mosquitto.org/debian/mosquitto-buster.list\n```\n\n\n\nThen update apt information:\n\n```\napt-get update\n```\n\nAnd discover what mosquitto packages are available:\n\n```\napt-cache search mosquitto\n```\n\nOr just install:\n\n```\napt-get install mosquitto\n```\n\n[how to make mosquitto debian packages]: /blog/2012/11/making-mosquitto-packages-for-debian-yourself/\n"
  },
  {
    "path": "www/posts/2013/01/version-1-1-1-released.md",
    "content": "<!--\n.. title: Version 1.1.1 released\n.. slug: version-1-1-1-released\n.. date: 2013-01-16 23:57:00\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Fix crash on reload if using acl patterns.\n\n# Client library\n\n * Fix static C++ functions not being exported on Windows. Fixes bug #1098256.\n\nBinaries should be available shortly.\n"
  },
  {
    "path": "www/posts/2013/01/version-1-1-2-released.md",
    "content": "<!--\n.. title: Version 1.1.2 released\n.. slug: version-1-1-2-released\n.. date: 2013-01-30 22:12:35\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Client library\n\n * Fix `tls_cert_reqs` not being set to `SSL_VERIFY_PEER` by default. This\n   meant that clients were not verifying the server certificate when connecting\n   over TLS. This affects the C, C++ and Python libraries.\n\nSource and binaries are available on the [download page].\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2013/02/mqtt-standardisation-oasis-call-for-participation.md",
    "content": "<!--\n.. title: MQTT standardisation - OASIS call for participation\n.. slug: mqtt-standardisation-oasis-call-for-participation\n.. date: 2013-02-04 20:35:31\n.. tags: Events,MQTT\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe MQTT protocol is going for standardisation at OASIS. A technical committee\nis being formed and there is a call for participation for interested parties.\nThere are details at the link below:\n\n<https://www.oasis-open.org/news/announcements/call-for-participation-message-queuing-telemetry-transport-mqtt-tc\">\n\nThe plan seems to be to take the 3.1 spec as it is for standardisation and see\nabout changes in the future. If you are interested in taking part see the link\nabove, but note that you need to be a paid up member of\nOASIS.\n"
  },
  {
    "path": "www/posts/2013/02/version-1-1-3-released.md",
    "content": "<!--\n.. title: Version 1.1.3 released\n.. slug: version-1-1-3-released\n.. date: 2013-02-11 22:13:21\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a minor bugfix release that addresses some problems identified during\nDebian packaging.\n\n# Broker\n\n * mosquitto_passwd utility now uses tmpfile() to generate its temporary data\n   storage file. It also creates a backup file that can be used to recover data\n   if an errors occur.\n\n<h4>Other</h4>\n\n * Build script fixes to help packaging on Debian.\n"
  },
  {
    "path": "www/posts/2013/04/some-interesting-mqtt-things.md",
    "content": "<!--\n.. title: Some interesting MQTT things\n.. slug: some-interesting-mqtt-things\n.. date: 2013-04-21 21:56:36\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIt's been a while since there has been an update here, so in lieu of one here\nare some interesting links I've come across recently. Add a comment to the post\nif you've done something cool not mentioned here! Work progresses on mosquitto\n1.2.\n\nInitial release of an MQTT-S gateway, written in ruby:\n\n * <https://github.com/njh/ruby-em-mqtts>\n\nAnd some MQTT-S tools:\n\n * <https://github.com/njh/mqtts-tools>\n\nA Pinoccio/MQTT/sensor powered Theramin:\n\n * <http://projectable.me/post/48408382189/scout6050-a-combination-of-pinoccio-mpu6050>\n\nVoice controlled MQTT LED:\n\n * <https://github.com/emmano/voicerecog4pi>\n\nAn MQTT notification plugin for Jenkins/Hudson:\n\n * <https://github.com/gdubya/mqtt-notification-plugin>\n"
  },
  {
    "path": "www/posts/2013/05/mosquitto-javascript-client-deprecated.md",
    "content": "<!--\n.. title: Mosquitto Javascript client deprecated\n.. slug: mosquitto-javascript-client-deprecated\n.. date: 2013-05-07 17:53:33\n.. tags: Misc,Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe [Paho] project recently made a new Javascript client available:\n<http://git.eclipse.org/c/paho/org.eclipse.paho.mqtt.javascript.git>\n\nThe mosquitto Javascript client, mosquitto.js, is neither as functional nor as\nwell written as the Paho client, so is being deprecated. If you are using\nmosquitto.js I strongly recommend that you look to the Paho client for the\nfuture. I will be carrying out minor bug fixes but no other development will\ntake place.\n\nThere are no plans to remove the existing files.\n\n[Paho]: http://www.eclipse.org/paho/\n"
  },
  {
    "path": "www/posts/2013/07/authentication-plugins.md",
    "content": "<!--\n.. title: Authentication plugins\n.. slug: authentication-plugins\n.. date: 2013-07-18 22:27:45\n.. tags: Solutions\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThere has been some interest in authentication plugins for mosquitto recently.\nSome examples have appeared:\n\nAuthentication based on md5 hashes: [mosquitto_auth_plugin_md5]\n\nAuthentication based on md5 hashed passwords in postgresql:\n[mosquitto_auth_plugin_pg_md5]\n\nAuthentication and topic ACL with redis and a PBKDF2\nhash: [mosquitto-redis-auth]\n\nI particularly like the redis based plugin for the interesting additions like\nthe \"superuser\" that is exempt from ACL checks.\n\nIf you've written an auth plugin and think it might be useful to others, let me\nknow.\n\n[mosquitto_auth_plugin_md5]: https://github.com/sskaje/mosquitto_auth_plugin_md5\n[mosquitto_auth_plugin_pg_md5]: https://github.com/sebaroesch/mosquitto_auth_plugin_pg_md5\n[mosquitto-redis-auth]: https://github.com/jpmens/mosquitto-redis-auth\n"
  },
  {
    "path": "www/posts/2013/07/version-1-2-near-complete.md",
    "content": "<!--\n.. title: Version 1.2 near complete\n.. slug: version-1-2-near-complete\n.. date: 2013-07-25 23:01:28\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nWith the most recent commit, \"Implement TLSv1.2 and TLSv1.1 support,\"\neverything that is planned for version 1.2 has been completed. If you haven't\ntried it out yet, now would be a good time to take a look.\n\nBefore the release is finalised, there still needs to be more testing done,\nparticularly on Windows. If you use another platform than Windows or Linux, I'd\nbe interested to hear if you have any problems with the 1.2 code. I will also\nbe updating the packaging for all of the binaries that I build or contribute to\ndirectly, so there is still time for bug reports.\n\nYou can get a copy of the source at one of the links below, or through the\nmercurial repository directly on the 1.2 branch.\n\n * <https://bitbucket.org/oojah/mosquitto/get/1.2.zip>\n * <https://bitbucket.org/oojah/mosquitto/get/1.2.tar.gz>\n"
  },
  {
    "path": "www/posts/2013/08/mosquitto-on-fedora.md",
    "content": "<!--\n.. title: Mosquitto on Fedora\n.. slug: mosquitto-on-fedora\n.. date: 2013-08-15 20:24:04\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto has been packaged for Fedora thanks to Rich Mattes. Fedora 19 users\nwill be able to install with \"yum install mosquitto\".\n\nThanks Rich!\n"
  },
  {
    "path": "www/posts/2013/08/mqtt-watchdir.md",
    "content": "<!--\n.. title: mqtt-watchdir\n.. slug: mqtt-watchdir\n.. date: 2013-08-15 20:43:13\n.. tags: Misc\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nRecursively watch a directory for modifications and publish file content to an\nMQTT broker\n\n`mqtt-watchdir` is a Python program by [Jan-Piet Mens] to watch a directory and\npublish new or modified files in that directory hierarchy to an MQTT broker,\nusing a matching topic. Source and instructions are available at\n<https://github.com/jpmens/mqtt-watchdir> and it is also available via pypi.\n\nIt is a similar idea to my [mqttfs] fuse filesystem, but ultimately implemented\nin a better (and portable) manner.\n\n[Jan-Piet Mens]: https://twitter.com/jpmens\n[mqttfs]: https://bitbucket.org/oojah/mqttfs\n"
  },
  {
    "path": "www/posts/2013/08/version-1-2-released.md",
    "content": "<!--\n.. title: Version 1.2 released\n.. slug: version-1-2-released\n.. date: 2013-08-07 23:13:34\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a (long overdue) feature release.\n\nThere is a potential gotcha when upgrading to this release because the default\nversion of TLS used has changed from 1.0 to 1.2. Python does not yet have\nsupport for TLS&gt;1.0 so Python clients will be unable to communicate with\nbrokers using the default TLS settings.\n\nThe source is available at the [download page] and binaries will become\navailable in the near future.\n\n# Broker\n\n * Replace O(n) username lookup on CONNECT with a roughly O(1) hashtable\n   version.\n * It is now possible to disable $SYS at compile time.\n * Add dropped publish messages to load tree in $SYS. Closes bug #1183318.\n * Add support for logging SUBSCRIBE/UNSUBSCRIBE events.\n * Add `log_dest file` logging support.\n * Auth plugin ACL check function now passes the client id as well as username\n   and password.\n * The `queue_qos0_messages` option wasn't working correctly, this has now been\n   fixed. Closes bug #1125200.\n * Don't drop all messages for disconnected durable clients when\n   `max_queued_messages=0`.\n * Add support for `log_type all`.\n * Add support for `-v` option on the command line to provide the equivalent of\n   `log_type all` without needing a config file.\n * Add the `upgrade_outgoing_qos` option, a non-standard feature.\n * Persistence data is now written to a temporary file which is atomically\n   renamed on completion, so a crash during writing will not produce a corrupt\n   file.\n * mosquitto.conf is now installed as mosquitto.conf.example\n * Configuration file errors are now reported with filename and line number.\n * The broker now uses a monotonic clock if available, to avoid changes in time\n   causing client disconnections or message retries.\n * Clean session and keepalive status are now display the log when a client\n   connects.\n * Add support for TLSv1.2 and TLSv1.1.\n * Clients that connect with zero length will topics are now rejected.\n * Add the ability to set a maximum allowed PUBLISH payload size.\n * Fix an ACL with topic `#` incorrectly granting access to $SYS.\n * Fix retained messages incorrectly being set on wildcard topics, leading to\n   duplicate retained messages being sent on subscription. Closes bug #1116233.\n * Don't discard listener values when no \"port\" option given. Closes bug\n   #1131406.\n * Client password check was always failing when security was being reapplied\n   after a config reload. This meant that all clients were being disconnected.\n   This has been fixed.\n * Fix build when `WITH_TLS=no`. Closes bug #1174971.\n * Fix single outgoing packets not being sent in a timely fashion if they were\n   not sent in one call to write(). Closes bug #1176796.\n * Fix remapping of messages for clients connected to a listener with\n   `mount_point` set. Closes bug #1180765.\n * Fix duplicate retained messages being sent for some wildcard patterns.\n * If a client connects with a will topic to which they do not have write\n   access, they are now disconnected with CONNACK \"not authorised\".\n * Fix retained messages on topic foo being incorrectly delivered to\n   subscriptions of /#\n * Fix handling of SSL errors on SSL_accept().\n * Fix handling of QoS 2 messages on client reconnect.\n * Drop privileges now sets supplementary groups correctly.\n * Fix load reporting interval (is now 60s).\n * Be strict with malformed PUBLISH packets - clients are now disconnected\n   rather than the packet discarded. This goes inline with future OASIS spec\n   changes and makes other changes more straightforward.\n * Process incoming messages denied by ACL properly so that clients don't keep\n   resending them.\n * Add support for `round_robin` bridge option.\n * Add bridge support for verifying remote server certificate subject against\n   the remote hostname.\n * Fix problem with out of order calls to free() when restarting a lazy bridge.\n * The broker now attempts to resolve `bind_address` and bridge addresses\n   immediately when parsing the config file in order to detect invalid hosts.\n * Bridges now set their notification state before attempting to connect, so if\n   they fail to connect the state can still be seen.\n * Fix bridge notification payload length - no need to send a null byte.\n * mosquitto_passwd utility now reports errors more clearly.\n * Fix `mosquitto_passwd -U`.\n\n \n# Client library\n\n * Add support for TLSv1.2 and TLSv1.1, except for on the Python module.\n * Add support for verifying remote server certificate subject against the\n   remote hostname.\n * Add mosquitto_reconnect_async() support and make asynchronous connections\n   truly asynchronous rather than simply deferred. DNS lookups are still\n   blocking, so asynchronous connections require an IP address instead of\n   hostname.\n * Allow control of reconnection timeouts in mosquitto_loop_forever() and after\n   mosquitto_loop_start() by using mosquitto_reconnect_delay_set().\n * Fix building on Android NDK.\n * Re-raise unhandled errors in Python so as not to provide confusing error\n   messages later on.\n * Python module supports IPv6 connections.\n * mosquitto_sub_topic_tokenise() was behaving incorrectly if the last topic\n   hierarchy had only a single character. This has been fixed. Closes bug\n   #1163348.\n * Fix possible crash after disconnects when using the threaded interface with\n   TLS.\n * Allow build/install without Python. Closes bug #1174972.\n * Add support for binding connection to a local interface.\n * Implement maximum inflight messages handling.\n * Fix Python client not handling `will_payload==None`.\n * Fix potential memory leak when setting username/password.\n * Fix handling of QoS 2 messages on reconnect.\n * Improve handling of mosquitto_disconnect() with threaded mode.\n\n# Clients\n\n * Add support for TLSv1.2 and TLSv1.1.\n * Sub client can now suppress printing of messages with the retain bit set.\n * Add support for binding connection to a local interface.\n * Implement maximum inflight messages handling for the pub client.\n\n[download page]: /download\n"
  },
  {
    "path": "www/posts/2013/09/version-1-2-1-released.md",
    "content": "<!--\n.. title: Version 1.2.1 released\n.. slug: version-1-2-1-released\n.. date: 2013-09-18 22:04:15\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker:\n\n * The broker no longer ignores the `auth_plugin_init()` return value. Closes\n   bug #1215084.\n * Use `RTLD_GLOBAL` when opening authentication plugins on posix systems.\n   Fixes resolving of symbols in libraries used by authentication plugins.\n * Add/fix some config documentation.\n * Fix ACLs for topics with $SYS.\n * Clients loaded from the persistence file on startup were not being added to\n   the client hash, causing subtle problems when the client reconnected,\n   including ACLs failing. This has been fixed.\n * Add note to mosquitto-tls man page stating that certificates need to be\n   unique. Closes bug #1221285.\n * Fix incorrect retained message delivery when using wildcard subs in some\n   circumstances. Fixes bug #1226040.\n\n# Client library\n\n * Fix support for Python 2.6, 3.0, 3.1.\n * Fix TLS subjectAltName verification and segfaults.\n * Handle EAGAIN in Python on Windows. Closes bug #1220004.\n * Fix compilation when using `WITH_TLS=no`.\n * Don't fail reconnecting in Python when broker is temporarily unavailable.\n"
  },
  {
    "path": "www/posts/2013/10/version-1-2-2-released.md",
    "content": "<!--\n.. title: Version 1.2.2 released\n.. slug: version-1-2-2-released\n.. date: 2013-10-21 23:05:04\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release:\n\n# Broker\n\n * Fix compliance with `max_inflight_messages` when a non-clean session client\n   reconnects. Closes one of the issues on bug #1237389.\n\n# Client library\n\n * Fix incorrect inflight message accounting, which caused messages to go\n * unsent. Partial fix for bug #1237351.\n * Fix potential memory corruption when sending QoS&gt;0 messages at a high\n   rate using the threaded interface. Further fix for #1237351.\n * Fix incorrect delay scaling when exponential_backoff=true in\n   mosquitto_reconnect_delay_set().\n * Some pep8 fixes for Python.\n"
  },
  {
    "path": "www/posts/2013/12/paho-mqtt-python-client.md",
    "content": "<!--\n.. title: Paho MQTT Python Client\n.. slug: paho-mqtt-python-client\n.. date: 2013-12-21 22:24:56\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe Mosquitto Python client was donated to the Eclipse Paho project in June of\nthis year. As mosquitto.py has been very popular, I have been maintaining both\ncode bases together.\n\nWith the Mosquitto project also moving to Eclipse it is now even more redundant\nto keep maintaining mosquitto.py so I would like to recommend that everybody\ncurrently using mosquitto.py move over to using the Paho Python client.\n\nThe current state of the Paho client is now available on [pypi] and can be\ninstalled using `pip install paho-mqtt`.\n\nTo port code from mosquitto.py, you should change:\n\n```\nimport mosquitto\nmqttc = mosquitto.Mosquitto()\n```\n\nto:\n\n```\nimport paho.mqtt.client as paho\nmqttc = paho.Client()\n```\n\nAll error codes e.g. `MOSQ_ERR_SUCCESS` change to `MQTT_ERR_SUCCESS`.\n\nThe Paho module has a compatibility Mosquitto class that means a very simple\n(but not recommended for the long term) port can be achieved with the following\nline, assuming none of the error codes are used:\n\n```\nimport paho.mqtt.client as mosquitto\n```\n\nI will keep applying updates to mosquitto.py until the Paho 1.0 release.\n\n[pypi]: https://pypi.python.org/pypi/paho-mqtt\n"
  },
  {
    "path": "www/posts/2013/12/version-1-2-3-released.md",
    "content": "<!--\n.. title: Version 1.2.3 released\n.. slug: version-1-2-3-released\n.. date: 2013-12-02 23:32:55\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIn time for the second day of [Thingmonk], which I regret not being able to go\nto, version 1.2.3 of mosquitto is released. This is a bugfix release.\n\n# All components\n\n * Various fixes caught by [Coverity Scan].\n\n# Broker\n\n * Don't always attempt to call read() for SSL clients, irrespective of whether\n   they were ready to read or not. Reduces syscalls significantly.\n * Possible memory leak fixes.\n * Further fix for bug #1226040: multiple retained messages being delivered for\n   subscriptions ending in #.\n * Fix bridge reconnections when using multiple bridge addresses.\n\n# Client library\n\n * Fix possible memory leak in C/C++ library when communicating with a broker\n   that doesn't follow the spec.\n * Block in Python `loop_stop()` until all messages are sent, as the\n   documentation states should happen.\n * Fix for asynchronous connections on Windows. Closes bug #1249202.\n * Module version is now available in mosquitto.py.\n\n# Clients\n\n * mosquitto_sub now uses fwrite() instead of printf() to output messages, so\n   messages with NULL characters aren't truncated.\n\n[Thingmonk]: http://redmonk.com/thingmonk/\n[Coverity Scan]: https://scan.coverity.com/\n"
  },
  {
    "path": "www/posts/2014/03/version-1-3-1-released.md",
    "content": "<!--\n.. title: Version 1.3.1 released\n.. slug: version-1-3-1-released\n.. date: 2014-03-24 23:55:32\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release:\n\n# Broker\n\n * Prevent possible crash on client reconnect. Closes bug #1294108.\n * Don't accept zero length unsubscription strings (MQTT v3.1.1 fix)\n * Don't accept QoS 3 (MQTT v3.1.1 fix)\n * Don't disconnect clients immediately on HUP to give chance for all data to\n   be read.\n * Reject invalid un/subscriptions e.g. `foo/+bar` `#/bar`.\n * Take more care not to disconnect clients that are sending large messages.\n\n# Client library\n\n * Fix socketpair code on the Mac.\n * Fix compilation for `WITH_THREADING=no`.\n * Break out of select() when calling `mosquitto_loop_stop()`.\n * Reject invalid un/subscriptions e.g. `foo/+bar` `#/bar`.\n\n# Clients\n\n * Fix keepalive value on mosquitto_pub.\n * Fix possibility of mosquitto_pub not exiting after sending messages when using -l.\n"
  },
  {
    "path": "www/posts/2014/03/version-1-3-released.md",
    "content": "<!--\n.. title: Version 1.3 released\n.. slug: version-1-3-released\n.. date: 2014-03-17 00:06:32\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n\n# Broker\n\n * The broker no longer ignores the `auth_plugin_init()` return value.\n * Accept SSLv2/SSLv3 HELLOs when using TLSv1, whilst keeping SSLv2 and SSLv3\n   disabled. This increases client compatibility without sacrificing security.\n * The $SYS tree can now be disabled at runtime as well as at compile time.\n * When remapping bridged topics, only check for matches when the message\n   direction is correct. This allows two identical topics to be remapped\n   differently for both in and out.\n * Change `$SYS/broker/heap/current size` to `$SYS/broker/heap/current` for\n   easier parsing.\n * Change `$SYS/broker/heap/maximum size` to `$SYS/broker/heap/maximum` for\n   easier parsing.\n * Topics are no longer normalised from e.g `a///topic` to `a/topic`. This\n   matches the behaviour as clarified by the Oasis MQTT spec. This will lead to\n   unexpected behaviour if you were using topics of this form.\n * Log when outgoing messages for a client begin to drop off the end of the\n   queue.\n * Bridge clients are recognised as bridges even after reloading from\n   persistence.\n * Basic support for MQTT v3.1.1. This does not include being able to bridge to\n   an MQTT v3.1.1 broker.\n * Username is displayed in log if present when a client connects.\n * Support for 0 length client ids (v3.1.1 only) that result in automatically\n   generated client ids on the broker (see option `allow_zero_length_clientid`).\n * Ability to set the prefix of automatically generated client ids (see option\n   `auto_id_prefix`).\n * Add support for TLS session resumption.\n * When using TLS, the server now chooses the cipher to use when negotiating\n   with the client.\n * Weak TLS ciphers are now disabled by default.\n\n# Client library\n\n * Fix support for Python 2.6, 3.0, 3.1.\n * Add support for un/subscribing to multiple topics at once in un/subscribe().\n * Clients now close their socket after sending DISCONNECT.\n * Python client now contains its version number.\n * C library `mosquitto_want_write()` now supports TLS clients.\n * Fix possible memory leak in C/C++ library when communicating with a broker\n   that doesn't follow the spec.\n * Return strerror() through `mosquitto_strerror()` to make error printing\n   easier.\n * Topics are no longer normalised from e.g `a///topic` to `a/topic`. This\n   matches the behaviour as clarified by the Oasis MQTT spec. This will lead to\n   unexpected behaviour if you were using topics of this form.\n * Add support for SRV lookups.\n * Break out of select() on publish(), subscribe() etc. when using the threaded\n   interface. Fixes bug #1270062.\n * Handle incoming and outgoing messages separately. Fixes bug #1263172.\n * Don't terminate threads on `mosquitto_destroy()` when a client is not using\n   the threaded interface but does use their own thread. Fixes bug #1291473.\n\n# Clients\n\n * Add `--ciphers` to allow specifying which TLS ciphers to support.\n * Add support for SRV lookups.\n * Add `-N` to sub client to suppress printing of EOL after the payload.\n * Add `-T` to sub client to suppress printing of a topic hierarchy.\n"
  },
  {
    "path": "www/posts/2014/05/new-arrival.attachments.json",
    "content": "{\"322\": {\"wordpress_user_name\": \"roger\", \"title\": \"14098345978_c15d12f19a_z\", \"date_utc\": \"2014-05-27 22:29:02\", \"files_meta\": [{\"height\": 427, \"width\": 640}, {\"height\": 200, \"size\": \"medium\", \"width\": 300}, {\"height\": 150, \"size\": \"thumbnail\", \"width\": 150}], \"files\": [\"/wp-content/uploads/2014/05/14098345978_c15d12f19a_z.jpg\", \"/wp-content/uploads/2014/05/14098345978_c15d12f19a_z-300x200.jpg\", \"/wp-content/uploads/2014/05/14098345978_c15d12f19a_z-150x150.jpg\"]}}"
  },
  {
    "path": "www/posts/2014/05/new-arrival.md",
    "content": "<!--\n.. title: New arrival\n.. slug: new-arrival\n.. date: 2014-05-27 23:29:39\n.. tags: Releases\n.. category: releases\n.. link:\n.. description:\n.. type: text\n.. author: Roger\n-->\n\nI'm pleased to say that I'm a new father again. My 7lb 12 (3.57kg) boy arrived\ntoday and is quite happy, as is his mother.\n\nApologies to anybody who has emailed me recently and I've not yet replied -\nthis is the main reason!\n\n[![baby][baby]](/blog/uploads/2014/05/14098345978_c15d12f19a_z.jpg)\n\n[baby]:/blog/uploads/2014/05/14098345978_c15d12f19a_z-300x200.jpg\n"
  },
  {
    "path": "www/posts/2014/07/version-1-3-2-released.md",
    "content": "<!--\n.. title: Version 1.3.2 released\n.. slug: version-1-3-2-released\n.. date: 2014-07-14 13:10:05\n.. tags: Releases,Security\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a security and bugfix release.\n\n# Security\n\nA bug in the way that mosquitto handles authentication plugins has been\nidentified. When using a plugin for authentication purposes, if the plugin\nreturns `MOSQ_ERR_UNKNOWN` when making an authentication check, as might happen\nif a database was unavailable for example, then mosquitto incorrectly treats\nthis as a successful authentication. This has the potential for unauthorised\nclients to access the running mosquitto broker and gain access to information\nto which they are not authorised. This is an important update for users of\nauthentication plugins in mosquitto.\n\n# Broker\n\n * Don't allow access to clients when authenticating if a security plugin\n   returns an application error. Fixes bug [#1340782].\n * Ensure that bridges verify certificates by default when using TLS.\n * Fix possible crash when using pattern ACLs that do not include a %u and\n   clients that connect without a username.\n * Fix subscriptions being deleted when clients subscribed to a topic beginning\n   with a $ but that is not $SYS.\n * When a durable client reconnects, its queued messages are now checked\n   against ACLs in case of a change in username/ACL state since it last\n   connected.\n * Anonymous clients are no longer accidentally disconnected from the broker\n   after a SIGHUP.\n * Fix bug [#1324411], which could have had unexpected consequences for delayed\n   messages in rare circumstances.\n\n# Client library\n\n * Fix topic matching edge case.\n * Fix callback deadlocks after calling `mosquitto_disconnect()`, when using\n   the threaded interfaces. Closes bug [#1313725].\n * Fix SRV support when building with CMake.\n\n# General\n\n * Use $(STRIP) for stripping binaries when installing, to allow easier cross\n   compilation.\n\n[#1313725]: https://bugs.launchpad.net/mosquitto/+bug/1313725\n[#1324411]: https://bugs.launchpad.net/mosquitto/+bug/1324411\n[#1340782]: https://bugs.launchpad.net/mosquitto/+bug/1340782\n"
  },
  {
    "path": "www/posts/2014/08/version-1-3-3-released.md",
    "content": "<!--\n.. title: Version 1.3.3 released\n.. slug: version-1-3-3-released\n.. date: 2014-08-01 22:55:42\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Fix incorrect handling of anonymous bridges on the local broker.\n\nBinaries will follow shortly.\n"
  },
  {
    "path": "www/posts/2014/08/version-1-3-4-released.md",
    "content": "<!--\n.. title: Version 1.3.4 released\n.. slug: version-1-3-4-released\n.. date: 2014-08-06 00:53:05\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release. The reason for the rapid release of the past two\nversions is down to a Debian developer reviewing the mosquitto package. This is\na good opportunity to ensure that as bug free a version as possible is present\nin Debian.\n\n# Broker\n\n * Don't ask client for certificate when `require_certificate` is **false**.\n * Backout incomplete functionality that was incorrectly included in 1.3.2.\n\nBinaries will follow shortly.\n"
  },
  {
    "path": "www/posts/2014/10/mosquitto-and-poodle.md",
    "content": "<!--\n.. title: Mosquitto and POODLE\n.. slug: mosquitto-and-poodle\n.. date: 2014-10-16 15:53:33\n.. tags: Security\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nDetails of the POODLE attack that targets SSLv3 have been released recently.\nMosquitto has never provided support for SSLv3 (or SSLv2) so should not be\nvulnerable to this attack and does not require any configuration\nchanges.\n"
  },
  {
    "path": "www/posts/2014/10/unintended-change-of-behaviour-in-1-3-4.md",
    "content": "<!--\n.. title: Unintended change of behaviour in 1.3.4\n.. slug: unintended-change-of-behaviour-in-1-3-4\n.. date: 2014-10-10 09:35:32\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 1.3.4 introduced the change that when using TLS with\n`require_certificate` set to false, the client is no longer asked for a client\ncertificate. This seemed to be causing problems in some situations,\nparticularly with embedded devices.\n\nIf `use_identity_as_username` is set to true when `require_certificate` is set\nto false, then the client will not be asked for a certificate, even if it has\none configured. This means that the client will be refused access with connack\ncode 4, \"bad username or password\", because if `use_identity_as_username`\ncurrently requires that a certificate is present, even if `allow_anonymous` is\nset to true.\n\nThis change may cause unexpected results, but does not represent a security\nflaw because the change results in more clients being rejected than would\notherwise have been.\n"
  },
  {
    "path": "www/posts/2014/10/version-1-3-5-released.md",
    "content": "<!--\n.. title: Version 1.3.5 released\n.. slug: version-1-3-5-released\n.. date: 2014-10-08 23:23:22\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Fix possible memory leak when using a topic that has a leading slash. Fixes\n   bug #1360985.\n * Fix saving persistent database on Windows.\n * Temporarily disable ACL checks on subscriptions when using MQTT v3.1.1. This\n   is due to the complexity of checking wildcard ACLs against wildcard\n   subscriptions. This does not have a negative impact on security because\n   checks are still made before a message is sent to a client. Fixes bug\n   #1374291.\n * When using -v and the broker receives a SIGHUP, verbose logging was being\n   disabled. This has been fixed.\n\n# Client library\n\n * Fix mutex being incorrectly passed by value. Fixes bug #1373785.\n"
  },
  {
    "path": "www/posts/2015/01/seeking-sponsorship.md",
    "content": "<!--\n.. title: Seeking sponsorship\n.. slug: seeking-sponsorship\n.. date: 2015-01-19 17:12:53\n.. tags: Misc,Support\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe mosquitto project has, or can get, access to a wide variety of different\nsystems to help with development. One important platform for which this is not\ntrue is Mac OS X. There are sufficient differences between Macs and other\nsystems that this makes life difficult.\n\nTo this end, I would like to reach out to the mosquitto community to ask for\nhelp with obtaining either\n\n * A remote login on a Mac system\n * Donation of hardware\n * Donation of money to buy some hardware\n\nI have been offered a remote account by a few individuals in the past, for\nwhich I'm very grateful, but only on a short term basis and, understandably,\nwith limited control. Something on a longer term, with the ability to install\npackages would be much more useful. Unfortunately I realise this is relatively\ndifficult to offer.\n\nOn the hardware side of things, there isn't a need for a modern, powerful\ncomputer. A second hand Mac Mini of Core2Duo vintage with 1GB RAM and a\nreasonably modern version of Mac OS X would be quite sufficient, and ideal for\nme in terms of the space it takes up. Regrettably I feel I would have to turn\ndown offers of an old iMac or Mac Pro.\n\n2007-era Mac Minis go on Ebay UK for around £100. I'm hopeful that there is a\ncompany out there using mosquitto, likes Macs and for whom £100 would be a drop\nin the ocean. If so, or any individuals want to help out with a small donation\ntowards this, please get in touch directly to roger@atchoo.org or head over to\nthe downloads page to see the paypal donation link, and thanks very much in\nadvance.\n\n<hr>\n\nUpdate:\n\nI have now awaiting delivery of a Mac mini. Thanks very much to all of you that\nhave contributed, it is very much appreciated. If you would still like to\nsupport mosquitto development please don't let this put you\noff...\n"
  },
  {
    "path": "www/posts/2015/02/version-1-4-released.md",
    "content": "<!--\n.. title: Version 1.4 released\n.. slug: version-1-4-released\n.. date: 2015-02-18 21:23:04\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a feature release and is also the first release of the mosquitto\nproject from the Eclipse Foundation umbrella. The code is now dual licenced\nunder the [EPL]. The EDL and BSD 3 clause license are essentially identical so\nif you were happy with the BSD license then you should be happy with the EDL.\n\nFiles distributed will remain in the same place but will in some cases also be\navailable on the Eclipse download servers.\n\n# Important changes\n\n * Websockets support in the broker.\n * Bridge behaviour on the local broker has changed due to the introduction of\n   the `local_*` options. This may affect you if you are using authentication \n   and/or ACLs with bridges.\n * The default TLS behaviour has changed to accept all of TLS v1.2, v1.1 and\n   v1.0, rather than only one version of the protocol. It is still possible to\n   restrict a listener to a single version of TLS.\n * The Python client has been removed now that the Eclipse Paho Python client\n   has had a release.\n * When a durable client reconnects, its queued messages are now checked\n   against ACLs in case of a change in username/ACL state since it last\n   connected.\n * New `use_username_as_clientid` option on the broker, for preventing\n   hijacking of a client id.\n * The client library and clients now have experimental SOCKS5 support.\n * Wildcard TLS certificates are now supported for bridges and clients.\n * The clients have support for config files with default options.\n * Client and client libraries have support for MQTT v3.1.1.\n * Bridge support for MQTT v3.1.1.\n\n# Broker\n\n * Websockets support in the broker.\n * Add `local_clientid`, `local_username`, `local_password` for bridge\n   connections to authenticate to the local broker.\n * Default TLS mode now accepts TLS v1.2, v1.1 and v1.0.\n * Support for ECDHE-ECDSA family ciphers.\n * Fix bug #1324411, which could have had unexpected consequences for delayed\n   messages in rare circumstances.\n * Add support for `session present` in CONNACK messages for MQTT v3.1.1.\n * Remove strict protocol #ifdefs.\n * Change $SYS/broker/clients/active -&gt; $SYS/broker/clients/connected\n * Change $SYS/broker/clients/inactive -&gt; $SYS/broker/clients/disconnected\n * When a durable client reconnects, its queued messages are now checked\n   against ACLs in case of a change in username/ACL state since it last\n   connected.\n * libuuid is used to generate client ids, where it is available, when an MQTT\n   v3.1.1 client connects with a zero length client id.\n * Anonymous clients are no longer accidently disconnected from the broker\n   after a SIGHUP.\n * mosquitto_passwd now supports `-b` (batch mode) to allow the password to be\n   provided at the command line.\n * Removed $SYS/broker/changeset. This was intended for use with debugging, but\n   in practice is of no use.\n * Add support for `use_username_as_clientid` which can be used with\n   authentication to restrict ownership of client ids and hence prevent one\n   client disconnecting another by using the same client id.\n * When `require_certificate` was false, the broker was incorrectly asking for\n   a certificate (but not checking it). This caused problems with some clients\n   and has been fixed so the broker no longer asks.\n * When using syslog logging on non-Windows OSs, it is now possible to specify\n   the logging facility to one of local0-7 instead of the default \"daemon\".\n * The `bridge_attempt_unsubscribe` option has been added, to allow the sending\n   of UNSUBSCRIBE requests to be disabled for topics with \"out\" direction.\n   Closes bug #456899.\n * Wildcard TLS certificates are now supported for bridges.\n * Support for \"hour\" client expiration lengths for the\n   `persistent_client_expiration` option. Closes bug #425835.\n * Bridge support for MQTT v3.1.1.\n * Root privileges are now dropped after starting listeners and loading\n   certificates/private keys, to allow private keys to have their permissions\n   restricted to the root user only. Closes bug #452914.\n * Usernames and topics given in ACL files can now include a space. Closes bug\n   #431780.\n * Fix hang if pattern acl contains a %u but an anonymous client connect.\n   Closes bug #455402.\n * Fix man page installation with cmake. Closes bug #458843.\n * When using `log_dest file` the output file is now flushed periodically.\n\n# Clients\n\n * Both clients can now load default configuration options from a file.\n * Add `-C` option to mosquitto_sub to allow the client to quit after receiving\n   a certain count of messages. Closes bug #453850.\n * Add `--proxy` SOCKS5 support for both clients.\n * Pub client supports setting its keepalive. Closes bug #454852.\n * Add support for config files with default options.\n * Add support for MQTT v3.1.1.\n\n# Client library\n\n * Add experimental SOCKS5 support.\n * mosquitto_loop_forever now quits after a fatal error, rather than blindly\n   retrying.\n * SRV support is now not compiled in by default.\n * Wildcard TLS certificates are now supported.\n * mosquittopp now has a virtual destructor. Closes bug #452915.\n * Add support for MQTT v3.1.1.\n * Don't quit mosquitto_loop_forever() if broker not available on first\n   connect. Closes bug #453293, but requires more work.\n\n# Dependencies\n\nThis release introduces two new dependencies, libwebsockets and libuuid. Both\nare optional. libuuid comes from the e2fsprogs project and allows the broker to\ngenerate random client ids for MQTT v.3.1.1. The libwebsockets dependency can\nuse either libwebsockets 1.3 or 1.2.x, with 1.3 being the preferred\nchoice.\n\n[EPL]: https://www.eclipse.org/legal/epl-v10.html\n[EDL]: https://eclipse.org/org/documents/edl-v10.php\n"
  },
  {
    "path": "www/posts/2015/04/version-1-4-1-released.md",
    "content": "<!--\n.. title: Version 1.4.1 released\n.. slug: version-1-4-1-released\n.. date: 2015-04-03 11:12:03\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix and security release. Users of mosquitto 1.4 are strongly\nadvised to upgrade. Upgrading from earlier versions is recommended but not as\nimportant.\n\n# Broker\n\n * Fix possible crash under heavy network load. Closes [#463241]. This bug only\n   affects version 1.4.\n * Fix possible crash when using pattern ACLs.\n * Fix problems parsing config strings with multiple leading spaces. Closes\n   [#462154].\n * Websockets clients are now periodically disconnected if they have not\n   maintained their keepalive timer. Closes [#461619].\n * Fix possible minor memory leak on acl parsing.\n\n# Client library\n\n * Inflight limits should only apply to outgoing messages. Closes [#461620].\n * Fix reconnect bug on Windows. Closes [#463000].\n * Return -1 on error from `mosquitto_socket()`. Closes [#461705].\n * Fix crash on multiple calls to `mosquitto_lib_init`/`mosquitto_lib_cleanup`.\n   Closes [#462780].\n * Allow longer paths on Windows. Closes [#462781].\n * Make `_mosquitto_mid_generate()` thread safe. Closes [#463479].\n\n[#463241]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=463241\n[#462154]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=462154\n[#461619]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=461619\n[#461620]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=461620\n[#463000]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=463000\n[#461705]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=461705\n[#462780]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=462780\n[#462781]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=462781\n[#463479]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=463479\n"
  },
  {
    "path": "www/posts/2015/05/mosquitto-and-current-unreleased-libwebsockets-branch.md",
    "content": "<!--\n.. title: Mosquitto and current unreleased libwebsockets branch\n.. slug: mosquitto-and-current-unreleased-libwebsockets-branch\n.. date: 2015-05-10 20:51:11\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe current unreleased libwebsockets master branch defines the VERSION macro in\nits header files. I believe this to be a bug in libwebsockets.\n\nThis bug causes compilation of mosquitto with websockets support to fail.\n\nPlease use a released version of libwebsockets, either 1.2, 1.3 or 1.4.\nMosquitto will compile with all of these versions.\n\nI do not recommend using an unreleased version of libwebsockets, the project is\nnot shy about making ABI/API incompatible changes between releases so it is\nimpractical to provide support for.\n"
  },
  {
    "path": "www/posts/2015/05/version-1-4-2-released.md",
    "content": "<!--\n.. title: Version 1.4.2 released\n.. slug: version-1-4-2-released\n.. date: 2015-05-07 22:58:11\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Fix bridge prefixes only working for the first outgoing message. Closes [#464437].\n * Fix incorrect bridge connection notifications on local broker.\n * Fix persistent db writing on Windows. Closes [#464779].\n * ACLs are now checked before sending a will message.\n * Fix possible crash when using bridges on Windows. Closes [#465384].\n * Fix parsing of `auth_opt_` arguments with extra spaces/tabs.\n * Broker will return CONNACK rc=5 when a username/password is not authorised.\n   This was being incorrectly set as rc=4.\n * Fix handling of payload lengths&gt;4096 with websockets.\n\n# Client library\n\n * Inflight message count wasn't being decreased for outgoing messages using\n   QoS 2, meaning that only up to 20 QoS 2 messages could be sent. This has\n   been fixed. Closes [#464436].\n * Fix CMake dependencies for C++ wrapper building. Closes [#463884].\n * Fix possibility of select() being called with a socket that is\n   &gt;FD_SETSIZE. This is a fix for [#464632].\n * Fix calls to `mosquitto_connect*_async()` not completing.\n\n[#464437]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=464437\n[#464779]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=464779\n[#465384]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=465384\n[#463884]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=463884\n[#464436]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=464436\n[#464632]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=464632\n"
  },
  {
    "path": "www/posts/2015/08/version-1-4-3-released.md",
    "content": "<!--\n.. title: Version 1.4.3 released\n.. slug: version-1-4-3-released\n.. date: 2015-08-19 15:42:27\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Fix incorrect bridge notification on initial connection. Closes [#467096].\n * Build fixes for OpenBSD.\n * Fix incorrect behaviour for `autosave_interval`, most noticeable for\n   `autosave_interval=1`. Closes [#465438].\n * Fix handling of outgoing QoS&gt;0 messages for bridges that could not be\n   sent because the bridge connection was down.\n * Free unused topic tree elements. Closes [#468987].\n * Fix some potential memory leaks. Closes [#470253].\n * Fix potential crash on libwebsockets error.\n\n# Client library\n\n * Add missing error strings to `mosquitto_strerror`.\n * Handle fragmented TLS packets without a delay. Closes [#470660].\n * Fix incorrect loop timeout being chosen when using threaded interface and\n   keepalive = 0. Closes [#471334].\n * Increment inflight messages count correctly. Closes [#474935].\n\n# Clients\n\n * Report error string on connection failure rather than error code.\n\n[#467096]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=467096\n[#465438]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=465438\n[#468987]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=468987\n[#470253]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=470253\n[#470660]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=470660\n[#471334]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=471334\n[#474935]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=474935\n"
  },
  {
    "path": "www/posts/2015/09/version-1-4-4-released.md",
    "content": "<!--\n.. title: Version 1.4.4 released\n.. slug: version-1-4-4-released\n.. date: 2015-09-19 09:53:33\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n * Don't leak sockets when outgoing bridge with multiple addresses cannot\n * connect. Closes [#477571].\n * Fix cross compiling of websockets. Closes [#475807].\n * Fix memory free related crashes on openwrt and FreeBSD. Closes [#475707].\n * Fix excessive calls to message retry check.\n\n[#477571]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=477571\n[#475707]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=475707\n[#475807]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=475807\n"
  },
  {
    "path": "www/posts/2015/11/version-1-4-5-released.md",
    "content": "<!--\n.. title: Version 1.4.5 released\n.. slug: version-1-4-5-released\n.. date: 2015-11-09 22:00:57\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release:\n\n# Broker\n\n * Fix possible memory leak if bridge using SSL attempts to connect to a host\n   that is not up.\n * Free unused topic tree elements (fix in 1.4.3 was incomplete). Closes\n   [#468987].\n\n# Clients\n\n * `mosquitto_pub -l` now no longer limited to 1024 byte lines. Closes\n   [#478917].\n\n[#468987]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=468987\n[#478917]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=478917\n"
  },
  {
    "path": "www/posts/2015/12/using-lets-encrypt-certificates-with-mosquitto.md",
    "content": "<!--\n.. title: Using Let's Encrypt certificates with mosquitto\n.. slug: using-lets-encrypt-certificates-with-mosquitto\n.. date: 2015-12-13 19:53:37\n.. tags: certificates,encryption,tls\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIf you want to use TLS certificates you've generated using the [Let's Encrypt]\nservice, this is how you should configure your listener (replace \"example.com\"\nwith your own domain of course):\n\nThen use the following for your mosquitto.conf:\n\n```\nlistener 8883\ncafile /etc/ssl/certs/ISRG_Root_X1.pem\ncertfile /etc/letsencrypt/live/example.com/fullchain.pem\nkeyfile /etc/letsencrypt/live/example.com/privkey.pem\n```\n\nSince version 2.0 of Mosquitto, you can send a SIGHUP to the broker to cause it\nto reload certificates. Prior to this version, mosquitto would never update\nlistener settings when running, so you will need to completely restart the\nbroker.\n\n[Let's Encrypt]: https://letsencrypt.org/\n"
  },
  {
    "path": "www/posts/2015/12/version-1-4-7-released.md",
    "content": "<!--\n.. title: Version 1.4.7 released\n.. slug: version-1-4-7-released\n.. date: 2015-12-31 22:15:06\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release. The changes below include the changes for 1.4.6,\nwhich wasn't announced.\n\n# Broker\n\n * Add support for libwebsockets 1.6.\n\n# Client library\n\n * Fix `_mosquitto_socketpair()` on Windows, reducing the chance of delays when\n * publishing. Closes [#483979].\n\n# Clients\n\n * Fix `mosquitto_pub -l` stripping the final character on a line. Closes\n   [#483981].\n\n[#483979]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=483979\n[#483981]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=483981\n"
  },
  {
    "path": "www/posts/2016/01/test6-mosquitto-org.md",
    "content": "<!--\n.. title: test6.mosquitto.org\n.. slug: test6-mosquitto-org\n.. date: 2016-01-14 20:35:33\n.. tags: Misc\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThanks to a short discussion on irc, test6.mosquitto.org now exists. This is a\nDNS entry that points to the same address as test.mosquitto.org, but only with\nan AAAA record. This means that test6.mosquitto.org can be used to test clients\nusing IPv6 and to be sure that IPv6 is actually being used.\n"
  },
  {
    "path": "www/posts/2016/02/version-1-4-8-released.md",
    "content": "<!--\n.. title: Version 1.4.8 released\n.. slug: version-1-4-8-released\n.. date: 2016-02-14 19:15:33\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a security bugfix release. Any users of the `mount_point` feature are\nstrongly advised to upgrade because versions prior to 1.4.8 allow clients to\ninject messages outside of their `mount_point` through the use of a Will.\n\n# Broker\n\n * Wills published by clients connected to a listener with `mount_point` defined\n   now correctly obey the mount point. This was a potential security risk\n   because it allowed clients to publish messages outside of their restricted\n   mount point. This is only affects brokers where the `mount_point` option is in\n   use. Closes [#487178].\n * Fix detection of broken connections on Windows. Closes [#485143].\n * Close stdin etc. when daemonised. Closes [#485589].\n * Fix incorrect detection of FreeBSD and OpenBSD. Closes [#485131].\n\n# Client library\n\n * `mosq-&gt;want_write` should be cleared immediately before a call to\n   `SSL_write`, to allow clients using `mosquitto_want_write()` to get accurate\n   results.\n\n[#487178]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=487178\n[#485143]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=485143\n[#485589]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=485589\n[#485131]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=485131\n"
  },
  {
    "path": "www/posts/2016/03/logo-contest-results-for-shortlisting.md",
    "content": "<!--\n.. title: Logo contest - results for shortlisting\n.. slug: logo-contest-results-for-shortlisting\n.. date: 2016-03-23 16:21:24\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe first round of the logo contest has closed and we now need to shortlist 6\ndesigners. A selection of 20 logos have been chosen out of the 100 entrants and\nyou are invited to vote on them and make comments. If you like a particular\nlogo but not the colour, or like an idea behind the logo but not another\nelement then please say so.\n\nThe links for voting (please do look at them all) are:\n\n<https://en.99designs.fr/logo-design/vote-d8v9u9>\n<https://en.99designs.fr/logo-design/vote-xlduhg>\n<https://99designs.fr/logo-design/vote-n4ynig>\n"
  },
  {
    "path": "www/posts/2016/03/logo-contest.md",
    "content": "<!--\n.. title: Logo contest\n.. slug: logo-contest\n.. date: 2016-03-18 11:42:14\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nWe have initiated a paid contest to create a new logo for the Mosquitto\nproject.\n\nIf you have graphics design skills or know someone who has,  please head over\nto the link below to see the design brief and submit your idea.\n\n<http://en.99designs.de/logo-design/contests/create-logo-eclipse-mosquitto-open-source-server-internet-605670/brief>\n"
  },
  {
    "path": "www/posts/2016/03/repository-moved-to-github.md",
    "content": "<!--\n.. title: Repository moved to github\n.. slug: repository-moved-to-github\n.. date: 2016-03-11 09:35:54\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe mosquitto repository is now hosted on github:\n<https://github.com/eclipse/mosquitto> This is now the canonical location for\nmosquitto development work.\n\nBug reports should also be made on github and the existing bug reports will be\nmigrated over shortly.\n\nThe documentation still needs updating with the new location and processes, so\nplease do be patient with regards that.\n\nContributions can now be made through a github pull request. If you want to\ncontribute a bug fix, please base your work off the \"fixes\" branch, if you are\ndeveloping a new feature please use the \"develop\" branch.\n\nHere's to a new stage in the mosquitto project!\n"
  },
  {
    "path": "www/posts/2016/05/stickers.attachments.json",
    "content": "{\"385\": {\"wordpress_user_name\": \"roger\", \"title\": \"stickers\", \"date_utc\": \"2016-05-10 14:43:38\", \"files_meta\": [{\"height\": 253, \"width\": 338, \"meta\": {\"created_timestamp\": 1462563610.0, \"shutter_speed\": 0.5, \"focal_length\": 48.0, \"camera\": \"NIKON D3200\", \"aperture\": 5.3, \"iso\": 800.0}}, {\"height\": 225, \"size\": \"medium\", \"width\": 300}, {\"height\": 150, \"size\": \"thumbnail\", \"width\": 150}], \"files\": [\"/wp-content/uploads/2016/05/stickers.jpg\", \"/wp-content/uploads/2016/05/stickers-300x225.jpg\", \"/wp-content/uploads/2016/05/stickers-150x150.jpg\"]}}"
  },
  {
    "path": "www/posts/2016/05/stickers.md",
    "content": "<!--\n.. title: Stickers\n.. slug: stickers\n.. date: 2016-05-11 09:00:49\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nTo celebrate the new mosquitto logo, stickers are now available:\n\n[![stickers](/blog/uploads/2016/05/stickers-300x225.jpg)](/blog/uploads/2016/05/stickers.jpg)\n\nIf you would like to obtain some stickers for yourself you have two options.\n\nThe first is to get in touch and I'll send you some for a small contribution.\nThis contribution is to cover the cost of the stickers plus postage: (cost of\npostage)+N\\*£0.45, where N is the number of sheets of 6 stickers that you want.\nCost of postage for a letter can be calculated using the [Royal Mail price\nfinder], but should be £1.05 for destinations outside of the UK. Please also\nconsider Paypal fees using a [fees calculator] to calculate the final sum. So\nfor a single sheet of stickers posted internationally, the cost would be £1.76\nincluding paypal fees. Two sheets would be £2.23.\n\nThe second option is to buy a full sticker book through moo.com. This can be\ndone very easily by navigating to <http://mosquitto.org/stickers/> This\nallows you to easily order a sticker book of 90 stickers with either the colour\nor blue monochrome stickers, or a mix of both.\n\nThere is a third option - get in touch to say why you deserve some stickers and\nmaybe we'll send you some. We're looking for things that make us say \"wow!\" If\nyou will be sending your sticker to space, getting mosquitto on television or\nusing MQTT in your Formula 1 technology, these are all things that would\nexciting to see with a mosquitto sticker in place. If you want to give out\nstickers at a local IoT related event or similar that's great, but we'd ask\nthat you make a small donation. It's only a small cost for you, but there are\nmany people in your situation and it becomes a noticeable cost for the project.\n\nPlease do post links of your kit sporting any stickers you use!\n\n[Royal Mail price finder]: http://www.royalmail.com/price-finder\n[fees calculator]: http://www.clothnappytree.com/ppcalculator/\n"
  },
  {
    "path": "www/posts/2016/06/version-1-4-9-released.md",
    "content": "<!--\n.. title: Version 1.4.9 released\n.. slug: version-1-4-9-released\n.. date: 2016-06-03 11:00:17\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Ensure websockets clients that previously connected with clean session set\n   to false have their queued messages delivered immediately on reconnecting.\n   Closes [#5].\n * Reconnecting client with clean session set to false doesn't start with mid=1\n   again.\n * Will topic isn't truncated by one byte when using a `mount_point` any more.\n * Network errors are printed correctly on Windows.\n * Fix incorrect $SYS heap memory reporting when using ACLs.\n * Bridge config parameters couldn't contain a space, this has been fixed.\n   Closes [#150].\n * Fix saving of persistence messages that start with a '/'. Closes [#151].\n * Fix reconnecting for bridges that use TLS on Windows. Closes [#154].\n * Broker and bridges can now cope with unknown incoming PUBACK, PUBREC,\n   PUBREL, PUBCOMP without disconnecting. Closes [#57].\n * Fix websockets listeners not being able to bind to an IP address. Closes\n   [#170].\n * mosquitto_passwd utility now correctly deals with unknown command line\n   arguments in all cases. Closes [#169].\n * Fix publishing of $SYS/broker/clients/maximum\n * Fix order of #includes in lib/send_mosq.c to ensure struct mosquitto doesn't\n   differ between source files when websockets is being used. Closes [#180].\n * Fix possible rare crash when writing out persistence file and a client has\n   incomplete messages inflight that it has been denied the right to publish.\n\n# Client library\n\n * Fix the case where a message received just before the keepalive timer\n   expired would cause the client to miss the keepalive timer.\n * Return value of pthread_create is now checked.\n * _mosquitto_destroy should not cancel threads that weren't created by\n   libmosquitto. Closes [#166].\n * Clients can now cope with unknown incoming PUBACK, PUBREC, PUBREL, PUBCOMP\n   without disconnecting. Closes [#57].\n * Fix mosquitto_topic_matches_sub() reporting matches on some invalid\n   subscriptions.\n\n# Clients\n\n * Handle some unchecked malloc() calls. Closes [#1].\n\n# Build\n\n * Fix string quoting in CMakeLists.txt. Closes [#4].\n * Fix building on Visual Studio 2015. Closes [#136].\n\n[#1]: https://github.com/eclipse/mosquitto/issues/1\n[#4]: https://github.com/eclipse/mosquitto/issues/4\n[#5]: https://github.com/eclipse/mosquitto/issues/5\n[#57]: https://github.com/eclipse/mosquitto/issues/57\n[#136]: https://github.com/eclipse/mosquitto/issues/136\n[#150]: https://github.com/eclipse/mosquitto/issues/150\n[#151]: https://github.com/eclipse/mosquitto/issues/151\n[#154]: https://github.com/eclipse/mosquitto/issues/154\n[#166]: https://github.com/eclipse/mosquitto/issues/166\n[#169]: https://github.com/eclipse/mosquitto/issues/169\n[#170]: https://github.com/eclipse/mosquitto/issues/170\n[#180]: https://github.com/eclipse/mosquitto/issues/180\n"
  },
  {
    "path": "www/posts/2016/08/mqtt-v5-draft-features.md",
    "content": "<!--\n.. title: MQTT v5 draft features\n.. slug: mqtt-v5-draft-features\n.. date: 2016-08-15 19:23:48\n.. tags: MQTT\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe [MQTT Technical Committee] at OASIS continue to work on improvements to\nMQTT. The next version looks set to be MQTT version 5 and has reached the\n\"working draft\" stage. This post lists some of the changes that are in the\nworking draft 02 and so gives at least a flavour of the improvements coming up.\nTake this with a pinch of salt, I may have missed some changes and there is no\ncommitment that any of these features will remain in the final specification as\nthey are described here.\n\n# Session management\n\nIn MQTT v3.1.1 and earlier, a client could control how the server treats its\nsession with the clean session flag. If set to 1, the server would delete any\nexisting session for that client and would not persist the session after\ndisconnecting. If set to 0, the server would restore any existing session for a\nclient when it reconnected, and persist the session when the client\ndisconnected.\n\nA session here means the subscriptions for a client and any queued messages.\n\nThe new spec changes this behaviour. The clean session flag has been renamed to\nclean start (this was actually the name of the flag in the old MQTT v3 spec)\nand now only affects how the broker handles a client session when the client\nconnects. If set to 1, the server discards any previous session information,\notherwise session information is kept.\n\nTo deal with removing of sessions at any other time, a new identifier/value\npair has been introduced. These identifier/value pairs are an addition to the\nvariable header part of some MQTT packets and allow configuring of different\nbehaviour. In the case of the CONNECT packet, a Session Expiry interval can be\nspecified which is a 4 byte integer that gives the number of seconds after a\nclient has disconnected that the server should remove session information for\nthat client. If the Session Expiry interval is absent from the CONNECT packet,\nthen the session will never expire. If it is set to 0, then the session is\nremoved as soon as the client disconnects.\n\nThe new clean start flag and session expiry interval allow the existing clean\nsession behaviour to be duplicated but also allow client sessions to be expired\nbased on time.\n\n# Updated Connect Return codes\n\nThe return codes passed to the client in a CONNACK packet have been expanded to\ninclude:\n\n * 6: Connection Refused, reason unspecified\n * 7: Connection Refused, implementation specific\n * 8: Connection Refused, CONNECT packet was malformed\n\n# Repeated topics when publishing\n\nWhen publishing data to a single topic, a new feature will help reduce\nbandwidth use. A client or server can set the topic in a PUBLISH message to be\na zero length string. This tells the client/server being published to, to use\nthe previous topic instead. This goes some way to reducing the current overhead\nassociated with publishing - a shame it isn't quite as good as the registered\ntopics available in MQTT-SN.\n\n# Payload Format Indicator\n\nAnother identifier/value pair is available for use when sending a PUBLISH\nmessage. This is the Payload Format indicator. If present and set to 1, this\nindicates that the PUBLISH payload is UTF-8 encoded data. If set to 0, or if\nthe indicator is not present then the payload is an unspecified byte format,\nexactly as with MQTT v3.1.1.\n\n# Publication Expiry interval\n\nThis is an identifier/value pair for use when publishing. If present, this\nvalue is a 4 byte integer which gives the number of seconds for which the\nserver will attempt to deliver this message to a subscriber. This means that an\noffline client with messages being queued may not receive all of the messages\nwhen it reconnects, due to some of them expiring. Interestingly, when the\nserver does deliver a message that had a Publication Expiry set, it sets the\nPublication Expiry on the outgoing message to the client but with the amount of\ntime that there is left until the message expires. This means that the true\ntime to expiry will propagate through bridges or similar.\n\n# Publish Return Codes\n\nThe PUBACK and PUBREC packets have a new entry in their variable header which\nis the Publish Return Code. This can be used to tell the client a message has\nbeen refused for various reasons, accepted, or accepted with no matching\nsubscribers.  For the PUBREC packet, if the message is refused or accepted with\nno matching subscribers then there is no expectation for the PUBREL/PUBCOMP\nmessages to be sent for that message.\n\nThe PUBCOMP packet also has a similar entry which has the same set of return\ncodes and an additional one for the case when a message had expired. This is\nfor the case when a client reconnects with clean start set to 0 and it has a\nQoS 2 message part way through its handshake, but the server has already\nexpired the message.\n\nThere is still no way to tell a client that its QoS 0 message was refused.\n\n# Disconnect notification\n\nIn MQTT v3.1.1 and before, only the client sends a DISCONNECT packet. In the\ndraft spec, either the client or the server can send DISCONNECT and it is used\nto indicate a reason for disconnection. The disconnect return codes are:\n\n * 0: Connection disconnected by application (sent by client)\n * 1: Server temporarily unavailable (server)\n * 2: Server unavailable (server)\n * 3: Malformed UNSUBSCRIBE packet received (server)\n * 4: Session taken over (server - for when another client connects with the same ID)\n * 5: Malformed packet received\n\nIt is clear that there is some duplication there, so I think this is a likely\nplace that changes will be made.\n\n# Disconnect expiry notification\n\nThe DISCONNECT packet can also include a Session Expiry interval value, as with\nCONNECT. This allows a client to clean up when it disconnects, or to set a long\nexpiry time, even if these were not specified at the initial connect.\n\n[MQTT Technical Committee]: https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=mqtt\n"
  },
  {
    "path": "www/posts/2016/08/version-1-4-10-released.md",
    "content": "<!--\n.. title: Version 1.4.10 released\n.. slug: version-1-4-10-released\n.. date: 2016-08-25 16:04:14\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Fix TLS operation with websockets listeners and libwebsockets 2.x. Closes\n   [#186].\n * Don't disconnect client on HUP before reading the pending data. Closes [#7].\n * Fix some $SYS messages being incorrectly persisted. Closes [#191].\n * Support OpenSSL 1.1.0.\n * Call fsync after persisting data to ensure it is correctly written. Closes\n   [#189].\n * Fix persistence saving of subscription QoS on big-endian machines.\n * Fix will retained flag handling on Windows. Closes [#222].\n * Broker now displays an error if it is unable to open the log file. Closes\n   [#234].\n\n# Client library\n\n * Support OpenSSL 1.1.0.\n * Fixed the C++ library not allowing SOCKS support to be used. Closes [#198].\n * Fix memory leak when verifying a server certificate with a subjectAltName\n   section. Closes [#237].\n\n# Build\n\n * Don't attempt to install docs when `WITH_DOCS=no`. Closes [#184].\n\n[#7]: https://github.com/eclipse/mosquitto/issues/7\n[#184]: https://github.com/eclipse/mosquitto/issues/184\n[#186]: https://github.com/eclipse/mosquitto/issues/186\n[#189]: https://github.com/eclipse/mosquitto/issues/189\n[#191]: https://github.com/eclipse/mosquitto/issues/191\n[#198]: https://github.com/eclipse/mosquitto/issues/198\n[#222]: https://github.com/eclipse/mosquitto/issues/222\n[#234]: https://github.com/eclipse/mosquitto/issues/234\n[#237]: https://github.com/eclipse/mosquitto/issues/237\n"
  },
  {
    "path": "www/posts/2016/12/pre-christmas-update.md",
    "content": "<!--\n.. title: Pre Christmas Update\n.. slug: pre-christmas-update\n.. date: 2016-12-12 23:03:10\n.. tags:\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI have taken a bit of a break from Mosquitto for the past few months, partly\nbecause I needed a break but also to work on another unrelated project. I'm now\nback and working on Mosquitto again, primarily implementing support for the\nupcoming MQTT v5 spec which has added even more features since I mentioned last\nwrote about it. Once that is in a state that is reasonably compliant if\nincomplete, I will be looking for testers.\n\nThere are a few fixes in the repository waiting for release, I anticipate\nreleasing 1.4.11 before the end of the year.\n\nThere have been some changes to test.mosquitto.org. On its original host I was\nseeing lots of bandwidth being used by lots of clients, but in particular lots\nand lots of tiny connections being made which not showing up on my bandwidth\nmonitoring, but were consuming bandwidth and causing problems at my provider.\nMy provider got in touch to say that at times approximately half of the network\nflows for their network were related to test.mosquitto.org, and could would I\nplease have a chat with the transit provider to discuss how best to manage this\nservice. In the face of that and the risk of exceeding 2TB bandwidth usage per\nmonth, test.mosquitto.org has been moved to a lower spec host with smaller\npipes and \"automatic DDOS protection\". This means I now get half a dozen emails\nper day to say that test.mosquitto.org is under attack. If you find you can't\nconnect to test.mosquitto.org, it might be because you have been blocked by\nthis DDOS protection - if so, maybe think about how you are using the service.\n\nThe final thought for this post - if you are part of a company that uses\nmosquitto and it adds value to your company, please consider making a\n[donation] to the project that reflects that value. If it is difficult for your\ncompany to make donations but you would still like to contribute back, please\nget in touch and maybe we can arrange something.\n\n[donation]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=J66JWQ3N76L5A\n"
  },
  {
    "path": "www/posts/2017/02/version-1-4-11-released.md",
    "content": "<!--\n.. title: Version 1.4.11 released\n.. slug: version-1-4-11-released\n.. date: 2017-02-21 20:27:39\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Broker\n\n * Fix crash when \"lazy\" type bridge attempts to reconnect. Closes [#259].\n * `maximum_connections` now applies to websockets listeners. Closes [#271].\n * Allow bridges to use TLS with IPv6.\n * Don't error on zero length persistence files. Closes [#316].\n * For http only websockets clients, close files served over http in all cases\n   when the client disconnects. Closes [#354].\n * Fix error message when websockets `http_dir` directory does not exist.\n * Improve password utility error message. Closes [#379].\n * Bridges can use asynchronous DNS lookups on systems with glibc. This can be\n   enabled at compile time using `WITH_ADNS=yes`.\n\n# Clients\n\n * Use of `--ciphers` no longer requires you to also pass `--tls-version`.\n   Closes [#380].\n\n# Client library\n\n * Clients can now use TLS with IPv6.\n * Fix potential socket leakage when reconnecting. Closes [#304].\n * Fix potential negative timeout being passed to pselect. Closes [#329].\n\n[#259]: https://github.com/eclipse/mosquitto/issues/259\n[#271]: https://github.com/eclipse/mosquitto/issues/271\n[#304]: https://github.com/eclipse/mosquitto/issues/304\n[#316]: https://github.com/eclipse/mosquitto/issues/316\n[#329]: https://github.com/eclipse/mosquitto/issues/329\n[#354]: https://github.com/eclipse/mosquitto/issues/354\n[#379]: https://github.com/eclipse/mosquitto/issues/379\n[#380]: https://github.com/eclipse/mosquitto/issues/380\n"
  },
  {
    "path": "www/posts/2017/03/for-the-final-time.attachments.json",
    "content": "{\"410\": {\"wordpress_user_name\": \"roger\", \"title\": \"img_20170308_155049248_33196894011_o\", \"date_utc\": \"2017-03-09 19:08:45\", \"files_meta\": [{\"height\": 720, \"width\": 1280, \"meta\": {\"created_timestamp\": 1488988249.0, \"shutter_speed\": 0.02999, \"focal_length\": 2.471, \"camera\": \"MotoE2(4G-LTE)\", \"aperture\": 2.2, \"iso\": 125.0}}, {\"height\": 432, \"size\": \"medium_large\", \"width\": 768}, {\"height\": 169, \"size\": \"medium\", \"width\": 300}, {\"height\": 150, \"size\": \"thumbnail\", \"width\": 150}, {\"height\": 576, \"size\": \"large\", \"width\": 1024}], \"files\": [\"/wp-content/uploads/2017/03/img_20170308_155049248_33196894011_o.jpg\", \"/wp-content/uploads/2017/03/img_20170308_155049248_33196894011_o-768x432.jpg\", \"/wp-content/uploads/2017/03/img_20170308_155049248_33196894011_o-300x169.jpg\", \"/wp-content/uploads/2017/03/img_20170308_155049248_33196894011_o-150x150.jpg\", \"/wp-content/uploads/2017/03/img_20170308_155049248_33196894011_o-1024x576.jpg\"]}}"
  },
  {
    "path": "www/posts/2017/03/for-the-final-time.md",
    "content": "<!--\n.. title: For the final time...\n.. slug: for-the-final-time\n.. date: 2017-03-09 19:09:59\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n.. author: Roger\n-->\n\nThis guy arrived on Tuesday, two weeks early and weighing 9lb 6oz / 4.26kg.\nApologies if I'm a bit out of touch for a while.\n\n[![baby picture](/blog/uploads/2017/03/img_20170308_155049248_33196894011_o-300x169.jpg \"baby picture\")](/blog/uploads/2017/03/img_20170308_155049248_33196894011_o.jpg)\n"
  },
  {
    "path": "www/posts/2017/05/security-advisory-cve-2017-7650.md",
    "content": "<!--\n.. title: Security advisory: CVE-2017-7650\n.. slug: security-advisory-cve-2017-7650\n.. date: 2017-05-29 12:41:48\n.. tags: Security,Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nA vulnerability exists in Mosquitto versions 0.15 to 1.4.11 inclusive known as\n[CVE-2017-7650].\n\nPattern based ACLs can be bypassed by clients that set their username/client id\nto '#' or '+'. This allows locally or remotely connected clients to access MQTT\ntopics that they do have the rights to. The same issue may be present in third\nparty authentication/access control plugins for Mosquitto.\n\nThe vulnerability only comes into effect where pattern based ACLs are in use,\nor potentially where third party plugins are in use.\n\nThe issue is fixed in Mosquitto 1.4.12, which has just been released. Patches\nfor older versions are available at <https://mosquitto.org/files/cve/2017-7650>\n\nThe fix addresses the problem by restricting access for clients with a '#',\n'+', or '/' in their username or client id. '/' has been included in the list\nof characters disallowed because it also has a special meaning in a topic and\nmay represent an additional risk. The restriction placed on clients is that\nthey may not receive or send messages that are subject to a pattern based ACL\ncheck, nor any message that is subject to a plugin check.\n\nThanks to Artem Zinenko from HackerDom CTF team for finding this vulnerability\nand responsibly reporting it.\n\nComplete list of fixes addressed in version 1.4.12:\n\n# Broker\n\n * Fix mosquitto.db from becoming corrupted due to client messages being\n   persisted with no stored message. Closes [#424].\n * Fix bridge not restarting properly. Closes [#428].\n * Fix uninitialised memory in `gets_quiet` on Windows. Closes [#426].\n * Fix building with `WITH_ADNS=no` for systems that don't use glibc. Closes\n   [#415].\n * Fixes to readme.md.\n * Fix deprecation warning for OpenSSL 1.1. PR [#416].\n * Don't segfault on duplicate bridge names. Closes [#446].\n * Fix [CVE-2017-7650].\n\n[CVE-2017-7650]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7650\n[#415]: https://github.com/eclipse/mosquitto/issues/415\n[#416]: https://github.com/eclipse/mosquitto/issues/416\n[#424]: https://github.com/eclipse/mosquitto/issues/424\n[#428]: https://github.com/eclipse/mosquitto/issues/428\n[#426]: https://github.com/eclipse/mosquitto/issues/426\n[#446]: https://github.com/eclipse/mosquitto/issues/446\n"
  },
  {
    "path": "www/posts/2017/06/citing-eclipse-mosquitto.md",
    "content": "<!--\n.. title: Citing Eclipse Mosquitto in your academic paper\n.. slug: citing-eclipse-mosquitto\n.. date: 2017-06-01 14:31:48\n.. tags: Misc\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nA short paper has been published on Mosquitto in [The Journal of Open Source\nSoftware] If you use Mosquitto in your academic work, please now use this paper\nas your citation.\n\n> R. A. Light, \"Mosquitto: server and client implementation of the MQTT\n> protocol,\" *The Journal of Open Source Software*, vol. 2, no. 13, May 2017,\n> DOI: [10.21105/joss.00265]\n\nThe paper link is <http://dx.doi.org/10.21105/joss.00265>\n\nA [bibtex] entry is available.\n\n[The Journal of Open Source Software]: http://joss.theoj.org\n[10.21105/joss.00265]: http://dx.doi.org/10.21105/joss.00265\n[bibtek]: http://www.doi2bib.org/#/doi/10.21105/joss.00265\n"
  },
  {
    "path": "www/posts/2017/06/security-advisory-cve-2017-9868.md",
    "content": "<!--\n.. title: Security advisory: CVE-2017-9868\n.. slug: security-advisory-cve-2017-9868\n.. date: 2017-06-26 11:45:51\n.. tags: Security\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nA vulnerability exists in Mosquitto versions 0.15 to 1.4.12 inclusive known as\n[CVE-2017-9868].\n\nIf persistence is enabled, then the persistence file is created world readable,\nwhich has the potential to make sensitive information available to any local\nuser.\n\nPatches are available to fix this for Unix like operating systems (i.e. not\nWindows): <https://mosquitto.org/files/cve/2017-9868/>\n\nThis will be fixed in version 1.4.13, due to be released shortly.\n\nThis can also be fixed administratively by removing world read permissions for\nthe directory that the persistence file is stored in. In many systems this can\nbe achieved with:\n\n```\nchmod 700 /var/lib/mosquitto\n```\n\n[CVE-2017-9868]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9868\n"
  },
  {
    "path": "www/posts/2017/07/version-1-4-13-released.md",
    "content": "<!--\n.. title: Version 1.4.13 released\n.. slug: version-1-4-13-released\n.. date: 2017-07-05 10:47:23\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix and security release.\n\n# Security\n\n * Fix [CVE-2017-9868].\n   The persistence file was readable by all local users, potentially allowing\n   sensitive information to be leaked.  This can also be fixed\n   administratively, by restricting access to the directory in which the\n   persistence file is stored.\n\n# Broker\n\n * Fix for poor websockets performance.\n * Fix lazy bridges not timing out for `idle_timeout`. Closes [#417].\n * Fix problems with large retained messages over websockets. Closes [#427].\n * Set persistence file to only be readable by owner, except on Windows. Closes\n   [#468].\n * Fix CONNECT check for reserved=0, as per MQTT v3.1.1 check MQTT-3.1.2-3.\n * When the broker stop, wills for any connected clients are now \"sent\". Closes\n   [#477].\n * Auth plugins can be configured to disable the check for +# in\n   usernames/client ids with the `auth_plugin_deny_special_chars` option.\n   Partially closes [#462].\n * Restrictions for [CVE-2017-7650] have been relaxed - '/' is allowed in\n   usernames/client ids.\n * Remainder of fix for [#462].\n\n# Clients\n\n * Don't use / in auto-generated client ids.\n\n[CVE-2017-7650]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7650\n[CVE-2017-9868]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9868\n[#417]: https://github.com/eclipse/mosquitto/issues/417\n[#427]: https://github.com/eclipse/mosquitto/issues/427\n[#462]: https://github.com/eclipse/mosquitto/issues/462\n[#468]: https://github.com/eclipse/mosquitto/issues/468\n[#477]: https://github.com/eclipse/mosquitto/issues/477\n"
  },
  {
    "path": "www/posts/2017/07/version-1-4-14-released.md",
    "content": "<!--\n.. title: Version 1.4.14 released\n.. slug: version-1-4-14-released\n.. date: 2017-07-11 00:13:42\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\nVersion 1.4.13 contained a regression that meant persistence data was only\nbeing saved after client information had been freed. This release fixes that.\n\nIf you use persistence then it is strongly recommended to avoid 1.4.13 so you\ndo not suffer data loss.\n"
  },
  {
    "path": "www/posts/2018/01/mosquitto-debian-repo-key-updated.md",
    "content": "<!--\n.. title: Mosquitto Debian repo key updated\n.. slug: mosquitto-debian-repo-key-updated\n.. date: 2018-01-05 22:09:10\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nIf you are using the [debian repository] at repo.mosquitto.org you may have\nnoticed that the repository signing key expired at the end of 2017. To get the\nupdated key use the following commands:\n\n```\nwget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key\nsudo apt-key add mosquitto-repo.gpg.key\n```\n\n[debian repository]:/blog/2013/01/mosquitto-debian-repository\n"
  },
  {
    "path": "www/posts/2018/02/security-advisory-cve-2017-7651-cve-2017-7652.md",
    "content": "<!--\n.. title: Security advisory: CVE-2017-7651, CVE-2017-7652\n.. slug: security-advisory-cve-2017-7651-cve-2017-7652\n.. date: 2018-02-27 16:37:29 UTC\n.. tags: Security,Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.4.15 has been released to address two security vulnerabilities.\n\n# CVE-2017-7651\n\nA vulnerability exists in all Mosquitto versions up to and including 1.4.14\nknown as [CVE-2017-7651].\n\nUnauthenticated clients can send a crafted CONNECT packet which causes large\namounts of memory use in the broker. If multiple clients do this, an out of\nmemory situation can occur and the system may become unresponsive or the broker\nwill be killed by the operating system.\n\nThe issue is fixed in Mosquitto 1.4.15. Patches for older versions are\navailable at <https://mosquitto.org/files/cve/2017-7651>\n\nThe fix addresses the problem by limiting the permissible size for CONNECT\npacket, and by adding a `memory_limit` configuration option that allows the\nbroker to self limit the amount of memory it uses.\n\nThanks to Felipe Balabanian for finding this vulnerability and responsibly\nreporting it.\n\n# CVE-2017-7652\n\nA vulnerability exists in Mosquitto versions 1.0 to 1.4.14 inclusive\nknown as [CVE-2017-7652].\n\nIf the broker has exhausted all of its free sockets/file descriptors and then a\nSIGHUP signal is received to trigger reloading of the configuration, then the\nreloading will fail. This results in many of the configuration options,\nincluding security options, being set to their default value. This means that\nauthorisation and access control may no longer be in place.\n\nThe issue is fixed in Mosquitto 1.4.15. Patches for older versions are\navailable at <https://mosquitto.org/files/cve/2017-7652>\n\nThe fix addresses the problem by only copying the new configuration options to\nthe in use configuration after a successful reload has taken place.\n\n# Version 1.4.15 Changes\n\nThe complete list of fixes addressed in version 1.4.15 is:\n\n## Security\n* Fix [CVE-2017-7652]. If a SIGHUP is sent to the broker when there are no more\n  file descriptors, then opening the configuration file will fail and security\n  settings will be set back to their default values.\n* Fix [CVE-2017-7651]. Unauthenticated clients can cause excessive memory use by\n  setting \"remaining length\" to be a large value. This is now mitigated by\n  limiting the size of remaining length to valid values. A `memory_limit`\n  configuration option has also been added to allow the overall memory used by\n  the broker to be limited.\n\n## Broker\n* Use constant time memcmp for password comparisons.\n* Fix incorrect PSK key being used if it had leading zeroes.\n* Fix memory leak if a client provided a username/password for a listener with\n  `use_identity_as_username` configured.\n* Fix `use_identity_as_username` not working on websockets clients.\n* Don't crash if an auth plugin returns `MOSQ_ERR_AUTH` for a username check on\n  a websockets client. Closes [#490].\n* Fix 08-ssl-bridge.py test when using async dns lookups. Closes [#507].\n* Lines in the config file are no longer limited to 1024 characters long.\n  Closes [#652].\n* Fix $SYS counters of messages and bytes sent when message is sent over\n  a Websockets. Closes [#250].\n* Fix `upgrade_outgoing_qos` for retained message. Closes [#534].\n* Fix CONNACK message not being sent for unauthorised connect on websockets.\n  Closes [#8].\n\n## Client library\n* Fix incorrect PSK key being used if it had leading zeroes.\n* Initialise \"result\" variable as soon as possible in\n  `mosquitto_topic_matches_sub`. Closes [#654].\n* No need to close socket again if setting non-blocking failed. Closes [#649].\n* Fix `mosquitto_topic_matches_sub()` not correctly matching `foo/bar` against\n  `foo/+/#`. Closes [#670].\n\n## Clients\n* Correctly handle empty files with `mosquitto_pub -l`. Closes [#676].\n\n## Build\n* Don't run TLS-PSK tests if TLS-PSK disabled at compile time. Closes [#636].\n\n\n[CVE-2017-7651]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7651\n[CVE-2017-7652]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7652\n[#8]: https://github.com/eclipse/mosquitto/issues/8\n[#250]: https://github.com/eclipse/mosquitto/issues/250\n[#490]: https://github.com/eclipse/mosquitto/issues/490\n[#507]: https://github.com/eclipse/mosquitto/issues/507\n[#534]: https://github.com/eclipse/mosquitto/issues/534\n[#636]: https://github.com/eclipse/mosquitto/issues/636\n[#649]: https://github.com/eclipse/mosquitto/issues/649\n[#652]: https://github.com/eclipse/mosquitto/issues/652\n[#654]: https://github.com/eclipse/mosquitto/issues/654\n[#670]: https://github.com/eclipse/mosquitto/issues/670\n[#676]: https://github.com/eclipse/mosquitto/issues/676\n"
  },
  {
    "path": "www/posts/2018/05/press-release.md",
    "content": "<!--\n.. title: Press release\n.. slug: press-release\n.. date: 2018-05-07 10:45:38 UTC+01:00\n.. tags: Press\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nI am pleased to announce that I am being paid to work on Mosquitto by Cedalo AG.\nI will be leaving my current job at the end of June this year, but have already\nstarted work for Cedalo on a limited basis.\n\nThe press release for this change is below:\n\n# New 1.5 release, MQTT 5.0 roadmap and commercial sponsor for Open-Source Eclipse Mosquitto MQTT Broker\n\nOpen-Source MQTT Broker Version 1.5 released – Estimate for availability of\nMQTT 5.0 compliant version – German based Cedalo AG becomes commercial sponsor\nfor future Mosquitto Open Source development\n\nA new version of the open source Eclipse Mosquitto MQTT broker is available on\nthe Mosquitto website at [mosquitto.org](https://mosquitto.org). Mosquitto version 1.5 brings a\nhost of changes to the broker, including performance improvements and more\nflexible authentication and access control, as well as numerous bug fixes. The\nclient library has added some helper functions to allow the creation of\nextremely simple MQTT clients.\n\nThe initiator and core developer of the Mosquitto project is now employed by\nthe German based company Cedalo AG ([www.cedalo.com](https://www.cedalo.com)). Cedalo has hired Roger\nLight to sponsor the further development of Mosquitto and to accelerate the\npublic availability of a powerful MQTT broker. Cedalo is the creator of the\nend-user oriented IoT modelling tool “Streamsheets”.\n\nWith the new sponsorship the project is now able to accelerate the path towards\nnew releases. The next version of Mosquitto will add support for MQTT version\n5, which is the most substantial revision of the protocol since the first\npublic specification was released. MQTT v5 adds error reporting, enhancements\nfor scalability at the server side, features to help resource constrained\nclients, and extensible metadata - which is used amongst other things to\nintroduce support for a request/response capability.\n\nThe MQTT v5 compliant release is planned for the end of August 2018.\n\nPress Contact:\nCedalo AG\n79098 Freiburg,\nSchnewlinstr. 6\n\nKristian Raue\nMail: presse@cedalo.com\n"
  },
  {
    "path": "www/posts/2018/05/version-1-5-released.md",
    "content": "<!--\n.. title: Version 1.5 released\n.. slug: version-1-5-released\n.. date: 2018-05-02 23:56:38 UTC+01:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\n1.5 - 20180502\n==============\n\nThis is a features release. Updated binaries will be available shortly.\n\n# Security\n\n *  Fix memory leak that could be caused by a malicious CONNECT packet. This\n    does not yet have a CVE assigned. Closes #533493 (on Eclipse bugtracker)\n\n# Broker features\n\n * Add `per_listener_settings` to allow authentication and access control to be\n   per listener.\n * Add limited support for reloading listener settings. This allows settings\n   for an already defined listener to be reloaded, but port numbers must not be\n   changed.\n * Add ability to deny access to SUBSCRIBE messages as well as the current\n   read/write accesses. Currently for auth plugins only.\n * Reduce calls to malloc through the use of UHPA.\n * Outgoing messages with QoS>1 are no longer retried after a timeout period.\n   Messages will be retried when a client reconnects.  This change in behaviour\n   can be justified by considering when the timeout may have occurred.\n   * If a connection is unreliable and has dropped, but without one end\n    noticing, the messages will be retried on reconnection. Sending\n    additional PUBLISH or PUBREL would not have changed anything.\n   * If a client is overloaded/unable to respond/has a slow connection then\n    sending additional PUBLISH or PUBREL would not help the client catch\n    up. Once the backlog has cleared the client will respond. If it is not\n    able to catch up, sending additional duplicates would not help either.\n * Add `use_subject_as_username` option for certificate based client\n   authentication to use the entire certificate subject as a username, rather\n   than just the CN. Closes #469467.\n * Change sys tree printing output. This format shouldn't be relied upon and\n   may change at any time. Closes #470246.\n * Minimum supported libwebsockets version is now 1.3.\n * Add systemd startup notification and services. Closes #471053.\n * Reduce unnecessary malloc and memcpy when receiving a message and storing\n   it. Closes #470258.\n * Support for Windows XP has been dropped.\n * Bridge connections now default to using MQTT v3.1.1.\n * `mosquitto_db_dump` tool can now output some stats on clients.\n * Perform utf-8 validation on incoming will, subscription and unsubscription\n   topics.\n * new $SYS/broker/store/messages/count (deprecates $SYS/broker/messages/stored)\n * new $SYS/broker/store/messages/bytes\n * `max_queued_bytes` feature to limit queues by real size rather than\n   than just message count. Closes Eclipse #452919 or Github #100\n * Add support for bridges to be configured to only send notifications to the\n   local broker.\n * Add `set_tcp_nodelay` option to allow Nagle's algorithm to be disabled on\n   client sockets. Closes #433.\n * The behaviour of `allow_anonymous` has changed. In the old behaviour, the\n   default if not set was to allow anonymous access. The new behaviour is to\n   default is to allow anonymous access unless another security option is set.\n   For example, if `password_file` is set and `allow_anonymous` is not set,\n   then anonymous access will be denied. It is still possible to allow\n   anonymous access by setting it explicitly.\n\n# Broker fixes\n * Fix UNSUBSCRIBE with no topic is accepted on MQTT 3.1.1. Closes #665.\n * Produce an error if two bridges share the same `local_clientid`.\n * Miscellaneous fixes on Windows.\n * `queue_qos0_messages` was not observing `max_queued_**` limits\n * When using the `include_dir` configuration option sort the files\n   alphabetically before loading them.  Closes #17.\n * IPv6 is no longer disabled for websockets listeners.\n * Remove all build timestamp information including $SYS/broker/timestamp.\n   Closes #651.\n * Correctly handle incoming strings that contain a NULL byte. Closes #693.\n * Use constant time memcmp for password comparisons.\n * Fix incorrect PSK key being used if it had leading zeroes.\n * Fix memory leak if a client provided a username/password for a listener with\n   `use_identity_as_username` configured.\n * Fix `use_identity_as_username` not working on websockets clients.\n * Don't crash if an auth plugin returns `MOSQ_ERR_AUTH` for a username check on\n   a websockets client. Closes #490.\n * Fix 08-ssl-bridge.py test when using async dns lookups. Closes #507.\n * Lines in the config file are no longer limited to 1024 characters long.\n   Closes #652.\n * Fix $SYS counters of messages and bytes sent when message is sent over\n   a Websockets. Closes #250.\n * Fix `upgrade_outgoing_qos` for retained message. Closes #534.\n * Fix CONNACK message not being sent for unauthorised connect on websockets.\n   Closes #8.\n * Maximum connections on Windows increased to 2048.\n * When a client with an in-use client-id connects, if the old client has a\n   will, send the will message. Closes #26.\n * Fix parsing of configuration options that end with a space. Closes #804.\n\n# Client library features\n * Outgoing messages with QoS>1 are no longer retried after a timeout period.\n   Messages will be retried when a client reconnects.\n * DNS-SRV support is now disabled by default.\n * Add `mosquitto_subscribe_simple()` This is a helper function to make\n   retrieving messages from a broker very straightforward. Examples of its use\n   are in `examples/subscribe_simple`.\n * Add `mosquitto_subscribe_callback()` This is a helper function to make\n   processing messages from a broker very straightforward. An example of its use\n   is in `examples/subscribe_simple`.\n * Connections now default to using MQTT v3.1.1.\n * Add `mosquitto_validate_utf8()` to check whether a string is valid UTF-8\n   according to the UTF-8 spec and to the additional restrictions imposed by\n   the MQTT spec.\n * Topic inputs are checked for UTF-8 validity.\n * Add `mosquitto_userdata` function to allow retrieving the client userdata\n   member variable. Closes #111.\n * Add `mosquitto_pub_topic_check2()`, `mosquitto_sub_topic_check2()`, and\n   `mosquitto_topic_matches_sub2()` which are identical to the similarly named\n   functions but also take length arguments.\n * Add `mosquitto_connect_with_flags_callback_set()`, which allows a second\n   connect callback to be used which also exposes the connect flags parameter.\n   Closes #738 and #128.\n * Add `MOSQ_OPT_SSL_CTX` option to allow a user specified `SSL_CTX` to be used\n   instead of the one generated by libmosquitto. This allows greater control\n   over what options can be set. Closes #715.\n * Add `MOSQ_OPT_SSL_CTX_WITH_DEFAULTS` to work with `MOSQ_OPT_SSL_CTX` and have\n   the default libmosquitto `SSL_CTX` configuration applied to the user provided\n   `SSL_CTX`. Closes #567.\n\n# Client library fixes\n * Fix incorrect PSK key being used if it had leading zeroes.\n * Initialise \"result\" variable as soon as possible in\n   `mosquitto_topic_matches_sub`. Closes #654.\n * No need to close socket again if setting non-blocking failed. Closes #649.\n * Fix `mosquitto_topic_matches_sub()` not correctly matching foo/bar against\n   foo/+/#. Closes #670.\n * SNI host support added.\n\n# Client features\n * Add -F to `mosquitto_sub` to allow the user to choose the output format.\n * Add -U to `mosquitto_sub` for unsubscribing from topics.\n * Add -c (clean session) to `mosquitto_pub`.\n * Add --retained-only to `mosquitto_sub` to exit after receiving all retained\n  messages.\n * Add -W to allow `mosquitto_sub` to stop processing incoming messages after a\n  timeout.\n * Connections now default to using MQTT v3.1.1.\n * Default to using port 8883 when using TLS.\n * `mosquitto_sub` doesn't continue to keep connecting if CONNACK tells it the\n  connection was refused.\n\n# Client fixes\n * Correctly handle empty files with `mosquitto_pub -l`. Closes #676.\n\n# Build\n * Add `WITH_STRIP` option (defaulting to \"no\") that when set to \"yes\" will strip\n   executables and shared libraries when installing.\n * Add `WITH_STATIC_LIBRARIES` (defaulting to \"no\") that when set to \"yes\" will\n   build and install static versions of the client libraries.\n * Don't run TLS-PSK tests if TLS-PSK disabled at compile time. Closes #636.\n * Support for openssl versions 1.0.0 and 1.0.1 has been removed as these are\n   no longer supported by openssl.\n\n# Documentation\n * Replace mentions of deprecated `c_rehash` with `openssl rehash`.\n"
  },
  {
    "path": "www/posts/2018/08/updated-debian-repository-backend.md",
    "content": "<!--\n.. title: Updated Debian Repository Backend\n.. slug: updated-debian-repository-backend\n.. date: 2018-08-08 23:00:43 UTC+01:00\n.. tags: Packaging\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe backend software for administering the Debian repository at\nhttps://repo.mosquitto.org/ has been migrated from `reprepro` to `aptly`. This\nhas the benefit of allowing multiple versions of a package to remain in the\nrepository.\n\nFor mosquitto, this now means that old versions of the Debian packages will\nremain available even after newer versions are published, and so you can depend\non a particular version. The recommendation is always to use the latest version\nof course.\n\nThis change should be transparent to all current users, but there is the\npossibility that something is different between the two repository tools. If\nyou do find a problem, please let us know.\n\nThe repository now has builds for versions 1.4.15 and 1.5.\n"
  },
  {
    "path": "www/posts/2018/08/version-151-released.md",
    "content": "<!--\n.. title: Version 1.5.1 released\n.. slug: version-151-released\n.. date: 2018-08-16 17:01:08 UTC+01:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Packaging changes\n\n* The snap package now has support for websockets included.\n* The Windows packages have changed.\n    - Support for Windows XP was dropped in Mosquitto 1.5, so the need for the\n\t  Cygwin build has gone, and this has been dropped.\n    - There are now 64-bit and 32-bit native packages.\n    - Websockets support is included.\n    - Threading support is not included in libmosquitto to simplify installation,\n\t  alternative solutions are being looked into for the future.\n    - The only external dependency is now OpenSSL.\n\n# Version 1.5.1 changes\n\n## Broker\n- Fix plugin cleanup function not being called on exit of the broker. Closes\n  [#900].\n- Print more OpenSSL errors when loading certificates/keys fail.\n- Use `AF_UNSPEC` etc. instead of `PF_UNSPEC` to comply with POSIX. Closes\n  [#863].\n- Remove use of `AI_ADDRCONFIG`, which means the broker can be used on systems\n  where only the loopback interface is defined. Closes [#869], Closes [#901].\n- Fix IPv6 addresses not being able to be used as bridge addresses.\n  Closes [#886].\n- All clients now time out if they exceed their keepalive\\*1.5, rather than\n  just reach it. This was inconsistent in two places.\n- Fix segfault on startup if bridge CA certificates could not be read.\n  Closes [#851].\n- Fix problem opening listeners on Pi caused by unsigned char being default.\n  Found via [#849].\n- ACL patterns that do not contain either `%c` or `%u` now produce a warning in\n  the log. Closes [#209].\n- Fix bridge publishing failing when `per_listener_settings` was true. Closes\n  [#860].\n- Fix `use_identity_as_username true` not working. Closes [#833].\n- Fix UNSUBACK messages not being logged. Closes [#903].\n- Fix possible endian issue when reading the `memory_limit` option.\n- Fix building for libwebsockets < 1.6.\n- Fix accessor functions for username and client id when used in plugin auth\n  check.\n\n## Library\n- Fix some places where return codes were incorrect, including to the\n  `on_disconnect()` callback. This has resulted in two new error codes,\n  `MOSQ_ERR_KEEPALIVE` and `MOSQ_ERR_LOOKUP`.\n- Fix connection problems when `mosquitto_loop_start()` was called before\n  `mosquitto_connect_async()`. Closes [#848].\n\n## Clients\n- When compiled using `WITH_TLS=no`, the default port was incorrectly being set\n  to -1. This has been fixed.\n- Fix compiling on Mac OS X <10.12. Closes `#813` and `#240`.\n\n## Build\n- Fixes for building on NetBSD. Closes `#258`.\n- Fixes for building on FreeBSD.\n- Add support for compiling with static libwebsockets library.\n\n\n[#209]: https://github.com/eclipse/mosquitto/issues/209\n[#240]: https://github.com/eclipse/mosquitto/issues/240\n[#258]: https://github.com/eclipse/mosquitto/issues/258\n[#813]: https://github.com/eclipse/mosquitto/issues/813\n[#833]: https://github.com/eclipse/mosquitto/issues/833\n[#848]: https://github.com/eclipse/mosquitto/issues/848\n[#849]: https://github.com/eclipse/mosquitto/issues/849\n[#851]: https://github.com/eclipse/mosquitto/issues/851\n[#860]: https://github.com/eclipse/mosquitto/issues/860\n[#863]: https://github.com/eclipse/mosquitto/issues/863\n[#869]: https://github.com/eclipse/mosquitto/issues/869\n[#886]: https://github.com/eclipse/mosquitto/issues/886\n[#900]: https://github.com/eclipse/mosquitto/issues/900\n[#901]: https://github.com/eclipse/mosquitto/issues/901\n[#903]: https://github.com/eclipse/mosquitto/issues/903\n"
  },
  {
    "path": "www/posts/2018/09/security-advisory-cve-2018-12543.md",
    "content": "<!--\n.. title: Security advisory: CVE-2018-12543\n.. slug: security-advisory-cve-2018-12543\n.. date: 2018-09-27 10:36:19 UTC+01:00\n.. tags: Security,Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.5.3 has been released to address a security vulnerability. It also\nincludes other bug fixes.\n\n# CVE-2018-12543\n\nA vulnerability exists in Mosquitto versions 1.5 to 1.5.2 inclusive, known as\n[CVE-2018-12543].\n\nIf a message received by the broker has a topic that begins with `$`, but that\ndoes not begin `$SYS`, an assert is triggered that should otherwise not be\naccessible, causing Mosquitto to exit.\n\nThe issue is fixed in Mosquitto 1.5.3. Patches for older versions are\navailable at <https://mosquitto.org/files/cve/2018-12543>\n\nThe fix addresses the problem by reverting a commit that intended to remove\nsome unused checks, but also stopped part of the topic hierarchy being created.\n\n# Version 1.5.3 Changes\n\nThe complete list of fixes addressed in version 1.5.3 is:\n\n## Security\n\n* Fix [CVE-2018-12543]. If a message is sent to Mosquitto with a topic that\n  begins with `$`, but is not `$SYS`, then an assert that should be unreachable\n  is triggered and Mosquitto will exit.\n\n## Broker\n* Elevate log level to warning for situation when socket limit is hit.\n* Remove requirement to use `user root` in snap package config files.\n* Fix retained messages not sent by bridges on outgoing topics at the first\n  connection. Closes [#701].\n* Documentation fixes. Closes [#520], [#600].\n* Fix duplicate clients being added to by_id hash before the old client was\n  removed. Closes [#645].\n* Fix Windows version not starting if `include_dir` did not contain any files.\n  Closes [#566].\n\n## Build\n* Various fixes to ease building.\n\n[CVE-2018-12543]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12543\n[#520]: https://github.com/eclipse/mosquitto/issues/520\n[#566]: https://github.com/eclipse/mosquitto/issues/566\n[#600]: https://github.com/eclipse/mosquitto/issues/600\n[#645]: https://github.com/eclipse/mosquitto/issues/645\n[#701]: https://github.com/eclipse/mosquitto/issues/701\n"
  },
  {
    "path": "www/posts/2018/09/version-152-released.md",
    "content": "<!--\n.. title: Version 1.5.2 released\n.. slug: version-152-released\n.. date: 2018-09-19 18:18:36 UTC+01:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Version 1.5.2 changes\n\n## Broker\n- Fix build when using `WITH_ADNS=yes`.\n- Fix incorrect call to setsockopt() for `TCP_NODELAY`. Closes [#941].\n- Fix excessive CPU usage when the number of sockets exceeds the system limit.\n  Closes [#948].\n- Fix for bridge connections when using `WITH_ADNS=yes`.\n- Fix `round_robin false` behaviour. Closes [#481].\n- Fix segfault on HUP when bridges and security options are configured.\n  Closes [#965].\n\n## Library\n- Fix situation where username and password is used with SOCKS5 proxy. Closes\n  [#927].\n- Fix SOCKS5 behaviour when passing IP addresses. Closes [#927].\n\n## Build\n- Make it easier to build without bundled uthash.h using `WITH_BUNDLED_DEPS=no`.\n- Fix build with OPENSSL_NO_ENGINE. Closes [#932].\n\n[#481]: https://github.com/eclipse/mosquitto/issues/481\n[#927]: https://github.com/eclipse/mosquitto/issues/927\n[#932]: https://github.com/eclipse/mosquitto/issues/932\n[#941]: https://github.com/eclipse/mosquitto/issues/941\n[#948]: https://github.com/eclipse/mosquitto/issues/948\n[#965]: https://github.com/eclipse/mosquitto/issues/965\n"
  },
  {
    "path": "www/posts/2018/11/mqtt5-progress.md",
    "content": "<!--\n.. title: MQTT 5 progress\n.. slug: mqtt5-progress\n.. date: 2018-11-29 21:33:29 UTC+00:00\n.. tags: MQTT5\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nDevelopment of support for MQTT 5 is ongoing and making good progress, but has\nbeen substantially delayed due to other non-Mosquitto work having to take\npriority.\n\nIt is possible to test the current state of MQTT 5 support by using the `mqtt5`\nbranch of the [repository]. Please note that this is very much a work in\nprogress, so parts are incomplete and interfaces may yet change. The client\nlibrary in particular has had to have an increase in functions available in\norder to provide the features needed whilst providing backwards compatibility.\nPart of the plan for the 2.0 release, which will follow after 1.6, is to\nconsolidate the libmosquitto API with breaking changes. There are more details\non the [roadmap].\n\nCurrent features include:\n\n* Support for all incoming and outgoing packets, although not everything is\n  processed.\n* Support for sending and receiving all properties, with not all properties\n  processed.\n* Client support for setting properties\n* Request/response support (client cannot process incoming correlation data)\n* Retain availability\n* Message expiry interval support\n* Server support for assigned client identifiers\n* Payload format indicator support\n* Content-type support\n* Basic topic alias support from client to broker\n* Lots of new tests\n\nBoth `mosquitto_pub` and `mosquitto_sub` support setting properties on the\ncommand line, for example:\n\n```\nmosquitto_sub -t topic -v -D connect session-expiry-interval 60 -D connect user-property key value -D subscribe user-property sub-key sub-value\n```\n\n```\nmosquitto_pub -t topic -m '{\"key\":\"value\"}' -D publish content-type \"application/json\"\n```\n\n```\n./sensor_read.sh | mosquitto_pub -t topic -l  -D publish topic-alias 1\n```\n\nFurther updates will be posted when more features are available.\n\n[repository]: https://github.com/eclipse/mosquitto/tree/mqtt5\n[roadmap]: https://mosquitto.org/roadmap/\n"
  },
  {
    "path": "www/posts/2018/11/version-154-released.md",
    "content": "<!--\n.. title: Version 1.5.4 released\n.. slug: version-154-released\n.. date: 2018-11-08 15:00:26 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix and security release.\n\n# Version 1.5.4 changes\n\n## Security\n- When using a TLS enabled websockets listener with `require_certificate`\n  enabled, the mosquitto broker does not correctly verify client certificates.\n  This is now fixed. All other security measures operate as expected, and in\n  particular non-websockets listeners are not affected by this. Closes [#996].\n\n## Broker\n- Process all pending messages even when a client has disconnected. This means\n  a client that send a PUBLISH then DISCONNECT quickly, then disconnects will\n  have its DISCONNECT message processed properly and so no Will will be sent.\n  Closes [#7].\n- $SYS/broker/clients/disconnected should never be negative. Closes [#287].\n- Give better error message if a client sends a password without a username.\n  Closes [#1015].\n- Fix bridge not honoring `restart_timeout`. Closes [#1019].\n- Don't disconnect a client if an auth plugin denies access to SUBSCRIBE.\n  Closes [#1016].\n\n## Library\n- Fix memory leak that occurred if `mosquitto_reconnect()` was used when TLS\n  errors were present. Closes [#592].\n- Fix TLS connections when using an external event loop with\n  `mosquitto_loop_read()` and `mosquitto_write()`. Closes [#990].\n\n## Build\n- Fix clients not being compiled with threading support when using CMake.\n  Closes [#983].\n- Header fixes for FreeBSD. Closes [#977].\n- Use `_GNU_SOURCE` to fix build errors in websockets and getaddrinfo usage.\n  Closes [#862] and [#933].\n- Fix builds on QNX 7.0.0. Closes [#1018].\n\n[#7]: https://github.com/eclipse/mosquitto/issues/7\n[#287]: https://github.com/eclipse/mosquitto/issues/287\n[#592]: https://github.com/eclipse/mosquitto/issues/592\n[#933]: https://github.com/eclipse/mosquitto/issues/933\n[#977]: https://github.com/eclipse/mosquitto/issues/977\n[#983]: https://github.com/eclipse/mosquitto/issues/983\n[#990]: https://github.com/eclipse/mosquitto/issues/990\n[#996]: https://github.com/eclipse/mosquitto/issues/996\n[#1015]: https://github.com/eclipse/mosquitto/issues/1015\n[#1016]: https://github.com/eclipse/mosquitto/issues/1016\n[#1018]: https://github.com/eclipse/mosquitto/issues/1018\n[#1019]: https://github.com/eclipse/mosquitto/issues/1019\n"
  },
  {
    "path": "www/posts/2018/12/version-155-released.md",
    "content": "<!--\n.. title: Version 1.5.5 released\n.. slug: version-155-released\n.. date: 2018-12-11 15:57:18 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix and security release.\n\n# Version 1.5.5 changes\n\n## Security\n- If `per_listener_settings` is set to true, then the `acl_file` setting was\n  ignored for the \"default listener\" only. This has been fixed. This does not\n  affect any listeners defined with the `listener` option. Closes [#1073].\n  This is now tracked as [CVE-2018-20145].\n\n## Broker\n- Add `socket_domain` option to allow listeners to disable IPv6 support.\n  This is required to work around a problem in libwebsockets that means\n  sockets only listen on IPv6 by default if IPv6 support is compiled in.\n  Closes [#1004].\n- When using ADNS, don't ask for all network protocols when connecting,\n  because this can lead to confusing \"Protocol not supported\" errors if the\n  network is down. Closes [#1062].\n- Fix outgoing retained messages not being sent by bridges on initial\n  connection. Closes [#1040].\n- Don't reload `auth_opt_` options on reload, to match the behaviour of the\n  other plugin options. Closes [#1068].\n- Print message on error when installing/uninstalling as a Windows service.\n- All non-error connect/disconnect messages are controlled by the\n  `connection_messages` option. Closes [#772]. Closes [#613]. Closes [#537].\n\n## Library\n- Fix reconnect delay backoff behaviour. Closes [#1027].\n- Don't call `on_disconnect()` twice if keepalive tests fail. Closes [#1067].\n\n## Client\n- Always print leading zeros in `mosquitto_sub` when output format is hex.\n  Closes [#1066].\n\n## Build\n- Fix building where TLS-PSK is not available. Closes [#68].\n\n\n[CVE-2018-20145]: https://nvd.nist.gov/vuln/detail/CVE-2018-20145\n[#68]: https://github.com/eclipse/mosquitto/issues/68\n[#537]: https://github.com/eclipse/mosquitto/issues/537\n[#613]: https://github.com/eclipse/mosquitto/issues/613\n[#772]: https://github.com/eclipse/mosquitto/issues/772\n[#1004]: https://github.com/eclipse/mosquitto/issues/1004\n[#1027]: https://github.com/eclipse/mosquitto/issues/1027\n[#1040]: https://github.com/eclipse/mosquitto/issues/1040\n[#1062]: https://github.com/eclipse/mosquitto/issues/1062\n[#1066]: https://github.com/eclipse/mosquitto/issues/1066\n[#1067]: https://github.com/eclipse/mosquitto/issues/1067\n[#1068]: https://github.com/eclipse/mosquitto/issues/1068\n[#1073]: https://github.com/eclipse/mosquitto/issues/1073\n"
  },
  {
    "path": "www/posts/2019/02/mqtt5-test-release.md",
    "content": "<!--\n.. title: MQTT 5 Test Release\n.. slug: mqtt5-test-release\n.. date: 2019-02-14 10:30:00 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe work on MQTT v5 support in Mosquitto has reached a point where it may be of interest to a range of people. It is not yet complete, but wider testing and feedback would be appreciated.\n\nThe source is available at [mosquitto-mqtt5-test1.tar.gz] and can be compiled as normal.\n\n## Supported features\n\n* Session expiry\n* Message expiry\n* Reason code on all ACKs (not all reason codes are used)\n* Reason string on all ACKs (no reason strings are provided by the broker however)\n* Payload format and content type\n* Request / response pattern\n* Subscription ID\n* Topic alias\n* Flow control\n* User properties\n* Optional server feature availability\n* Subscription options\n* Server keep alive\n* Assigned Client ID\n\n\n## Unsupported features\n\n* Shared subscriptions\n* Extended authentication\n* Server reference\n* Not all reason codes are used by the broker\n* Bridges do not use MQTT v5\n* On disk persistence does not include MQTT 5 property support\n* The broker will not create topic aliases\n\n\n[mosquitto-mqtt5-test1.tar.gz]: https://mosquitto.org/files/source/test/mosquitto-mqtt5-test1.tar.gz\n"
  },
  {
    "path": "www/posts/2019/02/version-1-5-6-released.md",
    "content": "<!--\n.. title: Version 1.5.6 released\n.. slug: version-1-5-6-released\n.. date: 2019-02-08 13:00:00 UTC\n.. tags: Security,Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.5.6 has been released to address three potential security vulnerabilities.\n\n# CVE-2018-12551\n\nIf Mosquitto is configured to use a password file for authentication, any\nmalformed data in the password file will be treated as valid. This typically\nmeans that the malformed data becomes a username and no password. If this\noccurs, clients can circumvent authentication and get access to the broker by\nusing the malformed username. In particular, a blank line will be treated as a\nvalid empty username. Other security measures are unaffected. **Users who have\nonly used the `mosquitto_passwd` utility to create and modify their password\nfiles are unaffected by this vulnerability**. Affects version 1.0 to 1.5.5\ninclusive.\n\nPatches for older versions are available at <https://mosquitto.org/files/cve/2018-12551>\n\n# CVE-2018-12550\n\nIf an ACL file is empty, or has only blank lines or comments, then mosquitto\ntreats the ACL file as not being defined, which means that no topic access is\ndenied. Although denying access to all topics is not a useful configuration,\nthis behaviour is unexpected and could lead to access being incorrectly granted\nin some circumstances. Affects versions 1.0 to 1.5.5 inclusive.\n\nPatches for older versions are available at <https://mosquitto.org/files/cve/2018-12550>\n\n# CVE-2018-12546\n\nIf a client publishes a retained message to a topic that they have access to,\nand then their access to that topic is revoked, the retained message will still\nbe delivered to future subscribers. This behaviour may be undesirable in some\napplications, so a configuration option `check_retain_source` has been\nintroduced to enforce checking of the retained message source on publish.\n\nPatches for older versions are available at <https://mosquitto.org/files/cve/2018-12546>\n\n# Version 1.5.6 Changes\n\nThe list of other fixes addressed in version 1.5.6 is:\n\n## Broker\n\n- Fixed comment handling for config options that have optional arguments.\n- Improved documentation around bridge topic remapping.\n- Handle mismatched handshakes (e.g. QoS1 PUBLISH with QoS2 reply) properly.\n- Fix spaces not being allowed in the bridge `remote_username option`. Closes\n  [#1131].\n- Allow broker to always restart on Windows when using `log_dest file`. Closes\n  [#1080].\n- Fix Will not being sent for Websockets clients. Closes [#1143].\n- Windows: Fix possible crash when client disconnects. Closes [#1137].\n- Fixed durable clients being unable to receive messages when offline, when\n  `per_listener_settings` was set to true. Closes [#1081].\n- Add log message for the case where a client is disconnected for sending a\n  topic with invalid UTF-8. Closes [#1144].\n\n## Library\n\n- Fix TLS connections not working over SOCKS.\n- Don't clear SSL context when TLS connection is closed, meaning if a user\n  provided an external SSL_CTX they have less chance of leaking references.\n\n## Build\n\n- Fix comparison of boolean values in CMake build. Closes [#1101].\n- Fix compilation when openssl deprecated APIs are not available.\n  Closes [#1094].\n- Man pages can now be built on any system. Closes [#1139].\n\n[#1080]: https://github.com/eclipse/mosquitto/issues/1080\n[#1081]: https://github.com/eclipse/mosquitto/issues/1081\n[#1094]: https://github.com/eclipse/mosquitto/issues/1094\n[#1101]: https://github.com/eclipse/mosquitto/issues/1101\n[#1131]: https://github.com/eclipse/mosquitto/issues/1131\n[#1137]: https://github.com/eclipse/mosquitto/issues/1137\n[#1139]: https://github.com/eclipse/mosquitto/issues/1139\n[#1143]: https://github.com/eclipse/mosquitto/issues/1143\n[#1144]: https://github.com/eclipse/mosquitto/issues/1144\n"
  },
  {
    "path": "www/posts/2019/02/version-1-5-7-released.md",
    "content": "<!--\n.. title: Version 1.5.7 released\n.. slug: version-1-5-7-released\n.. date: 2019-02-13 23:50:00 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n## Broker\n- Fix build failure when using `WITH_ADNS=yes`\n- Ensure that an error occurs if `per_listener_settings true` is given after\n  other security options. Closes [#1149].\n- Fix `include_dir` not sorting config files before loading. This was partially\n  fixed in 1.5 previously.\n- Improve documentation around the `include_dir` option. Closes [#1154].\n- Fix case where old unreferenced msg_store messages were being saved to the\n  persistence file, bloating its size unnecessarily. Closes [#389].\n\n## Library\n- Fix `mosquitto_topic_matches_sub()` not returning MOSQ_ERR_INVAL for\n  invalid subscriptions like `topic/#abc`. This only affects the return value,\n  not the match/no match result, which was already correct.\n\n## Build\n- Don't require C99 compiler.\n- Add rewritten build test script and remove some build warnings.\n\n\n[#389]: https://github.com/eclipse/mosquitto/issues/389\n[#1149]: https://github.com/eclipse/mosquitto/issues/1149\n[#1154]: https://github.com/eclipse/mosquitto/issues/1154\n"
  },
  {
    "path": "www/posts/2019/02/version-1-5-8-released.md",
    "content": "<!--\n.. title: Version 1.5.8 released\n.. slug: version-1-5-8-released\n.. date: 2019-02-28 17:01:00 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n## Broker\n- Fix clients being disconnected when ACLs are in use. This only affects the\n  case where a client connects using a username, and the anonymous ACL list is\n  defined but specific user ACLs are not defined. Closes [#1162].\n- Make error messages for missing config file clearer.\n- Fix some Coverity Scan reported errors that could occur when the broker was\n  already failing to start.\n- Fix broken `mosquitto_passwd` on FreeBSD. Closes [#1032].\n- Fix delayed bridge local subscriptions causing missing messages.\n  Closes [#1174].\n\n## Library\n- Use higher resolution timer for random initialisation of client id\n  generation. Closes [#1177].\n- Fix some Coverity Scan reported errors that could occur when the library was\n  already quitting.\n\n[#1032]: https://github.com/eclipse/mosquitto/issues/1032\n[#1162]: https://github.com/eclipse/mosquitto/issues/1162\n[#1174]: https://github.com/eclipse/mosquitto/issues/1174\n[#1177]: https://github.com/eclipse/mosquitto/issues/1177\n"
  },
  {
    "path": "www/posts/2019/04/version-1-6-1-released.md",
    "content": "<!--\n.. title: Version 1.6.1 released\n.. slug: version-1-6-1-released\n.. date: 2019-04-26 16:37:00 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a minor service release. The fixes are only related to documentation\nand the build process, and so is primarily of interest for people building\nMosquitto.\n\n## Broker\n- Document `memory_limit` option.\n\n## Clients\n- Fix compilation on non glibc systems due to missing sys/time.h header.\n\n## Build:\n- Add `make check` target and document testing procedure. Closes [#1230].\n- Document bundled dependencies and how to disable. Closes [#1231].\n- Split CFLAGS and CPPFLAGS, and LDFLAGS and LDADD/LIBADD.\n- test/unit now respects CPPFLAGS and LDFLAGS. Closes [#1232].\n- Don't call ldconfig in CMake scripts. Closes [#1048].\n- Use `CMAKE_INSTALL_*` variables when installing in CMake. Closes [#1049].\n\n[#1048]: https://github.com/eclipse/mosquitto/issues/1048\n[#1049]: https://github.com/eclipse/mosquitto/issues/1049\n[#1230]: https://github.com/eclipse/mosquitto/issues/1230\n[#1231]: https://github.com/eclipse/mosquitto/issues/1231\n[#1232]: https://github.com/eclipse/mosquitto/issues/1232\n"
  },
  {
    "path": "www/posts/2019/04/version-1-6-2-released.md",
    "content": "<!--\n.. title: Version 1.6.2 released\n.. slug: version-1-6-2-released\n.. date: 2019-04-30 13:07:00 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a security and bugfix release.\n\n## Security\n\nIf a client connects using MQTT v5, will a Will message that has MQTT v5\nproperties attached, and the very first Will property is one of `content-type`,\n`correlation-data`, `payload-format-indicator`, or `response-topic`, then at\nthe point the client disconnects, the broker will attempt to read from freed\nmemory, resulting in a possible crash.\n\n\n## Broker\n- Fix memory access after free, leading to possible crash, when v5 client with\n  Will message disconnects, where the Will message has as its first property\n  one of `content-type`, `correlation-data`, `payload-format-indicator`, or\n  `response-topic`.  Closes [#1244].\n- Fix build for `WITH_TLS=no`. Closes [#1250].\n- Fix Will message not allowing `user-property` properties.\n- Fix broker originated messages (e.g. `$SYS/broker/version`) not being\n  published when `check_retain_source` set to true. Closes [#1245].\n- Fix `$SYS/broker/version` being incorrectly expired after 60 seconds.\n  Closes [#1245].\n\n## Library\n- Fix crash after client has been unable to connect to a broker. This occurs\n  when the client is exiting and is part of the final library cleanup routine.\n  Closes [#1246].\n\n## Clients\n- Fix `-L` url parsing. Closes [#1248].\n\n[#1244]: https://github.com/eclipse/mosquitto/issues/1244\n[#1245]: https://github.com/eclipse/mosquitto/issues/1245\n[#1246]: https://github.com/eclipse/mosquitto/issues/1246\n[#1250]: https://github.com/eclipse/mosquitto/issues/1250\n"
  },
  {
    "path": "www/posts/2019/04/version-1-6-released.md",
    "content": "<!--\n.. title: Version 1.6 released\n.. slug: version-1-6-released\n.. date: 2019-04-17 22:08:00 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a feature release and represents a substantial amount of change in the\nproject. Since version 1.5, the overall code line count for the broker, library\nand clients has increased by 37% to 28k. Testing has been an important focus\nfor this release. The number of tests has increased from 102 to 412. The test\ncoverage, whilst still needing further improvement, has increased from 56% to\n61%.\n\nA summary of the notable features is given below.\n\n# MQTT v5 support\n\nThe big addition for this release is support for MQTT v5. This covers the\nbroker, client library and client, and gives full support for the new\nspecification, although not all features are accessible as they will be.\n\nYou can quickly test out a v5 client by using `-V 5` and adding properties with\nthe `-D` option, for example:\n\n```\nmosquitto_sub -V 5 -D connect receive-maximum 3 -D subscribe subscription-identifier 1 ...\n```\n\nThe authentication plugin interface has been extended to allow use of the v5\nextended authentication feature.\n\n# Performance improvements\n\nA number of performance improvements have been implemented in the broker,\nincluding the message routing logic, topic matching, and persistence file\nreading/writing.\n\nMore improvements are planned for the next release.\n\n# New client - mosquitto_rr\n\n`mosquitto_rr` is the \"request-response\" client, intended for the situation\nwhere you want to publish a request message and await a response. It works best\nwith the MQTT v5 request-response features, but can be used with v3.1.1 servers\nif the client it is talking to knows how to respond. This tool is almost\ncertainly not going to see as much use as `mosquitto_sub` or `mosquitto_pub`,\nbut is a useful utility to have available.\n\n# Contributed features\n\nSome notable features have been contributed by the community.\n\nOn the TLS front, support for ALPN allows bridges and clients to connect to\nservers that have multiple protocols on a single port. The new OCSP stapling\nsupport allows the status of TLS certificates to be validated. Finally, TLS\nEngine support has been added.\n\nAway from TLS, support for Automotive DLT logging has been added, disabled by\ndefault.\n\n# Deprecations\n\nThe C++ wrapper library, libmosquittopp is now deprecated and will be removed\nin version 2.0. It remains largely unchanged since v1.5.\n\nThe C library, libmosquitto, is having its interface changed for version 2.0,\nso any current function should be considered at risk. The rationale for this is\nto consolidate the changes introduced since version 1.0, in particular the\nlarge number of functions required to support MQTT v5, but that otherwise\nclosely match existing functions.\n\nSupport for TLS v1.0 has been dropped. Support for TLS v1.1 will be dropped in\nversion 2.0.\n\n# Changelog\n\nThe more detailed changelog is below, but does not include many of the\nfixes and improvements that have been made:\n\n## Broker features\n- Add support for MQTT v5\n- Add support for OCSP stapling.\n- Add support for ALPN on bridge TLS connections. Closes [#924].\n- Add support for Automotive DLT logging.\n- Add TLS Engine support.\n- Persistence file read/write performance improvements.\n- General performance improvements.\n- Add `max_keepalive option`, to allow a maximum keepalive value to be set for\n  MQTT v5 clients only.\n- Add `bind_interface` option which allows a listener to be bound to a\n  specific network interface, in a similar fashion to the `bind_address` option.\n  Linux only.\n- Add improved bridge restart interval based on Decorrelated Jitter.\n- Add `dhparamfile` option, to allow DH parameters to be loaded for Ephemeral\n  DH support\n- Disallow writing to $ topics where appropriate.\n- Add explicit support for TLS v1.3.\n- Drop support for TLS v1.0.\n- Improved general support for broker generated client ids. Removed libuuid\n  dependency.\n- `auto_id_prefix` now defaults to 'auto-'.\n- QoS 1 and 2 flow control improvements.\n\n## Client library features\n- Add support for MQTT v5\n- Add `mosquitto_subscribe_multiple()` for sending subscriptions to multiple\n  topics in one command.\n- Add TLS Engine support.\n- Add explicit support for TLS v1.3.\n- Drop support for TLS v1.0.\n- QoS 1 and 2 flow control improvements.\n\n## Client features\n- Add support for MQTT v5\n- Add `mosquitto_rr ` client, which can be used for \"request-response\" messaging,\n  by sending a request message and awaiting a response.\n- Add TLS Engine support.\n- Add support for ALPN on TLS connections. Closes [#924].\n- Add `-D ` option for all clients to specify MQTT v5 properties.\n- Add `-E ` to `mosquitto_sub `, which causes it to exit immediately after having\n  its subscriptions acknowledged. Use with `-c` to create a durable client\n  session without requiring a message to be received.\n- Add `--remove-retained` to `mosquitto_sub`, which can be used to clear retained\n  messages on a broker.\n- Add `--repeat` and `--repeat-delay` to `mosquitto_pub`, which can be used to\n  repeat single message publishes at a regular interval.\n- -V now accepts `5`, `311`, `31`, as well as `mqttv5` etc.\n- Add explicit support for TLS v1.3.\n- Drop support for TLS v1.0.\n\n## Broker fixes\n- Improve error reporting when creating listeners.\n- Fix `mosquitto_passwd` crashing on corrupt password file. Closes [#1207].\n- Fix build on SmartOS due to missing IPV6_V6ONLY. Closes [#1212].\n\n## Client library fixes\n- Add missing `mosquitto_userdata()` function.\n\n## Client fixes\n- `mosquitto_pub` wouldn't always publish all messages when using `-l` and\n  QoS>0. This has been fixed.\n- `mosquitto_sub` was incorrectly encoding special characters when using %j\n  output format. Closes [#1220].\n\n\n[#924]: https://github.com/eclipse/mosquitto/issues/924\n[#1208]: https://github.com/eclipse/mosquitto/issues/1208\n[#1212]: https://github.com/eclipse/mosquitto/issues/1212\n[#1220]: https://github.com/eclipse/mosquitto/issues/1220\n"
  },
  {
    "path": "www/posts/2019/06/version-1-6-3-released.md",
    "content": "<!--\n.. title: Version 1.6.3 released\n.. slug: version-1-6-3-released\n.. date: 2019-06-18 13:00:00 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n## Broker\n- Fix detection of incoming v3.1/v3.1.1 bridges. Closes [#1263].\n- Fix default `max_topic_alias` listener config not being copied to the in-use\n  listener when compiled without TLS support.\n- Fix random number generation if compiling using `WITH_TLS=no` and on Linux\n  with glibc >= 2.25. Without this fix, no random numbers would be generated\n  for e.g. on broker client id generation, and so clients connecting expecting\n  this feature would be unable to connect.\n- Fix compilation problem related to `getrandom()` on non-glibc systems.\n- Fix Will message for a persistent client incorrectly being sent when the\n  client reconnects after a clean disconnect. Closes [#1273].\n- Fix Will message for a persistent client not being sent on disconnect.\n  Closes [#1273].\n- Improve documentation around the upgrading of persistence files. Closes\n  [#1276].\n- Add 'extern \"C\"' on mosquitto_broker.h and mosquitto_plugin.h for C++ plugin\n  writing. Closes [#1290].\n- Fix persistent Websockets clients not receiving messages after they\n  reconnect, having sent DISCONNECT on a previous session. Closes [#1227].\n- Disable TLS renegotiation. Client initiated renegotiation is considered to\n  be a potential attack vector against servers. Closes [#1257].\n- Fix incorrect shared subscription topic '$shared'.\n- Fix zero length client ids being rejected for MQTT v5 clients with clean\n  start set to true.\n- Fix MQTT v5 overlapping subscription behaviour. Clients now receive message\n  from all matching subscriptions rather than the first one encountered, which\n  ensures the maximum QoS requirement is met.\n- Fix incoming/outgoing quota problems for QoS>0.\n- Remove obsolete `store_clean_interval` from documentation.\n\n## Client library\n- Fix typo causing build error on Windows when building without TLS support.\n  Closes [#1264].\n\n## Clients\n- Fix -L url parsing when `/topic` part is missing.\n- Stop some error messages being printed even when `--quiet` was used.\n  Closes [#1284].\n- Fix `mosquitto_pub` exiting with error code 0 when an error occurred.\n  Closes [#1285].\n- Fix `mosquitto_pub` not using the `-c` option. Closes [#1273].\n- Fix MQTT v5 clients not being able to specify a password without a username.\n  Closes [#1274].\n- Fix `mosquitto_pub -l` not handling network failures. Closes [#1152].\n- Fix `mosquitto_pub -l` not handling zero length input. Closes [#1302].\n- Fix double free on exit in `mosquitto_pub`. Closes [#1280].\n\n## Documentation:\n- Remove references to Python binding and C++ wrapper in libmosquitto man\n  page. Closes [#1266].\n\n## Build\n- CLIENT_LDFLAGS now uses LDFLAGS. Closes [#1294].\n\n[#1152]: https://github.com/eclipse/mosquitto/issues/1152\n[#1227]: https://github.com/eclipse/mosquitto/issues/1227\n[#1257]: https://github.com/eclipse/mosquitto/issues/1257\n[#1263]: https://github.com/eclipse/mosquitto/issues/1263\n[#1264]: https://github.com/eclipse/mosquitto/issues/1264\n[#1266]: https://github.com/eclipse/mosquitto/issues/1266\n[#1273]: https://github.com/eclipse/mosquitto/issues/1273\n[#1273]: https://github.com/eclipse/mosquitto/issues/1273\n[#1273]: https://github.com/eclipse/mosquitto/issues/1273\n[#1274]: https://github.com/eclipse/mosquitto/issues/1274\n[#1276]: https://github.com/eclipse/mosquitto/issues/1276\n[#1280]: https://github.com/eclipse/mosquitto/issues/1280\n[#1284]: https://github.com/eclipse/mosquitto/issues/1284\n[#1285]: https://github.com/eclipse/mosquitto/issues/1285\n[#1290]: https://github.com/eclipse/mosquitto/issues/1290\n[#1294]: https://github.com/eclipse/mosquitto/issues/1294\n[#1302]: https://github.com/eclipse/mosquitto/issues/1302\n"
  },
  {
    "path": "www/posts/2019/08/version-1-6-4-released.md",
    "content": "<!--\n.. title: Version 1.6.4 released\n.. slug: version-1-6-4-released\n.. date: 2019-08-01 18:00:00 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n## Broker\n- Fix persistent clients being incorrectly expired on Raspberry Pis.\n  Closes [#1272].\n- Windows: Allow other applications access to the log file when running.\n  Closes [#515].\n- Fix incoming QoS 2 messages being blocked when `max_inflight_messages` was\n  set to 1. Closes [#1332].\n- Fix incoming messages not being removed for a client if the topic being\n  published to does not have any subscribers. Closes [#1322].\n\n## Client library\n- Fix MQTT v5 subscription options being incorrectly set for MQTT v3\n  subscriptions. Closes [#1353].\n- Make behaviour of `mosquitto_connect_async()` consistent with\n  `mosquitto_connect()` when connecting to a non-existent server.\n  Closes [#1345].\n- `mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, ...)` was incorrectly\n  returning `MOSQ_ERR_INVAL` with valid input. This has been fixed.\n  Closes [#1360].\n- `on_connect` callback is now called with the correct v5 reason code if a v5\n  client connects to a v3.x broker and is sent a CONNACK with the\n  \"unacceptable protocol version\" connack reason code.\n- Fix memory leak when setting v5 properties in `mosquitto_connect_v5()`.\n- Fix properties not being sent on QoS>0 PUBLISH messages.\n\n## Clients\n- `mosquitto_pub`: fix error codes not being returned when `mosquitto_pub` exits.\n  Closes [#1354].\n- All clients: improve error messages when connecting to a v3.x broker when in\n  v5 mode. Closes [#1344].\n\n## Other\n- Various documentation fixes.\n\n[#515]: https://github.com/eclipse/mosquitto/issues/515\n[#1272]: https://github.com/eclipse/mosquitto/issues/1272\n[#1322]: https://github.com/eclipse/mosquitto/issues/1322\n[#1332]: https://github.com/eclipse/mosquitto/issues/1332\n[#1344]: https://github.com/eclipse/mosquitto/issues/1344\n[#1345]: https://github.com/eclipse/mosquitto/issues/1345\n[#1353]: https://github.com/eclipse/mosquitto/issues/1353\n[#1354]: https://github.com/eclipse/mosquitto/issues/1354\n[#1360]: https://github.com/eclipse/mosquitto/issues/1360\n"
  },
  {
    "path": "www/posts/2019/09/version-1-6-5-released.md",
    "content": "<!--\n.. title: Version 1.6.5 released\n.. slug: version-1-6-5-released\n.. date: 2019-09-12 15:00:00 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThis is a bugfix release.\n\n# Compatibility\n- The most recent version of libwebsockets (3.2.0) changed its behaviour and is\n  not compatible with Mosquitto. This has been fixed for the next libwebsockets\n  release. The 1.6.5 release refuses to compile with libwebsockets 3.2.0. All\n  previous versions of Mosquitto that use websockets are affected by the change\n  in behaviour.\n\n# Broker\n- Fix v5 DISCONNECT packets with remaining length == 2 being treated as a\n  protocol error. Closes [#1367].\n- Fix support for libwebsockets 3.x (excluding 3.2.0)\n- Fix slow websockets performance when sending large messages. Closes [#1390].\n- Fix bridges potentially not connecting on Windows. Closes [#478].\n- Fix clients authorised using `use_identity_as_username` or\n  `use_subject_as_username` being disconnected on SIGHUP. Closes [#1402].\n- Improve error messages in some situations when clients disconnect. Reduces\n  the number of \"Socket error on client X, disconnecting\" messages.\n- Fix Will for v5 clients not being sent if will delay interval was greater\n  than the session expiry interval. Closes [#1401].\n- Fix CRL file not being reloaded on HUP. Closes [#35].\n- Fix repeated \"Error in poll\" messages on Windows when only websockets\n  listeners are defined. Closes [#1391].\n\n# Client library\n- Fix reconnect backoff for the situation where connections are dropped rather\n  than refused. Closes [#737].\n- Fix missing locks on `mosq->state`. Closes [#1374].\n\n# Documentation\n- Improve details on global/per listener options in the mosquitto.conf man page.\n  Closes [#274].\n- Clarify behaviour when clients exceed the `message_size_limit`. Closes [#448].\n- Improve documentation for `max_inflight_bytes`, `max_inflight_messages`,\n  and `max_queued_messages`.\n\n# Build\n- Fix missing function warnings on NetBSD.\n- Fix `WITH_STATIC_LIBRARIES` using CMake on Windows. Closes [#1369].\n- Guard `ssize_t` definition on Windows. Closes [#522].\n\n\n[#35]: https://github.com/eclipse/mosquitto/issues/35\n[#274]: https://github.com/eclipse/mosquitto/issues/274\n[#448]: https://github.com/eclipse/mosquitto/issues/448\n[#478]: https://github.com/eclipse/mosquitto/issues/478\n[#522]: https://github.com/eclipse/mosquitto/issues/522\n[#737]: https://github.com/eclipse/mosquitto/issues/737\n[#1367]: https://github.com/eclipse/mosquitto/issues/1367\n[#1369]: https://github.com/eclipse/mosquitto/issues/1369\n[#1374]: https://github.com/eclipse/mosquitto/issues/1374\n[#1390]: https://github.com/eclipse/mosquitto/issues/1390\n[#1391]: https://github.com/eclipse/mosquitto/issues/1391\n[#1401]: https://github.com/eclipse/mosquitto/issues/1401\n[#1402]: https://github.com/eclipse/mosquitto/issues/1402\n"
  },
  {
    "path": "www/posts/2019/09/version-1-6-6-released.md",
    "content": "<!--\n.. title: Version 1.6.6 released\n.. slug: version-1-6-6-released\n.. date: 2018-09-27 10:36:19 UTC+01:00\n.. tags: Security,Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.6.6 and 1.5.9 have been released to address two security vulnerabilities.\n\nTitles and links will be updated once the CVE numbers are assigned.\n\n# CVE-2019-11779\n\nA vulnerability exists in Mosquitto versions 1.5 to 1.6.5 inclusive, known as\n[CVE-2019-11779].\n\nIf a client sends a SUBSCRIBE packet containing a topic that consists of\napproximately 65400 or more '/' characters, i.e. the topic hierarchy separator,\nthen a stack overflow will occur.\n\nThe issue is fixed in Mosquitto 1.6.6 and 1.5.9. Patches for older versions are\navailable at <https://mosquitto.org/files/cve/2019-11779>\n\nThe fix addresses the problem by restricting the allowed number of topic\nhierarchy levels to 200. An alternative fix is to increase the size of the\nstack by a small amount.\n\n# CVE-2019-11778\n\nA vulnerability exists in Mosquitto version 1.6 to 1.6.4 inclusive, known as\n[CVE-2019-11778]\n\nIf an MQTT v5 client connects to Mosquitto, sets a last will and testament,\nsets a will delay interval, sets a session expiry interval, and the will delay\ninterval is set longer than the session expiry interval, then a use after free\nerror occurs, which has the potential to cause a crash in some situations.\n\nThe issue is fixed in Mosquitto 1.6.5. Patches for older versions are available\nat <https://mosquitto.org/files/cve/2019-11778>\n\n# Version 1.6.6 Changes\n\nThe complete list of fixes addressed in version 1.6.6 is:\n\n## Security\n\n* Restrict topic hierarchy to 200 levels to prevent possible stack overflow.\n  Closes [#1412].\n\n## Broker\n* Restrict topic hierarchy to 200 levels to prevent possible stack overflow.\n  Closes [#1412].\n* `mosquitto_passwd` now returns 1 when attempting to update a user that does\n  not exist. Closes [#1414].\n\n[CVE-2019-11778]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11778\n[CVE-2019-11779]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11779\n[#1412]: https://github.com/eclipse/mosquitto/issues/1412\n[#1414]: https://github.com/eclipse/mosquitto/issues/1414\n"
  },
  {
    "path": "www/posts/2019/09/version-1-6-7-released.md",
    "content": "<!--\n.. title: Version 1.6.7 released.\n.. slug: version-1-6-7-released\n.. date: 2019-09-25 11:27:19 UTC+01:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.6.7 has been released, this is a bugfix release.\n\n# Broker\n- Add workaround for working with libwebsockets 3.2.0.\n- Fix potential crash when reloading config. Closes [#1424], [#1425].\n\n# Client library\n- Don't use `/` in autogenerated client ids, to avoid confusing with topics.\n- Fix `mosquitto_max_inflight_messages_set()` and `mosquitto_int_option(...,\n  MOSQ_OPT_*_MAX, ...)` behaviour. Closes [#1417].\n- Fix regression on use of `mosquitto_connect_async()` not working.\n  Closes [#1415] and [#1422].\n\n# Clients\n- mosquitto_sub: Fix `-E` incorrectly not working unless `-d` was also\n  specified. [Closes #1418].\n- Updated documentation around automatic client ids.\n\n[#1415]: https://github.com/eclipse/mosquitto/issues/1415\n[#1417]: https://github.com/eclipse/mosquitto/issues/1417\n[#1418]: https://github.com/eclipse/mosquitto/issues/1418\n[#1422]: https://github.com/eclipse/mosquitto/issues/1422\n[#1424]: https://github.com/eclipse/mosquitto/issues/1424\n[#1425]: https://github.com/eclipse/mosquitto/issues/1425\n"
  },
  {
    "path": "www/posts/2019/11/version-1-6-8-released.md",
    "content": "<!--\n.. title: Version 1.6.8 released.\n.. slug: version-1-6-8-released\n.. date: 2019-11-28 16:44:19 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.6.8 has been released, this is a bugfix release.\n\n# Broker\n- Various fixes for `allow_zero_length_clientid` config, where this option was\n  not being set correctly. Closes [#1429].\n- Fix incorrect memory tracking causing problems with `memory_limit` option.\n  Closes [#1437].\n- Fix subscription topics being limited to 200 characters instead of 200\n  hierarchy levels. Closes [#1441].\n- Only a single CRL could be loaded at once. This has been fixed.\n  Closes [#1442].\n- Fix problems with reloading config when `per_listener_settings` was true.\n  Closes [#1459].\n- Fix retained messages with an expiry interval not being expired after being\n  restored from persistence. Closes [#1464].\n- Fix messages with an expiry interval being sent without an expiry interval\n  property just before they were expired. Closes [#1464].\n- Fix TLS Websockets clients not receiving messages after taking over a\n  previous connection. Closes [#1489].\n- Fix MQTT 3.1.1 clients using clean session false, or MQTT 5.0 clients using\n  session-expiry-interval set to infinity never expiring, even when the global\n  `persistent_client_expiration` option was set. Closes [#1494].\n\n# Client library\n- Fix publish properties not being passed to `on_message_v5()` callback for QoS 2\n  messages. Closes [#1432].\n- Fix documentation issues in mosquitto.h. Closes [#1478].\n- Document `mosquitto_connect_srv()`. Closes [#1499].\n\n# Clients\n- Fix duplicate cfg definition in rr_client. Closes [#1453].\n- Fix `mosquitto_pub -l` hang when stdin stream ends. Closes [#1448].\n- Fix `mosquitto_pub -l` not sending the final line of stdin if it does not\n  end with a new line. Closes [#1473].\n- Make documentation for `mosquitto_pub -l` match reality - blank lines are\n  sent as empty messages. Closes [#1474].\n- Free memory in `mosquitto_sub` when quitting without having made a successful\n  connection. Closes [#1513].\n\n# Build\n- Added `CLIENT_STATIC_LDADD` to makefile builds to allow more libraries to be\n  linked when compiling the clients with a static libmosquitto, as required\n  for e.g. openssl on some systems.\n\n# Installer\n- Fix `mosquitto_rr.exe` not being included in Windows installers. Closes [#1463].\n\n[#1429]: https://github.com/eclipse/mosquitto/issues/1429\n[#1432]: https://github.com/eclipse/mosquitto/issues/1432\n[#1437]: https://github.com/eclipse/mosquitto/issues/1437\n[#1441]: https://github.com/eclipse/mosquitto/issues/1441\n[#1442]: https://github.com/eclipse/mosquitto/issues/1442\n[#1448]: https://github.com/eclipse/mosquitto/issues/1448\n[#1453]: https://github.com/eclipse/mosquitto/issues/1453\n[#1459]: https://github.com/eclipse/mosquitto/issues/1459\n[#1463]: https://github.com/eclipse/mosquitto/issues/1463\n[#1464]: https://github.com/eclipse/mosquitto/issues/1464\n[#1473]: https://github.com/eclipse/mosquitto/issues/1473\n[#1474]: https://github.com/eclipse/mosquitto/issues/1474\n[#1478]: https://github.com/eclipse/mosquitto/issues/1478\n[#1489]: https://github.com/eclipse/mosquitto/issues/1489\n[#1494]: https://github.com/eclipse/mosquitto/issues/1494\n[#1499]: https://github.com/eclipse/mosquitto/issues/1499\n[#1513]: https://github.com/eclipse/mosquitto/issues/1513\n"
  },
  {
    "path": "www/posts/2020/02/version-1-6-9-released.md",
    "content": "<!--\n.. title: Version 1.6.9 released.\n.. slug: version-1-6-9-released\n.. date: 2020-02-20 23:15:13 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.6.9 has been released, this is a bugfix release.\n\n# Broker\n- Fix session expiry with very large expiry intervals. Closes [#1525].\n- Check ACL patterns for validity when loading. Closes [#1539].\n- Use presence of password file as indicator for whether username checks\n  should take place, not whether usernames are defined in the password file.\n  Closes [#1545].\n- Strip whitespace from end of config file string options. Closes [#1566].\n- Satisfy valgrind when exiting on error due to not being able to open a\n  listening socket, by calling freeaddrinfo. Closes [#1565].\n- Fix `config->user` not being freed on exit. Closes [#1564].\n- Fix trailing whitespace not being trimmed on acl users. Closes [#1539].\n- Fix `bind_interface` not working for the default listener. Closes [#1533].\n- Improve password file parsing in the broker and `mosquitto_passwd`. Closes [#1584].\n- Print OpenSSL errors in more situations, like when loading certificates\n  fails. Closes [#1552].\n- Fix `mosquitto_client_protocol()` returning incorrect values.\n\n# Client library\n- Set minimum keepalive argument to `mosquitto_connect*()` to be 5 seconds.\n  Closes [#1550].\n- Fix `mosquitto_topic_matches_sub()` not returning `MOSQ_ERR_INVAL` if the\n  topic contains a wildcard. Closes [#1589].\n\n# Clients\n- Fix `--remove-retained` not obeying the `-T` option for filtering out\n  topics. Closes [#1585].\n- Default behaviour for v5 clients using `-c` is now to use infinite length\n  sessions, as with v3 clients. Closes [#1546].\n\n[#1525]: https://github.com/eclipse/mosquitto/issues/1525\n[#1533]: https://github.com/eclipse/mosquitto/issues/1533\n[#1539]: https://github.com/eclipse/mosquitto/issues/1539\n[#1539]: https://github.com/eclipse/mosquitto/issues/1539\n[#1545]: https://github.com/eclipse/mosquitto/issues/1545\n[#1546]: https://github.com/eclipse/mosquitto/issues/1546\n[#1550]: https://github.com/eclipse/mosquitto/issues/1550\n[#1552]: https://github.com/eclipse/mosquitto/issues/1552\n[#1564]: https://github.com/eclipse/mosquitto/issues/1564\n[#1565]: https://github.com/eclipse/mosquitto/issues/1565\n[#1566]: https://github.com/eclipse/mosquitto/issues/1566\n[#1584]: https://github.com/eclipse/mosquitto/issues/1584\n[#1585]: https://github.com/eclipse/mosquitto/issues/1585\n[#1589]: https://github.com/eclipse/mosquitto/issues/1589\n"
  },
  {
    "path": "www/posts/2020/05/version-1-6-10-released.md",
    "content": "<!--\n.. title: Version 1.6.10 released.\n.. slug: version-1-6-10-released\n.. date: 2020-05-25 23:45:13 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.6.10 has been released, this is a bugfix release.\n\n# Broker\n- Report invalid bridge prefix+pattern combinations at config parsing time\n  rather than letting the bridge fail later. Issue [#1635].\n- Fix `mosquitto_passwd -b` not updating passwords for existing users\n  correctly. Creating a new user with `-b` worked without problem.\n  Closes [#1664].\n- Fix memory leak when connecting clients rejected.\n- Don't disconnect clients that are already disconnected. This prevents the\n  session expiry being extended on SIGHUP. Closes [#1521].\n- Fix support for openssl 3.0.\n- Fix check when loading persistence file of a different version than the\n  native version. Closes [#1684].\n- Fix possible assert crash associated with bridge reconnecting when compiled\n  without epoll support. Closes [#1700].\n\n# Client library\n- Don't treat an unexpected PUBACK, PUBREL, or PUBCOMP as a fatal error.\n  Issue [#1629].\n- Fix support for openssl 3.0.\n- Fix memory leaks from multiple calls to\n  `mosquitto_lib_init()`/`mosquitto_lib_cleanup()`. Closes [#1691].\n- Fix documentation on return code of `mosquitto_lib_init()` for Windows.\n  Closes [#1690].\n\n# Clients\n- Fix mosquitto_sub `%j` or `%J` not working on Windows. Closes [#1674].\n\n# Build\n- Various fixes for building with below C99 support. Closes [#1622].\n- Fix use of sed on BSD. Closes [#1614].\n\n[#1521]: https://github.com/eclipse/mosquitto/issues/1521\n[#1614]: https://github.com/eclipse/mosquitto/issues/1614\n[#1622]: https://github.com/eclipse/mosquitto/issues/1622\n[#1629]: https://github.com/eclipse/mosquitto/issues/1629\n[#1635]: https://github.com/eclipse/mosquitto/issues/1635\n[#1664]: https://github.com/eclipse/mosquitto/issues/1664\n[#1674]: https://github.com/eclipse/mosquitto/issues/1674\n[#1684]: https://github.com/eclipse/mosquitto/issues/1684\n[#1690]: https://github.com/eclipse/mosquitto/issues/1690\n[#1691]: https://github.com/eclipse/mosquitto/issues/1691\n[#1700]: https://github.com/eclipse/mosquitto/issues/1700\n"
  },
  {
    "path": "www/posts/2020/06/mosquitto-ubuntu-appliance.md",
    "content": "<!--\n.. title: Mosquitto now an Ubuntu Appliance\n.. slug: mosquitto-now-an-ubuntu-appliance\n.. date: 2020-06-16 16:01:13 UTC+01:00\n.. tags:\n.. category: news\n.. link:\n.. description:\n.. type: text\n-->\n\nUbuntu has just announced their new [Ubuntu Appliance] initiative, which provides self contained images for a number of different applications, for use on a Raspberry Pi or PC. These are full Ubuntu derivatives that use snap packages, so will keep up to date with the latest releases.\n\nThere are five different applications in the first set of appliances, and Mosquitto is one of them.\n\nRead more at the [Ubuntu blog].\n\n[Ubuntu Appliance]: http://ubuntu.com/appliance\n[Ubuntu blog]: https://ubuntu.com/blog/the-ubuntu-appliance-portfolio\n"
  },
  {
    "path": "www/posts/2020/06/test-mosquitto-org-cert-updated.md",
    "content": "<!--\n.. title: test.mosquitto.org SSL cert updated\n.. slug: test-mosquitto-org-ssl-cert-updated\n.. date: 2020-06-09 12:30:13 UTC+01:00\n.. tags: Public-server\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe CA certificate and server certificate for the broker running at\n[test.mosquitto.org] has been updated to use a stronger key.\n\nThis means that if you have downloaded the CA certificate, you will need to do\nso again.\n\nLikewise, if you have used the [client certificate generator] then your\ncertificate will no longer be accepted and you must generate a new one.\n\n[test.mosquitto.org]: https://test.mosquitto.org\n[client certificate generator]: https://test.mosquitto.org/ssl/\n"
  },
  {
    "path": "www/posts/2020/08/version-1-6-11-released.md",
    "content": "<!--\n.. title: Version 1.6.11 released.\n.. slug: version-1-6-11-released\n.. date: 2020-08-11 12:09:13 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.6.11 has been released, this is a bugfix release.\n\n# Security\n- On Windows the Mosquitto service was being installed without appropriate\n  path quoting, this has been fixed. Closes [#565671].\n\n# Broker\n- Fix usage message only mentioning v3.1.1. Closes [#1713].\n- Fix broker refusing to start if only websockets listeners were defined.\n  Closes [#1740].\n- Change systemd unit files to create /var/log/mosquitto before starting.\n  Closes [#821].\n- Don't quit with an error if opening the log file isn't possible.\n  Closes [#821].\n- Fix bridge topic remapping when using \"\" as the topic. Closes [#1749].\n- Fix messages being queued for disconnected bridges when clean start was\n  set to true. Closes [#1729].\n- Fix `autosave_interval` not being triggered by messages being delivered.\n  Closes [#1726].\n- Fix websockets clients sometimes not being disconnected promptly.\n  Closes [#1718].\n- Fix \"slow\" file based logging by switching to line based buffering.\n  Closes [#1689]. Closes [#1741].\n- Log protocol error message where appropriate from a bad UNSUBSCRIBE, rather\n  than the generic \"socket error\".\n- Don't try to start DLT logging if DLT unavailable, to avoid a long delay\n  when shutting down the broker. Closes [#1735].\n- Fix potential memory leaks. Closes [#1773]. Closes [#1774].\n- Fix clients not receiving messages after a previous client with the same\n  client ID and positive will delay interval quit. Closes [#1752].\n- Fix overly broad `HAVE_PTHREAD_CANCEL` compile guard. Closes [#1547].\n\n# Client library\n- Improved documentation around connect callback return codes. Close [#1730].\n- Fix `mosquitto_publish*()` no longer returning `MOSQ_ERR_NO_CONN` when not\n  connected. Closes [#1725].\n- `mosquitto_loop_start()` now sets a thread name on Linux, FreeBSD, NetBSD,\n  and OpenBSD. Closes [#1777].\n- Fix `mosquitto_loop_stop()` not stopping on Windows. Closes [#1748]. Closes [#117].\n\n\n[#117]: https://github.com/eclipse/mosquitto/issues/117\n[#821]: https://github.com/eclipse/mosquitto/issues/821\n[#1547]: https://github.com/eclipse/mosquitto/issues/1547\n[#1689]: https://github.com/eclipse/mosquitto/issues/1689\n[#1713]: https://github.com/eclipse/mosquitto/issues/1713\n[#1718]: https://github.com/eclipse/mosquitto/issues/1718\n[#1725]: https://github.com/eclipse/mosquitto/issues/1725\n[#1726]: https://github.com/eclipse/mosquitto/issues/1726\n[#1729]: https://github.com/eclipse/mosquitto/issues/1729\n[#1730]: https://github.com/eclipse/mosquitto/issues/1730\n[#1735]: https://github.com/eclipse/mosquitto/issues/1735\n[#1740]: https://github.com/eclipse/mosquitto/issues/1740\n[#1741]: https://github.com/eclipse/mosquitto/issues/1741\n[#1748]: https://github.com/eclipse/mosquitto/issues/1748\n[#1749]: https://github.com/eclipse/mosquitto/issues/1749\n[#1752]: https://github.com/eclipse/mosquitto/issues/1752\n[#1773]: https://github.com/eclipse/mosquitto/issues/1773\n[#1774]: https://github.com/eclipse/mosquitto/issues/1774\n[#1777]: https://github.com/eclipse/mosquitto/issues/1777\n[#565671]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=565671\n"
  },
  {
    "path": "www/posts/2020/08/version-1-6-12-released.md",
    "content": "<!--\n.. title: Version 1.6.12 released.\n.. slug: version-1-6-12-released\n.. date: 2020-08-19 14:44:13 UTC+01:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nMosquitto 1.6.12 and 1.5.10 have been released.\n\n# Security\n- In some circumstances, Mosquitto could leak memory when handling PUBLISH\n  messages. This is limited to incoming QoS 2 messages, and is related\n  to the combination of the broker having persistence enabled, a clean\n  session=false client, which was connected prior to the broker restarting,\n  then has reconnected and has now sent messages at a sufficiently high rate\n  that the incoming queue at the broker has filled up and hence messages are\n  being dropped. This is more likely to have an effect where\n  `max_queued_messages` is a small value. This has now been fixed. Closes [#1793].\n\nThe following fixes apply to 1.6.12 only.\n\n# Broker\n- Build warning fixes when building with `WITH_BRIDGE=no` and `WITH_TLS=no`.\n\n# Clients\n- All clients exit with an error exit code on CONNACK failure. Closes [#1778].\n- Don't busy loop with `mosquitto_pub -l` on a slow connection.\n\n[#1778]: https://github.com/eclipse/mosquitto/issues/1778\n[#1793]: https://github.com/eclipse/mosquitto/issues/1793\n"
  },
  {
    "path": "www/posts/2020/12/version-2-0-0-released.md",
    "content": "<!--\n.. title: Version 2.0.0 released.\n.. slug: version-2-0-0-released\n.. date: 2020-12-03 16:00:00 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe Mosquitto project is happy to announce the release of version 2.0! This is\na big change with breaking behaviour changes in the broker. Users, packages and\nplugin authors should read [migrating from 1.x to 2.0] to help with the\nchanges.\n\n# Noteworthy changes\n\nMosquitto is now more secure by default and requires users to take an active\ndecision in how they configure security on their broker, instead of possibly\nrelying on the older very permissive behaviour, as well as dropping privileged\naccess more quickly. More details are in [migrating from 1.x to 2.0].\n\nA new plugin interface has been introduced which goes beyond the existing\nauthentication and access control plugin interface to offer more plugin\ncapabilities, whilst being easier to develop for and easier to extend. More\ndetails will follow. Existing plugins are still supported, although plugin\nauthors should look at [migrating from 1.x to 2.0] to ensure their plugins\nremain compatible when compiled against Mosquitto 2.0 headers.\n\nA new plugin has been introduced to provide client, group, and role based\nauthentication and access control. The plugin configuration is managed over\nspecial topics and can be updated on the fly. It provides a flexible and\nstraightforward means of configuring access to your broker. For more\ninformation, see [Dynamic Security plugin].\n\nThe broker performance has been improved, particularly for higher numbers of\nclients. We plan to run show some benchmarks to show the improvement.\n\nA new utility, `mosquitto_ctrl` has been added for controlling aspects of a\nrunning broker. At the present this is limited to controlling the dynamic\nsecurity plugin, but will be extended to other features in later releases.\n\nBridges now support MQTT v5.\n\nThe mosquitto command line clients have received a variety of small\nimprovements. mosquitto_sub can now format its output in fixed column widths,\nfor example, and filter its output randomly so you can keep an eye on the\noverall behaviour of a topic without having to see every message, for example.\n\n# Breaking changes\n- When the Mosquitto broker is run without configuring any listeners it will\n  now bind to the loopback interfaces 127.0.0.1 and/or ::1. This means that\n  only connections from the local host will be possible.\n\n  Running the broker as `mosquitto` or `mosquitto -p 1883` will bind to the\n  loopback interface.\n\n  Running the broker with a configuration file with no listeners configured\n  will bind to the loopback interface with port 1883.\n\n  Running the broker with a listener defined will bind by default to `0.0.0.0`\n  / `::` and so will be accessible from any interface. It is still possible to\n  bind to a specific address/interface.\n\n  If the broker is run as `mosquitto -c mosquitto.conf -p 1884`, and a\n  listener is defined in the configuration file, then the port defined on the\n  command line will be IGNORED, and no listener configured for it.\n- All listeners now default to `allow_anonymous false` unless explicitly set\n  to true in the configuration file. This means that when configuring a\n  listener the user must either configure an authentication and access control\n  method, or set `allow_anonymous true`. When the broker is run without a\n  configured listener, and so binds to the loopback interface, anonymous\n  connections are allowed.\n- If Mosquitto is run on as root on a unix like system, it will attempt to\n  drop privileges as soon as the configuration file has been read. This is in\n  contrast to the previous behaviour where elevated privileges were only\n  dropped after listeners had been started (and hence TLS certificates loaded)\n  and logging had been started. The change means that clients will never be\n  able to connect to the broker when it is running as root, unless the user\n  explicitly sets it to run as root, which is not advised. It also means that\n  all locations that the broker needs to access must be available to the\n  unprivileged user. In particular those people using TLS certificates from\n  Lets Encrypt will need to do something to allow Mosquitto to access\n  those certificates. An example deploy renewal hook script to help with this\n  is at `misc/letsencrypt/mosquitto-copy.sh`.\n  The user that Mosquitto will change to are the one provided in the\n  configuration, `mosquitto`, or `nobody`, in order of availability.\n- The `pid_file` option will now always attempt to write a pid file,\n  regardless of whether the `-d` argument is used when running the broker.\n- The `tls_version` option now defines the *minimum* TLS protocol version to\n  be used, rather than the exact version. Closes [#1258].\n- The `max_queued_messages` option has been increased from 100 to 1000 by\n  default, and now also applies to QoS 0 messages, when a client is connected.\n- The mosquitto_sub, mosquitto_pub, and mosquitto_rr clients will now load\n  OS provided CA certificates by default if `-L mqtts://...` is used, or if\n  the port is set to 8883 and no other CA certificates are loaded.\n- Minimum support libwebsockets version is now 2.4.0\n\n\n# Broker features\n- New plugin interface which is more flexible, easier to develop for and\n  easier to extend.\n- New dynamic security plugin, which allows clients, groups, and roles to be\n  defined and updated as the broker is running.\n- Performance improvements, particularly for higher numbers of clients.\n- When running as root, if dropping privileges to the \"mosquitto\" user fails,\n  then try \"nobody\" instead. This reduces the burden on users installing\n  Mosquitto themselves.\n- Add support for Unix domain socket listeners.\n- Add `bridge_outgoing_retain` option, to allow outgoing messages from a\n  bridge to have the retain bit completely disabled, which is useful when\n  bridging to e.g. Amazon or Google.\n- Add support for MQTT v5 bridges to handle the \"retain-available\" property\n  being false.\n- Allow MQTT v5.0 outgoing bridges to fall back to MQTT v3.1.1 if connecting\n  to a v3.x only broker.\n- DLT logging is now configurable at runtime with `log_dest dlt`.\n  Closes [#1735].\n- Add `mosquitto_plugin_publish()` function, which can be used by plugins to\n  publish messages.\n- Add `mosquitto_client_protocol_version()` function which can be used by\n  plugins to determine which version of MQTT a client has connected with.\n- Add `mosquitto_kick_client_by_clientid()` and `mosquitto_kick_client_by_username()`\n  functions, which can be used by plugins to disconnect clients.\n- Add support for handling $CONTROL/ topics in plugins.\n- Add support for PBKDF2-SHA512 password hashing.\n- Enabling certificate based TLS encryption is now through certfile and\n  keyfile, not capath or cafile.\n- Added support for controlling UNSUBSCRIBE calls in v5 plugin ACL checks.\n- Add \"deny\" acl type. Closes [#1611].\n- The broker now sends the receive-maximum property for MQTT v5 CONNACKs.\n- Add the `bridge_max_packet_size` option. Closes [#265].\n- Add the `bridge_bind_address` option. Closes [#1311].\n- TLS certificates for the server are now reloaded on SIGHUP.\n- Default for max_queued_messages has been changed to 1000.\n- Add `ciphers_tls1.3` option, to allow setting TLS v1.3 ciphersuites.\n  Closes [#1825].\n- Bridges now obey MQTT v5 server-keepalive.\n- Add bridge support for the MQTT v5 maximum-qos property.\n- Log client port on new connections. Closes [#1911].\n\n# Broker fixes\n- Send DISCONNECT with `malformed-packet` reason code on invalid PUBLISH,\n  SUBSCRIBE, and UNSUBSCRIBE packets.\n- Document that X509_free() must be called after using\n  mosquitto_client_certificate(). Closes [#1842].\n- Fix listener not being reassociated with client when reloading a persistence\n  file and `per_listener_settings true` is set and the client did not set a\n  username. Closes [#1891].\n- Fix bridge sock not being removed from sock hash on error. Closes [#1897].\n- mosquitto_password now forbids the : character. Closes [#1833].\n- Fix `log_timestamp_format` not applying to `log_dest topic`. Closes [#1862].\n- Fix crash on Windows if loading a plugin fails. Closes [#1866].\n- Fix file logging on Windows. Closes [#1880].\n- Report an error if the config file is set to a directory. Closes [#1814].\n- Fix bridges incorrectly setting Wills to manage remote notifications when\n  `notifications_local_only` was set true. Closes [#1902].\n\n# Client library features\n- Client no longer generates random client ids for v3.1.1 clients, these are\n  now expected to be generated on the broker. This matches the behaviour for\n  v5 clients. Closes [#291].\n- Add support for connecting to brokers through Unix domain sockets.\n- Add `mosquitto_property_identifier()`, for retrieving the identifier integer\n  for a property.\n- Add `mosquitto_property_identifier_to_string()` for converting a property\n  identifier integer to the corresponding property name string.\n- Add `mosquitto_property_next()` to retrieve the next property in a list, for\n  iterating over property lists.\n- mosquitto_pub now handles the MQTT v5 retain-available property by never\n  setting the retain bit.\n- Added MOSQ_OPT_TCP_NODELAY, to allow disabling Nagle's algorithm on client\n  sockets. Closes [#1526].\n- Add `mosquitto_ssl_get()` to allow clients to access their SSL structure and\n  perform additional verification.\n- Add MOSQ_OPT_BIND_ADDRESS to allow setting of a bind address independently\n  of the `mosquitto_connect*()` call.\n- Add `MOSQ_OPT_TLS_USE_OS_CERTS` option, to instruct the client to load and\n  trust OS provided CA certificates for use with TLS connections.\n\n# Client library fixes\n- Fix send quota being incorrectly reset on reconnect. Closes [#1822].\n- Don't use logging until log mutex is initialised. Closes [#1819].\n- Fix missing mach/mach_time.h header on OS X. Closes [#1831].\n- Fix connect properties not being sent when the client automatically\n  reconnects. Closes [#1846].\n\n# Client features\n- Add timeout return code (27) for `mosquitto_sub -W <secs>` and\n  `mosquitto_rr -W <secs>`. Closes [#275].\n- Add support for connecting to brokers through Unix domain sockets with the\n  `--unix` argument.\n- Use cJSON library for producing JSON output, where available. Closes [#1222].\n- Add support for outputting MQTT v5 property information to mosquitto_sub/rr\n  JSON output. Closes [#1416].\n- Add `--pretty` option to mosquitto_sub/rr for formatted/unformatted JSON\n  output.\n- Add support for v5 property printing to mosquitto_sub/rr in non-JSON mode.\n  Closes [#1416].\n- Add `--nodelay` to all clients to allow them to use the MOSQ_OPT_TCP_NODELAY\n  option.\n- Add `-x` to all clients to all the session-expiry-interval property to be\n  easily set for MQTT v5 clients.\n- Add `--random-filter` to mosquitto_sub, to allow only a certain proportion\n  of received messages to be printed.\n- mosquitto_sub %j and %J timestamps are now in a ISO 8601 compatible format.\n- mosquitto_sub now supports extra format specifiers for field width and\n  precision for some parameters.\n- Add `--version` for all clients.\n- All clients now load OS provided CA certificates if used with `-L\n  mqtts://...`, or if port is set to 8883 and no other CA certificates are\n  used. Closes [#1824].\n- Add the `--tls-use-os-certs` option to all clients.\n\n# Client fixes\n- mosquitto_sub will now exit if all subscriptions were denied.\n- mosquitto_pub now sends 0 length files without an error when using `-f`.\n- Fix description of `-e` and `-t` arguments in mosquitto_rr. Closes [#1881].\n- mosquitto_sub will now quit with an error if the %U option is used on\n  Windows, rather than just quitting. Closes [#1908].\n\n[migrating from 1.x to 2.0]:/documentation/migrating-to-2-0/\n[#265]: https://github.com/eclipse/mosquitto/issues/265\n[#275]: https://github.com/eclipse/mosquitto/issues/275\n[#291]: https://github.com/eclipse/mosquitto/issues/291\n[#1222]: https://github.com/eclipse/mosquitto/issues/1222\n[#1258]: https://github.com/eclipse/mosquitto/issues/1258\n[#1311]: https://github.com/eclipse/mosquitto/issues/1311\n[#1416]: https://github.com/eclipse/mosquitto/issues/1416\n[#1526]: https://github.com/eclipse/mosquitto/issues/1526\n[#1611]: https://github.com/eclipse/mosquitto/issues/1611\n[#1735]: https://github.com/eclipse/mosquitto/issues/1735\n[#1814]: https://github.com/eclipse/mosquitto/issues/1814\n[#1819]: https://github.com/eclipse/mosquitto/issues/1819\n[#1822]: https://github.com/eclipse/mosquitto/issues/1822\n[#1824]: https://github.com/eclipse/mosquitto/issues/1824\n[#1825]: https://github.com/eclipse/mosquitto/issues/1825\n[#1831]: https://github.com/eclipse/mosquitto/issues/1831\n[#1833]: https://github.com/eclipse/mosquitto/issues/1833\n[#1842]: https://github.com/eclipse/mosquitto/issues/1842\n[#1846]: https://github.com/eclipse/mosquitto/issues/1846\n[#1862]: https://github.com/eclipse/mosquitto/issues/1862\n[#1866]: https://github.com/eclipse/mosquitto/issues/1866\n[#1880]: https://github.com/eclipse/mosquitto/issues/1880\n[#1881]: https://github.com/eclipse/mosquitto/issues/1881\n[#1891]: https://github.com/eclipse/mosquitto/issues/1891\n[#1897]: https://github.com/eclipse/mosquitto/issues/1897\n[#1902]: https://github.com/eclipse/mosquitto/issues/1902\n[#1908]: https://github.com/eclipse/mosquitto/issues/1908\n[#1911]: https://github.com/eclipse/mosquitto/issues/1911\n"
  },
  {
    "path": "www/posts/2020/12/version-2-0-2-released.md",
    "content": "<!--\n.. title: Version 2.0.2 released.\n.. slug: version-2-0-2-released\n.. date: 2020-12-10 14:33:16 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.2 and 2.0.1 of Mosquitto has been released. These are bugfix releases.\n\nVersion 2.0.2 fixes a build regression introduced in 2.0.1 when websockets\nsupport was enabled on non-Linux systems.\n\nThe 2.0.1 changes are below.\n\n# Broker\n- Fix websockets connections on Windows blocking subsequent connections.\n  Closes [#1934].\n- Fix DH group not being set for TLS connections, which meant ciphers using\n  DHE couldn't be used. Closes [#1925]. Closes [#1476].\n- Fix websockets listeners not causing the main loop not to wake up.\n  Closes [#1936].\n\n# Client library\n- Fix DH group not being set for TLS connections, which meant ciphers using\n  DHE couldn't be used. Closes [#1925]. Closes [#1476].\n\n# Apps\n- Fix `mosquitto_passwd -U`\n\n# Build\n- Fix cjson include paths.\n- Fix build using WITH_TLS=no when the openssl headers aren't available.\n- Distribute cmake/ and snap/ directories in tar.\n\n\n[#1476]: https://github.com/eclipse/mosquitto/issues/1476\n[#1925]: https://github.com/eclipse/mosquitto/issues/1925\n[#1934]: https://github.com/eclipse/mosquitto/issues/1934\n[#1936]: https://github.com/eclipse/mosquitto/issues/1936\n"
  },
  {
    "path": "www/posts/2020/12/version-2-0-3-released.md",
    "content": "<!--\n.. title: Version 2.0.3 released.\n.. slug: version-2-0-3-released\n.. date: 2020-12-17 14:22:16 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.3 of Mosquitto has been released. This is a bugfix release.\n\n# Security\n- Running mosquitto_passwd with the following arguments only\n  `mosquitto_passwd -b password_file username password` would cause the\n  username to be used as the password.\n\n# Broker\n- Fix excessive CPU use on non-Linux systems when the open file limit is set\n  high. Closes [#1947].\n- Fix LWT not being sent on client takeover when the existing session wasn't\n  being continued. Closes [#1946].\n- Fix bridges possibly not completing connections when WITH_ADNS is in use.\n  Closes [#1960].\n- Fix QoS 0 messages not being delivered if max_queued_messages was set to 0.\n  Closes [#1956].\n- Fix local bridges being disconnected on SIGHUP. Closes [#1942].\n- Fix slow initial bridge connections for WITH_ADNS=no.\n\n# Clients\n- Fix mosquitto_sub being unable to terminate with Ctrl-C if a successful\n  connection is not made. Closes [#1957].\n\n# Apps\n- Fix `mosquitto_passwd -b` using username as password (not if `-c` is also\n  used). Closes [#1949].\n\n# Build\n- Fix `install` target when using WITH_CJSON=no. Closes [#1938].\n- Fix `generic` docker build. Closes [#1945].\n\n[#1938]: https://github.com/eclipse/mosquitto/issues/1938\n[#1942]: https://github.com/eclipse/mosquitto/issues/1942\n[#1945]: https://github.com/eclipse/mosquitto/issues/1945\n[#1946]: https://github.com/eclipse/mosquitto/issues/1946\n[#1947]: https://github.com/eclipse/mosquitto/issues/1947\n[#1949]: https://github.com/eclipse/mosquitto/issues/1949\n[#1956]: https://github.com/eclipse/mosquitto/issues/1956\n[#1957]: https://github.com/eclipse/mosquitto/issues/1957\n[#1960]: https://github.com/eclipse/mosquitto/issues/1960\n"
  },
  {
    "path": "www/posts/2020/12/version-2-0-4-released.md",
    "content": "<!--\n.. title: Version 2.0.4 released.\n.. slug: version-2-0-4-released\n.. date: 2020-12-17 14:22:16 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.4 of Mosquitto has been released. This is a bugfix release.\n\n# Broker\n- Fix $SYS/broker/publish/messages/+ counters not being updated for QoS 1, 2\n  messages. Closes [#1968].\n- `mosquitto_connect_bind_async()` and `mosquitto_connect_bind_v5()` should not\n  reset the bind address option if called with `bind_address == NULL`.\n- Fix dynamic security configuration possibly not being reloaded on Windows\n  only. Closes [#1962].\n- Add more log messages for dynsec load/save error conditions.\n- Fix websockets connections blocking non-websockets connections on Windows.\n  Closes [#1934].\n\n# Build\n- Fix man pages not being built when using CMake. Closes [#1969].\n\n[#1934]: https://github.com/eclipse/mosquitto/issues/1934\n[#1962]: https://github.com/eclipse/mosquitto/issues/1962\n[#1968]: https://github.com/eclipse/mosquitto/issues/1968\n[#1969]: https://github.com/eclipse/mosquitto/issues/1969\n"
  },
  {
    "path": "www/posts/2021/01/version-2-0-5-released.md",
    "content": "<!--\n.. title: Version 2.0.5 released.\n.. slug: version-2-0-5-released\n.. date: 2021-01-11 10:29:16 UTC+00:00\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.5 of Mosquitto has been released. This is a bugfix release.\n\n# Broker\n- Fix `auth_method` not being provided to the extended auth plugin event.\n  Closes [#1975].\n- Fix large packets not being completely published to slow clients.\n  Closes [#1977].\n- Fix bridge connection not relinquishing POLLOUT after messages are sent.\n  Closes [#1979].\n- Fix apparmor incorrectly denying access to\n  /var/lib/mosquitto/mosquitto.db.new. Closes [#1978].\n- Fix potential intermittent initial bridge connections when using poll().\n- Fix `bind_interface` option. Closes [#1999].\n- Fix invalid behaviour in dynsec plugin if a group or client is deleted\n  before a role that was attached to the group or client is deleted.\n  Closes [#1998].\n- Improve logging in dynsec addGroupRole command. Closes [#2005].\n- Improve logging in dynsec addGroupClient command. Closes [#2008].\n\n# Client library\n- Improve documentation around the `_v5()` and non-v5 functions, e.g.\n  `mosquitto_publish()` and `mosquitto_publish_v5().\n\n# Build\n- `install` Makefile target should depend on `all`, not `mosquitto`, to ensure\n  that man pages are always built. Closes [#1989].\n- Fixes for lots of minor build warnings highlighted by Visual Studio.\n\n# Apps\n- Disallow control characters in mosquitto_passwd usernames.\n- Fix incorrect description in mosquitto_ctrl man page. Closes [#1995].\n- Fix `mosquitto_ctrl dynsec getGroup` not showing roles. Closes [#1997].\n\n\n[#1975]: https://github.com/eclipse/mosquitto/issues/1975\n[#1977]: https://github.com/eclipse/mosquitto/issues/1977\n[#1978]: https://github.com/eclipse/mosquitto/issues/1978\n[#1979]: https://github.com/eclipse/mosquitto/issues/1979\n[#1989]: https://github.com/eclipse/mosquitto/issues/1989\n[#1995]: https://github.com/eclipse/mosquitto/issues/1995\n[#1997]: https://github.com/eclipse/mosquitto/issues/1997\n[#1998]: https://github.com/eclipse/mosquitto/issues/1998\n[#1999]: https://github.com/eclipse/mosquitto/issues/1999\n[#2005]: https://github.com/eclipse/mosquitto/issues/2005\n[#2008]: https://github.com/eclipse/mosquitto/issues/2008\n"
  },
  {
    "path": "www/posts/2021/01/version-2-0-6-released.md",
    "content": "<!--\n.. title: Version 2.0.6 released.\n.. slug: version-2-0-6-released\n.. date: 2021-01-28 12:24:38 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.6 of Mosquitto has been released. This is a bugfix release.\n\n# Broker\n- Fix calculation of remaining length parameter for websockets clients that\n  send fragmented packets. Closes [#1974].\n- Fix potential duplicate Will messages being sent when a will delay interval\n  has been set.\n- Fix message expiry interval property not being honoured in\n  `mosquitto_broker_publish` and `mosquitto_broker_publish_copy`.\n- Fix websockets listeners with TLS not responding. Closes [#2020].\n- Add notes that libsystemd-dev or similar is needed if building with systemd\n  support. Closes [#2019].\n- Improve logging in obscure cases when a client disconnects. Closes [#2017].\n- Fix reloading of listeners where multiple listeners have been defined with\n  the same port but different bind addresses. Closes [#2029].\n- Fix `message_size_limit` not applying to the Will payload. Closes [#2022].\n- The error topic-alias-invalid was being sent if an MQTT v5 client published\n  a message with empty topic and topic alias set, but the topic alias hadn't\n  already been configured on the broker. This has been fixed to send a\n  protocol error, as per section 3.3.4 of the specification.\n- Note in the man pages that SIGHUP reloads TLS certificates. Closes [#2037].\n- Fix bridges not always connecting on Windows. Closes [#2043].\n\n# Apps\n- Allow command line arguments to override config file options in\n  mosquitto_ctrl. Closes [#2010].\n- mosquitto_ctrl: produce an error when requesting a new password if both\n  attempts do not match. Closes [#2011].\n\n# Build\n- Fix cmake builds using `WITH_CJSON=no` not working if cJSON not found.\n  Closes [#2026].\n\n# Other\n- The SPDX identifiers for EDL-1.0 have been changed to BSD-3-Clause as per\n  The Eclipse legal documentation generator. The licenses are identical.\n\n[#1974]: https://github.com/eclipse/mosquitto/issues/1974\n[#2010]: https://github.com/eclipse/mosquitto/issues/2010\n[#2011]: https://github.com/eclipse/mosquitto/issues/2011\n[#2017]: https://github.com/eclipse/mosquitto/issues/2017\n[#2019]: https://github.com/eclipse/mosquitto/issues/2019\n[#2020]: https://github.com/eclipse/mosquitto/issues/2020\n[#2022]: https://github.com/eclipse/mosquitto/issues/2022\n[#2026]: https://github.com/eclipse/mosquitto/issues/2026\n[#2029]: https://github.com/eclipse/mosquitto/issues/2029\n[#2037]: https://github.com/eclipse/mosquitto/issues/2037\n[#2043]: https://github.com/eclipse/mosquitto/issues/2043\n"
  },
  {
    "path": "www/posts/2021/02/version-2-0-7-released.md",
    "content": "<!--\n.. title: Version 2.0.7 released.\n.. slug: version-2-0-7-released\n.. date: 2021-02-04 10:02:38 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.7 and 1.6.13 of Mosquitto have been released. These are bugfix releases.\n\n# 2.0.7\n\n## Broker\n- Fix exporting of executable symbols on BSD when building via makefile.\n- Fix some minor memory leaks on exit only.\n- Fix possible memory leak on connect. Closes [#2057].\n- Fix openssl engine not being able to load private key. Closes [#2066].\n\n## Clients\n- Fix config files truncating options after the first space. Closes [#2059].\n\n## Build\n- Fix man page building to not absolutely require xsltproc when using CMake.\n  This now handles the case where we are building from the released tar, or\n  building from git if xsltproc is available, or building from git if xsltproc\n  is not available.\n\n\n# 1.6.13\n\n## Broker:\n- Fix crash on Windows if loading a plugin fails. Closes [#1866].\n- Fix DH group not being set for TLS connections, which meant ciphers using\n  DHE couldn't be used. Closes [#1925]. Closes [#1476].\n- Fix local bridges being disconnected on SIGHUP. Closes [#1942].\n- Fix $SYS/broker/publish/messages/+ counters not being updated for QoS 1, 2\n  messages. Closes [#1968].\n- Fix listener not being reassociated with client when reloading a persistence\n  file and `per_listener_settings true` is set and the client did not set a\n  username. Closes [#1891].\n- Fix file logging on Windows. Closes [#1880].\n- Fix bridge sock not being removed from sock hash on error. Closes [#1897].\n\n## Client library:\n- Fix build on Mac Big Sur. Closes [#1905].\n- Fix DH group not being set for TLS connections, which meant ciphers using\n  DHE couldn't be used. Closes [#1925]. Closes [#1476].\n\n## Clients:\n- mosquitto_sub will now quit with an error if the %U option is used on\n  Windows, rather than just quitting. Closes [#1908].\n- Fix config files truncating options after the first space. Closes [#2059].\n\n## Apps:\n- Perform stricter parsing of input username in mosquitto_passwd. Closes\n  [#570126] (Eclipse bugzilla).\n\n## Build:\n- Enable epoll support in CMake builds.\n\n[#1476]: https://github.com/eclipse/mosquitto/issues/1476\n[#1866]: https://github.com/eclipse/mosquitto/issues/1866\n[#1880]: https://github.com/eclipse/mosquitto/issues/1880\n[#1891]: https://github.com/eclipse/mosquitto/issues/1891\n[#1897]: https://github.com/eclipse/mosquitto/issues/1897\n[#1905]: https://github.com/eclipse/mosquitto/issues/1905\n[#1908]: https://github.com/eclipse/mosquitto/issues/1908\n[#1925]: https://github.com/eclipse/mosquitto/issues/1925\n[#1942]: https://github.com/eclipse/mosquitto/issues/1942\n[#1968]: https://github.com/eclipse/mosquitto/issues/1968\n[#2057]: https://github.com/eclipse/mosquitto/issues/2057\n[#2059]: https://github.com/eclipse/mosquitto/issues/2059\n[#2066]: https://github.com/eclipse/mosquitto/issues/2066\n[#570126]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=570126\n"
  },
  {
    "path": "www/posts/2021/02/version-2-0-8-released.md",
    "content": "<!--\n.. title: Version 2.0.8 released.\n.. slug: version-2-0-8-released\n.. date: 2021-02-25 17:19:38 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.8 of Mosquitto has been released. This is a bugfix release.\n\n# Broker\n- Fix incorrect datatypes in `struct mosquitto_evt_tick`. This changes the\n  size and offset of two of the members of this struct, and changes the size\n  of the struct. This is an ABI break, but is considered to be acceptable\n  because plugins should never be allocating their own instance of this\n  struct, and currently none of the struct members are used for anything, so a\n  plugin should not be accessing them. It would also be safe to read/write\n  from the existing struct parameters.\n- Give compile time warning if libwebsockets compiled without external poll\n  support. Closes [#2060].\n- Fix memory tracking not being available on FreeBSD or macOS. Closes [#2096].\n\n# Client library\n- Fix `mosquitto_{pub|sub}_topic_check()` functions not returning `MOSQ_ERR_INVAL`\n  on topic == NULL.\n\n# Clients\n- Fix possible loss of data in `mosquitto_pub -l` when sending multiple long\n  lines. Closes [#2078].\n\n# Build\n- Provide a mechanism for Docker users to run a broker that doesn't use\n  authentication, without having to provide their own configuration file.\n  Closes [#2040].\n\n[#2040]: https://github.com/eclipse/mosquitto/issues/2040\n[#2060]: https://github.com/eclipse/mosquitto/issues/2060\n[#2078]: https://github.com/eclipse/mosquitto/issues/2078\n[#2096]: https://github.com/eclipse/mosquitto/issues/2096\n"
  },
  {
    "path": "www/posts/2021/03/version-2-0-9-released.md",
    "content": "<!--\n.. title: Version 2.0.9 released.\n.. slug: version-2-0-9-released\n.. date: 2021-03-11 22:19:38 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersions 2.0.9, 1.6.14, and 1.5.11 of Mosquitto have been released. These are\nbugfix releases and include a minor security fix.\n\n# 2.0.9\n\n## Security\n- If an empty or invalid CA file was provided to the client library for\n  verifying the remote broker, then the initial connection would fail but\n  subsequent connections would succeed without verifying the remote broker\n  certificate. Closes [#2130].\n- If an empty or invalid CA file was provided to the broker for verifying the\n  remote broker for an outgoing bridge connection then the initial connection\n  would fail but subsequent connections would succeed without verifying the\n  remote broker certificate. Closes [#2130].\n\n## Broker\n- Fix encrypted bridge connections incorrectly connecting when `bridge_cafile`\n  is empty or invalid. Closes [#2130].\n- Fix `tls_version` behaviour not matching documentation. It was setting the\n  exact TLS version to use, not the minimum TLS version to use. Closes [#2110].\n- Fix messages to `$` prefixed topics being rejected. Closes [#2111].\n- Fix QoS 0 messages not being delivered when max_queued_bytes was configured.\n  Closes [#2123].\n- Fix bridge increasing backoff calculation.\n- Improve handling of invalid combinations of listener address and bind\n  interface configurations. Closes [#2081].\n- Fix `max_keepalive` option not applying to clients connecting with keepalive\n  set to 0. Closes [#2117].\n\n## Client library\n- Fix encrypted connections incorrectly connecting when the CA file passed to\n  `mosquitto_tls_set()` is empty or invalid. Closes [#2130].\n- Fix connections retrying very rapidly in some situations.\n\n## Build\n- Fix cmake epoll detection.\n\n# 1.6.14\n\n## Security\n- If an empty or invalid CA file was provided to the client library for\n  verifying the remote broker, then the initial connection would fail but\n  subsequent connections would succeed without verifying the remote broker\n  certificate. Closes [#2130].\n- If an empty or invalid CA file was provided to the broker for verifying the\n  remote broker for an outgoing bridge connection then the initial connection\n  would fail but subsequent connections would succeed without verifying the\n  remote broker certificate. Closes [#2130].\n\n## Broker\n- Fix encrypted bridge connections incorrectly connecting when `bridge_cafile`\n  is empty or invalid. Closes [#2130].\n\n## Client library\n- Fix encrypted connections incorrectly connecting when the CA file passed to\n  `mosquitto_tls_set()` is empty or invalid. Closes [#2130].\n- Fix connections retrying very rapidly in some situations.\n\n## Clients\n- Fix possible loss of data in `mosquitto_pub -l` when sending multiple long\n  lines. Closes [#2078].\n\n# 1.5.11\n\n## Security\n- If an empty or invalid CA file was provided to the client library for\n  verifying the remote broker, then the initial connection would fail but\n  subsequent connections would succeed without verifying the remote broker\n  certificate. Closes [#2130].\n- If an empty or invalid CA file was provided to the broker for verifying the\n  remote broker for an outgoing bridge connection then the initial connection\n  would fail but subsequent connections would succeed without verifying the\n  remote broker certificate. Closes [#2130].\n\n## Broker\n- Fix encrypted bridge connections incorrectly connecting when `bridge_cafile`\n  is empty or invalid. Closes [#2130].\n\n## Client library\n- Fix encrypted connections incorrectly connecting when the CA file passed to\n  `mosquitto_tls_set()` is empty or invalid. Closes [#2130].\n\n[#2040]: https://github.com/eclipse/mosquitto/issues/2040\n[#2078]: https://github.com/eclipse/mosquitto/issues/2078\n[#2081]: https://github.com/eclipse/mosquitto/issues/2081\n[#2110]: https://github.com/eclipse/mosquitto/issues/2110\n[#2111]: https://github.com/eclipse/mosquitto/issues/2111\n[#2117]: https://github.com/eclipse/mosquitto/issues/2117\n[#2123]: https://github.com/eclipse/mosquitto/issues/2123\n[#2130]: https://github.com/eclipse/mosquitto/issues/2130\n"
  },
  {
    "path": "www/posts/2021/04/version-2-0-10-released.md",
    "content": "<!--\n.. title: Version 2.0.10 released.\n.. slug: version-2-0-10-released\n.. date: 2021-04-03 11:54:38 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersions 2.0.10 of Mosquitto has been released. This is a security and bugfix\nrelease.\n\n# Security\n- [CVE-2021-23980]: If an authenticated client connected with MQTT v5 sent a\n  malformed CONNACK message to the broker a NULL pointer dereference occurred,\n  most likely resulting in a segfault. This will be updated with the CVE\n  number when it is assigned.\n  Affects versions 2.0.0 to 2.0.9 inclusive.\n\n# Broker\n- Don't overwrite new receive-maximum if a v5 client connects and takes over\n  an old session. Closes [#2134].\n- Fix CVE-xxxx-xxxx. Closes [#2163].\n\n# Clients\n- Set `receive-maximum` to not exceed the `-C` message count in mosquitto_sub\n  and mosquitto_rr, to avoid potentially lost messages. Closes [#2134].\n- Fix TLS-PSK mode not working with port 8883. Closes [#2152].\n\n# Client library\n- Fix possible socket leak. This would occur if a client was using\n  `mosquitto_loop_start()`, then if the connection failed due to the remote\n  server being inaccessible they called `mosquitto_loop_stop(, true)` and\n  recreated the mosquitto object.\n\n# Build\n- A variety of minor build related fixes, like functions not having previous\n  declarations.\n- Fix CMake cross compile builds not finding opensslconf.h. Closes [#2160].\n- Fix build on Solaris non-sparc. Closes [#2136].\n\n[CVE-2021-23980]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-28166\n[#2134]: https://github.com/eclipse/mosquitto/issues/2134\n[#2136]: https://github.com/eclipse/mosquitto/issues/2136\n[#2152]: https://github.com/eclipse/mosquitto/issues/2152\n[#2160]: https://github.com/eclipse/mosquitto/issues/2160\n[#2163]: https://github.com/eclipse/mosquitto/issues/2163\n"
  },
  {
    "path": "www/posts/2021/06/version-2-0-11-released.md",
    "content": "<!--\n.. title: Version 2.0.11 released.\n.. slug: version-2-0-11-released\n.. date: 2021-06-08 11:54:38 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersions 2.0.11 and 1.6.15 of Mosquitto has been released. These are a security\nand bugfix releases.\n\n# 2.0.11\n\n## Security\n- If an authenticated client connected with MQTT v5 sent a crafted CONNECT\n  message to the broker a memory leak would occur.\n  Affects versions 1.6 to 2.0.10 inclusive.\n\n## Broker\n- Fix possible crash having just upgraded from 1.6 if `per_listener_settings\n  true` is set, and a SIGHUP is sent to the broker before a client has\n  reconnected to the broker. Closes [#2167].\n- Fix bridge not reconnectng if the first reconnection attempt fails.\n  Closes [#2207].\n- Improve QoS 0 outgoing packet queueing.\n- Fix non-reachable bridge blocking the broker on Windows. Closes #2172.\n- Fix possible corruption of pollfd array on Windows when bridges were\n  reconnecting. Closes [#2173].\n- Fix QoS 0 messages not being queued when `queue_qos0_messages` was enabled.\n  Closes [#2224].\n\n## Clients\n- If sending mosquitto_sub output to a pipe, mosquitto_sub will now detect\n  that the pipe has closed and disconnect. Closes [#2164].\n- Fix `mosquitto_pub -l` quitting if a message publication is attempted when\n  the broker is temporarily unavailable. Closes [#2187].\n\n# 1.6.15\n\n## Security\n- If an authenticated client connected with MQTT v5 sent a crafted CONNECT\n  message to the broker a memory leak would occur.\n  Affects versions 1.6 to 2.0.10 inclusive.\n\n[#2164]: https://github.com/eclipse/mosquitto/issues/2164\n[#2167]: https://github.com/eclipse/mosquitto/issues/2167\n[#2172]: https://github.com/eclipse/mosquitto/issues/2172\n[#2173]: https://github.com/eclipse/mosquitto/issues/2173\n[#2187]: https://github.com/eclipse/mosquitto/issues/2187\n[#2207]: https://github.com/eclipse/mosquitto/issues/2207\n[#2224]: https://github.com/eclipse/mosquitto/issues/2224\n"
  },
  {
    "path": "www/posts/2021/08/version-2-0-12-released.md",
    "content": "<!--\n.. title: Version 2.0.12 released.\n.. slug: version-2-0-12-released\n.. date: 2021-08-31 17:16:38 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersions 2.0.12 of Mosquitto has been released. This is a security\nand bugfix release.\n\n# Security\n- An MQTT v5 client connecting with a large number of user-property properties\n  could cause excessive CPU usage, leading to a loss of performance and\n  possible denial of service. This has been fixed.\n- Fix `max_keepalive` not applying to MQTT v3.1.1 and v3.1 connections.\n  These clients are now rejected if their keepalive value exceeds\n  max_keepalive. This option allows [CVE-2020-13849], which is for the MQTT\n  v3.1.1 protocol itself rather than an implementation, to be addressed.\n- Using certain listener related configuration options e.g. `cafile`, that\n  apply to the default listener without defining any listener would cause a\n  remotely accessible listener to be opened that was not confined to the local\n  machine but did have anonymous access enabled, contrary to the\n  documentation. This has been fixed. Closes [#2283].\n- [CVE-2021-34434]: If a plugin had granted ACL subscription access to a\n  durable/non-clean-session client, then removed that access, the client would\n  keep its existing subscription. This has been fixed.\n- Incoming QoS 2 messages that had not completed the QoS flow were not being\n  checked for ACL access when a clean session=False client was reconnecting.\n  This has been fixed.\n\n# Broker\n- Fix possible out of bounds memory reads when reading a corrupt/crafted\n  configuration file. Unless your configuration file is writable by untrusted\n  users this is not a risk. Closes [#567213].\n- Fix `max_connections` option not being correctly counted.\n- Fix TLS certificates and TLS-PSK not being able to be configured at the same\n  time.\n- Disable TLS v1.3 when using TLS-PSK, because it isn't correctly configured.\n- Fix `max_keepalive` not applying to MQTT v3.1.1 and v3.1 connections.\n  These clients are now rejected if their keepalive value exceeds\n  `max_keepalive`. This option allows CVE-2020-13849, which is for the MQTT\n  v3.1.1 protocol itself rather than an implementation, to be addressed.\n- Fix broker not quitting if e.g. the `password_file` is specified as a\n  directory. Closes [#2241].\n- Fix listener `mount_point` not being removed on outgoing messages.\n  Closes [#2244].\n- Strict protocol compliance fixes, plus test suite.\n- Fix $share subscriptions not being recovered for durable clients that\n  reconnect.\n- Update plugin configuration documentation. Closes [#2286].\n\n# Client library\n- If a client uses TLS-PSK then force the default cipher list to use \"PSK\"\n  ciphers only. This means that a client connecting to a broker configured\n  with x509 certificates only will now fail. Prior to this, the client would\n  connect successfully without verifying certificates, because they were not\n  configured.\n- Disable TLS v1.3 when using TLS-PSK, because it isn't correctly configured.\n- Threaded mode is deconfigured when the `mosquitto_loop_start()` thread ends,\n  which allows `mosquitto_loop_start()` to be called again. Closes [#2242].\n- Fix `MOSQ_OPT_SSL_CTX` not being able to be set to NULL. Closes [#2289].\n- Fix reconnecting failing when `MOSQ_OPT_TLS_USE_OS_CERTS` was in use, but none\n  of `capath`, `cafile`, `psk`, nor `MOSQ_OPT_SSL_CTX` were set, and\n  `MOSQ_OPT_SSL_CTX_WITH_DEFAULTS` was set to the default value of true.\n  Closes [#2288].\n\n# Apps\n- Fix `mosquitto_ctrl dynsec setDefaultACLAccess` command not working.\n\n# Clients\n- `mosquitto_sub` and `mosquitto_rr` now open stdout in binary mode on Windows\n  so binary payloads are not modified when printing.\n- Document TLS certificate behaviour when using `-p 8883`.\n\n# Build\n- Fix installation using `WITH_TLS=no`. Closes [#2281].\n- Fix builds with libressl 3.4.0. Closes [#2198].\n- Remove some unnecessary code guards related to libressl.\n- Fix printf format build warning on MIPS. Closes [#2271].\n\n[#2198]: https://github.com/eclipse/mosquitto/issues/2198\n[#2241]: https://github.com/eclipse/mosquitto/issues/2241\n[#2242]: https://github.com/eclipse/mosquitto/issues/2242\n[#2244]: https://github.com/eclipse/mosquitto/issues/2244\n[#2271]: https://github.com/eclipse/mosquitto/issues/2271\n[#2281]: https://github.com/eclipse/mosquitto/issues/2281\n[#2286]: https://github.com/eclipse/mosquitto/issues/2286\n[#2288]: https://github.com/eclipse/mosquitto/issues/2288\n[#2289]: https://github.com/eclipse/mosquitto/issues/2289\n[#567213]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=567213\n[CVE-2020-13849]: https://nvd.nist.gov/vuln/detail/CVE-2020-13849\n[CVE-2021-34434]: https://nvd.nist.gov/vuln/detail/CVE-2021-34434\n"
  },
  {
    "path": "www/posts/2021/10/version-2-0-13-released.md",
    "content": "<!--\n.. title: Version 2.0.13 released.\n.. slug: version-2-0-13-released\n.. date: 2021-10-27 16:35:38 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.13 of Mosquitto has been released. This is a bugfix release.\n\n# Broker\n- Fix `max_keepalive` option not being able to be set to 0.\n- Fix LWT messages not being delivered if `per_listener_settings` was set to\n  true. Closes [#2314].\n- Various fixes around inflight quota management. Closes [#2306].\n- Fix problem parsing config files with Windows line endings. Closes [#2297].\n- Don't send retained messages when a shared subscription is made.\n- Fix log being truncated in Windows.\n- Fix client id not showing in log on failed connections, where possible.\n- Fix broker sending duplicate CONNACK on failed MQTT v5 reauthentication.\n  Closes [#2339].\n- Fix mosquitto_plugin.h not including mosquitto_broker.h. Closes [#2350].\n\n# Client library\n- Initialise sockpairR/W to invalid in `mosquitto_reinitialise()` to avoid\n  closing invalid sockets in `mosquitto_destroy()` on error. Closes [#2326].\n\n# Clients\n- Fix date format in mosquitto_sub output. Closes [#2353].\n\n[#2297]: https://github.com/eclipse/mosquitto/issues/2297\n[#2306]: https://github.com/eclipse/mosquitto/issues/2306\n[#2314]: https://github.com/eclipse/mosquitto/issues/2314\n[#2326]: https://github.com/eclipse/mosquitto/issues/2326\n[#2339]: https://github.com/eclipse/mosquitto/issues/2339\n[#2350]: https://github.com/eclipse/mosquitto/issues/2350\n[#2353]: https://github.com/eclipse/mosquitto/issues/2353\n"
  },
  {
    "path": "www/posts/2021/11/version-2-0-14-released.md",
    "content": "<!--\n.. title: Version 2.0.14 released.\n.. slug: version-2-0-14-released\n.. date: 2021-11-17 00:25:38 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersions 2.0.14 of Mosquitto has been released. This is a bugfix release.\n\n# Broker\n- Fix bridge not respecting receive-maximum when reconnecting with MQTT v5.\n\n# Client library\n- Fix `mosquitto_topic_matches_sub2()` not using the length parameters.\n  Closes [#2364].\n- Fix incorrect `subscribe_callback` in mosquittopp.h. Closes [#2367].\n\n[#2364]: https://github.com/eclipse/mosquitto/issues/2364\n[#2367]: https://github.com/eclipse/mosquitto/issues/2367\n"
  },
  {
    "path": "www/posts/2022/08/version-2-0-15-released.md",
    "content": "<!--\n.. title: Version 2.0.15 released.\n.. slug: version-2-0-15-released\n.. date: 2022-08-16 12:57:38 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersions 2.0.15 of Mosquitto has been released. This is a security\nand bugfix release.\n\n# Security\n- Deleting the group configured as the anonymous group in the Dynamic Security\n  plugin, would leave a dangling pointer that could lead to a single crash.\n  This is considered a minor issue - only administrative users should have\n  access to dynsec, the impact on availability is one-off, and there is no\n  associated loss of data. It is now forbidden to delete the group configured\n  as the anonymous group.\n\n# Broker\n- Fix memory leak when a plugin modifies the topic of a message in\n  `MOSQ_EVT_MESSAGE`.\n- Fix bridge `restart_timeout` not being honoured.\n- Fix potential memory leaks if a plugin modifies the message in the\n  `MOSQ_EVT_MESSAGE` event.\n- Fix unused flags in CONNECT command being forced to be 0, which is not\n  required for MQTT v3.1. Closes [#2522].\n- Improve documentation of `persistent_client_expiration` option.\n  Closes [#2404].\n- Add clients to session expiry check list when restarting and reloading from\n  persistence. Closes [#2546].\n- Fix bridges not sending failure notification messages to the local broker if\n  the remote bridge connection fails. Closes [#2467]. Closes [#1488].\n- Fix some PUBLISH messages not being counted in $SYS stats. Closes [#2448].\n- Fix incorrect return code being sent in DISCONNECT when a client session is\n  taken over. Closes [#2607].\n- Fix confusing \"out of memory\" error when a client is kicked in the dynamic\n  security plugin. Closes [#2525].\n- Fix confusing error message when dynamic security config file was a\n  directory. Closes [#2520].\n- Fix bridge queued messages not being persisted when local_cleansession is\n  set to false and cleansession is set to true. Closes [#2604].\n- Dynamic security: Fix modifyClient and modifyGroup commands to not modify\n  the client/group if a new group/client being added is not valid.\n  Closes [#2598].\n- Dynamic security: Fix the plugin being able to be loaded twice. Currently\n  only a single plugin can interact with a unique $CONTROL topic. Using\n  multiple instances of the plugin would produce duplicate entries in the\n  config file. Closes [#2601]. Closes [#2470].\n- Fix case where expired messages were causing queued messages not to be\n  delivered. Closes [#2609].\n- Fix websockets not passing on the X-Forwarded-For header.\n\n# Client library\n- Fix threads library detection on Windows under cmake. Bumps the minimum\n  cmake version to 3.1, which is still ancient.\n- Fix use of `MOSQ_OPT_TLS_ENGINE` being unable to be used due to the openssl\n  ctx not being initialised until starting to connect. Closes [#2537].\n- Fix incorrect use of SSL_connect. Closes [#2594].\n- Don't set SIGPIPE to ignore, use MSG_NOSIGNAL instead. Closes [#2564].\n- Add documentation of struct mosquitto_message to header. Closes [#2561].\n- Fix documentation omission around mosquitto_reinitialise. Closes [#2489].\n- Fix use of MOSQ_OPT_SSL_CTX when used in conjunction with\n  MOSQ_OPT_SSL_CTX_DEFAULTS. Closes [#2463].\n- Fix failure to close thread in some situations. Closes [#2545].\n\n# Clients\n- Fix mosquitto_pub incorrectly reusing topic aliases when reconnecting.\n  Closes [#2494].\n\n# Apps\n- Fix `-o` not working in `mosquitto_ctrl`, and typo in related documentation.\n  Closes [#2471].\n\n\n[#1488]: https://github.com/eclipse/mosquitto/issues/1488\n[#2404]: https://github.com/eclipse/mosquitto/issues/2404\n[#2448]: https://github.com/eclipse/mosquitto/issues/2448\n[#2463]: https://github.com/eclipse/mosquitto/issues/2463\n[#2467]: https://github.com/eclipse/mosquitto/issues/2467\n[#2470]: https://github.com/eclipse/mosquitto/issues/2470\n[#2471]: https://github.com/eclipse/mosquitto/issues/2471\n[#2489]: https://github.com/eclipse/mosquitto/issues/2489\n[#2494]: https://github.com/eclipse/mosquitto/issues/2494\n[#2520]: https://github.com/eclipse/mosquitto/issues/2520\n[#2522]: https://github.com/eclipse/mosquitto/issues/2522\n[#2525]: https://github.com/eclipse/mosquitto/issues/2525\n[#2537]: https://github.com/eclipse/mosquitto/issues/2537\n[#2545]: https://github.com/eclipse/mosquitto/issues/2545\n[#2546]: https://github.com/eclipse/mosquitto/issues/2546\n[#2561]: https://github.com/eclipse/mosquitto/issues/2561\n[#2564]: https://github.com/eclipse/mosquitto/issues/2564\n[#2594]: https://github.com/eclipse/mosquitto/issues/2594\n[#2598]: https://github.com/eclipse/mosquitto/issues/2598\n[#2601]: https://github.com/eclipse/mosquitto/issues/2601\n[#2604]: https://github.com/eclipse/mosquitto/issues/2604\n[#2607]: https://github.com/eclipse/mosquitto/issues/2607\n[#2609]: https://github.com/eclipse/mosquitto/issues/2609\n"
  },
  {
    "path": "www/posts/2023/08/version-2-0-16-released.md",
    "content": "<!--\n.. title: Version 2.0.16 released.\n.. slug: version-2-0-16-released\n.. date: 2023-08-16 12:57:38 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.16 of Mosquitto has been released. This is a security\nand bugfix release.\n\n# Security\n- [CVE-2023-28366]: Fix memory leak in broker when clients send multiple QoS 2\n  messages with the same message ID, but then never respond to the PUBREC\n  commands.\n- [CVE-2023-0809]: Fix excessive memory being allocated based on malicious\n  initial packets that are not CONNECT packets.\n- [CVE-2023-3592]: Fix memory leak when clients send v5 CONNECT packets with a\n  will message that contains invalid property types.\n- Broker will now reject Will messages that attempt to publish to $CONTROL/.\n- Broker now validates usernames provided in a TLS certificate or TLS-PSK\n  identity are valid UTF-8.\n- Fix potential crash when loading invalid persistence file.\n- Library will no longer allow single level wildcard certificates, e.g. *.com\n\n# Broker\n- Fix $SYS messages being expired after 60 seconds and hence unchanged values\n  disappearing.\n- Fix some retained topic memory not being cleared immediately after used.\n- Fix error handling related to the `bind_interface` option.\n- Fix std* files not being redirected when daemonising, when built with\n  assertions removed. Closes [#2708].\n- Fix default settings incorrectly allowing TLS v1.1. Closes [#2722].\n- Use line buffered mode for stdout. Closes #2354. Closes [#2749].\n- Fix bridges with non-matching cleansession/local_cleansession being expired\n  on start after restoring from persistence. Closes [#2634].\n- Fix connections being limited to 2048 on Windows. The limit is now 8192,\n  where supported. Closes [#2732].\n- Broker will log warnings if sensitive files are world readable/writable, or\n  if the owner/group is not the same as the user/group the broker is running\n  as. In future versions the broker will refuse to open these files.\n- mosquitto_memcmp_const is now more constant time.\n- Only register with DLT if DLT logging is enabled.\n- Fix any possible case where a json string might be incorrectly loaded. This\n  could have caused a crash if a textname or textdescription field of a role was\n  not a string, when loading the dynsec config from file only.\n- Dynsec plugin will not allow duplicate clients/groups/roles when loading\n  config from file, which matches the behaviour for when creating them.\n- Fix heap overflow when reading corrupt config with \"log_dest file\".\n\n# Client library\n- Use CLOCK_BOOTTIME when available, to keep track of time. This solves the\n  problem of the client OS sleeping and the client hence not being able to\n  calculate the actual time for keepalive purposes. Closes [#2760].\n- Fix default settings incorrectly allowing TLS v1.1. Closes [#2722].\n- Fix high CPU use on slow TLS connect. Closes [#2794].\n\n# Clients\n- Fix incorrect topic-alias property value in mosquitto_sub json output.\n- Fix confusing message on TLS certificate verification. Closes [#2746].\n\n# Apps\n- mosquitto_passwd uses mkstemp() for backup files.\n- `mosquitto_ctrl dynsec init` will refuse to overwrite an existing file,\n  without a race-condition.\n\n[CVE-2023-0809]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-0809\n[CVE-2023-28366]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-27366\n[CVE-2023-3592]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-3592\n[#2354]: https://github.com/eclipse/mosquitto/issues/2354\n[#2634]: https://github.com/eclipse/mosquitto/issues/2634\n[#2708]: https://github.com/eclipse/mosquitto/issues/2708\n[#2722]: https://github.com/eclipse/mosquitto/issues/2722\n[#2722]: https://github.com/eclipse/mosquitto/issues/2722\n[#2732]: https://github.com/eclipse/mosquitto/issues/2732\n[#2746]: https://github.com/eclipse/mosquitto/issues/2746\n[#2749]: https://github.com/eclipse/mosquitto/issues/2749\n[#2760]: https://github.com/eclipse/mosquitto/issues/2760\n[#2794]: https://github.com/eclipse/mosquitto/issues/2794\n[#1488]: https://github.com/eclipse/mosquitto/issues/1488\n\n"
  },
  {
    "path": "www/posts/2023/08/version-2-0-17-released.md",
    "content": "<!--\n.. title: Version 2.0.17 released.\n.. slug: version-2-0-17-released\n.. date: 2023-08-16 12:57:38 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.17 of Mosquitto has been released. This is a bugfix release.\n\nBroker:\n- Fix `max_queued_messages 0` stopping clients from receiving messages.\n  Closes [#2879].\n- Fix `max_inflight_messages` not being set correctly. Closes [#2876].\n\nApps:\n- Fix `mosquitto_passwd -U` backup file creation. Closes [#2873].\n\n[#2873]: https://github.com/eclipse/mosquitto/issues/2873\n[#2876]: https://github.com/eclipse/mosquitto/issues/2876\n[#2879]: https://github.com/eclipse/mosquitto/issues/2879\n"
  },
  {
    "path": "www/posts/2023/09/version-2-0-18-released.md",
    "content": "<!--\n.. title: Version 2.0.18 released.\n.. slug: version-2-0-18-released\n.. date: 2023-09-18 22:18:38 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.18 of Mosquitto has been released. This is a bugfix release.\n\nBroker:\n- Fix crash on subscribe under certain unlikely conditions. Closes [#2885].\n  Closes [#2881].\n\nClients:\n- Fix mosquitto_rr not honouring `-R`. Closes [#2893].\n\n[#2881]: https://github.com/eclipse/mosquitto/issues/2881\n[#2885]: https://github.com/eclipse/mosquitto/issues/2885\n[#2893]: https://github.com/eclipse/mosquitto/issues/2893\n"
  },
  {
    "path": "www/posts/2024/10/version-2-0-19-released.md",
    "content": "<!--\n.. title: Version 2.0.19 released.\n.. slug: version-2-0-19-released\n.. date: 2024-10-02 10:46:38 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.19 of Mosquitto has been released. This is a security and bugfix release.\n\n# Security\n- Fix mismatched subscribe/unsubscribe with normal/shared topics.\n- Fix crash on bridge using remapped topic being sent a crafted packet.\n- Don't allow SUBACK with missing reason codes in client library.\n\n# Broker\n- Fix assert failure when loading a persistence file that contains\n  subscriptions with no client id.\n- Fix local bridges being incorrectly expired when `persistent_client_expiration`\n  is in use.\n- Fix use of CLOCK_BOOTTIME for getting time. Closes [#3089].\n- Fix mismatched subscribe/unsubscribe with normal/shared topics.\n- Fix crash on bridge using remapped topic being sent a crafted packet.\n\n# Client library\n- Fix some error codes being converted to string as \"unknown\". Closes [#2579].\n- Clear SSL error state to avoid spurious error reporting. Closes [#3054].\n- Fix \"payload format invalid\" not being allowed as a PUBREC reason code.\n- Don't allow SUBACK with missing reason codes.\n\n# Build\n- Thread support is re-enabled on Windows.\n\n[#2579]: https://github.com/eclipse/mosquitto/issues/2579\n[#3054]: https://github.com/eclipse/mosquitto/issues/3054\n[#3089]: https://github.com/eclipse/mosquitto/issues/3089\n"
  },
  {
    "path": "www/posts/2024/10/version-2-0-20-released.md",
    "content": "<!--\n.. title: Version 2.0.20 released.\n.. slug: version-2-0-20-released\n.. date: 2024-10-16 17:11:38 UTC+1\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.20 of Mosquitto has been released. This is a bugfix release.\n\n# Broker\n- Fix QoS 1 / QoS 2 publish incorrectly returning \"no subscribers\".\n  Closes #3128.\n- Open files with appropriate access on Windows. Closes #3119.\n- Don't allow invalid response topic values.\n- Fix some strict protocol compliance issues. Closes #3052.\n\n# Client library\n- Fix cmake build on OS X. Closes #3125.\n\n# Build\n- Fix build on NetBSD\n\n[#3052]: https://github.com/eclipse/mosquitto/issues/3052\n[#3119]: https://github.com/eclipse/mosquitto/issues/3119\n[#3125]: https://github.com/eclipse/mosquitto/issues/3125\n[#3128]: https://github.com/eclipse/mosquitto/issues/3128\n"
  },
  {
    "path": "www/posts/2025/03/version-2-0-21-released.md",
    "content": "<!--\n.. title: Version 2.0.21 released.\n.. slug: version-2-0-21-released\n.. date: 2025-03-06 14:53:38 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.21 of Mosquitto has been released. This is a security and bugfix release.\n\nSecurity:\n- Fix leak on malicious SUBSCRIBE by authenticated client.\n  Closes [eclipse #248].\n- Further fix for CVE-2023-28366.\n\n# Broker\n- Fix clients sending a RESERVED packet not being quickly disconnected.\n  Closes [#2325].\n- Fix `bind_interface` producing an error when used with an interface that has\n  an IPv6 link-local address and no other IPv6 addresses. Closes [#2696].\n- Fix mismatched wrapped/unwrapped memory alloc/free in properties. Closes [#3192].\n- Fix `allow_anonymous false` not being applied in local only mode. Closes [#3198].\n- Add `retain_expiry_interval` option to fix expired retained message not\n  being removed from memory if they are not subscribed to. Closes [#3221].\n- Produce an error if invalid combinations of cafile/capath/certfile/keyfile\n  are used. Closes [#1836]. Closes [#3130].\n- Backport keepalive checking from develop to fix problems in current\n  implementation. Closes [#3138].\n\n# Client library\n- Fix potential deadlock in mosquitto_sub if `-W` is used. Closes [#3175].\n\n# Apps\n- mosquitto_ctrl dynsec now also allows `-i` to specify a clientid as well as\n  `-c`. This matches the documentation which states `-i`. Closes [#3219].\nClient library:\n- Fix threads linking on Windows for static libmosquitto library\n  Closes [#3143]\n\n# Build\n- Fix Windows builds not having websockets enabled.\n- Add tzdata to docker images\n\n# Tests\n- Fix 08-ssl-connect-cert-auth-expired and 08-ssl-connect-cert-auth-revoked\n  tests when under load. Closes [#3208].\n\n[#eclipse 248]: https://gitlab.eclipse.org/security/vulnerability-reports/-/issues/248\n[#1836]: https://github.com/eclipse/mosquitto/issues/1836\n[#2325]: https://github.com/eclipse/mosquitto/issues/2325\n[#2696]: https://github.com/eclipse/mosquitto/issues/2696\n[#3130]: https://github.com/eclipse/mosquitto/issues/3130\n[#3138]: https://github.com/eclipse/mosquitto/issues/3138\n[#3143]: https://github.com/eclipse/mosquitto/issues/3143\n[#3175]: https://github.com/eclipse/mosquitto/issues/3175\n[#3192]: https://github.com/eclipse/mosquitto/issues/3192\n[#3198]: https://github.com/eclipse/mosquitto/issues/3198\n[#3208]: https://github.com/eclipse/mosquitto/issues/3208\n[#3219]: https://github.com/eclipse/mosquitto/issues/3219\n[#3221]: https://github.com/eclipse/mosquitto/issues/3221\n"
  },
  {
    "path": "www/posts/2025/07/version-2-0-22-released.md",
    "content": "<!--\n.. title: Version 2.0.22 released.\n.. slug: version-2-0-22-released\n.. date: 2025-07-11 21:40:38 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.0.22 of Mosquitto has been released. This is a bugfix release.\n\n# Broker\n\n- Windows: Fix broker crash on startup if using `log_dest stdout`\n- Bridge: Fix `idle_timeout` never occurring for lazy bridges.\n- Fix case where `max_queued_messages = 0` was not treated as unlimited.\n  Closes [#3244].\n- Fix `--version` exit code and output. Closes [#3267].\n- Fix crash on receiving a $CONTROL message over a bridge, if\n  `per_listener_settings` is set true and the bridge is carrying out topic\n  remapping. Closes [#3261].\n- Fix incorrect reference clock being selected on startup on Linux.\n  Closes [#3238].\n- Fix reporting of client disconnections being incorrectly attributed to \"out\n  of memory\". Closes [#3253].\n- Fix compilation when using `WITH_OLD_KEEPALIVE`. Closes [#3250].\n- Add Windows linker file for the broker to the installer. Closes [#3269].\n- Fix Websockets PING not being sent on Windows. Closes [#3272].\n- Fix problems with secure websockets. Closes [#1211].\n- Fix crash on exit when using `WITH_EPOLL=no`. Closes [#3302].\n- Fix clients being incorrectly expired when they have keepalive ==\n  `max_keepalive`. Closes [#3226], [#3286].\n\n# Dynamic security plugin\n- Fix mismatch memory free when saving config which caused memory tracking to\n  be incorrect.\n\n# Client library\n- Fix C++ symbols being removed when compiled with link time optimisation.\n  Closes [#3259].\n- TLS error handling was incorrectly setting a protocol error for non-TLS\n  errors.  This would cause the `mosquitto_loop_start()` thread to exit if no\n  broker was available on the first connection attempt. This has been fixed.\n  Closes [#3258].\n- Fix linker errors on some architectures using cmake. Closes [#3167].\n\n\nTests:\n- Fix 08-ssl-connect-cert-auth-expired and 08-ssl-connect-cert-auth-revoked\n  tests when running on a single CPU system. Closes [#3230].\n\n[#1211]: https://github.com/eclipse/mosquitto/issues/1211\n[#3167]: https://github.com/eclipse/mosquitto/issues/3167\n[#3226]: https://github.com/eclipse/mosquitto/issues/3226\n[#3230]: https://github.com/eclipse/mosquitto/issues/3230\n[#3238]: https://github.com/eclipse/mosquitto/issues/3238\n[#3244]: https://github.com/eclipse/mosquitto/issues/3244\n[#3250]: https://github.com/eclipse/mosquitto/issues/3250\n[#3253]: https://github.com/eclipse/mosquitto/issues/3253\n[#3258]: https://github.com/eclipse/mosquitto/issues/3258\n[#3259]: https://github.com/eclipse/mosquitto/issues/3259\n[#3261]: https://github.com/eclipse/mosquitto/issues/3261\n[#3267]: https://github.com/eclipse/mosquitto/issues/3267\n[#3269]: https://github.com/eclipse/mosquitto/issues/3269\n[#3272]: https://github.com/eclipse/mosquitto/issues/3272\n[#3286]: https://github.com/eclipse/mosquitto/issues/3286\n[#3302]: https://github.com/eclipse/mosquitto/issues/3302\n"
  },
  {
    "path": "www/posts/2026/01/rc-2.1.0rc1-available.md",
    "content": "<!--\n.. title: Release candidate 2.1.0rc1 available\n.. slug: rc-2.1.0rc1-available\n.. date: 2026-01-16 16:21:42 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nThe first release candidate for Mosquitto 2.1.0 is now available for testing.\nIf no release critical issues are reported, this will become 2.1.0 on\n2026-01-26.\n\nThe source and binary packages are available:\n\n* [Source](https://mosquitto.org/files/source/mosquitto-2.1.0rc1.tar.gz)\n* [Windows 64-bit](https://mosquitto.org/files/binary/win64/mosquitto-2.1.0rc1-install-windows-x64.exe)\n* [Windows 32-bit](https://mosquitto.org/files/binary/win32/mosquitto-2.1.0rc1-install-windows-x86.exe)\n* [Debian (amd64, armhf)](https://mosquitto.org/files/binary/debian) (note: the final release version will be available in the normal repository)\n* [Ubuntu (amd64)](https://mosquitto.org/files/binary/ubuntu) (note: the final release version will be available in the normal repository)\n* Snap (amd64): `snap refresh --edge mosquitto` or `snap install --edge mosquitto`\n"
  },
  {
    "path": "www/posts/2026/01/version-2-1-0-released.md",
    "content": "<!--\n.. title: Version 2.1.0 released.\n.. slug: version-2-1-0-released\n.. date: 2026-01-29 16:00:00 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.1.0 of Mosquitto has been released. This is a feature release.\n\n# Broker\n\n## Deprecations\n\n- The `acl_file` option is deprecated in favour of the acl-file plugin, which is\n  the same code but moved into a plugin. The `acl_file` option will be removed\n  in 3.0.\n- The `password_file` option is deprecated in favour of the password-file plugin,\n  which is the same code but moved into a plugin. The `password_file` option will\n  be removed in 3.0.\n- The `per_listener_settings` option is deprecated in favour of the new listener\n  specific options. The `per_listener_settings` option will be removed in 3.0.\n\n## Behaviour changes\n\n- `max_packet_size` now defaults to 2,000,000 bytes instead of the 256MB MQTT\n  limit. If you are using payloads that will result in a packet larger than\n  this, you need to manually set the option to a value that suits your\n  application.\n- `acl_file` and `password_file` will produce an error on invalid input when\n  reloading the config, causing the broker to quit.\n\n## Protocol related\n\n- Add support for broker created topic aliases. Topics are allocated on a\n  first come first serve basis.\n- Add support for bridges to allow remote brokers to create topic aliases when\n  running in MQTT v5 mode.\n- Enforce receive maximum on MQTT v5.\n- Return protocol error if a client attemps to subscribe to a shared\n  subscription and also sets no-local.\n- Protocol version numbers reported in the log when a client connects now\n  match the MQTT protocol version numbers, not internal Mosquitto values.\n- Send DISCONNECT With session-takeover return code to MQTT v5 clients when a\n  client connects with the same client id. Closes [#2340].\n- The `allow_duplicate_messages` now defaults to `true`.\n- Add `accept_protocol_versions` option to allow limiting which MQTT protocol\n  versions are allowed for a particular listener.\n\n## TLS related\n\n- Add `--tls-keylog` option which can be used to generate a file that can be\n  used by wireshark to decrypt TLS traffic for debugging purposes. Closes [#1818].\n- Add `disable_client_cert_date_checks` option to allow expired client\n  certificate to be considered valid.\n- Add `bridge_tls_use_os_certs` option to allow bridges to be easily configured\n  to trust default CA certificates. Closes [#2473].\n- Remove support for TLS v1.1 (clients only - it remains available in the\n  broker but is now undocumented)\n- Use openssl provided function for x509 certificate hostname verification,\n  rather than own function.\n\n## Bridge related\n- Add `bridge_receive_maximum` option for MQTT v5.0 bridges.\n- Add `bridge_session_expiry_interval` option for MQTT v5.0 bridges.\n- Bridge reconnection backoff improvements.\n\n## Transport related\n- Add the `websockets_origin` option to allow optional enforcement of origin\n  when a connection attempts an upgrade to WebSockets.\n- Add built-in websockets support that doesn't use libwebsockets. This is the\n  preferred websockets implementation.\n- Add support for X-Forwarded-For header for built in websockets.\n- Add suport for PROXY protocol v1 and v2.\n\n## Platform specific\n- Increase maximum connection count on Windows from 2048 to 8192 where\n  supported. Closes [#2122].\n- Allow multiple instances of mosquitto to run as services on Windows. See\n  README-windows.txt.\n- Add kqueue support.\n- Add support for systemd watchdog.\n\n## General\n- Report on what compile time options are enabled. Closes [#2193].\n- Performance: reduce memory allocations when sending packets.\n- Log protocol version and ciphers that a client negotiates when connecting.\n- Password salts are now 64 bytes long.\n- Add the `global_plugin` option, which gives global plugin loaded regardless\n  of `per_listener_settings`.\n- Add `global_max_clients` option to allow limiting client sessions globally\n  on the broker.\n- Add `global_max_connections` option to allow limiting client connections globally\n  on the broker.\n- Improve idle performance. The broker now calculates when the next event of\n  interest is, and uses that as the timeout for e.g. `epoll_wait()`. This can\n  reduce the number of process wakeups by 100x on an idle broker.\n- Add more efficient keepalive check.\n- Add support for sending the SIGRTMIN signal to trigger log rotation.\n  Closes [#2337].\n- Add `--test-config` option which can be used to test a configuration file\n  before trying to use it in a live broker. Closes [#2521].\n- Add support for PUID/PGID environment variables for setting the user/group\n  to drop privileges to. Closes [#2441].\n- Report persistence stats when starting.\n- $SYS updates are now aligned to `sys_interval` seconds, meaning that if set\n  to 10, for example, updates will be sent at times matching x0 seconds.\n  Previously update intervals were aligned to the time the broker was started.\n- Add `log_dest android` for logging to the Android logd daemon.\n- Fix some retained topic memory not being cleared immediately after used.\n- Add -q option to allow logging to be disabled at the command line.\n- Log message if a client attempts to connect with TLS to a non-TLS listener.\n- Add `listener_allow_anonymous` option.\n- Add `listener_auto_id_prefix` option.\n- Allow seconds when defining `persistent_client_expiration`.\n\n## Plugin interface\n- Add `mosquitto_topic_matches_sub_with_pattern()`, which can match against\n  subscriptions with `%c` and `%u` patterns for client id / username\n  substitution.\n- Add support for modifying outgoing messages using `MOSQ_EVT_MESSAGE_OUT`.\n- Add `mosquitto_client()` function for retrieving a client struct if that\n  client is connected.\n- Add `MOSQ_ERR_PLUGIN_IGNORE` to allow plugins to register basic auth or acl\n  check callbacks, but still act as though they are not registered. A plugin\n  that wanted to act as a blocklist for certain usernames, but wasn't carrying\n  out authentication could return `MOSQ_ERR_PLUGIN_IGNORE` for usernames not on\n  its blocklist. If no other plugins were configured, the client would be\n  authenticated. Using `MOSQ_ERR_PLUGIN_DEFER` instead would mean the clients\n  would be denied if no other plugins were configured.\n- Add `mosquitto_client_port()` function for plugins.\n- Add `MOSQ_EVT_CONNECT`, to allow plugins to know when a client has\n  successfully authenticated to the broker.\n- Add connection-state example plugin to demonstrate `MOSQ_EVT_CONNECT`.\n- Add `MOSQ_EVT_CLIENT_OFFLINE`, to allow plugins to know when a client with a\n  non-zero session expiry interval has gone offline.\n- Plugins on non-Windows platforms now no longer make their symbols globally\n  available, which means they are self contained.\n- Add support for delayed basic authentication in plugins.\n- Plugins using the `MOSQ_EVT_MESSAGE_WRITE` callback can now return\n  `MOSQ_ERR_QUOTA_EXCEEDED` to have the message be rejected. MQTT v5 clients\n  using QoS 1 or 2 will receive the quota-exceeded reason code in the\n  corresponding PUBACK/PUBREC.\n- `MOSQ_EVT_TICK` is now passed to plugins when `per_listener_settings` is true.\n- Add `mosquitto_sub_matches_acl()`, which can match one topic filter (a\n  subscription) against another topic filter (an ACL).\n- Registration of the `MOSQ_EVT_CONTROL` plugin event is now handled globally\n  across the broker, so only a single plugin can register for a given $CONTROL\n  topic.\n- Add `mosquitto_plugin_set_info()` to allow plugins to tell the broker their\n  name and version.\n- Add builtin $CONTROL/broker/v1 control topic with the `listPlugins`\n  command. This is disabled by default, but can be enabled with the\n  `enable_control_api` option.\n- Plugins no longer need to define `mosquitto_plugin_cleanup()` if they do not\n  need to do any of their own cleanup. Callbacks will be unregistered\n  automatically.\n- Add `mosquitto_set_clientid()` to allow plugins to force a client id for a\n  client.\n- Add `MOSQ_EVT_SUBSCRIBE` and `MOSQ_EVT_UNSUBSCRIBE` events that are called when\n  subscribe/unsubscribes actually succeed. Allow modifying topic and qos.\n- Add `mosquitto_persistence_location()` for plugins to use to find a valid\n  location for storing persistent data.\n- Plugins can now use the `next_s` and `next_ms` members of the tick event data\n  struct to set a minimum interval that the broker will wait before calling the\n  tick callback again.\n- MOSQ_EVT_ACL_CHECK event is now passed message properties where possible.\n\n# Plugins\n- Add acl-file plugin.\n- Add password-file plugin.\n- Add persist-sqlite plugin.\n- Add sparkplug-aware plugin.\n\n# Dynamic security plugin\n- Add ability to deny wildcard subscriptions for a role to the dynsec plugin.\n- The dynamic security plugin now only kicks clients at the start of the next\n  network loop, to give chance for PUBACK/PUBREC to be sent. Closes [#2474].\n- The dynamic security plugin now reports client connections in getClient and\n  listClients.\n- The dynamic security plugin now generates an initial configuration if none\n  is present, including a set of default roles.\n- The dynamic security plugin now supports `%c` and `%u` patterns for\n  substituting client id and username respectively, in all ACLs except for\n  subscribeLiteral and unsubscribeLiteral.\n- The dynamic security plugin now supports multiple ways to initialise the\n  first configuration file.\n\n# Client library\n- Add `MOSQ_OPT_DISABLE_SOCKETPAIR` to allow the disabling of the socketpair\n  feature that allows the network thread to be woken from select() by another\n  thread when e.g.  `mosquitto_publish()` is called. This reduces the number of\n  sockets used by each client by two.\n- Add `on_pre_connect()` callback to allow clients to update\n  username/password/TLS parameters before an automatic reconnection.\n- Callbacks no longer block other callbacks, and can be set from within a\n  callback. Closes [#2127].\n- Add support for MQTT v5 broker to client topic aliases.\n- Add `mosquitto_topic_matches_sub_with_pattern()`, which can match against\n  subscriptions with `%c` and `%u` patterns for client id / username\n  substitution.\n- Add `mosquitto_sub_matches_acl()`, which can match one topic filter (a\n  subscription) against another topic filter (an ACL).\n- Add `mosquitto_sub_matches_acl_with_pattern()`, which can match one topic\n  filter (a subscription) against another topic filter (an ACL), with `%c` and\n  `%u` patterns for client id / username substitution.\n- Performance: reduce memory allocations when sending packets.\n- Reintroduce threading support for Windows. Closes [#1509].\n- `mosquitto_subscribe*()` now returns `MOSQ_ERR_INVAL` if an empty string is\n  passed as a topic filter.\n- `mosquitto_unsubscribe*()` now returns `MOSQ_ERR_INVAL` if an empty string is\n  passed as a topic filter.\n- Add websockets support.\n- `mosquitto_property_read_binary/string/string_pair` will now set the\n  name/value parameter to NULL if the binary/string is empty. This aligns the\n  behaviour with other property functions. Closes [#2648].\n- Add `mosquitto_unsubscribe2_v5_callback_set`, which provides a callback that\n  gives access to reason codes for each of the unsubscription requests.\n- Add `mosquitto_property_remove`, for removing properties from property\n  lists.\n- Add `on_ext_auth()` callback to allow handling MQTT v5 extended authentication.\n- Add `mosquitto_ext_auth_continue()` function to continue an MQTT v5 extended\n  authentication.\n- Remove support for TLS v1.1.\n- Use openssl provided function for x509 certificate hostname verification,\n  rather than own function.\n\n# Clients\n\n## General\n- Add `-W` timeout support to Windows.\n- The `--insecure` option now disables all server certificate verification.\n- Add websockets support.\n- Using `-x` now sets the clients to use MQTT v5.0.\n- Fix parsing of IPv6 addresses in socks proxy urls.\n- Add `--tls-keylog` option which can be used to generate a file that can be\n  used by wireshark to decrypt TLS traffic for debugging purposes.\n- Remove support for TLS v1.1.\n\n## mosquitto_rr\n- Fix `-f` and `-s` options in mosquitto_rr.\n- Add `--latency` option to mosquitto_rr, for printing the request/response\n  latency.\n- Add `--retain-handling` option.\n\n## mosquitto_sub\n- Fix incorrect output formatting in mosquitto_sub when using field widths\n  with `%x` and `%X` for printing the payload in hex.\n- Add float printing option to mosquitto_sub.\n- mosquitto_sub payload hex output can now be split by fixed field length.\n- Add `--message-rate` option to mosquitto_sub, for printing the count of\n  messages received each second.\n- Add `--retain-handling` option.\n\n# Apps\n\n## mosquitto_signal\n- Add `mosquitto_signal` for helping send signals to mosquitto on Windows.\n\n## mosquitto_ctrl\n- Add interactive shell mode to mosquitto_ctrl.\n- Add support for `listPlugins` to mosquitto_ctrl.\n- Allow mosquitto_ctrl dynsec module to update passwords in files rather than\n  having to connect to a broker.\n\n## mosquitto_passwd\n- Print messages in mosquitto_passwd when adding/updating passwords.\n  Closes [#2544].\n- When creating a new file with `-c`, setting the output filename to a dash `-`\n  will output the result to stdout.\n\n## mosquitto_db_dump\n- Add `--json` output mode do mosquitto_db_dump.\n\n# Build\n- Increased CMake minimal required version to 3.14, which is required for the\n  preinstalled SQLite3 find module.\n- Add an CMake option `WITH_LTO` to enable/disable link time optimization.\n- Set C99 as the explicit, rather than implicit, build standard.\n- cJSON is now a required dependency.\n- Refactored headers for easier discovery.\n- Support for openssl < 3.0 removed.\n\n[#1509]: https://github.com/eclipse-mosquitto/mosquitto/issues/1509\n[#1818]: https://github.com/eclipse-mosquitto/mosquitto/issues/1818\n[#2122]: https://github.com/eclipse-mosquitto/mosquitto/issues/2122\n[#2172]: https://github.com/eclipse-mosquitto/mosquitto/issues/2172\n[#2193]: https://github.com/eclipse-mosquitto/mosquitto/issues/2193\n[#2337]: https://github.com/eclipse-mosquitto/mosquitto/issues/2337\n[#2340]: https://github.com/eclipse-mosquitto/mosquitto/issues/2340\n[#2441]: https://github.com/eclipse-mosquitto/mosquitto/issues/2441\n[#2473]: https://github.com/eclipse-mosquitto/mosquitto/issues/2473\n[#2474]: https://github.com/eclipse-mosquitto/mosquitto/issues/2474\n[#2521]: https://github.com/eclipse-mosquitto/mosquitto/issues/2521\n[#2544]: https://github.com/eclipse-mosquitto/mosquitto/issues/2544\n[#2648]: https://github.com/eclipse-mosquitto/mosquitto/issues/2648\n"
  },
  {
    "path": "www/posts/2026/02/version-2-1-1-released.md",
    "content": "<!--\n.. title: Version 2.1.1 released.\n.. slug: version-2-1-1-released\n.. date: 2026-02-04 22:30:00 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.1.1 of Mosquitto has been released. This is a bugfix release.\n\n# Broker\n\n- Fix PUID/PGID checking for docker\n- Add `MOSQUITTO_UNSAFE_ALLOW_SYMLINKS` environment variable to allow the\n  restrictions on reading files through symlinks to be lifted in safe\n  environments like kubernetes. Closes [#3461].\n- Fix inconsistent disconnect log message format, and add address:port.\n- Fix `plugin`/`global_plugin` option not allowing space characters.\n- Fix $SYS load values not being published initially. Closes [#3459].\n- Fix `max_connections not being honoured on libwebsockets listeners. This does\n  not affect the built-in websockets support. Closes [#3455].\n- Don't enforce receive-maximum, just log a warning. This allows badly\n  behaving clients to be fixed. Closes [#3471].\n\n# Plugins\n- Fix incorrect linking of libmosquitto_common.so for the acl and password\n  file plugins. Closes [#3460].\n\n# Build\n- Fix building with WITH_TLS=no\n\n[#3455]: https://github.com/eclipse-mosquitto/mosquitto/issues/3455\n[#3459]: https://github.com/eclipse-mosquitto/mosquitto/issues/3459\n[#3460]: https://github.com/eclipse-mosquitto/mosquitto/issues/3460\n[#3461]: https://github.com/eclipse-mosquitto/mosquitto/issues/3461\n[#3471]: https://github.com/eclipse-mosquitto/mosquitto/issues/3471\n"
  },
  {
    "path": "www/posts/2026/02/version-2-1-2-released.md",
    "content": "<!--\n.. title: Version 2.1.2 released.\n.. slug: version-2-1-2-released\n.. date: 2026-02-09 09:30:00 UTC\n.. tags: Releases\n.. category:\n.. link:\n.. description:\n.. type: text\n-->\n\nVersion 2.1.2 of Mosquitto has been released. This is a bugfix release.\n\n# Broker:\n- Forbid running with `persistence true` and with a persistence plugin at the\n  same time. Closes [#3480].\n\n# Build:\n- Build fixes for OpenBSD. Closes [#3474].\n- Add missing libedit to docker builds. Closes [#3476].\n- Fix static/shared linking of libwebsockets under cmake.\n\n[#3474]: https://github.com/eclipse-mosquitto/mosquitto/issues/3474\n[#3476]: https://github.com/eclipse-mosquitto/mosquitto/issues/3476\n[#3480]: https://github.com/eclipse-mosquitto/mosquitto/issues/3480\n"
  },
  {
    "path": "www/templates/book.tmpl",
    "content": "## -*- coding: utf-8 -*-\n<%namespace name=\"helper\" file=\"post_helper.tmpl\"/>\n<%namespace name=\"pheader\" file=\"post_header.tmpl\"/>\n<%namespace name=\"comments\" file=\"comments_helper.tmpl\"/>\n<%inherit file=\"post.tmpl\"/>\n\n<%block name=\"extra_head\">\n    ${parent.extra_head()}\n    <link href='https://fonts.googleapis.com/css?family=Gentium+Book+Basic' rel='stylesheet' type='text/css'>\n    <style>\n        .smallcaps {\n            font-variant: small-caps;\n        }\n        .chapter {\n            width: 100%;\n            padding: 10px;\n            -webkit-column-gap: 40px;\n               -moz-column-gap: 40px;\n                    column-gap: 40px;\n            -webkit-column-width: 400px;\n               -moz-column-width: 400px;\n                    column-width: 400px;\n            -webkit-column-count: 2;\n               -moz-column-count: 2;\n                    column-count: 2;\n            -webkit-column-rule: 1px solid #ddd;\n               -moz-column-rule: 1px solid #ddd;\n                    column-rule: 1px solid #ddd;\n            height: 90vh;\n            font-family: 'Gentium Book Basic', serif;\n            color: #2d2e2e;\n            font-weight: 500;\n        }\n        div.frame {\n            overflow: hidden;\n            padding: 0;\n            margin: 0;\n        }\n        div.scrolling-cont {\n            overflow-x: scroll;\n            padding: 0;\n            margin: 0;\n        }\n        h1, h2, h3, h4 {\n            text-align: center;\n            width: 100%;\n            font-family: 'Gentium Book Basic', serif;\n            font-size: 120%;\n            font-weight: 900;\n        }\n        h1 {\n            font-size: 150%;\n        }\n        .subtitle {\n            text-align: center;\n            width: 100%;\n        }\n        .bookfig {\n            width: 100%;\n            height: auto;\n            max-width: 100%;\n            max-height: 100%;\n        }\n        div.figure {\n            height: 88vh;\n            margin: 0;\n        }\n        div.topic {\n            margin: 0;\n        }\n        div.section > p {\n            text-indent: 1em;\n            margin-bottom: 0;\n            text-align: justify;\n        }\n    </style>\n</%block>\n\n<%block name=\"content\">\n<article class=\"storypage\" itemscope=\"itemscope\" itemtype=\"http://schema.org/Article\">\n    <div class=\"frame\">\n    <div class=\"scrolling-cont\" id=\"scrolling-cont\" name=\"scrolling-cont\">\n    <div class=\"e-content entry-content chapter\" itemprop=\"articleBody text\">\n    <h1>${post.title()}</h1>\n    ${post.text()}\n    </div>\n    </div>\n    </div>\n</article>\n</%block>\n\n<%block name=\"extra_js\">\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/Flowtype.js/1.1.0/flowtype.min.js\"></script>\n    <script>\n        $('#scrolling-cont').flowtype({\n            minimum: 500,\n            maximum: 1200,\n            minFont: 20,\n            maxFont: 40,\n            fontRatio: 50\n        });\n        $(document).ready(function() {\n            var elem = $('#scrolling-cont');\n            elem.click(function(event) {\n                var x1 = elem.position().left;\n                var pw = elem.width() + 20;\n                var x2 = event.pageX;\n                if (x2 - x1 < pw / 2) {\n                    pw = -pw;\n                }\n                elem.animate({\n                    scrollLeft: '+=' + pw\n                }, 500)\n            });\n        });\n    </script>\n</%block>\n"
  },
  {
    "path": "www/themes/mosquitto/assets/css/bulma.css",
    "content": "/*! bulma.io v0.6.1 | MIT License | github.com/jgthms/bulma */\n@-webkit-keyframes spinAround {\n  from {\n    -webkit-transform: rotate(0deg);\n            transform: rotate(0deg);\n  }\n  to {\n    -webkit-transform: rotate(359deg);\n            transform: rotate(359deg);\n  }\n}\n@keyframes spinAround {\n  from {\n    -webkit-transform: rotate(0deg);\n            transform: rotate(0deg);\n  }\n  to {\n    -webkit-transform: rotate(359deg);\n            transform: rotate(359deg);\n  }\n}\n\n/*! minireset.css v0.0.2 | MIT License | github.com/jgthms/minireset.css */\nhtml,\nbody,\np,\nol,\nul,\nli,\ndl,\ndt,\ndd,\nblockquote,\nfigure,\nfieldset,\nlegend,\ntextarea,\npre,\niframe,\nhr,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  margin: 0;\n  padding: 0;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  font-size: 100%;\n  font-weight: normal;\n}\n\nul {\n  list-style: none;\n}\n\nbutton,\ninput,\nselect,\ntextarea {\n  margin: 0;\n}\n\nhtml {\n  -webkit-box-sizing: border-box;\n          box-sizing: border-box;\n}\n\n* {\n  -webkit-box-sizing: inherit;\n          box-sizing: inherit;\n}\n\n*:before, *:after {\n  -webkit-box-sizing: inherit;\n          box-sizing: inherit;\n}\n\nimg,\nembed,\nobject,\naudio,\nvideo {\n  max-width: 100%;\n}\n\niframe {\n  border: 0;\n}\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n  text-align: left;\n}\n\nhtml {\n  background-color: white;\n  font-size: 16px;\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-font-smoothing: antialiased;\n  min-width: 300px;\n  overflow-x: hidden;\n  overflow-y: scroll;\n  text-rendering: optimizeLegibility;\n  -webkit-text-size-adjust: 100%;\n     -moz-text-size-adjust: 100%;\n      -ms-text-size-adjust: 100%;\n          text-size-adjust: 100%;\n}\n\narticle,\naside,\nfigure,\nfooter,\nheader,\nhgroup,\nsection {\n  display: block;\n}\n\nbody,\nbutton,\ninput,\nselect,\ntextarea {\n  font-family: BlinkMacSystemFont, -apple-system, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", \"Helvetica\", \"Arial\", sans-serif;\n}\n\ncode,\npre {\n  -moz-osx-font-smoothing: auto;\n  -webkit-font-smoothing: auto;\n  font-family: monospace;\n}\n\nbody {\n  color: #4a4a4a;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n}\n\na {\n  color: #7a91c1;\n  cursor: pointer;\n  text-decoration: none;\n}\n\na strong {\n  color: currentColor;\n}\n\na:hover {\n  color: #363636;\n}\n\ncode {\n  background-color: whitesmoke;\n  color: #ff3860;\n  font-size: 0.875em;\n  font-weight: normal;\n  padding: 0.25em 0.5em 0.25em;\n}\n\nhr {\n  background-color: #dbdbdb;\n  border: none;\n  display: block;\n  height: 1px;\n  margin: 1.5rem 0;\n}\n\nimg {\n  height: auto;\n  max-width: 100%;\n}\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  vertical-align: baseline;\n}\n\nsmall {\n  font-size: 0.875em;\n}\n\nspan {\n  font-style: inherit;\n  font-weight: inherit;\n}\n\nstrong {\n  color: #363636;\n  font-weight: 700;\n}\n\npre {\n  -webkit-overflow-scrolling: touch;\n  background-color: whitesmoke;\n  color: #4a4a4a;\n  font-size: 0.875em;\n  overflow-x: auto;\n  padding: 1.25rem 1.5rem;\n  white-space: pre;\n  word-wrap: normal;\n}\n\npre code {\n  background-color: transparent;\n  color: currentColor;\n  font-size: 1em;\n  padding: 0;\n}\n\ntable td,\ntable th {\n  text-align: left;\n  vertical-align: top;\n}\n\ntable th {\n  color: #363636;\n}\n\n.is-clearfix:after {\n  clear: both;\n  content: \" \";\n  display: table;\n}\n\n.is-pulled-left {\n  float: left !important;\n}\n\n.is-pulled-right {\n  float: right !important;\n}\n\n.is-clipped {\n  overflow: hidden !important;\n}\n\n.is-overlay {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n}\n\n.is-size-1 {\n  font-size: 3rem !important;\n}\n\n.is-size-2 {\n  font-size: 2.5rem !important;\n}\n\n.is-size-3 {\n  font-size: 2rem !important;\n}\n\n.is-size-4 {\n  font-size: 1.5rem !important;\n}\n\n.is-size-5 {\n  font-size: 1.25rem !important;\n}\n\n.is-size-6 {\n  font-size: 1rem !important;\n}\n\n.is-size-7 {\n  font-size: 0.75rem !important;\n}\n\n@media screen and (max-width: 768px) {\n  .is-size-1-mobile {\n    font-size: 3rem !important;\n  }\n  .is-size-2-mobile {\n    font-size: 2.5rem !important;\n  }\n  .is-size-3-mobile {\n    font-size: 2rem !important;\n  }\n  .is-size-4-mobile {\n    font-size: 1.5rem !important;\n  }\n  .is-size-5-mobile {\n    font-size: 1.25rem !important;\n  }\n  .is-size-6-mobile {\n    font-size: 1rem !important;\n  }\n  .is-size-7-mobile {\n    font-size: 0.75rem !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .is-size-1-tablet {\n    font-size: 3rem !important;\n  }\n  .is-size-2-tablet {\n    font-size: 2.5rem !important;\n  }\n  .is-size-3-tablet {\n    font-size: 2rem !important;\n  }\n  .is-size-4-tablet {\n    font-size: 1.5rem !important;\n  }\n  .is-size-5-tablet {\n    font-size: 1.25rem !important;\n  }\n  .is-size-6-tablet {\n    font-size: 1rem !important;\n  }\n  .is-size-7-tablet {\n    font-size: 0.75rem !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .is-size-1-touch {\n    font-size: 3rem !important;\n  }\n  .is-size-2-touch {\n    font-size: 2.5rem !important;\n  }\n  .is-size-3-touch {\n    font-size: 2rem !important;\n  }\n  .is-size-4-touch {\n    font-size: 1.5rem !important;\n  }\n  .is-size-5-touch {\n    font-size: 1.25rem !important;\n  }\n  .is-size-6-touch {\n    font-size: 1rem !important;\n  }\n  .is-size-7-touch {\n    font-size: 0.75rem !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .is-size-1-desktop {\n    font-size: 3rem !important;\n  }\n  .is-size-2-desktop {\n    font-size: 2.5rem !important;\n  }\n  .is-size-3-desktop {\n    font-size: 2rem !important;\n  }\n  .is-size-4-desktop {\n    font-size: 1.5rem !important;\n  }\n  .is-size-5-desktop {\n    font-size: 1.25rem !important;\n  }\n  .is-size-6-desktop {\n    font-size: 1rem !important;\n  }\n  .is-size-7-desktop {\n    font-size: 0.75rem !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .is-size-1-widescreen {\n    font-size: 3rem !important;\n  }\n  .is-size-2-widescreen {\n    font-size: 2.5rem !important;\n  }\n  .is-size-3-widescreen {\n    font-size: 2rem !important;\n  }\n  .is-size-4-widescreen {\n    font-size: 1.5rem !important;\n  }\n  .is-size-5-widescreen {\n    font-size: 1.25rem !important;\n  }\n  .is-size-6-widescreen {\n    font-size: 1rem !important;\n  }\n  .is-size-7-widescreen {\n    font-size: 0.75rem !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .is-size-1-fullhd {\n    font-size: 3rem !important;\n  }\n  .is-size-2-fullhd {\n    font-size: 2.5rem !important;\n  }\n  .is-size-3-fullhd {\n    font-size: 2rem !important;\n  }\n  .is-size-4-fullhd {\n    font-size: 1.5rem !important;\n  }\n  .is-size-5-fullhd {\n    font-size: 1.25rem !important;\n  }\n  .is-size-6-fullhd {\n    font-size: 1rem !important;\n  }\n  .is-size-7-fullhd {\n    font-size: 0.75rem !important;\n  }\n}\n\n.has-text-centered {\n  text-align: center !important;\n}\n\n@media screen and (max-width: 768px) {\n  .has-text-centered-mobile {\n    text-align: center !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .has-text-centered-tablet {\n    text-align: center !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .has-text-centered-tablet-only {\n    text-align: center !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .has-text-centered-touch {\n    text-align: center !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .has-text-centered-desktop {\n    text-align: center !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .has-text-centered-desktop-only {\n    text-align: center !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .has-text-centered-widescreen {\n    text-align: center !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .has-text-centered-widescreen-only {\n    text-align: center !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .has-text-centered-fullhd {\n    text-align: center !important;\n  }\n}\n\n.has-text-justified {\n  text-align: justify !important;\n}\n\n@media screen and (max-width: 768px) {\n  .has-text-justified-mobile {\n    text-align: justify !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .has-text-justified-tablet {\n    text-align: justify !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .has-text-justified-tablet-only {\n    text-align: justify !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .has-text-justified-touch {\n    text-align: justify !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .has-text-justified-desktop {\n    text-align: justify !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .has-text-justified-desktop-only {\n    text-align: justify !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .has-text-justified-widescreen {\n    text-align: justify !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .has-text-justified-widescreen-only {\n    text-align: justify !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .has-text-justified-fullhd {\n    text-align: justify !important;\n  }\n}\n\n.has-text-left {\n  text-align: left !important;\n}\n\n@media screen and (max-width: 768px) {\n  .has-text-left-mobile {\n    text-align: left !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .has-text-left-tablet {\n    text-align: left !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .has-text-left-tablet-only {\n    text-align: left !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .has-text-left-touch {\n    text-align: left !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .has-text-left-desktop {\n    text-align: left !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .has-text-left-desktop-only {\n    text-align: left !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .has-text-left-widescreen {\n    text-align: left !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .has-text-left-widescreen-only {\n    text-align: left !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .has-text-left-fullhd {\n    text-align: left !important;\n  }\n}\n\n.has-text-right {\n  text-align: right !important;\n}\n\n@media screen and (max-width: 768px) {\n  .has-text-right-mobile {\n    text-align: right !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .has-text-right-tablet {\n    text-align: right !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .has-text-right-tablet-only {\n    text-align: right !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .has-text-right-touch {\n    text-align: right !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .has-text-right-desktop {\n    text-align: right !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .has-text-right-desktop-only {\n    text-align: right !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .has-text-right-widescreen {\n    text-align: right !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .has-text-right-widescreen-only {\n    text-align: right !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .has-text-right-fullhd {\n    text-align: right !important;\n  }\n}\n\n.is-capitalized {\n  text-transform: capitalize !important;\n}\n\n.is-lowercase {\n  text-transform: lowercase !important;\n}\n\n.is-uppercase {\n  text-transform: uppercase !important;\n}\n\n.has-text-white {\n  color: white !important;\n}\n\na.has-text-white:hover, a.has-text-white:focus {\n  color: #e6e6e6 !important;\n}\n\n.has-text-black {\n  color: #0a0a0a !important;\n}\n\na.has-text-black:hover, a.has-text-black:focus {\n  color: black !important;\n}\n\n.has-text-light {\n  color: whitesmoke !important;\n}\n\na.has-text-light:hover, a.has-text-light:focus {\n  color: #dbdbdb !important;\n}\n\n.has-text-dark {\n  color: #363636 !important;\n}\n\na.has-text-dark:hover, a.has-text-dark:focus {\n  color: #1c1c1c !important;\n}\n\n.has-text-primary {\n  color: #00d1b2 !important;\n}\n\na.has-text-primary:hover, a.has-text-primary:focus {\n  color: #009e86 !important;\n}\n\n.has-text-link {\n  color: #7a91c1 !important;\n}\n\na.has-text-link:hover, a.has-text-link:focus {\n  color: #205bbc !important;\n}\n\n.has-text-info {\n  color: #209cee !important;\n}\n\na.has-text-info:hover, a.has-text-info:focus {\n  color: #0f81cc !important;\n}\n\n.has-text-success {\n  color: #23d160 !important;\n}\n\na.has-text-success:hover, a.has-text-success:focus {\n  color: #1ca64c !important;\n}\n\n.has-text-warning {\n  color: #ffdd57 !important;\n}\n\na.has-text-warning:hover, a.has-text-warning:focus {\n  color: #ffd324 !important;\n}\n\n.has-text-danger {\n  color: #ff3860 !important;\n}\n\na.has-text-danger:hover, a.has-text-danger:focus {\n  color: #ff0537 !important;\n}\n\n.has-text-black-bis {\n  color: #121212 !important;\n}\n\n.has-text-black-ter {\n  color: #242424 !important;\n}\n\n.has-text-grey-darker {\n  color: #363636 !important;\n}\n\n.has-text-grey-dark {\n  color: #4a4a4a !important;\n}\n\n.has-text-grey {\n  color: #7a7a7a !important;\n}\n\n.has-text-grey-light {\n  color: #b5b5b5 !important;\n}\n\n.has-text-grey-lighter {\n  color: #dbdbdb !important;\n}\n\n.has-text-white-ter {\n  color: whitesmoke !important;\n}\n\n.has-text-white-bis {\n  color: #fafafa !important;\n}\n\n.has-text-weight-light {\n  font-weight: 300 !important;\n}\n\n.has-text-weight-normal {\n  font-weight: 400 !important;\n}\n\n.has-text-weight-semibold {\n  font-weight: 600 !important;\n}\n\n.has-text-weight-bold {\n  font-weight: 700 !important;\n}\n\n.is-block {\n  display: block !important;\n}\n\n@media screen and (max-width: 768px) {\n  .is-block-mobile {\n    display: block !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .is-block-tablet {\n    display: block !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .is-block-tablet-only {\n    display: block !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .is-block-touch {\n    display: block !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .is-block-desktop {\n    display: block !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .is-block-desktop-only {\n    display: block !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .is-block-widescreen {\n    display: block !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .is-block-widescreen-only {\n    display: block !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .is-block-fullhd {\n    display: block !important;\n  }\n}\n\n.is-flex {\n  display: -webkit-box !important;\n  display: -ms-flexbox !important;\n  display: flex !important;\n}\n\n@media screen and (max-width: 768px) {\n  .is-flex-mobile {\n    display: -webkit-box !important;\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .is-flex-tablet {\n    display: -webkit-box !important;\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .is-flex-tablet-only {\n    display: -webkit-box !important;\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .is-flex-touch {\n    display: -webkit-box !important;\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .is-flex-desktop {\n    display: -webkit-box !important;\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .is-flex-desktop-only {\n    display: -webkit-box !important;\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .is-flex-widescreen {\n    display: -webkit-box !important;\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .is-flex-widescreen-only {\n    display: -webkit-box !important;\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .is-flex-fullhd {\n    display: -webkit-box !important;\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n}\n\n.is-inline {\n  display: inline !important;\n}\n\n@media screen and (max-width: 768px) {\n  .is-inline-mobile {\n    display: inline !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .is-inline-tablet {\n    display: inline !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .is-inline-tablet-only {\n    display: inline !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .is-inline-touch {\n    display: inline !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .is-inline-desktop {\n    display: inline !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .is-inline-desktop-only {\n    display: inline !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .is-inline-widescreen {\n    display: inline !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .is-inline-widescreen-only {\n    display: inline !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .is-inline-fullhd {\n    display: inline !important;\n  }\n}\n\n.is-inline-block {\n  display: inline-block !important;\n}\n\n@media screen and (max-width: 768px) {\n  .is-inline-block-mobile {\n    display: inline-block !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .is-inline-block-tablet {\n    display: inline-block !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .is-inline-block-tablet-only {\n    display: inline-block !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .is-inline-block-touch {\n    display: inline-block !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .is-inline-block-desktop {\n    display: inline-block !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .is-inline-block-desktop-only {\n    display: inline-block !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .is-inline-block-widescreen {\n    display: inline-block !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .is-inline-block-widescreen-only {\n    display: inline-block !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .is-inline-block-fullhd {\n    display: inline-block !important;\n  }\n}\n\n.is-inline-flex {\n  display: -webkit-inline-box !important;\n  display: -ms-inline-flexbox !important;\n  display: inline-flex !important;\n}\n\n@media screen and (max-width: 768px) {\n  .is-inline-flex-mobile {\n    display: -webkit-inline-box !important;\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .is-inline-flex-tablet {\n    display: -webkit-inline-box !important;\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .is-inline-flex-tablet-only {\n    display: -webkit-inline-box !important;\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .is-inline-flex-touch {\n    display: -webkit-inline-box !important;\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .is-inline-flex-desktop {\n    display: -webkit-inline-box !important;\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .is-inline-flex-desktop-only {\n    display: -webkit-inline-box !important;\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .is-inline-flex-widescreen {\n    display: -webkit-inline-box !important;\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .is-inline-flex-widescreen-only {\n    display: -webkit-inline-box !important;\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .is-inline-flex-fullhd {\n    display: -webkit-inline-box !important;\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n.is-hidden {\n  display: none !important;\n}\n\n@media screen and (max-width: 768px) {\n  .is-hidden-mobile {\n    display: none !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .is-hidden-tablet {\n    display: none !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .is-hidden-tablet-only {\n    display: none !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .is-hidden-touch {\n    display: none !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .is-hidden-desktop {\n    display: none !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .is-hidden-desktop-only {\n    display: none !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .is-hidden-widescreen {\n    display: none !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .is-hidden-widescreen-only {\n    display: none !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .is-hidden-fullhd {\n    display: none !important;\n  }\n}\n\n.is-invisible {\n  visibility: hidden !important;\n}\n\n@media screen and (max-width: 768px) {\n  .is-invisible-mobile {\n    visibility: hidden !important;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .is-invisible-tablet {\n    visibility: hidden !important;\n  }\n}\n\n@media screen and (min-width: 769px) and (max-width: 1023px) {\n  .is-invisible-tablet-only {\n    visibility: hidden !important;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .is-invisible-touch {\n    visibility: hidden !important;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .is-invisible-desktop {\n    visibility: hidden !important;\n  }\n}\n\n@media screen and (min-width: 1024px) and (max-width: 1215px) {\n  .is-invisible-desktop-only {\n    visibility: hidden !important;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .is-invisible-widescreen {\n    visibility: hidden !important;\n  }\n}\n\n@media screen and (min-width: 1216px) and (max-width: 1407px) {\n  .is-invisible-widescreen-only {\n    visibility: hidden !important;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .is-invisible-fullhd {\n    visibility: hidden !important;\n  }\n}\n\n.is-marginless {\n  margin: 0 !important;\n}\n\n.is-paddingless {\n  padding: 0 !important;\n}\n\n.is-radiusless {\n  border-radius: 0 !important;\n}\n\n.is-shadowless {\n  -webkit-box-shadow: none !important;\n          box-shadow: none !important;\n}\n\n.is-unselectable {\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n\n.box {\n  background-color: white;\n  border-radius: 5px;\n  -webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);\n          box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);\n  color: #4a4a4a;\n  display: block;\n  padding: 1.25rem;\n}\n\n.box:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\na.box:hover, a.box:focus {\n  -webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px #7a91c1;\n          box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px #7a91c1;\n}\n\na.box:active {\n  -webkit-box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2), 0 0 0 1px #7a91c1;\n          box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2), 0 0 0 1px #7a91c1;\n}\n\n.button {\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  border: 1px solid transparent;\n  border-radius: 3px;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  display: -webkit-inline-box;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  font-size: 1rem;\n  height: 2.25em;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n  line-height: 1.5;\n  padding-bottom: calc(0.375em - 1px);\n  padding-left: calc(0.625em - 1px);\n  padding-right: calc(0.625em - 1px);\n  padding-top: calc(0.375em - 1px);\n  position: relative;\n  vertical-align: top;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  background-color: white;\n  border-color: #dbdbdb;\n  color: #363636;\n  cursor: pointer;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  padding-left: 0.75em;\n  padding-right: 0.75em;\n  text-align: center;\n  white-space: nowrap;\n}\n\n.button:focus, .button.is-focused, .button:active, .button.is-active {\n  outline: none;\n}\n\n.button[disabled] {\n  cursor: not-allowed;\n}\n\n.button strong {\n  color: inherit;\n}\n\n.button .icon, .button .icon.is-small, .button .icon.is-medium, .button .icon.is-large {\n  height: 1.5em;\n  width: 1.5em;\n}\n\n.button .icon:first-child:not(:last-child) {\n  margin-left: calc(-0.375em - 1px);\n  margin-right: 0.1875em;\n}\n\n.button .icon:last-child:not(:first-child) {\n  margin-left: 0.1875em;\n  margin-right: calc(-0.375em - 1px);\n}\n\n.button .icon:first-child:last-child {\n  margin-left: calc(-0.375em - 1px);\n  margin-right: calc(-0.375em - 1px);\n}\n\n.button:hover, .button.is-hovered {\n  border-color: #b5b5b5;\n  color: #363636;\n}\n\n.button:focus, .button.is-focused {\n  border-color: #7a91c1;\n  color: #363636;\n}\n\n.button:focus:not(:active), .button.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n}\n\n.button:active, .button.is-active {\n  border-color: #4a4a4a;\n  color: #363636;\n}\n\n.button.is-text {\n  background-color: transparent;\n  border-color: transparent;\n  color: #4a4a4a;\n  text-decoration: underline;\n}\n\n.button.is-text:hover, .button.is-text.is-hovered, .button.is-text:focus, .button.is-text.is-focused {\n  background-color: whitesmoke;\n  color: #363636;\n}\n\n.button.is-text:active, .button.is-text.is-active {\n  background-color: #e8e8e8;\n  color: #363636;\n}\n\n.button.is-text[disabled] {\n  background-color: transparent;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-white {\n  background-color: white;\n  border-color: transparent;\n  color: #0a0a0a;\n}\n\n.button.is-white:hover, .button.is-white.is-hovered {\n  background-color: #f9f9f9;\n  border-color: transparent;\n  color: #0a0a0a;\n}\n\n.button.is-white:focus, .button.is-white.is-focused {\n  border-color: transparent;\n  color: #0a0a0a;\n}\n\n.button.is-white:focus:not(:active), .button.is-white.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25);\n}\n\n.button.is-white:active, .button.is-white.is-active {\n  background-color: #f2f2f2;\n  border-color: transparent;\n  color: #0a0a0a;\n}\n\n.button.is-white[disabled] {\n  background-color: white;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-white.is-inverted {\n  background-color: #0a0a0a;\n  color: white;\n}\n\n.button.is-white.is-inverted:hover {\n  background-color: black;\n}\n\n.button.is-white.is-inverted[disabled] {\n  background-color: #0a0a0a;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: white;\n}\n\n.button.is-white.is-loading:after {\n  border-color: transparent transparent #0a0a0a #0a0a0a !important;\n}\n\n.button.is-white.is-outlined {\n  background-color: transparent;\n  border-color: white;\n  color: white;\n}\n\n.button.is-white.is-outlined:hover, .button.is-white.is-outlined:focus {\n  background-color: white;\n  border-color: white;\n  color: #0a0a0a;\n}\n\n.button.is-white.is-outlined.is-loading:after {\n  border-color: transparent transparent white white !important;\n}\n\n.button.is-white.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: white;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: white;\n}\n\n.button.is-white.is-inverted.is-outlined {\n  background-color: transparent;\n  border-color: #0a0a0a;\n  color: #0a0a0a;\n}\n\n.button.is-white.is-inverted.is-outlined:hover, .button.is-white.is-inverted.is-outlined:focus {\n  background-color: #0a0a0a;\n  color: white;\n}\n\n.button.is-white.is-inverted.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #0a0a0a;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #0a0a0a;\n}\n\n.button.is-black {\n  background-color: #0a0a0a;\n  border-color: transparent;\n  color: white;\n}\n\n.button.is-black:hover, .button.is-black.is-hovered {\n  background-color: #040404;\n  border-color: transparent;\n  color: white;\n}\n\n.button.is-black:focus, .button.is-black.is-focused {\n  border-color: transparent;\n  color: white;\n}\n\n.button.is-black:focus:not(:active), .button.is-black.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25);\n}\n\n.button.is-black:active, .button.is-black.is-active {\n  background-color: black;\n  border-color: transparent;\n  color: white;\n}\n\n.button.is-black[disabled] {\n  background-color: #0a0a0a;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-black.is-inverted {\n  background-color: white;\n  color: #0a0a0a;\n}\n\n.button.is-black.is-inverted:hover {\n  background-color: #f2f2f2;\n}\n\n.button.is-black.is-inverted[disabled] {\n  background-color: white;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #0a0a0a;\n}\n\n.button.is-black.is-loading:after {\n  border-color: transparent transparent white white !important;\n}\n\n.button.is-black.is-outlined {\n  background-color: transparent;\n  border-color: #0a0a0a;\n  color: #0a0a0a;\n}\n\n.button.is-black.is-outlined:hover, .button.is-black.is-outlined:focus {\n  background-color: #0a0a0a;\n  border-color: #0a0a0a;\n  color: white;\n}\n\n.button.is-black.is-outlined.is-loading:after {\n  border-color: transparent transparent #0a0a0a #0a0a0a !important;\n}\n\n.button.is-black.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #0a0a0a;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #0a0a0a;\n}\n\n.button.is-black.is-inverted.is-outlined {\n  background-color: transparent;\n  border-color: white;\n  color: white;\n}\n\n.button.is-black.is-inverted.is-outlined:hover, .button.is-black.is-inverted.is-outlined:focus {\n  background-color: white;\n  color: #0a0a0a;\n}\n\n.button.is-black.is-inverted.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: white;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: white;\n}\n\n.button.is-light {\n  background-color: whitesmoke;\n  border-color: transparent;\n  color: #363636;\n}\n\n.button.is-light:hover, .button.is-light.is-hovered {\n  background-color: #eeeeee;\n  border-color: transparent;\n  color: #363636;\n}\n\n.button.is-light:focus, .button.is-light.is-focused {\n  border-color: transparent;\n  color: #363636;\n}\n\n.button.is-light:focus:not(:active), .button.is-light.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25);\n}\n\n.button.is-light:active, .button.is-light.is-active {\n  background-color: #e8e8e8;\n  border-color: transparent;\n  color: #363636;\n}\n\n.button.is-light[disabled] {\n  background-color: whitesmoke;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-light.is-inverted {\n  background-color: #363636;\n  color: whitesmoke;\n}\n\n.button.is-light.is-inverted:hover {\n  background-color: #292929;\n}\n\n.button.is-light.is-inverted[disabled] {\n  background-color: #363636;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: whitesmoke;\n}\n\n.button.is-light.is-loading:after {\n  border-color: transparent transparent #363636 #363636 !important;\n}\n\n.button.is-light.is-outlined {\n  background-color: transparent;\n  border-color: whitesmoke;\n  color: whitesmoke;\n}\n\n.button.is-light.is-outlined:hover, .button.is-light.is-outlined:focus {\n  background-color: whitesmoke;\n  border-color: whitesmoke;\n  color: #363636;\n}\n\n.button.is-light.is-outlined.is-loading:after {\n  border-color: transparent transparent whitesmoke whitesmoke !important;\n}\n\n.button.is-light.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: whitesmoke;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: whitesmoke;\n}\n\n.button.is-light.is-inverted.is-outlined {\n  background-color: transparent;\n  border-color: #363636;\n  color: #363636;\n}\n\n.button.is-light.is-inverted.is-outlined:hover, .button.is-light.is-inverted.is-outlined:focus {\n  background-color: #363636;\n  color: whitesmoke;\n}\n\n.button.is-light.is-inverted.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #363636;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #363636;\n}\n\n.button.is-dark {\n  background-color: #363636;\n  border-color: transparent;\n  color: whitesmoke;\n}\n\n.button.is-dark:hover, .button.is-dark.is-hovered {\n  background-color: #2f2f2f;\n  border-color: transparent;\n  color: whitesmoke;\n}\n\n.button.is-dark:focus, .button.is-dark.is-focused {\n  border-color: transparent;\n  color: whitesmoke;\n}\n\n.button.is-dark:focus:not(:active), .button.is-dark.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25);\n}\n\n.button.is-dark:active, .button.is-dark.is-active {\n  background-color: #292929;\n  border-color: transparent;\n  color: whitesmoke;\n}\n\n.button.is-dark[disabled] {\n  background-color: #363636;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-dark.is-inverted {\n  background-color: whitesmoke;\n  color: #363636;\n}\n\n.button.is-dark.is-inverted:hover {\n  background-color: #e8e8e8;\n}\n\n.button.is-dark.is-inverted[disabled] {\n  background-color: whitesmoke;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #363636;\n}\n\n.button.is-dark.is-loading:after {\n  border-color: transparent transparent whitesmoke whitesmoke !important;\n}\n\n.button.is-dark.is-outlined {\n  background-color: transparent;\n  border-color: #363636;\n  color: #363636;\n}\n\n.button.is-dark.is-outlined:hover, .button.is-dark.is-outlined:focus {\n  background-color: #363636;\n  border-color: #363636;\n  color: whitesmoke;\n}\n\n.button.is-dark.is-outlined.is-loading:after {\n  border-color: transparent transparent #363636 #363636 !important;\n}\n\n.button.is-dark.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #363636;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #363636;\n}\n\n.button.is-dark.is-inverted.is-outlined {\n  background-color: transparent;\n  border-color: whitesmoke;\n  color: whitesmoke;\n}\n\n.button.is-dark.is-inverted.is-outlined:hover, .button.is-dark.is-inverted.is-outlined:focus {\n  background-color: whitesmoke;\n  color: #363636;\n}\n\n.button.is-dark.is-inverted.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: whitesmoke;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: whitesmoke;\n}\n\n.button.is-primary {\n  background-color: #00d1b2;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-primary:hover, .button.is-primary.is-hovered {\n  background-color: #00c4a7;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-primary:focus, .button.is-primary.is-focused {\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-primary:focus:not(:active), .button.is-primary.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25);\n}\n\n.button.is-primary:active, .button.is-primary.is-active {\n  background-color: #00b89c;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-primary[disabled] {\n  background-color: #00d1b2;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-primary.is-inverted {\n  background-color: #fff;\n  color: #00d1b2;\n}\n\n.button.is-primary.is-inverted:hover {\n  background-color: #f2f2f2;\n}\n\n.button.is-primary.is-inverted[disabled] {\n  background-color: #fff;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #00d1b2;\n}\n\n.button.is-primary.is-loading:after {\n  border-color: transparent transparent #fff #fff !important;\n}\n\n.button.is-primary.is-outlined {\n  background-color: transparent;\n  border-color: #00d1b2;\n  color: #00d1b2;\n}\n\n.button.is-primary.is-outlined:hover, .button.is-primary.is-outlined:focus {\n  background-color: #00d1b2;\n  border-color: #00d1b2;\n  color: #fff;\n}\n\n.button.is-primary.is-outlined.is-loading:after {\n  border-color: transparent transparent #00d1b2 #00d1b2 !important;\n}\n\n.button.is-primary.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #00d1b2;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #00d1b2;\n}\n\n.button.is-primary.is-inverted.is-outlined {\n  background-color: transparent;\n  border-color: #fff;\n  color: #fff;\n}\n\n.button.is-primary.is-inverted.is-outlined:hover, .button.is-primary.is-inverted.is-outlined:focus {\n  background-color: #fff;\n  color: #00d1b2;\n}\n\n.button.is-primary.is-inverted.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #fff;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #fff;\n}\n\n.button.is-link {\n  background-color: #7a91c1;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-link:hover, .button.is-link.is-hovered {\n  background-color: #276cda;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-link:focus, .button.is-link.is-focused {\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-link:focus:not(:active), .button.is-link.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n}\n\n.button.is-link:active, .button.is-link.is-active {\n  background-color: #2366d1;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-link[disabled] {\n  background-color: #7a91c1;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-link.is-inverted {\n  background-color: #fff;\n  color: #7a91c1;\n}\n\n.button.is-link.is-inverted:hover {\n  background-color: #f2f2f2;\n}\n\n.button.is-link.is-inverted[disabled] {\n  background-color: #fff;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #7a91c1;\n}\n\n.button.is-link.is-loading:after {\n  border-color: transparent transparent #fff #fff !important;\n}\n\n.button.is-link.is-outlined {\n  background-color: transparent;\n  border-color: #7a91c1;\n  color: #7a91c1;\n}\n\n.button.is-link.is-outlined:hover, .button.is-link.is-outlined:focus {\n  background-color: #7a91c1;\n  border-color: #7a91c1;\n  color: #fff;\n}\n\n.button.is-link.is-outlined.is-loading:after {\n  border-color: transparent transparent #7a91c1 #7a91c1 !important;\n}\n\n.button.is-link.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #7a91c1;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #7a91c1;\n}\n\n.button.is-link.is-inverted.is-outlined {\n  background-color: transparent;\n  border-color: #fff;\n  color: #fff;\n}\n\n.button.is-link.is-inverted.is-outlined:hover, .button.is-link.is-inverted.is-outlined:focus {\n  background-color: #fff;\n  color: #7a91c1;\n}\n\n.button.is-link.is-inverted.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #fff;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #fff;\n}\n\n.button.is-info {\n  background-color: #209cee;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-info:hover, .button.is-info.is-hovered {\n  background-color: #1496ed;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-info:focus, .button.is-info.is-focused {\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-info:focus:not(:active), .button.is-info.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25);\n}\n\n.button.is-info:active, .button.is-info.is-active {\n  background-color: #118fe4;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-info[disabled] {\n  background-color: #209cee;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-info.is-inverted {\n  background-color: #fff;\n  color: #209cee;\n}\n\n.button.is-info.is-inverted:hover {\n  background-color: #f2f2f2;\n}\n\n.button.is-info.is-inverted[disabled] {\n  background-color: #fff;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #209cee;\n}\n\n.button.is-info.is-loading:after {\n  border-color: transparent transparent #fff #fff !important;\n}\n\n.button.is-info.is-outlined {\n  background-color: transparent;\n  border-color: #209cee;\n  color: #209cee;\n}\n\n.button.is-info.is-outlined:hover, .button.is-info.is-outlined:focus {\n  background-color: #209cee;\n  border-color: #209cee;\n  color: #fff;\n}\n\n.button.is-info.is-outlined.is-loading:after {\n  border-color: transparent transparent #209cee #209cee !important;\n}\n\n.button.is-info.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #209cee;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #209cee;\n}\n\n.button.is-info.is-inverted.is-outlined {\n  background-color: transparent;\n  border-color: #fff;\n  color: #fff;\n}\n\n.button.is-info.is-inverted.is-outlined:hover, .button.is-info.is-inverted.is-outlined:focus {\n  background-color: #fff;\n  color: #209cee;\n}\n\n.button.is-info.is-inverted.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #fff;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #fff;\n}\n\n.button.is-success {\n  background-color: #23d160;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-success:hover, .button.is-success.is-hovered {\n  background-color: #22c65b;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-success:focus, .button.is-success.is-focused {\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-success:focus:not(:active), .button.is-success.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25);\n}\n\n.button.is-success:active, .button.is-success.is-active {\n  background-color: #20bc56;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-success[disabled] {\n  background-color: #23d160;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-success.is-inverted {\n  background-color: #fff;\n  color: #23d160;\n}\n\n.button.is-success.is-inverted:hover {\n  background-color: #f2f2f2;\n}\n\n.button.is-success.is-inverted[disabled] {\n  background-color: #fff;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #23d160;\n}\n\n.button.is-success.is-loading:after {\n  border-color: transparent transparent #fff #fff !important;\n}\n\n.button.is-success.is-outlined {\n  background-color: transparent;\n  border-color: #23d160;\n  color: #23d160;\n}\n\n.button.is-success.is-outlined:hover, .button.is-success.is-outlined:focus {\n  background-color: #23d160;\n  border-color: #23d160;\n  color: #fff;\n}\n\n.button.is-success.is-outlined.is-loading:after {\n  border-color: transparent transparent #23d160 #23d160 !important;\n}\n\n.button.is-success.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #23d160;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #23d160;\n}\n\n.button.is-success.is-inverted.is-outlined {\n  background-color: transparent;\n  border-color: #fff;\n  color: #fff;\n}\n\n.button.is-success.is-inverted.is-outlined:hover, .button.is-success.is-inverted.is-outlined:focus {\n  background-color: #fff;\n  color: #23d160;\n}\n\n.button.is-success.is-inverted.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #fff;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #fff;\n}\n\n.button.is-warning {\n  background-color: #ffdd57;\n  border-color: transparent;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.button.is-warning:hover, .button.is-warning.is-hovered {\n  background-color: #ffdb4a;\n  border-color: transparent;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.button.is-warning:focus, .button.is-warning.is-focused {\n  border-color: transparent;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.button.is-warning:focus:not(:active), .button.is-warning.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25);\n}\n\n.button.is-warning:active, .button.is-warning.is-active {\n  background-color: #ffd83d;\n  border-color: transparent;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.button.is-warning[disabled] {\n  background-color: #ffdd57;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-warning.is-inverted {\n  background-color: rgba(0, 0, 0, 0.7);\n  color: #ffdd57;\n}\n\n.button.is-warning.is-inverted:hover {\n  background-color: rgba(0, 0, 0, 0.7);\n}\n\n.button.is-warning.is-inverted[disabled] {\n  background-color: rgba(0, 0, 0, 0.7);\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #ffdd57;\n}\n\n.button.is-warning.is-loading:after {\n  border-color: transparent transparent rgba(0, 0, 0, 0.7) rgba(0, 0, 0, 0.7) !important;\n}\n\n.button.is-warning.is-outlined {\n  background-color: transparent;\n  border-color: #ffdd57;\n  color: #ffdd57;\n}\n\n.button.is-warning.is-outlined:hover, .button.is-warning.is-outlined:focus {\n  background-color: #ffdd57;\n  border-color: #ffdd57;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.button.is-warning.is-outlined.is-loading:after {\n  border-color: transparent transparent #ffdd57 #ffdd57 !important;\n}\n\n.button.is-warning.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #ffdd57;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #ffdd57;\n}\n\n.button.is-warning.is-inverted.is-outlined {\n  background-color: transparent;\n  border-color: rgba(0, 0, 0, 0.7);\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.button.is-warning.is-inverted.is-outlined:hover, .button.is-warning.is-inverted.is-outlined:focus {\n  background-color: rgba(0, 0, 0, 0.7);\n  color: #ffdd57;\n}\n\n.button.is-warning.is-inverted.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: rgba(0, 0, 0, 0.7);\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.button.is-danger {\n  background-color: #ff3860;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-danger:hover, .button.is-danger.is-hovered {\n  background-color: #ff2b56;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-danger:focus, .button.is-danger.is-focused {\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-danger:focus:not(:active), .button.is-danger.is-focused:not(:active) {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25);\n}\n\n.button.is-danger:active, .button.is-danger.is-active {\n  background-color: #ff1f4b;\n  border-color: transparent;\n  color: #fff;\n}\n\n.button.is-danger[disabled] {\n  background-color: #ff3860;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.button.is-danger.is-inverted {\n  background-color: #fff;\n  color: #ff3860;\n}\n\n.button.is-danger.is-inverted:hover {\n  background-color: #f2f2f2;\n}\n\n.button.is-danger.is-inverted[disabled] {\n  background-color: #fff;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #ff3860;\n}\n\n.button.is-danger.is-loading:after {\n  border-color: transparent transparent #fff #fff !important;\n}\n\n.button.is-danger.is-outlined {\n  background-color: transparent;\n  border-color: #ff3860;\n  color: #ff3860;\n}\n\n.button.is-danger.is-outlined:hover, .button.is-danger.is-outlined:focus {\n  background-color: #ff3860;\n  border-color: #ff3860;\n  color: #fff;\n}\n\n.button.is-danger.is-outlined.is-loading:after {\n  border-color: transparent transparent #ff3860 #ff3860 !important;\n}\n\n.button.is-danger.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #ff3860;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #ff3860;\n}\n\n.button.is-danger.is-inverted.is-outlined {\n  background-color: transparent;\n  border-color: #fff;\n  color: #fff;\n}\n\n.button.is-danger.is-inverted.is-outlined:hover, .button.is-danger.is-inverted.is-outlined:focus {\n  background-color: #fff;\n  color: #ff3860;\n}\n\n.button.is-danger.is-inverted.is-outlined[disabled] {\n  background-color: transparent;\n  border-color: #fff;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #fff;\n}\n\n.button.is-small {\n  border-radius: 2px;\n  font-size: 0.75rem;\n}\n\n.button.is-medium {\n  font-size: 1.25rem;\n}\n\n.button.is-large {\n  font-size: 1.5rem;\n}\n\n.button[disabled] {\n  background-color: white;\n  border-color: #dbdbdb;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  opacity: 0.5;\n}\n\n.button.is-fullwidth {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  width: 100%;\n}\n\n.button.is-loading {\n  color: transparent !important;\n  pointer-events: none;\n}\n\n.button.is-loading:after {\n  -webkit-animation: spinAround 500ms infinite linear;\n          animation: spinAround 500ms infinite linear;\n  border: 2px solid #dbdbdb;\n  border-radius: 290486px;\n  border-right-color: transparent;\n  border-top-color: transparent;\n  content: \"\";\n  display: block;\n  height: 1em;\n  position: relative;\n  width: 1em;\n  position: absolute;\n  left: calc(50% - (1em / 2));\n  top: calc(50% - (1em / 2));\n  position: absolute !important;\n}\n\n.button.is-static {\n  background-color: whitesmoke;\n  border-color: #dbdbdb;\n  color: #7a7a7a;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  pointer-events: none;\n}\n\n.buttons {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n      flex-wrap: wrap;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n}\n\n.buttons .button {\n  margin-bottom: 0.5rem;\n}\n\n.buttons .button:not(:last-child) {\n  margin-right: 0.5rem;\n}\n\n.buttons:last-child {\n  margin-bottom: -0.5rem;\n}\n\n.buttons:not(:last-child) {\n  margin-bottom: 1rem;\n}\n\n.buttons.has-addons .button:not(:first-child) {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.buttons.has-addons .button:not(:last-child) {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n  margin-right: -1px;\n}\n\n.buttons.has-addons .button:last-child {\n  margin-right: 0;\n}\n\n.buttons.has-addons .button:hover, .buttons.has-addons .button.is-hovered {\n  z-index: 2;\n}\n\n.buttons.has-addons .button:focus, .buttons.has-addons .button.is-focused, .buttons.has-addons .button:active, .buttons.has-addons .button.is-active, .buttons.has-addons .button.is-selected {\n  z-index: 3;\n}\n\n.buttons.has-addons .button:focus:hover, .buttons.has-addons .button.is-focused:hover, .buttons.has-addons .button:active:hover, .buttons.has-addons .button.is-active:hover, .buttons.has-addons .button.is-selected:hover {\n  z-index: 4;\n}\n\n.buttons.is-centered {\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.buttons.is-right {\n  -webkit-box-pack: end;\n      -ms-flex-pack: end;\n          justify-content: flex-end;\n}\n\n.container {\n  margin: 0 auto;\n  position: relative;\n}\n\n@media screen and (min-width: 1024px) {\n  .container {\n    max-width: 960px;\n    width: 960px;\n  }\n  .container.is-fluid {\n    margin-left: 32px;\n    margin-right: 32px;\n    max-width: none;\n    width: auto;\n  }\n}\n\n@media screen and (max-width: 1215px) {\n  .container.is-widescreen {\n    max-width: 1152px;\n    width: auto;\n  }\n}\n\n@media screen and (max-width: 1407px) {\n  .container.is-fullhd {\n    max-width: 1344px;\n    width: auto;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .container {\n    max-width: 1152px;\n    width: 1152px;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .container {\n    max-width: 1344px;\n    width: 1344px;\n  }\n}\n\n.content:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.content li + li {\n  margin-top: 0.25em;\n}\n\n.content p:not(:last-child),\n.content dl:not(:last-child),\n.content ol:not(:last-child),\n.content ul:not(:last-child),\n.content blockquote:not(:last-child),\n.content pre:not(:last-child),\n.content table:not(:last-child) {\n  margin-bottom: 1em;\n}\n\n.content h1,\n.content h2,\n.content h3,\n.content h4,\n.content h5,\n.content h6 {\n  color: #363636;\n  font-weight: 400;\n  line-height: 1.125;\n}\n\n.content h1 {\n  font-size: 2em;\n  margin-bottom: 0.5em;\n}\n\n.content h1:not(:first-child) {\n  margin-top: 1em;\n}\n\n.content h2 {\n  font-size: 1.75em;\n  margin-bottom: 0.5714em;\n}\n\n.content h2:not(:first-child) {\n  margin-top: 1.1428em;\n}\n\n.content h3 {\n  font-size: 1.5em;\n  margin-bottom: 0.6666em;\n}\n\n.content h3:not(:first-child) {\n  margin-top: 1.3333em;\n}\n\n.content h4 {\n  font-size: 1.25em;\n  margin-bottom: 0.8em;\n}\n\n.content h5 {\n  font-size: 1.125em;\n  margin-bottom: 0.8888em;\n}\n\n.content h6 {\n  font-size: 1em;\n  margin-bottom: 1em;\n}\n\n.content blockquote {\n  background-color: whitesmoke;\n  border-left: 5px solid #dbdbdb;\n  padding: 1.25em 1.5em;\n}\n\n.content ol {\n  list-style: decimal outside;\n  margin-left: 2em;\n  margin-top: 1em;\n}\n\n.content ul {\n  list-style: disc outside;\n  margin-left: 2em;\n  margin-top: 1em;\n}\n\n.content ul ul {\n  list-style-type: circle;\n  margin-top: 0.5em;\n}\n\n.content ul ul ul {\n  list-style-type: square;\n}\n\n.content dd {\n  margin-left: 2em;\n}\n\n.content figure {\n  margin-left: 2em;\n  margin-right: 2em;\n  text-align: center;\n}\n\n.content figure:not(:first-child) {\n  margin-top: 2em;\n}\n\n.content figure:not(:last-child) {\n  margin-bottom: 2em;\n}\n\n.content figure img {\n  display: inline-block;\n}\n\n.content figure figcaption {\n  font-style: italic;\n}\n\n.content pre {\n  -webkit-overflow-scrolling: touch;\n  overflow-x: auto;\n  padding: 1.25em 1.5em;\n  white-space: pre;\n  word-wrap: normal;\n}\n\n.content sup,\n.content sub {\n  font-size: 75%;\n}\n\n.content table {\n  width: 100%;\n}\n\n.content table td,\n.content table th {\n  border: 1px solid #dbdbdb;\n  border-width: 0 0 1px;\n  padding: 0.5em 0.75em;\n  vertical-align: top;\n}\n\n.content table th {\n  color: #363636;\n  text-align: left;\n}\n\n.content table tr:hover {\n  background-color: whitesmoke;\n}\n\n.content table thead td,\n.content table thead th {\n  border-width: 0 0 2px;\n  color: #363636;\n}\n\n.content table tfoot td,\n.content table tfoot th {\n  border-width: 2px 0 0;\n  color: #363636;\n}\n\n.content table tbody tr:last-child td,\n.content table tbody tr:last-child th {\n  border-bottom-width: 0;\n}\n\n.content.is-small {\n  font-size: 0.75rem;\n}\n\n.content.is-medium {\n  font-size: 1.25rem;\n}\n\n.content.is-large {\n  font-size: 1.5rem;\n}\n\n.input,\n.textarea {\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  border: 1px solid transparent;\n  border-radius: 3px;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  display: -webkit-inline-box;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  font-size: 1rem;\n  height: 2.25em;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n  line-height: 1.5;\n  padding-bottom: calc(0.375em - 1px);\n  padding-left: calc(0.625em - 1px);\n  padding-right: calc(0.625em - 1px);\n  padding-top: calc(0.375em - 1px);\n  position: relative;\n  vertical-align: top;\n  background-color: white;\n  border-color: #dbdbdb;\n  color: #363636;\n  -webkit-box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1);\n          box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1);\n  max-width: 100%;\n  width: 100%;\n}\n\n.input:focus, .input.is-focused, .input:active, .input.is-active,\n.textarea:focus,\n.textarea.is-focused,\n.textarea:active,\n.textarea.is-active {\n  outline: none;\n}\n\n.input[disabled],\n.textarea[disabled] {\n  cursor: not-allowed;\n}\n\n.input::-moz-placeholder,\n.textarea::-moz-placeholder {\n  color: rgba(54, 54, 54, 0.3);\n}\n\n.input::-webkit-input-placeholder,\n.textarea::-webkit-input-placeholder {\n  color: rgba(54, 54, 54, 0.3);\n}\n\n.input:-moz-placeholder,\n.textarea:-moz-placeholder {\n  color: rgba(54, 54, 54, 0.3);\n}\n\n.input:-ms-input-placeholder,\n.textarea:-ms-input-placeholder {\n  color: rgba(54, 54, 54, 0.3);\n}\n\n.input:hover, .input.is-hovered,\n.textarea:hover,\n.textarea.is-hovered {\n  border-color: #b5b5b5;\n}\n\n.input:focus, .input.is-focused, .input:active, .input.is-active,\n.textarea:focus,\n.textarea.is-focused,\n.textarea:active,\n.textarea.is-active {\n  border-color: #7a91c1;\n  -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n}\n\n.input[disabled],\n.textarea[disabled] {\n  background-color: whitesmoke;\n  border-color: whitesmoke;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #7a7a7a;\n}\n\n.input[disabled]::-moz-placeholder,\n.textarea[disabled]::-moz-placeholder {\n  color: rgba(122, 122, 122, 0.3);\n}\n\n.input[disabled]::-webkit-input-placeholder,\n.textarea[disabled]::-webkit-input-placeholder {\n  color: rgba(122, 122, 122, 0.3);\n}\n\n.input[disabled]:-moz-placeholder,\n.textarea[disabled]:-moz-placeholder {\n  color: rgba(122, 122, 122, 0.3);\n}\n\n.input[disabled]:-ms-input-placeholder,\n.textarea[disabled]:-ms-input-placeholder {\n  color: rgba(122, 122, 122, 0.3);\n}\n\n.input[type=\"search\"],\n.textarea[type=\"search\"] {\n  border-radius: 290486px;\n}\n\n.input[readonly],\n.textarea[readonly] {\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.input.is-white,\n.textarea.is-white {\n  border-color: white;\n}\n\n.input.is-white:focus, .input.is-white.is-focused, .input.is-white:active, .input.is-white.is-active,\n.textarea.is-white:focus,\n.textarea.is-white.is-focused,\n.textarea.is-white:active,\n.textarea.is-white.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25);\n}\n\n.input.is-black,\n.textarea.is-black {\n  border-color: #0a0a0a;\n}\n\n.input.is-black:focus, .input.is-black.is-focused, .input.is-black:active, .input.is-black.is-active,\n.textarea.is-black:focus,\n.textarea.is-black.is-focused,\n.textarea.is-black:active,\n.textarea.is-black.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25);\n}\n\n.input.is-light,\n.textarea.is-light {\n  border-color: whitesmoke;\n}\n\n.input.is-light:focus, .input.is-light.is-focused, .input.is-light:active, .input.is-light.is-active,\n.textarea.is-light:focus,\n.textarea.is-light.is-focused,\n.textarea.is-light:active,\n.textarea.is-light.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25);\n}\n\n.input.is-dark,\n.textarea.is-dark {\n  border-color: #363636;\n}\n\n.input.is-dark:focus, .input.is-dark.is-focused, .input.is-dark:active, .input.is-dark.is-active,\n.textarea.is-dark:focus,\n.textarea.is-dark.is-focused,\n.textarea.is-dark:active,\n.textarea.is-dark.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25);\n}\n\n.input.is-primary,\n.textarea.is-primary {\n  border-color: #00d1b2;\n}\n\n.input.is-primary:focus, .input.is-primary.is-focused, .input.is-primary:active, .input.is-primary.is-active,\n.textarea.is-primary:focus,\n.textarea.is-primary.is-focused,\n.textarea.is-primary:active,\n.textarea.is-primary.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25);\n}\n\n.input.is-link,\n.textarea.is-link {\n  border-color: #7a91c1;\n}\n\n.input.is-link:focus, .input.is-link.is-focused, .input.is-link:active, .input.is-link.is-active,\n.textarea.is-link:focus,\n.textarea.is-link.is-focused,\n.textarea.is-link:active,\n.textarea.is-link.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n}\n\n.input.is-info,\n.textarea.is-info {\n  border-color: #209cee;\n}\n\n.input.is-info:focus, .input.is-info.is-focused, .input.is-info:active, .input.is-info.is-active,\n.textarea.is-info:focus,\n.textarea.is-info.is-focused,\n.textarea.is-info:active,\n.textarea.is-info.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25);\n}\n\n.input.is-success,\n.textarea.is-success {\n  border-color: #23d160;\n}\n\n.input.is-success:focus, .input.is-success.is-focused, .input.is-success:active, .input.is-success.is-active,\n.textarea.is-success:focus,\n.textarea.is-success.is-focused,\n.textarea.is-success:active,\n.textarea.is-success.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25);\n}\n\n.input.is-warning,\n.textarea.is-warning {\n  border-color: #ffdd57;\n}\n\n.input.is-warning:focus, .input.is-warning.is-focused, .input.is-warning:active, .input.is-warning.is-active,\n.textarea.is-warning:focus,\n.textarea.is-warning.is-focused,\n.textarea.is-warning:active,\n.textarea.is-warning.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25);\n}\n\n.input.is-danger,\n.textarea.is-danger {\n  border-color: #ff3860;\n}\n\n.input.is-danger:focus, .input.is-danger.is-focused, .input.is-danger:active, .input.is-danger.is-active,\n.textarea.is-danger:focus,\n.textarea.is-danger.is-focused,\n.textarea.is-danger:active,\n.textarea.is-danger.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25);\n}\n\n.input.is-small,\n.textarea.is-small {\n  border-radius: 2px;\n  font-size: 0.75rem;\n}\n\n.input.is-medium,\n.textarea.is-medium {\n  font-size: 1.25rem;\n}\n\n.input.is-large,\n.textarea.is-large {\n  font-size: 1.5rem;\n}\n\n.input.is-fullwidth,\n.textarea.is-fullwidth {\n  display: block;\n  width: 100%;\n}\n\n.input.is-inline,\n.textarea.is-inline {\n  display: inline;\n  width: auto;\n}\n\n.input.is-static {\n  background-color: transparent;\n  border-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.textarea {\n  display: block;\n  max-width: 100%;\n  min-width: 100%;\n  padding: 0.625em;\n  resize: vertical;\n}\n\n.textarea:not([rows]) {\n  max-height: 600px;\n  min-height: 120px;\n}\n\n.textarea[rows] {\n  height: unset;\n}\n\n.textarea.has-fixed-size {\n  resize: none;\n}\n\n.checkbox,\n.radio {\n  cursor: pointer;\n  display: inline-block;\n  line-height: 1.25;\n  position: relative;\n}\n\n.checkbox input,\n.radio input {\n  cursor: pointer;\n}\n\n.checkbox:hover,\n.radio:hover {\n  color: #363636;\n}\n\n.checkbox[disabled],\n.radio[disabled] {\n  color: #7a7a7a;\n  cursor: not-allowed;\n}\n\n.radio + .radio {\n  margin-left: 0.5em;\n}\n\n.select {\n  display: inline-block;\n  max-width: 100%;\n  position: relative;\n  vertical-align: top;\n}\n\n.select:not(.is-multiple) {\n  height: 2.25em;\n}\n\n.select:not(.is-multiple)::after {\n  border: 1px solid #7a91c1;\n  border-right: 0;\n  border-top: 0;\n  content: \" \";\n  display: block;\n  height: 0.5em;\n  pointer-events: none;\n  position: absolute;\n  -webkit-transform: rotate(-45deg);\n          transform: rotate(-45deg);\n  -webkit-transform-origin: center;\n          transform-origin: center;\n  width: 0.5em;\n  margin-top: -0.375em;\n  right: 1.125em;\n  top: 50%;\n  z-index: 4;\n}\n\n.select select {\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  border: 1px solid transparent;\n  border-radius: 3px;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  display: -webkit-inline-box;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  font-size: 1rem;\n  height: 2.25em;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n  line-height: 1.5;\n  padding-bottom: calc(0.375em - 1px);\n  padding-left: calc(0.625em - 1px);\n  padding-right: calc(0.625em - 1px);\n  padding-top: calc(0.375em - 1px);\n  position: relative;\n  vertical-align: top;\n  background-color: white;\n  border-color: #dbdbdb;\n  color: #363636;\n  cursor: pointer;\n  display: block;\n  font-size: 1em;\n  max-width: 100%;\n  outline: none;\n}\n\n.select select:focus, .select select.is-focused, .select select:active, .select select.is-active {\n  outline: none;\n}\n\n.select select[disabled] {\n  cursor: not-allowed;\n}\n\n.select select::-moz-placeholder {\n  color: rgba(54, 54, 54, 0.3);\n}\n\n.select select::-webkit-input-placeholder {\n  color: rgba(54, 54, 54, 0.3);\n}\n\n.select select:-moz-placeholder {\n  color: rgba(54, 54, 54, 0.3);\n}\n\n.select select:-ms-input-placeholder {\n  color: rgba(54, 54, 54, 0.3);\n}\n\n.select select:hover, .select select.is-hovered {\n  border-color: #b5b5b5;\n}\n\n.select select:focus, .select select.is-focused, .select select:active, .select select.is-active {\n  border-color: #7a91c1;\n  -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n}\n\n.select select[disabled] {\n  background-color: whitesmoke;\n  border-color: whitesmoke;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #7a7a7a;\n}\n\n.select select[disabled]::-moz-placeholder {\n  color: rgba(122, 122, 122, 0.3);\n}\n\n.select select[disabled]::-webkit-input-placeholder {\n  color: rgba(122, 122, 122, 0.3);\n}\n\n.select select[disabled]:-moz-placeholder {\n  color: rgba(122, 122, 122, 0.3);\n}\n\n.select select[disabled]:-ms-input-placeholder {\n  color: rgba(122, 122, 122, 0.3);\n}\n\n.select select::-ms-expand {\n  display: none;\n}\n\n.select select[disabled]:hover {\n  border-color: whitesmoke;\n}\n\n.select select:not([multiple]) {\n  padding-right: 2.5em;\n}\n\n.select select[multiple] {\n  height: unset;\n  padding: 0;\n}\n\n.select select[multiple] option {\n  padding: 0.5em 1em;\n}\n\n.select:hover::after {\n  border-color: #363636;\n}\n\n.select.is-white select {\n  border-color: white;\n}\n\n.select.is-white select:focus, .select.is-white select.is-focused, .select.is-white select:active, .select.is-white select.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25);\n}\n\n.select.is-black select {\n  border-color: #0a0a0a;\n}\n\n.select.is-black select:focus, .select.is-black select.is-focused, .select.is-black select:active, .select.is-black select.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25);\n}\n\n.select.is-light select {\n  border-color: whitesmoke;\n}\n\n.select.is-light select:focus, .select.is-light select.is-focused, .select.is-light select:active, .select.is-light select.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25);\n}\n\n.select.is-dark select {\n  border-color: #363636;\n}\n\n.select.is-dark select:focus, .select.is-dark select.is-focused, .select.is-dark select:active, .select.is-dark select.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25);\n}\n\n.select.is-primary select {\n  border-color: #00d1b2;\n}\n\n.select.is-primary select:focus, .select.is-primary select.is-focused, .select.is-primary select:active, .select.is-primary select.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25);\n}\n\n.select.is-link select {\n  border-color: #7a91c1;\n}\n\n.select.is-link select:focus, .select.is-link select.is-focused, .select.is-link select:active, .select.is-link select.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);\n}\n\n.select.is-info select {\n  border-color: #209cee;\n}\n\n.select.is-info select:focus, .select.is-info select.is-focused, .select.is-info select:active, .select.is-info select.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25);\n}\n\n.select.is-success select {\n  border-color: #23d160;\n}\n\n.select.is-success select:focus, .select.is-success select.is-focused, .select.is-success select:active, .select.is-success select.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25);\n}\n\n.select.is-warning select {\n  border-color: #ffdd57;\n}\n\n.select.is-warning select:focus, .select.is-warning select.is-focused, .select.is-warning select:active, .select.is-warning select.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25);\n}\n\n.select.is-danger select {\n  border-color: #ff3860;\n}\n\n.select.is-danger select:focus, .select.is-danger select.is-focused, .select.is-danger select:active, .select.is-danger select.is-active {\n  -webkit-box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25);\n          box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25);\n}\n\n.select.is-small {\n  border-radius: 2px;\n  font-size: 0.75rem;\n}\n\n.select.is-medium {\n  font-size: 1.25rem;\n}\n\n.select.is-large {\n  font-size: 1.5rem;\n}\n\n.select.is-disabled::after {\n  border-color: #7a7a7a;\n}\n\n.select.is-fullwidth {\n  width: 100%;\n}\n\n.select.is-fullwidth select {\n  width: 100%;\n}\n\n.select.is-loading::after {\n  -webkit-animation: spinAround 500ms infinite linear;\n          animation: spinAround 500ms infinite linear;\n  border: 2px solid #dbdbdb;\n  border-radius: 290486px;\n  border-right-color: transparent;\n  border-top-color: transparent;\n  content: \"\";\n  display: block;\n  height: 1em;\n  position: relative;\n  width: 1em;\n  margin-top: 0;\n  position: absolute;\n  right: 0.625em;\n  top: 0.625em;\n  -webkit-transform: none;\n          transform: none;\n}\n\n.select.is-loading.is-small:after {\n  font-size: 0.75rem;\n}\n\n.select.is-loading.is-medium:after {\n  font-size: 1.25rem;\n}\n\n.select.is-loading.is-large:after {\n  font-size: 1.5rem;\n}\n\n.file {\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  -webkit-box-align: stretch;\n      -ms-flex-align: stretch;\n          align-items: stretch;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n  position: relative;\n}\n\n.file.is-white .file-cta {\n  background-color: white;\n  border-color: transparent;\n  color: #0a0a0a;\n}\n\n.file.is-white:hover .file-cta, .file.is-white.is-hovered .file-cta {\n  background-color: #f9f9f9;\n  border-color: transparent;\n  color: #0a0a0a;\n}\n\n.file.is-white:focus .file-cta, .file.is-white.is-focused .file-cta {\n  border-color: transparent;\n  -webkit-box-shadow: 0 0 0.5em rgba(255, 255, 255, 0.25);\n          box-shadow: 0 0 0.5em rgba(255, 255, 255, 0.25);\n  color: #0a0a0a;\n}\n\n.file.is-white:active .file-cta, .file.is-white.is-active .file-cta {\n  background-color: #f2f2f2;\n  border-color: transparent;\n  color: #0a0a0a;\n}\n\n.file.is-black .file-cta {\n  background-color: #0a0a0a;\n  border-color: transparent;\n  color: white;\n}\n\n.file.is-black:hover .file-cta, .file.is-black.is-hovered .file-cta {\n  background-color: #040404;\n  border-color: transparent;\n  color: white;\n}\n\n.file.is-black:focus .file-cta, .file.is-black.is-focused .file-cta {\n  border-color: transparent;\n  -webkit-box-shadow: 0 0 0.5em rgba(10, 10, 10, 0.25);\n          box-shadow: 0 0 0.5em rgba(10, 10, 10, 0.25);\n  color: white;\n}\n\n.file.is-black:active .file-cta, .file.is-black.is-active .file-cta {\n  background-color: black;\n  border-color: transparent;\n  color: white;\n}\n\n.file.is-light .file-cta {\n  background-color: whitesmoke;\n  border-color: transparent;\n  color: #363636;\n}\n\n.file.is-light:hover .file-cta, .file.is-light.is-hovered .file-cta {\n  background-color: #eeeeee;\n  border-color: transparent;\n  color: #363636;\n}\n\n.file.is-light:focus .file-cta, .file.is-light.is-focused .file-cta {\n  border-color: transparent;\n  -webkit-box-shadow: 0 0 0.5em rgba(245, 245, 245, 0.25);\n          box-shadow: 0 0 0.5em rgba(245, 245, 245, 0.25);\n  color: #363636;\n}\n\n.file.is-light:active .file-cta, .file.is-light.is-active .file-cta {\n  background-color: #e8e8e8;\n  border-color: transparent;\n  color: #363636;\n}\n\n.file.is-dark .file-cta {\n  background-color: #363636;\n  border-color: transparent;\n  color: whitesmoke;\n}\n\n.file.is-dark:hover .file-cta, .file.is-dark.is-hovered .file-cta {\n  background-color: #2f2f2f;\n  border-color: transparent;\n  color: whitesmoke;\n}\n\n.file.is-dark:focus .file-cta, .file.is-dark.is-focused .file-cta {\n  border-color: transparent;\n  -webkit-box-shadow: 0 0 0.5em rgba(54, 54, 54, 0.25);\n          box-shadow: 0 0 0.5em rgba(54, 54, 54, 0.25);\n  color: whitesmoke;\n}\n\n.file.is-dark:active .file-cta, .file.is-dark.is-active .file-cta {\n  background-color: #292929;\n  border-color: transparent;\n  color: whitesmoke;\n}\n\n.file.is-primary .file-cta {\n  background-color: #00d1b2;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-primary:hover .file-cta, .file.is-primary.is-hovered .file-cta {\n  background-color: #00c4a7;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-primary:focus .file-cta, .file.is-primary.is-focused .file-cta {\n  border-color: transparent;\n  -webkit-box-shadow: 0 0 0.5em rgba(0, 209, 178, 0.25);\n          box-shadow: 0 0 0.5em rgba(0, 209, 178, 0.25);\n  color: #fff;\n}\n\n.file.is-primary:active .file-cta, .file.is-primary.is-active .file-cta {\n  background-color: #00b89c;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-link .file-cta {\n  background-color: #7a91c1;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-link:hover .file-cta, .file.is-link.is-hovered .file-cta {\n  background-color: #276cda;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-link:focus .file-cta, .file.is-link.is-focused .file-cta {\n  border-color: transparent;\n  -webkit-box-shadow: 0 0 0.5em rgba(50, 115, 220, 0.25);\n          box-shadow: 0 0 0.5em rgba(50, 115, 220, 0.25);\n  color: #fff;\n}\n\n.file.is-link:active .file-cta, .file.is-link.is-active .file-cta {\n  background-color: #2366d1;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-info .file-cta {\n  background-color: #209cee;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-info:hover .file-cta, .file.is-info.is-hovered .file-cta {\n  background-color: #1496ed;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-info:focus .file-cta, .file.is-info.is-focused .file-cta {\n  border-color: transparent;\n  -webkit-box-shadow: 0 0 0.5em rgba(32, 156, 238, 0.25);\n          box-shadow: 0 0 0.5em rgba(32, 156, 238, 0.25);\n  color: #fff;\n}\n\n.file.is-info:active .file-cta, .file.is-info.is-active .file-cta {\n  background-color: #118fe4;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-success .file-cta {\n  background-color: #23d160;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-success:hover .file-cta, .file.is-success.is-hovered .file-cta {\n  background-color: #22c65b;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-success:focus .file-cta, .file.is-success.is-focused .file-cta {\n  border-color: transparent;\n  -webkit-box-shadow: 0 0 0.5em rgba(35, 209, 96, 0.25);\n          box-shadow: 0 0 0.5em rgba(35, 209, 96, 0.25);\n  color: #fff;\n}\n\n.file.is-success:active .file-cta, .file.is-success.is-active .file-cta {\n  background-color: #20bc56;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-warning .file-cta {\n  background-color: #ffdd57;\n  border-color: transparent;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.file.is-warning:hover .file-cta, .file.is-warning.is-hovered .file-cta {\n  background-color: #ffdb4a;\n  border-color: transparent;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.file.is-warning:focus .file-cta, .file.is-warning.is-focused .file-cta {\n  border-color: transparent;\n  -webkit-box-shadow: 0 0 0.5em rgba(255, 221, 87, 0.25);\n          box-shadow: 0 0 0.5em rgba(255, 221, 87, 0.25);\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.file.is-warning:active .file-cta, .file.is-warning.is-active .file-cta {\n  background-color: #ffd83d;\n  border-color: transparent;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.file.is-danger .file-cta {\n  background-color: #ff3860;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-danger:hover .file-cta, .file.is-danger.is-hovered .file-cta {\n  background-color: #ff2b56;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-danger:focus .file-cta, .file.is-danger.is-focused .file-cta {\n  border-color: transparent;\n  -webkit-box-shadow: 0 0 0.5em rgba(255, 56, 96, 0.25);\n          box-shadow: 0 0 0.5em rgba(255, 56, 96, 0.25);\n  color: #fff;\n}\n\n.file.is-danger:active .file-cta, .file.is-danger.is-active .file-cta {\n  background-color: #ff1f4b;\n  border-color: transparent;\n  color: #fff;\n}\n\n.file.is-small {\n  font-size: 0.75rem;\n}\n\n.file.is-medium {\n  font-size: 1.25rem;\n}\n\n.file.is-medium .file-icon .fa {\n  font-size: 21px;\n}\n\n.file.is-large {\n  font-size: 1.5rem;\n}\n\n.file.is-large .file-icon .fa {\n  font-size: 28px;\n}\n\n.file.has-name .file-cta {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.file.has-name .file-name {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.file.has-name.is-empty .file-cta {\n  border-radius: 3px;\n}\n\n.file.has-name.is-empty .file-name {\n  display: none;\n}\n\n.file.is-centered {\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.file.is-right {\n  -webkit-box-pack: end;\n      -ms-flex-pack: end;\n          justify-content: flex-end;\n}\n\n.file.is-boxed .file-label {\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-direction: column;\n          flex-direction: column;\n}\n\n.file.is-boxed .file-cta {\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-direction: column;\n          flex-direction: column;\n  height: auto;\n  padding: 1em 3em;\n}\n\n.file.is-boxed .file-name {\n  border-width: 0 1px 1px;\n}\n\n.file.is-boxed .file-icon {\n  height: 1.5em;\n  width: 1.5em;\n}\n\n.file.is-boxed .file-icon .fa {\n  font-size: 21px;\n}\n\n.file.is-boxed.is-small .file-icon .fa {\n  font-size: 14px;\n}\n\n.file.is-boxed.is-medium .file-icon .fa {\n  font-size: 28px;\n}\n\n.file.is-boxed.is-large .file-icon .fa {\n  font-size: 35px;\n}\n\n.file.is-boxed.has-name .file-cta {\n  border-radius: 3px 3px 0 0;\n}\n\n.file.is-boxed.has-name .file-name {\n  border-radius: 0 0 3px 3px;\n  border-width: 0 1px 1px;\n}\n\n.file.is-right .file-cta {\n  border-radius: 0 3px 3px 0;\n}\n\n.file.is-right .file-name {\n  border-radius: 3px 0 0 3px;\n  border-width: 1px 0 1px 1px;\n  -webkit-box-ordinal-group: 0;\n      -ms-flex-order: -1;\n          order: -1;\n}\n\n.file.is-fullwidth .file-label {\n  width: 100%;\n}\n\n.file.is-fullwidth .file-name {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  max-width: none;\n}\n\n.file-label {\n  -webkit-box-align: stretch;\n      -ms-flex-align: stretch;\n          align-items: stretch;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  cursor: pointer;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n  overflow: hidden;\n  position: relative;\n}\n\n.file-label:hover .file-cta {\n  background-color: #eeeeee;\n  color: #363636;\n}\n\n.file-label:hover .file-name {\n  border-color: #d5d5d5;\n}\n\n.file-label:active .file-cta {\n  background-color: #e8e8e8;\n  color: #363636;\n}\n\n.file-label:active .file-name {\n  border-color: #cfcfcf;\n}\n\n.file-input {\n  height: 0.01em;\n  left: 0;\n  outline: none;\n  position: absolute;\n  top: 0;\n  width: 0.01em;\n}\n\n.file-cta,\n.file-name {\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  border: 1px solid transparent;\n  border-radius: 3px;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  display: -webkit-inline-box;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  font-size: 1rem;\n  height: 2.25em;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n  line-height: 1.5;\n  padding-bottom: calc(0.375em - 1px);\n  padding-left: calc(0.625em - 1px);\n  padding-right: calc(0.625em - 1px);\n  padding-top: calc(0.375em - 1px);\n  position: relative;\n  vertical-align: top;\n  border-color: #dbdbdb;\n  border-radius: 3px;\n  font-size: 1em;\n  padding-left: 1em;\n  padding-right: 1em;\n  white-space: nowrap;\n}\n\n.file-cta:focus, .file-cta.is-focused, .file-cta:active, .file-cta.is-active,\n.file-name:focus,\n.file-name.is-focused,\n.file-name:active,\n.file-name.is-active {\n  outline: none;\n}\n\n.file-cta[disabled],\n.file-name[disabled] {\n  cursor: not-allowed;\n}\n\n.file-cta {\n  background-color: whitesmoke;\n  color: #4a4a4a;\n}\n\n.file-name {\n  border-color: #dbdbdb;\n  border-style: solid;\n  border-width: 1px 1px 1px 0;\n  display: block;\n  max-width: 16em;\n  overflow: hidden;\n  text-align: left;\n  text-overflow: ellipsis;\n}\n\n.file-icon {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  height: 1em;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  margin-right: 0.5em;\n  width: 1em;\n}\n\n.file-icon .fa {\n  font-size: 14px;\n}\n\n.label {\n  color: #363636;\n  display: block;\n  font-size: 1rem;\n  font-weight: 700;\n}\n\n.label:not(:last-child) {\n  margin-bottom: 0.5em;\n}\n\n.label.is-small {\n  font-size: 0.75rem;\n}\n\n.label.is-medium {\n  font-size: 1.25rem;\n}\n\n.label.is-large {\n  font-size: 1.5rem;\n}\n\n.help {\n  display: block;\n  font-size: 0.75rem;\n  margin-top: 0.25rem;\n}\n\n.help.is-white {\n  color: white;\n}\n\n.help.is-black {\n  color: #0a0a0a;\n}\n\n.help.is-light {\n  color: whitesmoke;\n}\n\n.help.is-dark {\n  color: #363636;\n}\n\n.help.is-primary {\n  color: #00d1b2;\n}\n\n.help.is-link {\n  color: #7a91c1;\n}\n\n.help.is-info {\n  color: #209cee;\n}\n\n.help.is-success {\n  color: #23d160;\n}\n\n.help.is-warning {\n  color: #ffdd57;\n}\n\n.help.is-danger {\n  color: #ff3860;\n}\n\n.field:not(:last-child) {\n  margin-bottom: 0.75rem;\n}\n\n.field.has-addons {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n}\n\n.field.has-addons .control:not(:last-child) {\n  margin-right: -1px;\n}\n\n.field.has-addons .control:first-child .button,\n.field.has-addons .control:first-child .input,\n.field.has-addons .control:first-child .select select {\n  border-bottom-left-radius: 3px;\n  border-top-left-radius: 3px;\n}\n\n.field.has-addons .control:last-child .button,\n.field.has-addons .control:last-child .input,\n.field.has-addons .control:last-child .select select {\n  border-bottom-right-radius: 3px;\n  border-top-right-radius: 3px;\n}\n\n.field.has-addons .control .button,\n.field.has-addons .control .input,\n.field.has-addons .control .select select {\n  border-radius: 0;\n}\n\n.field.has-addons .control .button:hover, .field.has-addons .control .button.is-hovered,\n.field.has-addons .control .input:hover,\n.field.has-addons .control .input.is-hovered,\n.field.has-addons .control .select select:hover,\n.field.has-addons .control .select select.is-hovered {\n  z-index: 2;\n}\n\n.field.has-addons .control .button:focus, .field.has-addons .control .button.is-focused, .field.has-addons .control .button:active, .field.has-addons .control .button.is-active,\n.field.has-addons .control .input:focus,\n.field.has-addons .control .input.is-focused,\n.field.has-addons .control .input:active,\n.field.has-addons .control .input.is-active,\n.field.has-addons .control .select select:focus,\n.field.has-addons .control .select select.is-focused,\n.field.has-addons .control .select select:active,\n.field.has-addons .control .select select.is-active {\n  z-index: 3;\n}\n\n.field.has-addons .control .button:focus:hover, .field.has-addons .control .button.is-focused:hover, .field.has-addons .control .button:active:hover, .field.has-addons .control .button.is-active:hover,\n.field.has-addons .control .input:focus:hover,\n.field.has-addons .control .input.is-focused:hover,\n.field.has-addons .control .input:active:hover,\n.field.has-addons .control .input.is-active:hover,\n.field.has-addons .control .select select:focus:hover,\n.field.has-addons .control .select select.is-focused:hover,\n.field.has-addons .control .select select:active:hover,\n.field.has-addons .control .select select.is-active:hover {\n  z-index: 4;\n}\n\n.field.has-addons .control.is-expanded {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n}\n\n.field.has-addons.has-addons-centered {\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.field.has-addons.has-addons-right {\n  -webkit-box-pack: end;\n      -ms-flex-pack: end;\n          justify-content: flex-end;\n}\n\n.field.has-addons.has-addons-fullwidth .control {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n}\n\n.field.is-grouped {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n}\n\n.field.is-grouped > .control {\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n}\n\n.field.is-grouped > .control:not(:last-child) {\n  margin-bottom: 0;\n  margin-right: 0.75rem;\n}\n\n.field.is-grouped > .control.is-expanded {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 1;\n      flex-shrink: 1;\n}\n\n.field.is-grouped.is-grouped-centered {\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.field.is-grouped.is-grouped-right {\n  -webkit-box-pack: end;\n      -ms-flex-pack: end;\n          justify-content: flex-end;\n}\n\n.field.is-grouped.is-grouped-multiline {\n  -ms-flex-wrap: wrap;\n      flex-wrap: wrap;\n}\n\n.field.is-grouped.is-grouped-multiline > .control:last-child, .field.is-grouped.is-grouped-multiline > .control:not(:last-child) {\n  margin-bottom: 0.75rem;\n}\n\n.field.is-grouped.is-grouped-multiline:last-child {\n  margin-bottom: -0.75rem;\n}\n\n.field.is-grouped.is-grouped-multiline:not(:last-child) {\n  margin-bottom: 0;\n}\n\n@media screen and (min-width: 769px), print {\n  .field.is-horizontal {\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n}\n\n.field-label .label {\n  font-size: inherit;\n}\n\n@media screen and (max-width: 768px) {\n  .field-label {\n    margin-bottom: 0.5rem;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .field-label {\n    -ms-flex-preferred-size: 0;\n        flex-basis: 0;\n    -webkit-box-flex: 1;\n        -ms-flex-positive: 1;\n            flex-grow: 1;\n    -ms-flex-negative: 0;\n        flex-shrink: 0;\n    margin-right: 1.5rem;\n    text-align: right;\n  }\n  .field-label.is-small {\n    font-size: 0.75rem;\n    padding-top: 0.375em;\n  }\n  .field-label.is-normal {\n    padding-top: 0.375em;\n  }\n  .field-label.is-medium {\n    font-size: 1.25rem;\n    padding-top: 0.375em;\n  }\n  .field-label.is-large {\n    font-size: 1.5rem;\n    padding-top: 0.375em;\n  }\n}\n\n.field-body .field .field {\n  margin-bottom: 0;\n}\n\n@media screen and (min-width: 769px), print {\n  .field-body {\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-preferred-size: 0;\n        flex-basis: 0;\n    -webkit-box-flex: 5;\n        -ms-flex-positive: 5;\n            flex-grow: 5;\n    -ms-flex-negative: 1;\n        flex-shrink: 1;\n  }\n  .field-body .field {\n    margin-bottom: 0;\n  }\n  .field-body > .field {\n    -ms-flex-negative: 1;\n        flex-shrink: 1;\n  }\n  .field-body > .field:not(.is-narrow) {\n    -webkit-box-flex: 1;\n        -ms-flex-positive: 1;\n            flex-grow: 1;\n  }\n  .field-body > .field:not(:last-child) {\n    margin-right: 0.75rem;\n  }\n}\n\n.control {\n  font-size: 1rem;\n  position: relative;\n  text-align: left;\n}\n\n.control.has-icon .icon {\n  color: #dbdbdb;\n  height: 2.25em;\n  pointer-events: none;\n  position: absolute;\n  top: 0;\n  width: 2.25em;\n  z-index: 4;\n}\n\n.control.has-icon .input:focus + .icon {\n  color: #7a7a7a;\n}\n\n.control.has-icon .input.is-small + .icon {\n  font-size: 0.75rem;\n}\n\n.control.has-icon .input.is-medium + .icon {\n  font-size: 1.25rem;\n}\n\n.control.has-icon .input.is-large + .icon {\n  font-size: 1.5rem;\n}\n\n.control.has-icon:not(.has-icon-right) .icon {\n  left: 0;\n}\n\n.control.has-icon:not(.has-icon-right) .input {\n  padding-left: 2.25em;\n}\n\n.control.has-icon.has-icon-right .icon {\n  right: 0;\n}\n\n.control.has-icon.has-icon-right .input {\n  padding-right: 2.25em;\n}\n\n.control.has-icons-left .input:focus ~ .icon,\n.control.has-icons-left .select:focus ~ .icon, .control.has-icons-right .input:focus ~ .icon,\n.control.has-icons-right .select:focus ~ .icon {\n  color: #7a7a7a;\n}\n\n.control.has-icons-left .input.is-small ~ .icon,\n.control.has-icons-left .select.is-small ~ .icon, .control.has-icons-right .input.is-small ~ .icon,\n.control.has-icons-right .select.is-small ~ .icon {\n  font-size: 0.75rem;\n}\n\n.control.has-icons-left .input.is-medium ~ .icon,\n.control.has-icons-left .select.is-medium ~ .icon, .control.has-icons-right .input.is-medium ~ .icon,\n.control.has-icons-right .select.is-medium ~ .icon {\n  font-size: 1.25rem;\n}\n\n.control.has-icons-left .input.is-large ~ .icon,\n.control.has-icons-left .select.is-large ~ .icon, .control.has-icons-right .input.is-large ~ .icon,\n.control.has-icons-right .select.is-large ~ .icon {\n  font-size: 1.5rem;\n}\n\n.control.has-icons-left .icon, .control.has-icons-right .icon {\n  color: #dbdbdb;\n  height: 2.25em;\n  pointer-events: none;\n  position: absolute;\n  top: 0;\n  width: 2.25em;\n  z-index: 4;\n}\n\n.control.has-icons-left .input,\n.control.has-icons-left .select select {\n  padding-left: 2.25em;\n}\n\n.control.has-icons-left .icon.is-left {\n  left: 0;\n}\n\n.control.has-icons-right .input,\n.control.has-icons-right .select select {\n  padding-right: 2.25em;\n}\n\n.control.has-icons-right .icon.is-right {\n  right: 0;\n}\n\n.control.is-loading::after {\n  -webkit-animation: spinAround 500ms infinite linear;\n          animation: spinAround 500ms infinite linear;\n  border: 2px solid #dbdbdb;\n  border-radius: 290486px;\n  border-right-color: transparent;\n  border-top-color: transparent;\n  content: \"\";\n  display: block;\n  height: 1em;\n  position: relative;\n  width: 1em;\n  position: absolute !important;\n  right: 0.625em;\n  top: 0.625em;\n}\n\n.control.is-loading.is-small:after {\n  font-size: 0.75rem;\n}\n\n.control.is-loading.is-medium:after {\n  font-size: 1.25rem;\n}\n\n.control.is-loading.is-large:after {\n  font-size: 1.5rem;\n}\n\n.icon {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: -webkit-inline-box;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  height: 1.5rem;\n  width: 1.5rem;\n}\n\n.icon.is-small {\n  height: 1rem;\n  width: 1rem;\n}\n\n.icon.is-medium {\n  height: 2rem;\n  width: 2rem;\n}\n\n.icon.is-large {\n  height: 3rem;\n  width: 3rem;\n}\n\n.image {\n  display: block;\n  position: relative;\n}\n\n.image img {\n  display: block;\n  height: auto;\n  width: 100%;\n}\n\n.image.is-square img, .image.is-1by1 img, .image.is-4by3 img, .image.is-3by2 img, .image.is-16by9 img, .image.is-2by1 img {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  height: 100%;\n  width: 100%;\n}\n\n.image.is-square, .image.is-1by1 {\n  padding-top: 100%;\n}\n\n.image.is-4by3 {\n  padding-top: 75%;\n}\n\n.image.is-3by2 {\n  padding-top: 66.6666%;\n}\n\n.image.is-16by9 {\n  padding-top: 56.25%;\n}\n\n.image.is-2by1 {\n  padding-top: 50%;\n}\n\n.image.is-16x16 {\n  height: 16px;\n  width: 16px;\n}\n\n.image.is-24x24 {\n  height: 24px;\n  width: 24px;\n}\n\n.image.is-32x32 {\n  height: 32px;\n  width: 32px;\n}\n\n.image.is-48x48 {\n  height: 48px;\n  width: 48px;\n}\n\n.image.is-64x64 {\n  height: 64px;\n  width: 64px;\n}\n\n.image.is-96x96 {\n  height: 96px;\n  width: 96px;\n}\n\n.image.is-128x128 {\n  height: 128px;\n  width: 128px;\n}\n\n.notification {\n  background-color: whitesmoke;\n  border-radius: 3px;\n  padding: 1.25rem 2.5rem 1.25rem 1.5rem;\n  position: relative;\n}\n\n.notification:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.notification a:not(.button) {\n  color: currentColor;\n  text-decoration: underline;\n}\n\n.notification strong {\n  color: currentColor;\n}\n\n.notification code,\n.notification pre {\n  background: white;\n}\n\n.notification pre code {\n  background: transparent;\n}\n\n.notification > .delete {\n  position: absolute;\n  right: 0.5em;\n  top: 0.5em;\n}\n\n.notification .title,\n.notification .subtitle,\n.notification .content {\n  color: currentColor;\n}\n\n.notification.is-white {\n  background-color: white;\n  color: #0a0a0a;\n}\n\n.notification.is-black {\n  background-color: #0a0a0a;\n  color: white;\n}\n\n.notification.is-light {\n  background-color: whitesmoke;\n  color: #363636;\n}\n\n.notification.is-dark {\n  background-color: #363636;\n  color: whitesmoke;\n}\n\n.notification.is-primary {\n  background-color: #00d1b2;\n  color: #fff;\n}\n\n.notification.is-link {\n  background-color: #7a91c1;\n  color: #fff;\n}\n\n.notification.is-info {\n  background-color: #209cee;\n  color: #fff;\n}\n\n.notification.is-success {\n  background-color: #23d160;\n  color: #fff;\n}\n\n.notification.is-warning {\n  background-color: #ffdd57;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.notification.is-danger {\n  background-color: #ff3860;\n  color: #fff;\n}\n\n.progress {\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  border: none;\n  border-radius: 290486px;\n  display: block;\n  height: 1rem;\n  overflow: hidden;\n  padding: 0;\n  width: 100%;\n}\n\n.progress:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.progress::-webkit-progress-bar {\n  background-color: #dbdbdb;\n}\n\n.progress::-webkit-progress-value {\n  background-color: #4a4a4a;\n}\n\n.progress::-moz-progress-bar {\n  background-color: #4a4a4a;\n}\n\n.progress::-ms-fill {\n  background-color: #4a4a4a;\n  border: none;\n}\n\n.progress.is-white::-webkit-progress-value {\n  background-color: white;\n}\n\n.progress.is-white::-moz-progress-bar {\n  background-color: white;\n}\n\n.progress.is-white::-ms-fill {\n  background-color: white;\n}\n\n.progress.is-black::-webkit-progress-value {\n  background-color: #0a0a0a;\n}\n\n.progress.is-black::-moz-progress-bar {\n  background-color: #0a0a0a;\n}\n\n.progress.is-black::-ms-fill {\n  background-color: #0a0a0a;\n}\n\n.progress.is-light::-webkit-progress-value {\n  background-color: whitesmoke;\n}\n\n.progress.is-light::-moz-progress-bar {\n  background-color: whitesmoke;\n}\n\n.progress.is-light::-ms-fill {\n  background-color: whitesmoke;\n}\n\n.progress.is-dark::-webkit-progress-value {\n  background-color: #363636;\n}\n\n.progress.is-dark::-moz-progress-bar {\n  background-color: #363636;\n}\n\n.progress.is-dark::-ms-fill {\n  background-color: #363636;\n}\n\n.progress.is-primary::-webkit-progress-value {\n  background-color: #00d1b2;\n}\n\n.progress.is-primary::-moz-progress-bar {\n  background-color: #00d1b2;\n}\n\n.progress.is-primary::-ms-fill {\n  background-color: #00d1b2;\n}\n\n.progress.is-link::-webkit-progress-value {\n  background-color: #7a91c1;\n}\n\n.progress.is-link::-moz-progress-bar {\n  background-color: #7a91c1;\n}\n\n.progress.is-link::-ms-fill {\n  background-color: #7a91c1;\n}\n\n.progress.is-info::-webkit-progress-value {\n  background-color: #209cee;\n}\n\n.progress.is-info::-moz-progress-bar {\n  background-color: #209cee;\n}\n\n.progress.is-info::-ms-fill {\n  background-color: #209cee;\n}\n\n.progress.is-success::-webkit-progress-value {\n  background-color: #23d160;\n}\n\n.progress.is-success::-moz-progress-bar {\n  background-color: #23d160;\n}\n\n.progress.is-success::-ms-fill {\n  background-color: #23d160;\n}\n\n.progress.is-warning::-webkit-progress-value {\n  background-color: #ffdd57;\n}\n\n.progress.is-warning::-moz-progress-bar {\n  background-color: #ffdd57;\n}\n\n.progress.is-warning::-ms-fill {\n  background-color: #ffdd57;\n}\n\n.progress.is-danger::-webkit-progress-value {\n  background-color: #ff3860;\n}\n\n.progress.is-danger::-moz-progress-bar {\n  background-color: #ff3860;\n}\n\n.progress.is-danger::-ms-fill {\n  background-color: #ff3860;\n}\n\n.progress.is-small {\n  height: 0.75rem;\n}\n\n.progress.is-medium {\n  height: 1.25rem;\n}\n\n.progress.is-large {\n  height: 1.5rem;\n}\n\n.table {\n  background-color: white;\n  color: #363636;\n  margin-bottom: 1.5rem;\n}\n\n.table td,\n.table th {\n  border: 1px solid #dbdbdb;\n  border-width: 0 0 1px;\n  padding: 0.5em 0.75em;\n  vertical-align: top;\n}\n\n.table td.is-white,\n.table th.is-white {\n  background-color: white;\n  border-color: white;\n  color: #0a0a0a;\n}\n\n.table td.is-black,\n.table th.is-black {\n  background-color: #0a0a0a;\n  border-color: #0a0a0a;\n  color: white;\n}\n\n.table td.is-light,\n.table th.is-light {\n  background-color: whitesmoke;\n  border-color: whitesmoke;\n  color: #363636;\n}\n\n.table td.is-dark,\n.table th.is-dark {\n  background-color: #363636;\n  border-color: #363636;\n  color: whitesmoke;\n}\n\n.table td.is-primary,\n.table th.is-primary {\n  background-color: #00d1b2;\n  border-color: #00d1b2;\n  color: #fff;\n}\n\n.table td.is-link,\n.table th.is-link {\n  background-color: #7a91c1;\n  border-color: #7a91c1;\n  color: #fff;\n}\n\n.table td.is-info,\n.table th.is-info {\n  background-color: #209cee;\n  border-color: #209cee;\n  color: #fff;\n}\n\n.table td.is-success,\n.table th.is-success {\n  background-color: #23d160;\n  border-color: #23d160;\n  color: #fff;\n}\n\n.table td.is-warning,\n.table th.is-warning {\n  background-color: #ffdd57;\n  border-color: #ffdd57;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.table td.is-danger,\n.table th.is-danger {\n  background-color: #ff3860;\n  border-color: #ff3860;\n  color: #fff;\n}\n\n.table td.is-narrow,\n.table th.is-narrow {\n  white-space: nowrap;\n  width: 1%;\n}\n\n.table td.is-selected,\n.table th.is-selected {\n  background-color: #00d1b2;\n  color: #fff;\n}\n\n.table td.is-selected a,\n.table td.is-selected strong,\n.table th.is-selected a,\n.table th.is-selected strong {\n  color: currentColor;\n}\n\n.table th {\n  color: #363636;\n  text-align: left;\n}\n\n.table tr.is-selected {\n  background-color: #00d1b2;\n  color: #fff;\n}\n\n.table tr.is-selected a,\n.table tr.is-selected strong {\n  color: currentColor;\n}\n\n.table tr.is-selected td,\n.table tr.is-selected th {\n  border-color: #fff;\n  color: currentColor;\n}\n\n.table thead td,\n.table thead th {\n  border-width: 0 0 2px;\n  color: #363636;\n}\n\n.table tfoot td,\n.table tfoot th {\n  border-width: 2px 0 0;\n  color: #363636;\n}\n\n.table tbody tr:last-child td,\n.table tbody tr:last-child th {\n  border-bottom-width: 0;\n}\n\n.table.is-bordered td,\n.table.is-bordered th {\n  border-width: 1px;\n}\n\n.table.is-bordered tr:last-child td,\n.table.is-bordered tr:last-child th {\n  border-bottom-width: 1px;\n}\n\n.table.is-fullwidth {\n  width: 100%;\n}\n\n.table.is-hoverable tbody tr:not(.is-selected):hover {\n  background-color: #fafafa;\n}\n\n.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover {\n  background-color: whitesmoke;\n}\n\n.table.is-narrow td,\n.table.is-narrow th {\n  padding: 0.25em 0.5em;\n}\n\n.table.is-striped tbody tr:not(.is-selected):nth-child(even) {\n  background-color: #fafafa;\n}\n\n.tags {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n      flex-wrap: wrap;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n}\n\n.tags .tag {\n  margin-bottom: 0.5rem;\n}\n\n.tags .tag:not(:last-child) {\n  margin-right: 0.5rem;\n}\n\n.tags:last-child {\n  margin-bottom: -0.5rem;\n}\n\n.tags:not(:last-child) {\n  margin-bottom: 1rem;\n}\n\n.tags.has-addons .tag {\n  margin-right: 0;\n}\n\n.tags.has-addons .tag:not(:first-child) {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.tags.has-addons .tag:not(:last-child) {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.tags.is-centered {\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.tags.is-centered .tag {\n  margin-right: 0.25rem;\n  margin-left: 0.25rem;\n}\n\n.tags.is-right {\n  -webkit-box-pack: end;\n      -ms-flex-pack: end;\n          justify-content: flex-end;\n}\n\n.tags.is-right .tag:not(:first-child) {\n  margin-left: 0.5rem;\n}\n\n.tags.is-right .tag:not(:last-child) {\n  margin-right: 0;\n}\n\n.tag:not(body) {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  background-color: whitesmoke;\n  border-radius: 3px;\n  color: #4a4a4a;\n  display: -webkit-inline-box;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  font-size: 0.75rem;\n  height: 2em;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  line-height: 1.5;\n  padding-left: 0.75em;\n  padding-right: 0.75em;\n  white-space: nowrap;\n}\n\n.tag:not(body) .delete {\n  margin-left: 0.25em;\n  margin-right: -0.375em;\n}\n\n.tag:not(body).is-white {\n  background-color: white;\n  color: #0a0a0a;\n}\n\n.tag:not(body).is-black {\n  background-color: #0a0a0a;\n  color: white;\n}\n\n.tag:not(body).is-light {\n  background-color: whitesmoke;\n  color: #363636;\n}\n\n.tag:not(body).is-dark {\n  background-color: #363636;\n  color: whitesmoke;\n}\n\n.tag:not(body).is-primary {\n  background-color: #00d1b2;\n  color: #fff;\n}\n\n.tag:not(body).is-link {\n  background-color: #7a91c1;\n  color: #fff;\n}\n\n.tag:not(body).is-info {\n  background-color: #209cee;\n  color: #fff;\n}\n\n.tag:not(body).is-success {\n  background-color: #23d160;\n  color: #fff;\n}\n\n.tag:not(body).is-warning {\n  background-color: #ffdd57;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.tag:not(body).is-danger {\n  background-color: #ff3860;\n  color: #fff;\n}\n\n.tag:not(body).is-medium {\n  font-size: 1rem;\n}\n\n.tag:not(body).is-large {\n  font-size: 1.25rem;\n}\n\n.tag:not(body) .icon:first-child:not(:last-child) {\n  margin-left: -0.375em;\n  margin-right: 0.1875em;\n}\n\n.tag:not(body) .icon:last-child:not(:first-child) {\n  margin-left: 0.1875em;\n  margin-right: -0.375em;\n}\n\n.tag:not(body) .icon:first-child:last-child {\n  margin-left: -0.375em;\n  margin-right: -0.375em;\n}\n\n.tag:not(body).is-delete {\n  margin-left: 1px;\n  padding: 0;\n  position: relative;\n  width: 2em;\n}\n\n.tag:not(body).is-delete:before, .tag:not(body).is-delete:after {\n  background-color: currentColor;\n  content: \"\";\n  display: block;\n  left: 50%;\n  position: absolute;\n  top: 50%;\n  -webkit-transform: translateX(-50%) translateY(-50%) rotate(45deg);\n          transform: translateX(-50%) translateY(-50%) rotate(45deg);\n  -webkit-transform-origin: center center;\n          transform-origin: center center;\n}\n\n.tag:not(body).is-delete:before {\n  height: 1px;\n  width: 50%;\n}\n\n.tag:not(body).is-delete:after {\n  height: 50%;\n  width: 1px;\n}\n\n.tag:not(body).is-delete:hover, .tag:not(body).is-delete:focus {\n  background-color: #e8e8e8;\n}\n\n.tag:not(body).is-delete:active {\n  background-color: #dbdbdb;\n}\n\n.tag:not(body).is-rounded {\n  border-radius: 290486px;\n}\n\na.tag:hover {\n  text-decoration: underline;\n}\n\n.title,\n.subtitle {\n  word-break: break-word;\n}\n\n.title:not(:last-child),\n.subtitle:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.title em,\n.title span,\n.subtitle em,\n.subtitle span {\n  font-weight: inherit;\n}\n\n.title .tag,\n.subtitle .tag {\n  vertical-align: middle;\n}\n\n.title {\n  color: #363636;\n  font-size: 2rem;\n  font-weight: 600;\n  line-height: 1.125;\n}\n\n.title strong {\n  color: inherit;\n  font-weight: inherit;\n}\n\n.title + .highlight {\n  margin-top: -0.75rem;\n}\n\n.title:not(.is-spaced) + .subtitle {\n  margin-top: -1.5rem;\n}\n\n.title.is-1 {\n  font-size: 3rem;\n}\n\n.title.is-2 {\n  font-size: 2.5rem;\n}\n\n.title.is-3 {\n  font-size: 2rem;\n}\n\n.title.is-4 {\n  font-size: 1.5rem;\n}\n\n.title.is-5 {\n  font-size: 1.25rem;\n}\n\n.title.is-6 {\n  font-size: 1rem;\n}\n\n.title.is-7 {\n  font-size: 0.75rem;\n}\n\n.subtitle {\n  color: #4a4a4a;\n  font-size: 1.25rem;\n  font-weight: 400;\n  line-height: 1.25;\n}\n\n.subtitle strong {\n  color: #363636;\n  font-weight: 600;\n}\n\n.subtitle:not(.is-spaced) + .title {\n  margin-top: -1.5rem;\n}\n\n.subtitle.is-1 {\n  font-size: 3rem;\n}\n\n.subtitle.is-2 {\n  font-size: 2.5rem;\n}\n\n.subtitle.is-3 {\n  font-size: 2rem;\n}\n\n.subtitle.is-4 {\n  font-size: 1.5rem;\n}\n\n.subtitle.is-5 {\n  font-size: 1.25rem;\n}\n\n.subtitle.is-6 {\n  font-size: 1rem;\n}\n\n.subtitle.is-7 {\n  font-size: 0.75rem;\n}\n\n.block:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.delete {\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  background-color: rgba(10, 10, 10, 0.2);\n  border: none;\n  border-radius: 290486px;\n  cursor: pointer;\n  display: inline-block;\n  -webkit-box-flex: 0;\n      -ms-flex-positive: 0;\n          flex-grow: 0;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  font-size: 0;\n  height: 20px;\n  max-height: 20px;\n  max-width: 20px;\n  min-height: 20px;\n  min-width: 20px;\n  outline: none;\n  position: relative;\n  vertical-align: top;\n  width: 20px;\n}\n\n.delete:before, .delete:after {\n  background-color: white;\n  content: \"\";\n  display: block;\n  left: 50%;\n  position: absolute;\n  top: 50%;\n  -webkit-transform: translateX(-50%) translateY(-50%) rotate(45deg);\n          transform: translateX(-50%) translateY(-50%) rotate(45deg);\n  -webkit-transform-origin: center center;\n          transform-origin: center center;\n}\n\n.delete:before {\n  height: 2px;\n  width: 50%;\n}\n\n.delete:after {\n  height: 50%;\n  width: 2px;\n}\n\n.delete:hover, .delete:focus {\n  background-color: rgba(10, 10, 10, 0.3);\n}\n\n.delete:active {\n  background-color: rgba(10, 10, 10, 0.4);\n}\n\n.delete.is-small {\n  height: 16px;\n  max-height: 16px;\n  max-width: 16px;\n  min-height: 16px;\n  min-width: 16px;\n  width: 16px;\n}\n\n.delete.is-medium {\n  height: 24px;\n  max-height: 24px;\n  max-width: 24px;\n  min-height: 24px;\n  min-width: 24px;\n  width: 24px;\n}\n\n.delete.is-large {\n  height: 32px;\n  max-height: 32px;\n  max-width: 32px;\n  min-height: 32px;\n  min-width: 32px;\n  width: 32px;\n}\n\n.heading {\n  display: block;\n  font-size: 11px;\n  letter-spacing: 1px;\n  margin-bottom: 5px;\n  text-transform: uppercase;\n}\n\n.highlight {\n  font-weight: 400;\n  max-width: 100%;\n  overflow: hidden;\n  padding: 0;\n}\n\n.highlight:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.highlight pre {\n  overflow: auto;\n  max-width: 100%;\n}\n\n.loader {\n  -webkit-animation: spinAround 500ms infinite linear;\n          animation: spinAround 500ms infinite linear;\n  border: 2px solid #dbdbdb;\n  border-radius: 290486px;\n  border-right-color: transparent;\n  border-top-color: transparent;\n  content: \"\";\n  display: block;\n  height: 1em;\n  position: relative;\n  width: 1em;\n}\n\n.number {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  background-color: whitesmoke;\n  border-radius: 290486px;\n  display: -webkit-inline-box;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  font-size: 1.25rem;\n  height: 2em;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  margin-right: 1.5rem;\n  min-width: 2.5em;\n  padding: 0.25rem 0.5rem;\n  text-align: center;\n  vertical-align: top;\n}\n\n.breadcrumb {\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  -webkit-box-align: stretch;\n      -ms-flex-align: stretch;\n          align-items: stretch;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  font-size: 1rem;\n  overflow: hidden;\n  overflow-x: auto;\n  white-space: nowrap;\n}\n\n.breadcrumb:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.breadcrumb a {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  color: #7a91c1;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  padding: 0.5em 0.75em;\n}\n\n.breadcrumb a:hover {\n  color: #363636;\n}\n\n.breadcrumb li {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.breadcrumb li:first-child a {\n  padding-left: 0;\n}\n\n.breadcrumb li.is-active a {\n  color: #363636;\n  cursor: default;\n  pointer-events: none;\n}\n\n.breadcrumb li + li::before {\n  color: #4a4a4a;\n  content: \"\\0002f\";\n}\n\n.breadcrumb ul, .breadcrumb ol {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n}\n\n.breadcrumb .icon:first-child {\n  margin-right: 0.5em;\n}\n\n.breadcrumb .icon:last-child {\n  margin-left: 0.5em;\n}\n\n.breadcrumb.is-centered ol, .breadcrumb.is-centered ul {\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.breadcrumb.is-right ol, .breadcrumb.is-right ul {\n  -webkit-box-pack: end;\n      -ms-flex-pack: end;\n          justify-content: flex-end;\n}\n\n.breadcrumb.is-small {\n  font-size: 0.75rem;\n}\n\n.breadcrumb.is-medium {\n  font-size: 1.25rem;\n}\n\n.breadcrumb.is-large {\n  font-size: 1.5rem;\n}\n\n.breadcrumb.has-arrow-separator li + li::before {\n  content: \"\\02192\";\n}\n\n.breadcrumb.has-bullet-separator li + li::before {\n  content: \"\\02022\";\n}\n\n.breadcrumb.has-dot-separator li + li::before {\n  content: \"\\000b7\";\n}\n\n.breadcrumb.has-succeeds-separator li + li::before {\n  content: \"\\0227B\";\n}\n\n.card {\n  background-color: white;\n  -webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);\n          box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);\n  color: #4a4a4a;\n  max-width: 100%;\n  position: relative;\n}\n\n.card-header {\n  -webkit-box-align: stretch;\n      -ms-flex-align: stretch;\n          align-items: stretch;\n  -webkit-box-shadow: 0 1px 2px rgba(10, 10, 10, 0.1);\n          box-shadow: 0 1px 2px rgba(10, 10, 10, 0.1);\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.card-header-title {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  color: #363636;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  font-weight: 700;\n  padding: 0.75rem;\n}\n\n.card-header-title.is-centered {\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.card-header-icon {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  cursor: pointer;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  padding: 0.75rem;\n}\n\n.card-image {\n  display: block;\n  position: relative;\n}\n\n.card-content {\n  padding: 1.5rem;\n}\n\n.card-footer {\n  border-top: 1px solid #dbdbdb;\n  -webkit-box-align: stretch;\n      -ms-flex-align: stretch;\n          align-items: stretch;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.card-footer-item {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-preferred-size: 0;\n      flex-basis: 0;\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  padding: 0.75rem;\n}\n\n.card-footer-item:not(:last-child) {\n  border-right: 1px solid #dbdbdb;\n}\n\n.card .media:not(:last-child) {\n  margin-bottom: 0.75rem;\n}\n\n.dropdown {\n  display: -webkit-inline-box;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  position: relative;\n  vertical-align: top;\n}\n\n.dropdown.is-active .dropdown-menu, .dropdown.is-hoverable:hover .dropdown-menu {\n  display: block;\n}\n\n.dropdown.is-right .dropdown-menu {\n  left: auto;\n  right: 0;\n}\n\n.dropdown.is-up .dropdown-menu {\n  bottom: 100%;\n  padding-bottom: 4px;\n  padding-top: unset;\n  top: auto;\n}\n\n.dropdown-menu {\n  display: none;\n  left: 0;\n  min-width: 12rem;\n  padding-top: 4px;\n  position: absolute;\n  top: 100%;\n  z-index: 20;\n}\n\n.dropdown-content {\n  background-color: white;\n  border-radius: 3px;\n  -webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);\n          box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);\n  padding-bottom: 0.5rem;\n  padding-top: 0.5rem;\n}\n\n.dropdown-item {\n  color: #4a4a4a;\n  display: block;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  padding: 0.375rem 1rem;\n  position: relative;\n}\n\na.dropdown-item {\n  padding-right: 3rem;\n  white-space: nowrap;\n}\n\na.dropdown-item:hover {\n  background-color: whitesmoke;\n  color: #0a0a0a;\n}\n\na.dropdown-item.is-active {\n  background-color: #7a91c1;\n  color: #fff;\n}\n\n.dropdown-divider {\n  background-color: #dbdbdb;\n  border: none;\n  display: block;\n  height: 1px;\n  margin: 0.5rem 0;\n}\n\n.level {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  -webkit-box-pack: justify;\n      -ms-flex-pack: justify;\n          justify-content: space-between;\n}\n\n.level:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.level code {\n  border-radius: 3px;\n}\n\n.level img {\n  display: inline-block;\n  vertical-align: top;\n}\n\n.level.is-mobile {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.level.is-mobile .level-left,\n.level.is-mobile .level-right {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.level.is-mobile .level-left + .level-right {\n  margin-top: 0;\n}\n\n.level.is-mobile .level-item {\n  margin-right: 0.75rem;\n}\n\n.level.is-mobile .level-item:not(:last-child) {\n  margin-bottom: 0;\n}\n\n.level.is-mobile .level-item:not(.is-narrow) {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n}\n\n@media screen and (min-width: 769px), print {\n  .level {\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n  .level > .level-item:not(.is-narrow) {\n    -webkit-box-flex: 1;\n        -ms-flex-positive: 1;\n            flex-grow: 1;\n  }\n}\n\n.level-item {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-preferred-size: auto;\n      flex-basis: auto;\n  -webkit-box-flex: 0;\n      -ms-flex-positive: 0;\n          flex-grow: 0;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.level-item .title,\n.level-item .subtitle {\n  margin-bottom: 0;\n}\n\n@media screen and (max-width: 768px) {\n  .level-item:not(:last-child) {\n    margin-bottom: 0.75rem;\n  }\n}\n\n.level-left,\n.level-right {\n  -ms-flex-preferred-size: auto;\n      flex-basis: auto;\n  -webkit-box-flex: 0;\n      -ms-flex-positive: 0;\n          flex-grow: 0;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n}\n\n.level-left .level-item.is-flexible,\n.level-right .level-item.is-flexible {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n}\n\n@media screen and (min-width: 769px), print {\n  .level-left .level-item:not(:last-child),\n  .level-right .level-item:not(:last-child) {\n    margin-right: 0.75rem;\n  }\n}\n\n.level-left {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n}\n\n@media screen and (max-width: 768px) {\n  .level-left + .level-right {\n    margin-top: 1.5rem;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .level-left {\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n}\n\n.level-right {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  -webkit-box-pack: end;\n      -ms-flex-pack: end;\n          justify-content: flex-end;\n}\n\n@media screen and (min-width: 769px), print {\n  .level-right {\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n}\n\n.media {\n  -webkit-box-align: start;\n      -ms-flex-align: start;\n          align-items: flex-start;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  text-align: left;\n}\n\n.media .content:not(:last-child) {\n  margin-bottom: 0.75rem;\n}\n\n.media .media {\n  border-top: 1px solid rgba(219, 219, 219, 0.5);\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  padding-top: 0.75rem;\n}\n\n.media .media .content:not(:last-child),\n.media .media .control:not(:last-child) {\n  margin-bottom: 0.5rem;\n}\n\n.media .media .media {\n  padding-top: 0.5rem;\n}\n\n.media .media .media + .media {\n  margin-top: 0.5rem;\n}\n\n.media + .media {\n  border-top: 1px solid rgba(219, 219, 219, 0.5);\n  margin-top: 1rem;\n  padding-top: 1rem;\n}\n\n.media.is-large + .media {\n  margin-top: 1.5rem;\n  padding-top: 1.5rem;\n}\n\n.media-left,\n.media-right {\n  -ms-flex-preferred-size: auto;\n      flex-basis: auto;\n  -webkit-box-flex: 0;\n      -ms-flex-positive: 0;\n          flex-grow: 0;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n}\n\n.media-left {\n  margin-right: 1rem;\n}\n\n.media-right {\n  margin-left: 1rem;\n}\n\n.media-content {\n  -ms-flex-preferred-size: auto;\n      flex-basis: auto;\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 1;\n      flex-shrink: 1;\n  text-align: left;\n}\n\n.menu {\n  font-size: 1rem;\n}\n\n.menu.is-small {\n  font-size: 0.75rem;\n}\n\n.menu.is-medium {\n  font-size: 1.25rem;\n}\n\n.menu.is-large {\n  font-size: 1.5rem;\n}\n\n.menu-list {\n  line-height: 1.25;\n}\n\n.menu-list a {\n  border-radius: 2px;\n  color: #4a4a4a;\n  display: block;\n  padding: 0.5em 0.75em;\n}\n\n.menu-list a:hover {\n  background-color: whitesmoke;\n  color: #363636;\n}\n\n.menu-list a.is-active {\n  background-color: #7a91c1;\n  color: #fff;\n}\n\n.menu-list li ul {\n  border-left: 1px solid #dbdbdb;\n  margin: 0.75em;\n  padding-left: 0.75em;\n}\n\n.menu-label {\n  color: #7a7a7a;\n  font-size: 0.75em;\n  letter-spacing: 0.1em;\n  text-transform: uppercase;\n}\n\n.menu-label:not(:first-child) {\n  margin-top: 1em;\n}\n\n.menu-label:not(:last-child) {\n  margin-bottom: 1em;\n}\n\n.message {\n  background-color: whitesmoke;\n  border-radius: 3px;\n  font-size: 1rem;\n}\n\n.message:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.message strong {\n  color: currentColor;\n}\n\n.message a:not(.button):not(.tag) {\n  color: currentColor;\n  text-decoration: underline;\n}\n\n.message.is-small {\n  font-size: 0.75rem;\n}\n\n.message.is-medium {\n  font-size: 1.25rem;\n}\n\n.message.is-large {\n  font-size: 1.5rem;\n}\n\n.message.is-white {\n  background-color: white;\n}\n\n.message.is-white .message-header {\n  background-color: white;\n  color: #0a0a0a;\n}\n\n.message.is-white .message-body {\n  border-color: white;\n  color: #4d4d4d;\n}\n\n.message.is-black {\n  background-color: #fafafa;\n}\n\n.message.is-black .message-header {\n  background-color: #0a0a0a;\n  color: white;\n}\n\n.message.is-black .message-body {\n  border-color: #0a0a0a;\n  color: #090909;\n}\n\n.message.is-light {\n  background-color: #fafafa;\n}\n\n.message.is-light .message-header {\n  background-color: whitesmoke;\n  color: #363636;\n}\n\n.message.is-light .message-body {\n  border-color: whitesmoke;\n  color: #505050;\n}\n\n.message.is-dark {\n  background-color: #fafafa;\n}\n\n.message.is-dark .message-header {\n  background-color: #363636;\n  color: whitesmoke;\n}\n\n.message.is-dark .message-body {\n  border-color: #363636;\n  color: #2a2a2a;\n}\n\n.message.is-primary {\n  background-color: #f5fffd;\n}\n\n.message.is-primary .message-header {\n  background-color: #00d1b2;\n  color: #fff;\n}\n\n.message.is-primary .message-body {\n  border-color: #00d1b2;\n  color: #021310;\n}\n\n.message.is-link {\n  background-color: #f6f9fe;\n}\n\n.message.is-link .message-header {\n  background-color: #7a91c1;\n  color: #fff;\n}\n\n.message.is-link .message-body {\n  border-color: #7a91c1;\n  color: #22509a;\n}\n\n.message.is-info {\n  background-color: #f6fbfe;\n}\n\n.message.is-info .message-header {\n  background-color: #209cee;\n  color: #fff;\n}\n\n.message.is-info .message-body {\n  border-color: #209cee;\n  color: #12537e;\n}\n\n.message.is-success {\n  background-color: #f6fef9;\n}\n\n.message.is-success .message-header {\n  background-color: #23d160;\n  color: #fff;\n}\n\n.message.is-success .message-body {\n  border-color: #23d160;\n  color: #0e301a;\n}\n\n.message.is-warning {\n  background-color: #fffdf5;\n}\n\n.message.is-warning .message-header {\n  background-color: #ffdd57;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.message.is-warning .message-body {\n  border-color: #ffdd57;\n  color: #3b3108;\n}\n\n.message.is-danger {\n  background-color: #fff5f7;\n}\n\n.message.is-danger .message-header {\n  background-color: #ff3860;\n  color: #fff;\n}\n\n.message.is-danger .message-body {\n  border-color: #ff3860;\n  color: #cd0930;\n}\n\n.message-header {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  background-color: #4a4a4a;\n  border-radius: 3px 3px 0 0;\n  color: #fff;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-pack: justify;\n      -ms-flex-pack: justify;\n          justify-content: space-between;\n  line-height: 1.25;\n  padding: 0.5em 0.75em;\n  position: relative;\n}\n\n.message-header .delete {\n  -webkit-box-flex: 0;\n      -ms-flex-positive: 0;\n          flex-grow: 0;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  margin-left: 0.75em;\n}\n\n.message-header + .message-body {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n  border-top: none;\n}\n\n.message-body {\n  border: 1px solid #dbdbdb;\n  border-radius: 3px;\n  color: #4a4a4a;\n  padding: 1em 1.25em;\n}\n\n.message-body code,\n.message-body pre {\n  background-color: white;\n}\n\n.message-body pre code {\n  background-color: transparent;\n}\n\n.modal {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: none;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  overflow: hidden;\n  position: fixed;\n  z-index: 20;\n}\n\n.modal.is-active {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.modal-background {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  background-color: rgba(10, 10, 10, 0.86);\n}\n\n.modal-content,\n.modal-card {\n  margin: 0 20px;\n  max-height: calc(100vh - 160px);\n  overflow: auto;\n  position: relative;\n  width: 100%;\n}\n\n@media screen and (min-width: 769px), print {\n  .modal-content,\n  .modal-card {\n    margin: 0 auto;\n    max-height: calc(100vh - 40px);\n    width: 640px;\n  }\n}\n\n.modal-close {\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  background-color: rgba(10, 10, 10, 0.2);\n  border: none;\n  border-radius: 290486px;\n  cursor: pointer;\n  display: inline-block;\n  -webkit-box-flex: 0;\n      -ms-flex-positive: 0;\n          flex-grow: 0;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  font-size: 0;\n  height: 20px;\n  max-height: 20px;\n  max-width: 20px;\n  min-height: 20px;\n  min-width: 20px;\n  outline: none;\n  position: relative;\n  vertical-align: top;\n  width: 20px;\n  background: none;\n  height: 40px;\n  position: fixed;\n  right: 20px;\n  top: 20px;\n  width: 40px;\n}\n\n.modal-close:before, .modal-close:after {\n  background-color: white;\n  content: \"\";\n  display: block;\n  left: 50%;\n  position: absolute;\n  top: 50%;\n  -webkit-transform: translateX(-50%) translateY(-50%) rotate(45deg);\n          transform: translateX(-50%) translateY(-50%) rotate(45deg);\n  -webkit-transform-origin: center center;\n          transform-origin: center center;\n}\n\n.modal-close:before {\n  height: 2px;\n  width: 50%;\n}\n\n.modal-close:after {\n  height: 50%;\n  width: 2px;\n}\n\n.modal-close:hover, .modal-close:focus {\n  background-color: rgba(10, 10, 10, 0.3);\n}\n\n.modal-close:active {\n  background-color: rgba(10, 10, 10, 0.4);\n}\n\n.modal-close.is-small {\n  height: 16px;\n  max-height: 16px;\n  max-width: 16px;\n  min-height: 16px;\n  min-width: 16px;\n  width: 16px;\n}\n\n.modal-close.is-medium {\n  height: 24px;\n  max-height: 24px;\n  max-width: 24px;\n  min-height: 24px;\n  min-width: 24px;\n  width: 24px;\n}\n\n.modal-close.is-large {\n  height: 32px;\n  max-height: 32px;\n  max-width: 32px;\n  min-height: 32px;\n  min-width: 32px;\n  width: 32px;\n}\n\n.modal-card {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-direction: column;\n          flex-direction: column;\n  max-height: calc(100vh - 40px);\n  overflow: hidden;\n}\n\n.modal-card-head,\n.modal-card-foot {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  background-color: whitesmoke;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n  padding: 20px;\n  position: relative;\n}\n\n.modal-card-head {\n  border-bottom: 1px solid #dbdbdb;\n  border-top-left-radius: 5px;\n  border-top-right-radius: 5px;\n}\n\n.modal-card-title {\n  color: #363636;\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  font-size: 1.5rem;\n  line-height: 1;\n}\n\n.modal-card-foot {\n  border-bottom-left-radius: 5px;\n  border-bottom-right-radius: 5px;\n  border-top: 1px solid #dbdbdb;\n}\n\n.modal-card-foot .button:not(:last-child) {\n  margin-right: 10px;\n}\n\n.modal-card-body {\n  -webkit-overflow-scrolling: touch;\n  background-color: white;\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 1;\n      flex-shrink: 1;\n  overflow: auto;\n  padding: 20px;\n}\n\n.navbar {\n  background-color: white;\n  min-height: 3.25rem;\n  position: relative;\n}\n\n.navbar.is-white {\n  background-color: white;\n  color: #0a0a0a;\n}\n\n.navbar.is-white .navbar-brand > .navbar-item,\n.navbar.is-white .navbar-brand .navbar-link {\n  color: #0a0a0a;\n}\n\n.navbar.is-white .navbar-brand > a.navbar-item:hover, .navbar.is-white .navbar-brand > a.navbar-item.is-active,\n.navbar.is-white .navbar-brand .navbar-link:hover,\n.navbar.is-white .navbar-brand .navbar-link.is-active {\n  background-color: #f2f2f2;\n  color: #0a0a0a;\n}\n\n.navbar.is-white .navbar-brand .navbar-link::after {\n  border-color: #0a0a0a;\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar.is-white .navbar-start > .navbar-item,\n  .navbar.is-white .navbar-start .navbar-link,\n  .navbar.is-white .navbar-end > .navbar-item,\n  .navbar.is-white .navbar-end .navbar-link {\n    color: #0a0a0a;\n  }\n  .navbar.is-white .navbar-start > a.navbar-item:hover, .navbar.is-white .navbar-start > a.navbar-item.is-active,\n  .navbar.is-white .navbar-start .navbar-link:hover,\n  .navbar.is-white .navbar-start .navbar-link.is-active,\n  .navbar.is-white .navbar-end > a.navbar-item:hover,\n  .navbar.is-white .navbar-end > a.navbar-item.is-active,\n  .navbar.is-white .navbar-end .navbar-link:hover,\n  .navbar.is-white .navbar-end .navbar-link.is-active {\n    background-color: #f2f2f2;\n    color: #0a0a0a;\n  }\n  .navbar.is-white .navbar-start .navbar-link::after,\n  .navbar.is-white .navbar-end .navbar-link::after {\n    border-color: #0a0a0a;\n  }\n  .navbar.is-white .navbar-item.has-dropdown:hover .navbar-link,\n  .navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: #f2f2f2;\n    color: #0a0a0a;\n  }\n  .navbar.is-white .navbar-dropdown a.navbar-item.is-active {\n    background-color: white;\n    color: #0a0a0a;\n  }\n}\n\n.navbar.is-black {\n  background-color: #0a0a0a;\n  color: white;\n}\n\n.navbar.is-black .navbar-brand > .navbar-item,\n.navbar.is-black .navbar-brand .navbar-link {\n  color: white;\n}\n\n.navbar.is-black .navbar-brand > a.navbar-item:hover, .navbar.is-black .navbar-brand > a.navbar-item.is-active,\n.navbar.is-black .navbar-brand .navbar-link:hover,\n.navbar.is-black .navbar-brand .navbar-link.is-active {\n  background-color: black;\n  color: white;\n}\n\n.navbar.is-black .navbar-brand .navbar-link::after {\n  border-color: white;\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar.is-black .navbar-start > .navbar-item,\n  .navbar.is-black .navbar-start .navbar-link,\n  .navbar.is-black .navbar-end > .navbar-item,\n  .navbar.is-black .navbar-end .navbar-link {\n    color: white;\n  }\n  .navbar.is-black .navbar-start > a.navbar-item:hover, .navbar.is-black .navbar-start > a.navbar-item.is-active,\n  .navbar.is-black .navbar-start .navbar-link:hover,\n  .navbar.is-black .navbar-start .navbar-link.is-active,\n  .navbar.is-black .navbar-end > a.navbar-item:hover,\n  .navbar.is-black .navbar-end > a.navbar-item.is-active,\n  .navbar.is-black .navbar-end .navbar-link:hover,\n  .navbar.is-black .navbar-end .navbar-link.is-active {\n    background-color: black;\n    color: white;\n  }\n  .navbar.is-black .navbar-start .navbar-link::after,\n  .navbar.is-black .navbar-end .navbar-link::after {\n    border-color: white;\n  }\n  .navbar.is-black .navbar-item.has-dropdown:hover .navbar-link,\n  .navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: black;\n    color: white;\n  }\n  .navbar.is-black .navbar-dropdown a.navbar-item.is-active {\n    background-color: #0a0a0a;\n    color: white;\n  }\n}\n\n.navbar.is-light {\n  background-color: whitesmoke;\n  color: #363636;\n}\n\n.navbar.is-light .navbar-brand > .navbar-item,\n.navbar.is-light .navbar-brand .navbar-link {\n  color: #363636;\n}\n\n.navbar.is-light .navbar-brand > a.navbar-item:hover, .navbar.is-light .navbar-brand > a.navbar-item.is-active,\n.navbar.is-light .navbar-brand .navbar-link:hover,\n.navbar.is-light .navbar-brand .navbar-link.is-active {\n  background-color: #e8e8e8;\n  color: #363636;\n}\n\n.navbar.is-light .navbar-brand .navbar-link::after {\n  border-color: #363636;\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar.is-light .navbar-start > .navbar-item,\n  .navbar.is-light .navbar-start .navbar-link,\n  .navbar.is-light .navbar-end > .navbar-item,\n  .navbar.is-light .navbar-end .navbar-link {\n    color: #363636;\n  }\n  .navbar.is-light .navbar-start > a.navbar-item:hover, .navbar.is-light .navbar-start > a.navbar-item.is-active,\n  .navbar.is-light .navbar-start .navbar-link:hover,\n  .navbar.is-light .navbar-start .navbar-link.is-active,\n  .navbar.is-light .navbar-end > a.navbar-item:hover,\n  .navbar.is-light .navbar-end > a.navbar-item.is-active,\n  .navbar.is-light .navbar-end .navbar-link:hover,\n  .navbar.is-light .navbar-end .navbar-link.is-active {\n    background-color: #e8e8e8;\n    color: #363636;\n  }\n  .navbar.is-light .navbar-start .navbar-link::after,\n  .navbar.is-light .navbar-end .navbar-link::after {\n    border-color: #363636;\n  }\n  .navbar.is-light .navbar-item.has-dropdown:hover .navbar-link,\n  .navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: #e8e8e8;\n    color: #363636;\n  }\n  .navbar.is-light .navbar-dropdown a.navbar-item.is-active {\n    background-color: whitesmoke;\n    color: #363636;\n  }\n}\n\n.navbar.is-dark {\n  background-color: #363636;\n  color: whitesmoke;\n}\n\n.navbar.is-dark .navbar-brand > .navbar-item,\n.navbar.is-dark .navbar-brand .navbar-link {\n  color: whitesmoke;\n}\n\n.navbar.is-dark .navbar-brand > a.navbar-item:hover, .navbar.is-dark .navbar-brand > a.navbar-item.is-active,\n.navbar.is-dark .navbar-brand .navbar-link:hover,\n.navbar.is-dark .navbar-brand .navbar-link.is-active {\n  background-color: #292929;\n  color: whitesmoke;\n}\n\n.navbar.is-dark .navbar-brand .navbar-link::after {\n  border-color: whitesmoke;\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar.is-dark .navbar-start > .navbar-item,\n  .navbar.is-dark .navbar-start .navbar-link,\n  .navbar.is-dark .navbar-end > .navbar-item,\n  .navbar.is-dark .navbar-end .navbar-link {\n    color: whitesmoke;\n  }\n  .navbar.is-dark .navbar-start > a.navbar-item:hover, .navbar.is-dark .navbar-start > a.navbar-item.is-active,\n  .navbar.is-dark .navbar-start .navbar-link:hover,\n  .navbar.is-dark .navbar-start .navbar-link.is-active,\n  .navbar.is-dark .navbar-end > a.navbar-item:hover,\n  .navbar.is-dark .navbar-end > a.navbar-item.is-active,\n  .navbar.is-dark .navbar-end .navbar-link:hover,\n  .navbar.is-dark .navbar-end .navbar-link.is-active {\n    background-color: #292929;\n    color: whitesmoke;\n  }\n  .navbar.is-dark .navbar-start .navbar-link::after,\n  .navbar.is-dark .navbar-end .navbar-link::after {\n    border-color: whitesmoke;\n  }\n  .navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link,\n  .navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: #292929;\n    color: whitesmoke;\n  }\n  .navbar.is-dark .navbar-dropdown a.navbar-item.is-active {\n    background-color: #363636;\n    color: whitesmoke;\n  }\n}\n\n.navbar.is-primary {\n  background-color: #00d1b2;\n  color: #fff;\n}\n\n.navbar.is-primary .navbar-brand > .navbar-item,\n.navbar.is-primary .navbar-brand .navbar-link {\n  color: #fff;\n}\n\n.navbar.is-primary .navbar-brand > a.navbar-item:hover, .navbar.is-primary .navbar-brand > a.navbar-item.is-active,\n.navbar.is-primary .navbar-brand .navbar-link:hover,\n.navbar.is-primary .navbar-brand .navbar-link.is-active {\n  background-color: #00b89c;\n  color: #fff;\n}\n\n.navbar.is-primary .navbar-brand .navbar-link::after {\n  border-color: #fff;\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar.is-primary .navbar-start > .navbar-item,\n  .navbar.is-primary .navbar-start .navbar-link,\n  .navbar.is-primary .navbar-end > .navbar-item,\n  .navbar.is-primary .navbar-end .navbar-link {\n    color: #fff;\n  }\n  .navbar.is-primary .navbar-start > a.navbar-item:hover, .navbar.is-primary .navbar-start > a.navbar-item.is-active,\n  .navbar.is-primary .navbar-start .navbar-link:hover,\n  .navbar.is-primary .navbar-start .navbar-link.is-active,\n  .navbar.is-primary .navbar-end > a.navbar-item:hover,\n  .navbar.is-primary .navbar-end > a.navbar-item.is-active,\n  .navbar.is-primary .navbar-end .navbar-link:hover,\n  .navbar.is-primary .navbar-end .navbar-link.is-active {\n    background-color: #00b89c;\n    color: #fff;\n  }\n  .navbar.is-primary .navbar-start .navbar-link::after,\n  .navbar.is-primary .navbar-end .navbar-link::after {\n    border-color: #fff;\n  }\n  .navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link,\n  .navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: #00b89c;\n    color: #fff;\n  }\n  .navbar.is-primary .navbar-dropdown a.navbar-item.is-active {\n    background-color: #00d1b2;\n    color: #fff;\n  }\n}\n\n.navbar.is-link {\n  background-color: #7a91c1;\n  color: #fff;\n}\n\n.navbar.is-link .navbar-brand > .navbar-item,\n.navbar.is-link .navbar-brand .navbar-link {\n  color: #fff;\n}\n\n.navbar.is-link .navbar-brand > a.navbar-item:hover, .navbar.is-link .navbar-brand > a.navbar-item.is-active,\n.navbar.is-link .navbar-brand .navbar-link:hover,\n.navbar.is-link .navbar-brand .navbar-link.is-active {\n  background-color: #2366d1;\n  color: #fff;\n}\n\n.navbar.is-link .navbar-brand .navbar-link::after {\n  border-color: #fff;\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar.is-link .navbar-start > .navbar-item,\n  .navbar.is-link .navbar-start .navbar-link,\n  .navbar.is-link .navbar-end > .navbar-item,\n  .navbar.is-link .navbar-end .navbar-link {\n    color: #fff;\n  }\n  .navbar.is-link .navbar-start > a.navbar-item:hover, .navbar.is-link .navbar-start > a.navbar-item.is-active,\n  .navbar.is-link .navbar-start .navbar-link:hover,\n  .navbar.is-link .navbar-start .navbar-link.is-active,\n  .navbar.is-link .navbar-end > a.navbar-item:hover,\n  .navbar.is-link .navbar-end > a.navbar-item.is-active,\n  .navbar.is-link .navbar-end .navbar-link:hover,\n  .navbar.is-link .navbar-end .navbar-link.is-active {\n    background-color: #2366d1;\n    color: #fff;\n  }\n  .navbar.is-link .navbar-start .navbar-link::after,\n  .navbar.is-link .navbar-end .navbar-link::after {\n    border-color: #fff;\n  }\n  .navbar.is-link .navbar-item.has-dropdown:hover .navbar-link,\n  .navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: #2366d1;\n    color: #fff;\n  }\n  .navbar.is-link .navbar-dropdown a.navbar-item.is-active {\n    background-color: #7a91c1;\n    color: #fff;\n  }\n}\n\n.navbar.is-info {\n  background-color: #209cee;\n  color: #fff;\n}\n\n.navbar.is-info .navbar-brand > .navbar-item,\n.navbar.is-info .navbar-brand .navbar-link {\n  color: #fff;\n}\n\n.navbar.is-info .navbar-brand > a.navbar-item:hover, .navbar.is-info .navbar-brand > a.navbar-item.is-active,\n.navbar.is-info .navbar-brand .navbar-link:hover,\n.navbar.is-info .navbar-brand .navbar-link.is-active {\n  background-color: #118fe4;\n  color: #fff;\n}\n\n.navbar.is-info .navbar-brand .navbar-link::after {\n  border-color: #fff;\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar.is-info .navbar-start > .navbar-item,\n  .navbar.is-info .navbar-start .navbar-link,\n  .navbar.is-info .navbar-end > .navbar-item,\n  .navbar.is-info .navbar-end .navbar-link {\n    color: #fff;\n  }\n  .navbar.is-info .navbar-start > a.navbar-item:hover, .navbar.is-info .navbar-start > a.navbar-item.is-active,\n  .navbar.is-info .navbar-start .navbar-link:hover,\n  .navbar.is-info .navbar-start .navbar-link.is-active,\n  .navbar.is-info .navbar-end > a.navbar-item:hover,\n  .navbar.is-info .navbar-end > a.navbar-item.is-active,\n  .navbar.is-info .navbar-end .navbar-link:hover,\n  .navbar.is-info .navbar-end .navbar-link.is-active {\n    background-color: #118fe4;\n    color: #fff;\n  }\n  .navbar.is-info .navbar-start .navbar-link::after,\n  .navbar.is-info .navbar-end .navbar-link::after {\n    border-color: #fff;\n  }\n  .navbar.is-info .navbar-item.has-dropdown:hover .navbar-link,\n  .navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: #118fe4;\n    color: #fff;\n  }\n  .navbar.is-info .navbar-dropdown a.navbar-item.is-active {\n    background-color: #209cee;\n    color: #fff;\n  }\n}\n\n.navbar.is-success {\n  background-color: #23d160;\n  color: #fff;\n}\n\n.navbar.is-success .navbar-brand > .navbar-item,\n.navbar.is-success .navbar-brand .navbar-link {\n  color: #fff;\n}\n\n.navbar.is-success .navbar-brand > a.navbar-item:hover, .navbar.is-success .navbar-brand > a.navbar-item.is-active,\n.navbar.is-success .navbar-brand .navbar-link:hover,\n.navbar.is-success .navbar-brand .navbar-link.is-active {\n  background-color: #20bc56;\n  color: #fff;\n}\n\n.navbar.is-success .navbar-brand .navbar-link::after {\n  border-color: #fff;\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar.is-success .navbar-start > .navbar-item,\n  .navbar.is-success .navbar-start .navbar-link,\n  .navbar.is-success .navbar-end > .navbar-item,\n  .navbar.is-success .navbar-end .navbar-link {\n    color: #fff;\n  }\n  .navbar.is-success .navbar-start > a.navbar-item:hover, .navbar.is-success .navbar-start > a.navbar-item.is-active,\n  .navbar.is-success .navbar-start .navbar-link:hover,\n  .navbar.is-success .navbar-start .navbar-link.is-active,\n  .navbar.is-success .navbar-end > a.navbar-item:hover,\n  .navbar.is-success .navbar-end > a.navbar-item.is-active,\n  .navbar.is-success .navbar-end .navbar-link:hover,\n  .navbar.is-success .navbar-end .navbar-link.is-active {\n    background-color: #20bc56;\n    color: #fff;\n  }\n  .navbar.is-success .navbar-start .navbar-link::after,\n  .navbar.is-success .navbar-end .navbar-link::after {\n    border-color: #fff;\n  }\n  .navbar.is-success .navbar-item.has-dropdown:hover .navbar-link,\n  .navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: #20bc56;\n    color: #fff;\n  }\n  .navbar.is-success .navbar-dropdown a.navbar-item.is-active {\n    background-color: #23d160;\n    color: #fff;\n  }\n}\n\n.navbar.is-warning {\n  background-color: #ffdd57;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar.is-warning .navbar-brand > .navbar-item,\n.navbar.is-warning .navbar-brand .navbar-link {\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar.is-warning .navbar-brand > a.navbar-item:hover, .navbar.is-warning .navbar-brand > a.navbar-item.is-active,\n.navbar.is-warning .navbar-brand .navbar-link:hover,\n.navbar.is-warning .navbar-brand .navbar-link.is-active {\n  background-color: #ffd83d;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar.is-warning .navbar-brand .navbar-link::after {\n  border-color: rgba(0, 0, 0, 0.7);\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar.is-warning .navbar-start > .navbar-item,\n  .navbar.is-warning .navbar-start .navbar-link,\n  .navbar.is-warning .navbar-end > .navbar-item,\n  .navbar.is-warning .navbar-end .navbar-link {\n    color: rgba(0, 0, 0, 0.7);\n  }\n  .navbar.is-warning .navbar-start > a.navbar-item:hover, .navbar.is-warning .navbar-start > a.navbar-item.is-active,\n  .navbar.is-warning .navbar-start .navbar-link:hover,\n  .navbar.is-warning .navbar-start .navbar-link.is-active,\n  .navbar.is-warning .navbar-end > a.navbar-item:hover,\n  .navbar.is-warning .navbar-end > a.navbar-item.is-active,\n  .navbar.is-warning .navbar-end .navbar-link:hover,\n  .navbar.is-warning .navbar-end .navbar-link.is-active {\n    background-color: #ffd83d;\n    color: rgba(0, 0, 0, 0.7);\n  }\n  .navbar.is-warning .navbar-start .navbar-link::after,\n  .navbar.is-warning .navbar-end .navbar-link::after {\n    border-color: rgba(0, 0, 0, 0.7);\n  }\n  .navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link,\n  .navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: #ffd83d;\n    color: rgba(0, 0, 0, 0.7);\n  }\n  .navbar.is-warning .navbar-dropdown a.navbar-item.is-active {\n    background-color: #ffdd57;\n    color: rgba(0, 0, 0, 0.7);\n  }\n}\n\n.navbar.is-danger {\n  background-color: #ff3860;\n  color: #fff;\n}\n\n.navbar.is-danger .navbar-brand > .navbar-item,\n.navbar.is-danger .navbar-brand .navbar-link {\n  color: #fff;\n}\n\n.navbar.is-danger .navbar-brand > a.navbar-item:hover, .navbar.is-danger .navbar-brand > a.navbar-item.is-active,\n.navbar.is-danger .navbar-brand .navbar-link:hover,\n.navbar.is-danger .navbar-brand .navbar-link.is-active {\n  background-color: #ff1f4b;\n  color: #fff;\n}\n\n.navbar.is-danger .navbar-brand .navbar-link::after {\n  border-color: #fff;\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar.is-danger .navbar-start > .navbar-item,\n  .navbar.is-danger .navbar-start .navbar-link,\n  .navbar.is-danger .navbar-end > .navbar-item,\n  .navbar.is-danger .navbar-end .navbar-link {\n    color: #fff;\n  }\n  .navbar.is-danger .navbar-start > a.navbar-item:hover, .navbar.is-danger .navbar-start > a.navbar-item.is-active,\n  .navbar.is-danger .navbar-start .navbar-link:hover,\n  .navbar.is-danger .navbar-start .navbar-link.is-active,\n  .navbar.is-danger .navbar-end > a.navbar-item:hover,\n  .navbar.is-danger .navbar-end > a.navbar-item.is-active,\n  .navbar.is-danger .navbar-end .navbar-link:hover,\n  .navbar.is-danger .navbar-end .navbar-link.is-active {\n    background-color: #ff1f4b;\n    color: #fff;\n  }\n  .navbar.is-danger .navbar-start .navbar-link::after,\n  .navbar.is-danger .navbar-end .navbar-link::after {\n    border-color: #fff;\n  }\n  .navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link,\n  .navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: #ff1f4b;\n    color: #fff;\n  }\n  .navbar.is-danger .navbar-dropdown a.navbar-item.is-active {\n    background-color: #ff3860;\n    color: #fff;\n  }\n}\n\n.navbar > .container {\n  -webkit-box-align: stretch;\n      -ms-flex-align: stretch;\n          align-items: stretch;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  min-height: 3.25rem;\n  width: 100%;\n}\n\n.navbar.has-shadow {\n  -webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1);\n          box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1);\n}\n\n.navbar.is-fixed-bottom, .navbar.is-fixed-top {\n  left: 0;\n  position: fixed;\n  right: 0;\n  z-index: 30;\n}\n\n.navbar.is-fixed-bottom {\n  bottom: 0;\n}\n\n.navbar.is-fixed-bottom.has-shadow {\n  -webkit-box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1);\n          box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1);\n}\n\n.navbar.is-fixed-top {\n  top: 0;\n}\n\nhtml.has-navbar-fixed-top {\n  padding-top: 3.25rem;\n}\n\nhtml.has-navbar-fixed-bottom {\n  padding-bottom: 3.25rem;\n}\n\n.navbar-brand,\n.navbar-tabs {\n  -webkit-box-align: stretch;\n      -ms-flex-align: stretch;\n          align-items: stretch;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  min-height: 3.25rem;\n}\n\n.navbar-tabs {\n  -webkit-overflow-scrolling: touch;\n  max-width: 100vw;\n  overflow-x: auto;\n  overflow-y: hidden;\n}\n\n.navbar-burger {\n  cursor: pointer;\n  display: block;\n  height: 3.25rem;\n  position: relative;\n  width: 3.25rem;\n  margin-left: auto;\n}\n\n.navbar-burger span {\n  background-color: currentColor;\n  display: block;\n  height: 1px;\n  left: calc(50% - 8px);\n  position: absolute;\n  -webkit-transform-origin: center;\n          transform-origin: center;\n  -webkit-transition-duration: 86ms;\n          transition-duration: 86ms;\n  -webkit-transition-property: background-color, opacity, -webkit-transform;\n  transition-property: background-color, opacity, -webkit-transform;\n  transition-property: background-color, opacity, transform;\n  transition-property: background-color, opacity, transform, -webkit-transform;\n  -webkit-transition-timing-function: ease-out;\n          transition-timing-function: ease-out;\n  width: 16px;\n}\n\n.navbar-burger span:nth-child(1) {\n  top: calc(50% - 6px);\n}\n\n.navbar-burger span:nth-child(2) {\n  top: calc(50% - 1px);\n}\n\n.navbar-burger span:nth-child(3) {\n  top: calc(50% + 4px);\n}\n\n.navbar-burger:hover {\n  background-color: rgba(0, 0, 0, 0.05);\n}\n\n.navbar-burger.is-active span:nth-child(1) {\n  -webkit-transform: translateY(5px) rotate(45deg);\n          transform: translateY(5px) rotate(45deg);\n}\n\n.navbar-burger.is-active span:nth-child(2) {\n  opacity: 0;\n}\n\n.navbar-burger.is-active span:nth-child(3) {\n  -webkit-transform: translateY(-5px) rotate(-45deg);\n          transform: translateY(-5px) rotate(-45deg);\n}\n\n.navbar-menu {\n  display: none;\n}\n\n.navbar-item,\n.navbar-link {\n  color: #4a4a4a;\n  display: block;\n  line-height: 1.5;\n  padding: 0.5rem 1rem;\n  position: relative;\n}\n\na.navbar-item:hover, a.navbar-item.is-active,\na.navbar-link:hover,\na.navbar-link.is-active {\n  background-color: whitesmoke;\n  color: #7a91c1;\n}\n\n.navbar-item {\n  -webkit-box-flex: 0;\n      -ms-flex-positive: 0;\n          flex-grow: 0;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n}\n\n.navbar-item img {\n  max-height: 1.75rem;\n}\n\n.navbar-item.has-dropdown {\n  padding: 0;\n}\n\n.navbar-item.is-expanded {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 1;\n      flex-shrink: 1;\n}\n\n.navbar-item.is-tab {\n  border-bottom: 1px solid transparent;\n  min-height: 3.25rem;\n  padding-bottom: calc(0.5rem - 1px);\n}\n\n.navbar-item.is-tab:hover {\n  background-color: transparent;\n  border-bottom-color: #7a91c1;\n}\n\n.navbar-item.is-tab.is-active {\n  background-color: transparent;\n  border-bottom-color: #7a91c1;\n  border-bottom-style: solid;\n  border-bottom-width: 3px;\n  color: #7a91c1;\n  padding-bottom: calc(0.5rem - 3px);\n}\n\n.navbar-content {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 1;\n      flex-shrink: 1;\n}\n\n.navbar-link {\n  padding-right: 2.5em;\n}\n\n.navbar-dropdown {\n  font-size: 0.875rem;\n  padding-bottom: 0.5rem;\n  padding-top: 0.5rem;\n}\n\n.navbar-dropdown .navbar-item {\n  padding-left: 1.5rem;\n  padding-right: 1.5rem;\n}\n\n.navbar-divider {\n  background-color: #dbdbdb;\n  border: none;\n  display: none;\n  height: 1px;\n  margin: 0.5rem 0;\n}\n\n@media screen and (max-width: 1023px) {\n  .navbar > .container {\n    display: block;\n  }\n  .navbar-brand .navbar-item,\n  .navbar-tabs .navbar-item {\n    -webkit-box-align: center;\n        -ms-flex-align: center;\n            align-items: center;\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n  .navbar-menu {\n    background-color: white;\n    -webkit-box-shadow: 0 8px 16px rgba(10, 10, 10, 0.1);\n            box-shadow: 0 8px 16px rgba(10, 10, 10, 0.1);\n    padding: 0.5rem 0;\n  }\n  .navbar-menu.is-active {\n    display: block;\n  }\n  .navbar.is-fixed-bottom-touch, .navbar.is-fixed-top-touch {\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 30;\n  }\n  .navbar.is-fixed-bottom-touch {\n    bottom: 0;\n  }\n  .navbar.is-fixed-bottom-touch.has-shadow {\n    -webkit-box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1);\n            box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1);\n  }\n  .navbar.is-fixed-top-touch {\n    top: 0;\n  }\n  .navbar.is-fixed-top .navbar-menu, .navbar.is-fixed-top-touch .navbar-menu {\n    -webkit-overflow-scrolling: touch;\n    max-height: calc(100vh - 3.25rem);\n    overflow: auto;\n  }\n  html.has-navbar-fixed-top-touch {\n    padding-top: 3.25rem;\n  }\n  html.has-navbar-fixed-bottom-touch {\n    padding-bottom: 3.25rem;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .navbar,\n  .navbar-menu,\n  .navbar-start,\n  .navbar-end {\n    -webkit-box-align: stretch;\n        -ms-flex-align: stretch;\n            align-items: stretch;\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n  .navbar {\n    min-height: 3.25rem;\n  }\n  .navbar.is-transparent a.navbar-item:hover, .navbar.is-transparent a.navbar-item.is-active,\n  .navbar.is-transparent a.navbar-link:hover,\n  .navbar.is-transparent a.navbar-link.is-active {\n    background-color: transparent !important;\n  }\n  .navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link, .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link {\n    background-color: transparent !important;\n  }\n  .navbar.is-transparent .navbar-dropdown a.navbar-item:hover {\n    background-color: whitesmoke;\n    color: #0a0a0a;\n  }\n  .navbar.is-transparent .navbar-dropdown a.navbar-item.is-active {\n    background-color: whitesmoke;\n    color: #7a91c1;\n  }\n  .navbar-burger {\n    display: none;\n  }\n  .navbar-item,\n  .navbar-link {\n    -webkit-box-align: center;\n        -ms-flex-align: center;\n            align-items: center;\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n  .navbar-item.has-dropdown {\n    -webkit-box-align: stretch;\n        -ms-flex-align: stretch;\n            align-items: stretch;\n  }\n  .navbar-item.has-dropdown-up .navbar-link::after {\n    -webkit-transform: rotate(135deg) translate(0.25em, -0.25em);\n            transform: rotate(135deg) translate(0.25em, -0.25em);\n  }\n  .navbar-item.has-dropdown-up .navbar-dropdown {\n    border-bottom: 1px solid #dbdbdb;\n    border-radius: 5px 5px 0 0;\n    border-top: none;\n    bottom: 100%;\n    -webkit-box-shadow: 0 -8px 8px rgba(10, 10, 10, 0.1);\n            box-shadow: 0 -8px 8px rgba(10, 10, 10, 0.1);\n    top: auto;\n  }\n  .navbar-item.is-active .navbar-dropdown, .navbar-item.is-hoverable:hover .navbar-dropdown {\n    display: block;\n  }\n  .navbar-item.is-active .navbar-dropdown.is-boxed, .navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed {\n    opacity: 1;\n    pointer-events: auto;\n    -webkit-transform: translateY(0);\n            transform: translateY(0);\n  }\n  .navbar-link::after {\n    border: 1px solid #7a91c1;\n    border-right: 0;\n    border-top: 0;\n    content: \" \";\n    display: block;\n    height: 0.5em;\n    pointer-events: none;\n    position: absolute;\n    -webkit-transform: rotate(-45deg);\n            transform: rotate(-45deg);\n    -webkit-transform-origin: center;\n            transform-origin: center;\n    width: 0.5em;\n    margin-top: -0.375em;\n    right: 1.125em;\n    top: 50%;\n  }\n  .navbar-menu {\n    -webkit-box-flex: 1;\n        -ms-flex-positive: 1;\n            flex-grow: 1;\n    -ms-flex-negative: 0;\n        flex-shrink: 0;\n  }\n  .navbar-start {\n    -webkit-box-pack: start;\n        -ms-flex-pack: start;\n            justify-content: flex-start;\n    margin-right: auto;\n  }\n  .navbar-end {\n    -webkit-box-pack: end;\n        -ms-flex-pack: end;\n            justify-content: flex-end;\n    margin-left: auto;\n  }\n  .navbar-dropdown {\n    background-color: white;\n    border-bottom-left-radius: 5px;\n    border-bottom-right-radius: 5px;\n    border-top: 1px solid #dbdbdb;\n    -webkit-box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1);\n            box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1);\n    display: none;\n    font-size: 0.875rem;\n    left: 0;\n    min-width: 100%;\n    position: absolute;\n    top: 100%;\n    z-index: 20;\n  }\n  .navbar-dropdown .navbar-item {\n    padding: 0.375rem 1rem;\n    white-space: nowrap;\n  }\n  .navbar-dropdown a.navbar-item {\n    padding-right: 3rem;\n  }\n  .navbar-dropdown a.navbar-item:hover {\n    background-color: whitesmoke;\n    color: #0a0a0a;\n  }\n  .navbar-dropdown a.navbar-item.is-active {\n    background-color: whitesmoke;\n    color: #7a91c1;\n  }\n  .navbar-dropdown.is-boxed {\n    border-radius: 5px;\n    border-top: none;\n    -webkit-box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);\n            box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);\n    display: block;\n    opacity: 0;\n    pointer-events: none;\n    top: calc(100% + (-4px));\n    -webkit-transform: translateY(-5px);\n            transform: translateY(-5px);\n    -webkit-transition-duration: 86ms;\n            transition-duration: 86ms;\n    -webkit-transition-property: opacity, -webkit-transform;\n    transition-property: opacity, -webkit-transform;\n    transition-property: opacity, transform;\n    transition-property: opacity, transform, -webkit-transform;\n  }\n  .navbar-dropdown.is-right {\n    left: auto;\n    right: 0;\n  }\n  .navbar-divider {\n    display: block;\n  }\n  .navbar > .container .navbar-brand,\n  .container > .navbar .navbar-brand {\n    margin-left: -1rem;\n  }\n  .navbar > .container .navbar-menu,\n  .container > .navbar .navbar-menu {\n    margin-right: -1rem;\n  }\n  .navbar.is-fixed-bottom-desktop, .navbar.is-fixed-top-desktop {\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 30;\n  }\n  .navbar.is-fixed-bottom-desktop {\n    bottom: 0;\n  }\n  .navbar.is-fixed-bottom-desktop.has-shadow {\n    -webkit-box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1);\n            box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1);\n  }\n  .navbar.is-fixed-top-desktop {\n    top: 0;\n  }\n  html.has-navbar-fixed-top-desktop {\n    padding-top: 3.25rem;\n  }\n  html.has-navbar-fixed-bottom-desktop {\n    padding-bottom: 3.25rem;\n  }\n  a.navbar-item.is-active,\n  a.navbar-link.is-active {\n    color: #0a0a0a;\n  }\n  a.navbar-item.is-active:not(:hover),\n  a.navbar-link.is-active:not(:hover) {\n    background-color: transparent;\n  }\n  .navbar-item.has-dropdown:hover .navbar-link, .navbar-item.has-dropdown.is-active .navbar-link {\n    background-color: whitesmoke;\n  }\n}\n\n.pagination {\n  font-size: 1rem;\n  margin: -0.25rem;\n}\n\n.pagination.is-small {\n  font-size: 0.75rem;\n}\n\n.pagination.is-medium {\n  font-size: 1.25rem;\n}\n\n.pagination.is-large {\n  font-size: 1.5rem;\n}\n\n.pagination,\n.pagination-list {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  text-align: center;\n}\n\n.pagination-previous,\n.pagination-next,\n.pagination-link,\n.pagination-ellipsis {\n  -moz-appearance: none;\n  -webkit-appearance: none;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  border: 1px solid transparent;\n  border-radius: 3px;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  display: -webkit-inline-box;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  font-size: 1rem;\n  height: 2.25em;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n  line-height: 1.5;\n  padding-bottom: calc(0.375em - 1px);\n  padding-left: calc(0.625em - 1px);\n  padding-right: calc(0.625em - 1px);\n  padding-top: calc(0.375em - 1px);\n  position: relative;\n  vertical-align: top;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  font-size: 1em;\n  padding-left: 0.5em;\n  padding-right: 0.5em;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  margin: 0.25rem;\n  text-align: center;\n}\n\n.pagination-previous:focus, .pagination-previous.is-focused, .pagination-previous:active, .pagination-previous.is-active,\n.pagination-next:focus,\n.pagination-next.is-focused,\n.pagination-next:active,\n.pagination-next.is-active,\n.pagination-link:focus,\n.pagination-link.is-focused,\n.pagination-link:active,\n.pagination-link.is-active,\n.pagination-ellipsis:focus,\n.pagination-ellipsis.is-focused,\n.pagination-ellipsis:active,\n.pagination-ellipsis.is-active {\n  outline: none;\n}\n\n.pagination-previous[disabled],\n.pagination-next[disabled],\n.pagination-link[disabled],\n.pagination-ellipsis[disabled] {\n  cursor: not-allowed;\n}\n\n.pagination-previous,\n.pagination-next,\n.pagination-link {\n  border-color: #dbdbdb;\n  min-width: 2.25em;\n}\n\n.pagination-previous:hover,\n.pagination-next:hover,\n.pagination-link:hover {\n  border-color: #b5b5b5;\n  color: #363636;\n}\n\n.pagination-previous:focus,\n.pagination-next:focus,\n.pagination-link:focus {\n  border-color: #7a91c1;\n}\n\n.pagination-previous:active,\n.pagination-next:active,\n.pagination-link:active {\n  -webkit-box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2);\n          box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2);\n}\n\n.pagination-previous[disabled],\n.pagination-next[disabled],\n.pagination-link[disabled] {\n  background-color: #dbdbdb;\n  border-color: #dbdbdb;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  color: #7a7a7a;\n  opacity: 0.5;\n}\n\n.pagination-previous,\n.pagination-next {\n  padding-left: 0.75em;\n  padding-right: 0.75em;\n  white-space: nowrap;\n}\n\n.pagination-link.is-current {\n  background-color: #7a91c1;\n  border-color: #7a91c1;\n  color: #fff;\n}\n\n.pagination-ellipsis {\n  color: #b5b5b5;\n  pointer-events: none;\n}\n\n.pagination-list {\n  -ms-flex-wrap: wrap;\n      flex-wrap: wrap;\n}\n\n@media screen and (max-width: 768px) {\n  .pagination {\n    -ms-flex-wrap: wrap;\n        flex-wrap: wrap;\n  }\n  .pagination-previous,\n  .pagination-next {\n    -webkit-box-flex: 1;\n        -ms-flex-positive: 1;\n            flex-grow: 1;\n    -ms-flex-negative: 1;\n        flex-shrink: 1;\n  }\n  .pagination-list li {\n    -webkit-box-flex: 1;\n        -ms-flex-positive: 1;\n            flex-grow: 1;\n    -ms-flex-negative: 1;\n        flex-shrink: 1;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .pagination-list {\n    -webkit-box-flex: 1;\n        -ms-flex-positive: 1;\n            flex-grow: 1;\n    -ms-flex-negative: 1;\n        flex-shrink: 1;\n    -webkit-box-pack: start;\n        -ms-flex-pack: start;\n            justify-content: flex-start;\n    -webkit-box-ordinal-group: 2;\n        -ms-flex-order: 1;\n            order: 1;\n  }\n  .pagination-previous {\n    -webkit-box-ordinal-group: 3;\n        -ms-flex-order: 2;\n            order: 2;\n  }\n  .pagination-next {\n    -webkit-box-ordinal-group: 4;\n        -ms-flex-order: 3;\n            order: 3;\n  }\n  .pagination {\n    -webkit-box-pack: justify;\n        -ms-flex-pack: justify;\n            justify-content: space-between;\n  }\n  .pagination.is-centered .pagination-previous {\n    -webkit-box-ordinal-group: 2;\n        -ms-flex-order: 1;\n            order: 1;\n  }\n  .pagination.is-centered .pagination-list {\n    -webkit-box-pack: center;\n        -ms-flex-pack: center;\n            justify-content: center;\n    -webkit-box-ordinal-group: 3;\n        -ms-flex-order: 2;\n            order: 2;\n  }\n  .pagination.is-centered .pagination-next {\n    -webkit-box-ordinal-group: 4;\n        -ms-flex-order: 3;\n            order: 3;\n  }\n  .pagination.is-right .pagination-previous {\n    -webkit-box-ordinal-group: 2;\n        -ms-flex-order: 1;\n            order: 1;\n  }\n  .pagination.is-right .pagination-next {\n    -webkit-box-ordinal-group: 3;\n        -ms-flex-order: 2;\n            order: 2;\n  }\n  .pagination.is-right .pagination-list {\n    -webkit-box-pack: end;\n        -ms-flex-pack: end;\n            justify-content: flex-end;\n    -webkit-box-ordinal-group: 4;\n        -ms-flex-order: 3;\n            order: 3;\n  }\n}\n\n.panel {\n  font-size: 1rem;\n}\n\n.panel:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.panel-heading,\n.panel-tabs,\n.panel-block {\n  border-bottom: 1px solid #dbdbdb;\n  border-left: 1px solid #dbdbdb;\n  border-right: 1px solid #dbdbdb;\n}\n\n.panel-heading:first-child,\n.panel-tabs:first-child,\n.panel-block:first-child {\n  border-top: 1px solid #dbdbdb;\n}\n\n.panel-heading {\n  background-color: whitesmoke;\n  border-radius: 3px 3px 0 0;\n  color: #363636;\n  font-size: 1.25em;\n  font-weight: 300;\n  line-height: 1.25;\n  padding: 0.5em 0.75em;\n}\n\n.panel-tabs {\n  -webkit-box-align: end;\n      -ms-flex-align: end;\n          align-items: flex-end;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  font-size: 0.875em;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.panel-tabs a {\n  border-bottom: 1px solid #dbdbdb;\n  margin-bottom: -1px;\n  padding: 0.5em;\n}\n\n.panel-tabs a.is-active {\n  border-bottom-color: #4a4a4a;\n  color: #363636;\n}\n\n.panel-list a {\n  color: #4a4a4a;\n}\n\n.panel-list a:hover {\n  color: #7a91c1;\n}\n\n.panel-block {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  color: #363636;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n  padding: 0.5em 0.75em;\n}\n\n.panel-block input[type=\"checkbox\"] {\n  margin-right: 0.75em;\n}\n\n.panel-block > .control {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 1;\n      flex-shrink: 1;\n  width: 100%;\n}\n\n.panel-block.is-wrapped {\n  -ms-flex-wrap: wrap;\n      flex-wrap: wrap;\n}\n\n.panel-block.is-active {\n  border-left-color: #7a91c1;\n  color: #363636;\n}\n\n.panel-block.is-active .panel-icon {\n  color: #7a91c1;\n}\n\na.panel-block,\nlabel.panel-block {\n  cursor: pointer;\n}\n\na.panel-block:hover,\nlabel.panel-block:hover {\n  background-color: whitesmoke;\n}\n\n.panel-icon {\n  display: inline-block;\n  font-size: 14px;\n  height: 1em;\n  line-height: 1em;\n  text-align: center;\n  vertical-align: top;\n  width: 1em;\n  color: #7a7a7a;\n  margin-right: 0.75em;\n}\n\n.panel-icon .fa {\n  font-size: inherit;\n  line-height: inherit;\n}\n\n.tabs {\n  -webkit-overflow-scrolling: touch;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  -webkit-box-align: stretch;\n      -ms-flex-align: stretch;\n          align-items: stretch;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  font-size: 1rem;\n  -webkit-box-pack: justify;\n      -ms-flex-pack: justify;\n          justify-content: space-between;\n  overflow: hidden;\n  overflow-x: auto;\n  white-space: nowrap;\n}\n\n.tabs:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.tabs a {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  border-bottom-color: #dbdbdb;\n  border-bottom-style: solid;\n  border-bottom-width: 1px;\n  color: #4a4a4a;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  margin-bottom: -1px;\n  padding: 0.5em 1em;\n  vertical-align: top;\n}\n\n.tabs a:hover {\n  border-bottom-color: #363636;\n  color: #363636;\n}\n\n.tabs li {\n  display: block;\n}\n\n.tabs li.is-active a {\n  border-bottom-color: #7a91c1;\n  color: #7a91c1;\n}\n\n.tabs ul {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  border-bottom-color: #dbdbdb;\n  border-bottom-style: solid;\n  border-bottom-width: 1px;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  -webkit-box-pack: start;\n      -ms-flex-pack: start;\n          justify-content: flex-start;\n}\n\n.tabs ul.is-left {\n  padding-right: 0.75em;\n}\n\n.tabs ul.is-center {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  padding-left: 0.75em;\n  padding-right: 0.75em;\n}\n\n.tabs ul.is-right {\n  -webkit-box-pack: end;\n      -ms-flex-pack: end;\n          justify-content: flex-end;\n  padding-left: 0.75em;\n}\n\n.tabs .icon:first-child {\n  margin-right: 0.5em;\n}\n\n.tabs .icon:last-child {\n  margin-left: 0.5em;\n}\n\n.tabs.is-centered ul {\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.tabs.is-right ul {\n  -webkit-box-pack: end;\n      -ms-flex-pack: end;\n          justify-content: flex-end;\n}\n\n.tabs.is-boxed a {\n  border: 1px solid transparent;\n  border-radius: 3px 3px 0 0;\n}\n\n.tabs.is-boxed a:hover {\n  background-color: whitesmoke;\n  border-bottom-color: #dbdbdb;\n}\n\n.tabs.is-boxed li.is-active a {\n  background-color: white;\n  border-color: #dbdbdb;\n  border-bottom-color: transparent !important;\n}\n\n.tabs.is-fullwidth li {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n}\n\n.tabs.is-toggle a {\n  border-color: #dbdbdb;\n  border-style: solid;\n  border-width: 1px;\n  margin-bottom: 0;\n  position: relative;\n}\n\n.tabs.is-toggle a:hover {\n  background-color: whitesmoke;\n  border-color: #b5b5b5;\n  z-index: 2;\n}\n\n.tabs.is-toggle li + li {\n  margin-left: -1px;\n}\n\n.tabs.is-toggle li:first-child a {\n  border-radius: 3px 0 0 3px;\n}\n\n.tabs.is-toggle li:last-child a {\n  border-radius: 0 3px 3px 0;\n}\n\n.tabs.is-toggle li.is-active a {\n  background-color: #7a91c1;\n  border-color: #7a91c1;\n  color: #fff;\n  z-index: 1;\n}\n\n.tabs.is-toggle ul {\n  border-bottom: none;\n}\n\n.tabs.is-small {\n  font-size: 0.75rem;\n}\n\n.tabs.is-medium {\n  font-size: 1.25rem;\n}\n\n.tabs.is-large {\n  font-size: 1.5rem;\n}\n\n.column {\n  display: block;\n  -ms-flex-preferred-size: 0;\n      flex-basis: 0;\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 1;\n      flex-shrink: 1;\n  padding: 0.75rem;\n}\n\n.columns.is-mobile > .column.is-narrow {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n}\n\n.columns.is-mobile > .column.is-full {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 100%;\n}\n\n.columns.is-mobile > .column.is-three-quarters {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 75%;\n}\n\n.columns.is-mobile > .column.is-two-thirds {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 66.6666%;\n}\n\n.columns.is-mobile > .column.is-half {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 50%;\n}\n\n.columns.is-mobile > .column.is-one-third {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 33.3333%;\n}\n\n.columns.is-mobile > .column.is-one-quarter {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 25%;\n}\n\n.columns.is-mobile > .column.is-one-fifth {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 20%;\n}\n\n.columns.is-mobile > .column.is-two-fifths {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 40%;\n}\n\n.columns.is-mobile > .column.is-three-fifths {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 60%;\n}\n\n.columns.is-mobile > .column.is-four-fifths {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 80%;\n}\n\n.columns.is-mobile > .column.is-offset-three-quarters {\n  margin-left: 75%;\n}\n\n.columns.is-mobile > .column.is-offset-two-thirds {\n  margin-left: 66.6666%;\n}\n\n.columns.is-mobile > .column.is-offset-half {\n  margin-left: 50%;\n}\n\n.columns.is-mobile > .column.is-offset-one-third {\n  margin-left: 33.3333%;\n}\n\n.columns.is-mobile > .column.is-offset-one-quarter {\n  margin-left: 25%;\n}\n\n.columns.is-mobile > .column.is-offset-one-fifth {\n  margin-left: 20%;\n}\n\n.columns.is-mobile > .column.is-offset-two-fifths {\n  margin-left: 40%;\n}\n\n.columns.is-mobile > .column.is-offset-three-fifths {\n  margin-left: 60%;\n}\n\n.columns.is-mobile > .column.is-offset-four-fifths {\n  margin-left: 80%;\n}\n\n.columns.is-mobile > .column.is-1 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 8.33333%;\n}\n\n.columns.is-mobile > .column.is-offset-1 {\n  margin-left: 8.33333%;\n}\n\n.columns.is-mobile > .column.is-2 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 16.66667%;\n}\n\n.columns.is-mobile > .column.is-offset-2 {\n  margin-left: 16.66667%;\n}\n\n.columns.is-mobile > .column.is-3 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 25%;\n}\n\n.columns.is-mobile > .column.is-offset-3 {\n  margin-left: 25%;\n}\n\n.columns.is-mobile > .column.is-4 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 33.33333%;\n}\n\n.columns.is-mobile > .column.is-offset-4 {\n  margin-left: 33.33333%;\n}\n\n.columns.is-mobile > .column.is-5 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 41.66667%;\n}\n\n.columns.is-mobile > .column.is-offset-5 {\n  margin-left: 41.66667%;\n}\n\n.columns.is-mobile > .column.is-6 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 50%;\n}\n\n.columns.is-mobile > .column.is-offset-6 {\n  margin-left: 50%;\n}\n\n.columns.is-mobile > .column.is-7 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 58.33333%;\n}\n\n.columns.is-mobile > .column.is-offset-7 {\n  margin-left: 58.33333%;\n}\n\n.columns.is-mobile > .column.is-8 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 66.66667%;\n}\n\n.columns.is-mobile > .column.is-offset-8 {\n  margin-left: 66.66667%;\n}\n\n.columns.is-mobile > .column.is-9 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 75%;\n}\n\n.columns.is-mobile > .column.is-offset-9 {\n  margin-left: 75%;\n}\n\n.columns.is-mobile > .column.is-10 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 83.33333%;\n}\n\n.columns.is-mobile > .column.is-offset-10 {\n  margin-left: 83.33333%;\n}\n\n.columns.is-mobile > .column.is-11 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 91.66667%;\n}\n\n.columns.is-mobile > .column.is-offset-11 {\n  margin-left: 91.66667%;\n}\n\n.columns.is-mobile > .column.is-12 {\n  -webkit-box-flex: 0;\n      -ms-flex: none;\n          flex: none;\n  width: 100%;\n}\n\n.columns.is-mobile > .column.is-offset-12 {\n  margin-left: 100%;\n}\n\n@media screen and (max-width: 768px) {\n  .column.is-narrow-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n  }\n  .column.is-full-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-three-quarters-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-two-thirds-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.6666%;\n  }\n  .column.is-half-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-one-third-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.3333%;\n  }\n  .column.is-one-quarter-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-one-fifth-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 20%;\n  }\n  .column.is-two-fifths-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 40%;\n  }\n  .column.is-three-fifths-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 60%;\n  }\n  .column.is-four-fifths-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 80%;\n  }\n  .column.is-offset-three-quarters-mobile {\n    margin-left: 75%;\n  }\n  .column.is-offset-two-thirds-mobile {\n    margin-left: 66.6666%;\n  }\n  .column.is-offset-half-mobile {\n    margin-left: 50%;\n  }\n  .column.is-offset-one-third-mobile {\n    margin-left: 33.3333%;\n  }\n  .column.is-offset-one-quarter-mobile {\n    margin-left: 25%;\n  }\n  .column.is-offset-one-fifth-mobile {\n    margin-left: 20%;\n  }\n  .column.is-offset-two-fifths-mobile {\n    margin-left: 40%;\n  }\n  .column.is-offset-three-fifths-mobile {\n    margin-left: 60%;\n  }\n  .column.is-offset-four-fifths-mobile {\n    margin-left: 80%;\n  }\n  .column.is-1-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 8.33333%;\n  }\n  .column.is-offset-1-mobile {\n    margin-left: 8.33333%;\n  }\n  .column.is-2-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 16.66667%;\n  }\n  .column.is-offset-2-mobile {\n    margin-left: 16.66667%;\n  }\n  .column.is-3-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-offset-3-mobile {\n    margin-left: 25%;\n  }\n  .column.is-4-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.33333%;\n  }\n  .column.is-offset-4-mobile {\n    margin-left: 33.33333%;\n  }\n  .column.is-5-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 41.66667%;\n  }\n  .column.is-offset-5-mobile {\n    margin-left: 41.66667%;\n  }\n  .column.is-6-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-offset-6-mobile {\n    margin-left: 50%;\n  }\n  .column.is-7-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 58.33333%;\n  }\n  .column.is-offset-7-mobile {\n    margin-left: 58.33333%;\n  }\n  .column.is-8-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.66667%;\n  }\n  .column.is-offset-8-mobile {\n    margin-left: 66.66667%;\n  }\n  .column.is-9-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-offset-9-mobile {\n    margin-left: 75%;\n  }\n  .column.is-10-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 83.33333%;\n  }\n  .column.is-offset-10-mobile {\n    margin-left: 83.33333%;\n  }\n  .column.is-11-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 91.66667%;\n  }\n  .column.is-offset-11-mobile {\n    margin-left: 91.66667%;\n  }\n  .column.is-12-mobile {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-offset-12-mobile {\n    margin-left: 100%;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .column.is-narrow, .column.is-narrow-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n  }\n  .column.is-full, .column.is-full-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-three-quarters, .column.is-three-quarters-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-two-thirds, .column.is-two-thirds-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.6666%;\n  }\n  .column.is-half, .column.is-half-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-one-third, .column.is-one-third-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.3333%;\n  }\n  .column.is-one-quarter, .column.is-one-quarter-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-one-fifth, .column.is-one-fifth-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 20%;\n  }\n  .column.is-two-fifths, .column.is-two-fifths-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 40%;\n  }\n  .column.is-three-fifths, .column.is-three-fifths-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 60%;\n  }\n  .column.is-four-fifths, .column.is-four-fifths-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 80%;\n  }\n  .column.is-offset-three-quarters, .column.is-offset-three-quarters-tablet {\n    margin-left: 75%;\n  }\n  .column.is-offset-two-thirds, .column.is-offset-two-thirds-tablet {\n    margin-left: 66.6666%;\n  }\n  .column.is-offset-half, .column.is-offset-half-tablet {\n    margin-left: 50%;\n  }\n  .column.is-offset-one-third, .column.is-offset-one-third-tablet {\n    margin-left: 33.3333%;\n  }\n  .column.is-offset-one-quarter, .column.is-offset-one-quarter-tablet {\n    margin-left: 25%;\n  }\n  .column.is-offset-one-fifth, .column.is-offset-one-fifth-tablet {\n    margin-left: 20%;\n  }\n  .column.is-offset-two-fifths, .column.is-offset-two-fifths-tablet {\n    margin-left: 40%;\n  }\n  .column.is-offset-three-fifths, .column.is-offset-three-fifths-tablet {\n    margin-left: 60%;\n  }\n  .column.is-offset-four-fifths, .column.is-offset-four-fifths-tablet {\n    margin-left: 80%;\n  }\n  .column.is-1, .column.is-1-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 8.33333%;\n  }\n  .column.is-offset-1, .column.is-offset-1-tablet {\n    margin-left: 8.33333%;\n  }\n  .column.is-2, .column.is-2-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 16.66667%;\n  }\n  .column.is-offset-2, .column.is-offset-2-tablet {\n    margin-left: 16.66667%;\n  }\n  .column.is-3, .column.is-3-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-offset-3, .column.is-offset-3-tablet {\n    margin-left: 25%;\n  }\n  .column.is-4, .column.is-4-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.33333%;\n  }\n  .column.is-offset-4, .column.is-offset-4-tablet {\n    margin-left: 33.33333%;\n  }\n  .column.is-5, .column.is-5-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 41.66667%;\n  }\n  .column.is-offset-5, .column.is-offset-5-tablet {\n    margin-left: 41.66667%;\n  }\n  .column.is-6, .column.is-6-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-offset-6, .column.is-offset-6-tablet {\n    margin-left: 50%;\n  }\n  .column.is-7, .column.is-7-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 58.33333%;\n  }\n  .column.is-offset-7, .column.is-offset-7-tablet {\n    margin-left: 58.33333%;\n  }\n  .column.is-8, .column.is-8-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.66667%;\n  }\n  .column.is-offset-8, .column.is-offset-8-tablet {\n    margin-left: 66.66667%;\n  }\n  .column.is-9, .column.is-9-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-offset-9, .column.is-offset-9-tablet {\n    margin-left: 75%;\n  }\n  .column.is-10, .column.is-10-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 83.33333%;\n  }\n  .column.is-offset-10, .column.is-offset-10-tablet {\n    margin-left: 83.33333%;\n  }\n  .column.is-11, .column.is-11-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 91.66667%;\n  }\n  .column.is-offset-11, .column.is-offset-11-tablet {\n    margin-left: 91.66667%;\n  }\n  .column.is-12, .column.is-12-tablet {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-offset-12, .column.is-offset-12-tablet {\n    margin-left: 100%;\n  }\n}\n\n@media screen and (max-width: 1023px) {\n  .column.is-narrow-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n  }\n  .column.is-full-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-three-quarters-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-two-thirds-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.6666%;\n  }\n  .column.is-half-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-one-third-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.3333%;\n  }\n  .column.is-one-quarter-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-one-fifth-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 20%;\n  }\n  .column.is-two-fifths-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 40%;\n  }\n  .column.is-three-fifths-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 60%;\n  }\n  .column.is-four-fifths-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 80%;\n  }\n  .column.is-offset-three-quarters-touch {\n    margin-left: 75%;\n  }\n  .column.is-offset-two-thirds-touch {\n    margin-left: 66.6666%;\n  }\n  .column.is-offset-half-touch {\n    margin-left: 50%;\n  }\n  .column.is-offset-one-third-touch {\n    margin-left: 33.3333%;\n  }\n  .column.is-offset-one-quarter-touch {\n    margin-left: 25%;\n  }\n  .column.is-offset-one-fifth-touch {\n    margin-left: 20%;\n  }\n  .column.is-offset-two-fifths-touch {\n    margin-left: 40%;\n  }\n  .column.is-offset-three-fifths-touch {\n    margin-left: 60%;\n  }\n  .column.is-offset-four-fifths-touch {\n    margin-left: 80%;\n  }\n  .column.is-1-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 8.33333%;\n  }\n  .column.is-offset-1-touch {\n    margin-left: 8.33333%;\n  }\n  .column.is-2-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 16.66667%;\n  }\n  .column.is-offset-2-touch {\n    margin-left: 16.66667%;\n  }\n  .column.is-3-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-offset-3-touch {\n    margin-left: 25%;\n  }\n  .column.is-4-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.33333%;\n  }\n  .column.is-offset-4-touch {\n    margin-left: 33.33333%;\n  }\n  .column.is-5-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 41.66667%;\n  }\n  .column.is-offset-5-touch {\n    margin-left: 41.66667%;\n  }\n  .column.is-6-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-offset-6-touch {\n    margin-left: 50%;\n  }\n  .column.is-7-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 58.33333%;\n  }\n  .column.is-offset-7-touch {\n    margin-left: 58.33333%;\n  }\n  .column.is-8-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.66667%;\n  }\n  .column.is-offset-8-touch {\n    margin-left: 66.66667%;\n  }\n  .column.is-9-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-offset-9-touch {\n    margin-left: 75%;\n  }\n  .column.is-10-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 83.33333%;\n  }\n  .column.is-offset-10-touch {\n    margin-left: 83.33333%;\n  }\n  .column.is-11-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 91.66667%;\n  }\n  .column.is-offset-11-touch {\n    margin-left: 91.66667%;\n  }\n  .column.is-12-touch {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-offset-12-touch {\n    margin-left: 100%;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .column.is-narrow-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n  }\n  .column.is-full-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-three-quarters-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-two-thirds-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.6666%;\n  }\n  .column.is-half-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-one-third-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.3333%;\n  }\n  .column.is-one-quarter-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-one-fifth-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 20%;\n  }\n  .column.is-two-fifths-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 40%;\n  }\n  .column.is-three-fifths-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 60%;\n  }\n  .column.is-four-fifths-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 80%;\n  }\n  .column.is-offset-three-quarters-desktop {\n    margin-left: 75%;\n  }\n  .column.is-offset-two-thirds-desktop {\n    margin-left: 66.6666%;\n  }\n  .column.is-offset-half-desktop {\n    margin-left: 50%;\n  }\n  .column.is-offset-one-third-desktop {\n    margin-left: 33.3333%;\n  }\n  .column.is-offset-one-quarter-desktop {\n    margin-left: 25%;\n  }\n  .column.is-offset-one-fifth-desktop {\n    margin-left: 20%;\n  }\n  .column.is-offset-two-fifths-desktop {\n    margin-left: 40%;\n  }\n  .column.is-offset-three-fifths-desktop {\n    margin-left: 60%;\n  }\n  .column.is-offset-four-fifths-desktop {\n    margin-left: 80%;\n  }\n  .column.is-1-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 8.33333%;\n  }\n  .column.is-offset-1-desktop {\n    margin-left: 8.33333%;\n  }\n  .column.is-2-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 16.66667%;\n  }\n  .column.is-offset-2-desktop {\n    margin-left: 16.66667%;\n  }\n  .column.is-3-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-offset-3-desktop {\n    margin-left: 25%;\n  }\n  .column.is-4-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.33333%;\n  }\n  .column.is-offset-4-desktop {\n    margin-left: 33.33333%;\n  }\n  .column.is-5-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 41.66667%;\n  }\n  .column.is-offset-5-desktop {\n    margin-left: 41.66667%;\n  }\n  .column.is-6-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-offset-6-desktop {\n    margin-left: 50%;\n  }\n  .column.is-7-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 58.33333%;\n  }\n  .column.is-offset-7-desktop {\n    margin-left: 58.33333%;\n  }\n  .column.is-8-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.66667%;\n  }\n  .column.is-offset-8-desktop {\n    margin-left: 66.66667%;\n  }\n  .column.is-9-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-offset-9-desktop {\n    margin-left: 75%;\n  }\n  .column.is-10-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 83.33333%;\n  }\n  .column.is-offset-10-desktop {\n    margin-left: 83.33333%;\n  }\n  .column.is-11-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 91.66667%;\n  }\n  .column.is-offset-11-desktop {\n    margin-left: 91.66667%;\n  }\n  .column.is-12-desktop {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-offset-12-desktop {\n    margin-left: 100%;\n  }\n}\n\n@media screen and (min-width: 1216px) {\n  .column.is-narrow-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n  }\n  .column.is-full-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-three-quarters-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-two-thirds-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.6666%;\n  }\n  .column.is-half-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-one-third-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.3333%;\n  }\n  .column.is-one-quarter-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-one-fifth-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 20%;\n  }\n  .column.is-two-fifths-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 40%;\n  }\n  .column.is-three-fifths-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 60%;\n  }\n  .column.is-four-fifths-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 80%;\n  }\n  .column.is-offset-three-quarters-widescreen {\n    margin-left: 75%;\n  }\n  .column.is-offset-two-thirds-widescreen {\n    margin-left: 66.6666%;\n  }\n  .column.is-offset-half-widescreen {\n    margin-left: 50%;\n  }\n  .column.is-offset-one-third-widescreen {\n    margin-left: 33.3333%;\n  }\n  .column.is-offset-one-quarter-widescreen {\n    margin-left: 25%;\n  }\n  .column.is-offset-one-fifth-widescreen {\n    margin-left: 20%;\n  }\n  .column.is-offset-two-fifths-widescreen {\n    margin-left: 40%;\n  }\n  .column.is-offset-three-fifths-widescreen {\n    margin-left: 60%;\n  }\n  .column.is-offset-four-fifths-widescreen {\n    margin-left: 80%;\n  }\n  .column.is-1-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 8.33333%;\n  }\n  .column.is-offset-1-widescreen {\n    margin-left: 8.33333%;\n  }\n  .column.is-2-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 16.66667%;\n  }\n  .column.is-offset-2-widescreen {\n    margin-left: 16.66667%;\n  }\n  .column.is-3-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-offset-3-widescreen {\n    margin-left: 25%;\n  }\n  .column.is-4-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.33333%;\n  }\n  .column.is-offset-4-widescreen {\n    margin-left: 33.33333%;\n  }\n  .column.is-5-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 41.66667%;\n  }\n  .column.is-offset-5-widescreen {\n    margin-left: 41.66667%;\n  }\n  .column.is-6-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-offset-6-widescreen {\n    margin-left: 50%;\n  }\n  .column.is-7-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 58.33333%;\n  }\n  .column.is-offset-7-widescreen {\n    margin-left: 58.33333%;\n  }\n  .column.is-8-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.66667%;\n  }\n  .column.is-offset-8-widescreen {\n    margin-left: 66.66667%;\n  }\n  .column.is-9-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-offset-9-widescreen {\n    margin-left: 75%;\n  }\n  .column.is-10-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 83.33333%;\n  }\n  .column.is-offset-10-widescreen {\n    margin-left: 83.33333%;\n  }\n  .column.is-11-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 91.66667%;\n  }\n  .column.is-offset-11-widescreen {\n    margin-left: 91.66667%;\n  }\n  .column.is-12-widescreen {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-offset-12-widescreen {\n    margin-left: 100%;\n  }\n}\n\n@media screen and (min-width: 1408px) {\n  .column.is-narrow-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n  }\n  .column.is-full-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-three-quarters-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-two-thirds-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.6666%;\n  }\n  .column.is-half-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-one-third-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.3333%;\n  }\n  .column.is-one-quarter-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-one-fifth-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 20%;\n  }\n  .column.is-two-fifths-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 40%;\n  }\n  .column.is-three-fifths-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 60%;\n  }\n  .column.is-four-fifths-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 80%;\n  }\n  .column.is-offset-three-quarters-fullhd {\n    margin-left: 75%;\n  }\n  .column.is-offset-two-thirds-fullhd {\n    margin-left: 66.6666%;\n  }\n  .column.is-offset-half-fullhd {\n    margin-left: 50%;\n  }\n  .column.is-offset-one-third-fullhd {\n    margin-left: 33.3333%;\n  }\n  .column.is-offset-one-quarter-fullhd {\n    margin-left: 25%;\n  }\n  .column.is-offset-one-fifth-fullhd {\n    margin-left: 20%;\n  }\n  .column.is-offset-two-fifths-fullhd {\n    margin-left: 40%;\n  }\n  .column.is-offset-three-fifths-fullhd {\n    margin-left: 60%;\n  }\n  .column.is-offset-four-fifths-fullhd {\n    margin-left: 80%;\n  }\n  .column.is-1-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 8.33333%;\n  }\n  .column.is-offset-1-fullhd {\n    margin-left: 8.33333%;\n  }\n  .column.is-2-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 16.66667%;\n  }\n  .column.is-offset-2-fullhd {\n    margin-left: 16.66667%;\n  }\n  .column.is-3-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .column.is-offset-3-fullhd {\n    margin-left: 25%;\n  }\n  .column.is-4-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.33333%;\n  }\n  .column.is-offset-4-fullhd {\n    margin-left: 33.33333%;\n  }\n  .column.is-5-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 41.66667%;\n  }\n  .column.is-offset-5-fullhd {\n    margin-left: 41.66667%;\n  }\n  .column.is-6-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .column.is-offset-6-fullhd {\n    margin-left: 50%;\n  }\n  .column.is-7-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 58.33333%;\n  }\n  .column.is-offset-7-fullhd {\n    margin-left: 58.33333%;\n  }\n  .column.is-8-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.66667%;\n  }\n  .column.is-offset-8-fullhd {\n    margin-left: 66.66667%;\n  }\n  .column.is-9-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .column.is-offset-9-fullhd {\n    margin-left: 75%;\n  }\n  .column.is-10-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 83.33333%;\n  }\n  .column.is-offset-10-fullhd {\n    margin-left: 83.33333%;\n  }\n  .column.is-11-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 91.66667%;\n  }\n  .column.is-offset-11-fullhd {\n    margin-left: 91.66667%;\n  }\n  .column.is-12-fullhd {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n  .column.is-offset-12-fullhd {\n    margin-left: 100%;\n  }\n}\n\n.columns {\n  margin-left: -0.75rem;\n  margin-right: -0.75rem;\n  margin-top: -0.75rem;\n}\n\n.columns:last-child {\n  margin-bottom: -0.75rem;\n}\n\n.columns:not(:last-child) {\n  margin-bottom: calc(1.5rem - 0.75rem);\n}\n\n.columns.is-centered {\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n}\n\n.columns.is-gapless {\n  margin-left: 0;\n  margin-right: 0;\n  margin-top: 0;\n}\n\n.columns.is-gapless > .column {\n  margin: 0;\n  padding: 0 !important;\n}\n\n.columns.is-gapless:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n\n.columns.is-gapless:last-child {\n  margin-bottom: 0;\n}\n\n.columns.is-mobile {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.columns.is-multiline {\n  -ms-flex-wrap: wrap;\n      flex-wrap: wrap;\n}\n\n.columns.is-vcentered {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n}\n\n@media screen and (min-width: 769px), print {\n  .columns:not(.is-desktop) {\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n}\n\n@media screen and (min-width: 1024px) {\n  .columns.is-desktop {\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n}\n\n.columns.is-variable {\n  --columnGap: 0.75rem;\n  margin-left: calc(-1 * var(--columnGap));\n  margin-right: calc(-1 * var(--columnGap));\n}\n\n.columns.is-variable .column {\n  padding-left: var(--columnGap);\n  padding-right: var(--columnGap);\n}\n\n.columns.is-variable.is-0 {\n  --columnGap: 0rem;\n}\n\n.columns.is-variable.is-1 {\n  --columnGap: 0.25rem;\n}\n\n.columns.is-variable.is-2 {\n  --columnGap: 0.5rem;\n}\n\n.columns.is-variable.is-3 {\n  --columnGap: 0.75rem;\n}\n\n.columns.is-variable.is-4 {\n  --columnGap: 1rem;\n}\n\n.columns.is-variable.is-5 {\n  --columnGap: 1.25rem;\n}\n\n.columns.is-variable.is-6 {\n  --columnGap: 1.5rem;\n}\n\n.columns.is-variable.is-7 {\n  --columnGap: 1.75rem;\n}\n\n.columns.is-variable.is-8 {\n  --columnGap: 2rem;\n}\n\n.tile {\n  -webkit-box-align: stretch;\n      -ms-flex-align: stretch;\n          align-items: stretch;\n  display: block;\n  -ms-flex-preferred-size: 0;\n      flex-basis: 0;\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 1;\n      flex-shrink: 1;\n  min-height: -webkit-min-content;\n  min-height: -moz-min-content;\n  min-height: min-content;\n}\n\n.tile.is-ancestor {\n  margin-left: -0.75rem;\n  margin-right: -0.75rem;\n  margin-top: -0.75rem;\n}\n\n.tile.is-ancestor:last-child {\n  margin-bottom: -0.75rem;\n}\n\n.tile.is-ancestor:not(:last-child) {\n  margin-bottom: 0.75rem;\n}\n\n.tile.is-child {\n  margin: 0 !important;\n}\n\n.tile.is-parent {\n  padding: 0.75rem;\n}\n\n.tile.is-vertical {\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-direction: column;\n          flex-direction: column;\n}\n\n.tile.is-vertical > .tile.is-child:not(:last-child) {\n  margin-bottom: 1.5rem !important;\n}\n\n@media screen and (min-width: 769px), print {\n  .tile:not(.is-child) {\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n  .tile.is-1 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 8.33333%;\n  }\n  .tile.is-2 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 16.66667%;\n  }\n  .tile.is-3 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 25%;\n  }\n  .tile.is-4 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 33.33333%;\n  }\n  .tile.is-5 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 41.66667%;\n  }\n  .tile.is-6 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 50%;\n  }\n  .tile.is-7 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 58.33333%;\n  }\n  .tile.is-8 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 66.66667%;\n  }\n  .tile.is-9 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 75%;\n  }\n  .tile.is-10 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 83.33333%;\n  }\n  .tile.is-11 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 91.66667%;\n  }\n  .tile.is-12 {\n    -webkit-box-flex: 0;\n        -ms-flex: none;\n            flex: none;\n    width: 100%;\n  }\n}\n\n.hero {\n  -webkit-box-align: stretch;\n      -ms-flex-align: stretch;\n          align-items: stretch;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-direction: column;\n          flex-direction: column;\n  -webkit-box-pack: justify;\n      -ms-flex-pack: justify;\n          justify-content: space-between;\n}\n\n.hero .navbar {\n  background: none;\n}\n\n.hero .tabs ul {\n  border-bottom: none;\n}\n\n.hero.is-white {\n  background-color: white;\n  color: #0a0a0a;\n}\n\n.hero.is-white a:not(.button),\n.hero.is-white strong {\n  color: inherit;\n}\n\n.hero.is-white .title {\n  color: #0a0a0a;\n}\n\n.hero.is-white .subtitle {\n  color: rgba(10, 10, 10, 0.9);\n}\n\n.hero.is-white .subtitle a:not(.button),\n.hero.is-white .subtitle strong {\n  color: #0a0a0a;\n}\n\n@media screen and (max-width: 1023px) {\n  .hero.is-white .navbar-menu {\n    background-color: white;\n  }\n}\n\n.hero.is-white .navbar-item,\n.hero.is-white .navbar-link {\n  color: rgba(10, 10, 10, 0.7);\n}\n\n.hero.is-white a.navbar-item:hover, .hero.is-white a.navbar-item.is-active,\n.hero.is-white .navbar-link:hover,\n.hero.is-white .navbar-link.is-active {\n  background-color: #f2f2f2;\n  color: #0a0a0a;\n}\n\n.hero.is-white .tabs a {\n  color: #0a0a0a;\n  opacity: 0.9;\n}\n\n.hero.is-white .tabs a:hover {\n  opacity: 1;\n}\n\n.hero.is-white .tabs li.is-active a {\n  opacity: 1;\n}\n\n.hero.is-white .tabs.is-boxed a, .hero.is-white .tabs.is-toggle a {\n  color: #0a0a0a;\n}\n\n.hero.is-white .tabs.is-boxed a:hover, .hero.is-white .tabs.is-toggle a:hover {\n  background-color: rgba(10, 10, 10, 0.1);\n}\n\n.hero.is-white .tabs.is-boxed li.is-active a, .hero.is-white .tabs.is-boxed li.is-active a:hover, .hero.is-white .tabs.is-toggle li.is-active a, .hero.is-white .tabs.is-toggle li.is-active a:hover {\n  background-color: #0a0a0a;\n  border-color: #0a0a0a;\n  color: white;\n}\n\n.hero.is-white.is-bold {\n  background-image: linear-gradient(141deg, #e6e6e6 0%, white 71%, white 100%);\n}\n\n@media screen and (max-width: 768px) {\n  .hero.is-white.is-bold .navbar-menu {\n    background-image: linear-gradient(141deg, #e6e6e6 0%, white 71%, white 100%);\n  }\n}\n\n.hero.is-black {\n  background-color: #0a0a0a;\n  color: white;\n}\n\n.hero.is-black a:not(.button),\n.hero.is-black strong {\n  color: inherit;\n}\n\n.hero.is-black .title {\n  color: white;\n}\n\n.hero.is-black .subtitle {\n  color: rgba(255, 255, 255, 0.9);\n}\n\n.hero.is-black .subtitle a:not(.button),\n.hero.is-black .subtitle strong {\n  color: white;\n}\n\n@media screen and (max-width: 1023px) {\n  .hero.is-black .navbar-menu {\n    background-color: #0a0a0a;\n  }\n}\n\n.hero.is-black .navbar-item,\n.hero.is-black .navbar-link {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.hero.is-black a.navbar-item:hover, .hero.is-black a.navbar-item.is-active,\n.hero.is-black .navbar-link:hover,\n.hero.is-black .navbar-link.is-active {\n  background-color: black;\n  color: white;\n}\n\n.hero.is-black .tabs a {\n  color: white;\n  opacity: 0.9;\n}\n\n.hero.is-black .tabs a:hover {\n  opacity: 1;\n}\n\n.hero.is-black .tabs li.is-active a {\n  opacity: 1;\n}\n\n.hero.is-black .tabs.is-boxed a, .hero.is-black .tabs.is-toggle a {\n  color: white;\n}\n\n.hero.is-black .tabs.is-boxed a:hover, .hero.is-black .tabs.is-toggle a:hover {\n  background-color: rgba(10, 10, 10, 0.1);\n}\n\n.hero.is-black .tabs.is-boxed li.is-active a, .hero.is-black .tabs.is-boxed li.is-active a:hover, .hero.is-black .tabs.is-toggle li.is-active a, .hero.is-black .tabs.is-toggle li.is-active a:hover {\n  background-color: white;\n  border-color: white;\n  color: #0a0a0a;\n}\n\n.hero.is-black.is-bold {\n  background-image: linear-gradient(141deg, black 0%, #0a0a0a 71%, #181616 100%);\n}\n\n@media screen and (max-width: 768px) {\n  .hero.is-black.is-bold .navbar-menu {\n    background-image: linear-gradient(141deg, black 0%, #0a0a0a 71%, #181616 100%);\n  }\n}\n\n.hero.is-light {\n  background-color: whitesmoke;\n  color: #363636;\n}\n\n.hero.is-light a:not(.button),\n.hero.is-light strong {\n  color: inherit;\n}\n\n.hero.is-light .title {\n  color: #363636;\n}\n\n.hero.is-light .subtitle {\n  color: rgba(54, 54, 54, 0.9);\n}\n\n.hero.is-light .subtitle a:not(.button),\n.hero.is-light .subtitle strong {\n  color: #363636;\n}\n\n@media screen and (max-width: 1023px) {\n  .hero.is-light .navbar-menu {\n    background-color: whitesmoke;\n  }\n}\n\n.hero.is-light .navbar-item,\n.hero.is-light .navbar-link {\n  color: rgba(54, 54, 54, 0.7);\n}\n\n.hero.is-light a.navbar-item:hover, .hero.is-light a.navbar-item.is-active,\n.hero.is-light .navbar-link:hover,\n.hero.is-light .navbar-link.is-active {\n  background-color: #e8e8e8;\n  color: #363636;\n}\n\n.hero.is-light .tabs a {\n  color: #363636;\n  opacity: 0.9;\n}\n\n.hero.is-light .tabs a:hover {\n  opacity: 1;\n}\n\n.hero.is-light .tabs li.is-active a {\n  opacity: 1;\n}\n\n.hero.is-light .tabs.is-boxed a, .hero.is-light .tabs.is-toggle a {\n  color: #363636;\n}\n\n.hero.is-light .tabs.is-boxed a:hover, .hero.is-light .tabs.is-toggle a:hover {\n  background-color: rgba(10, 10, 10, 0.1);\n}\n\n.hero.is-light .tabs.is-boxed li.is-active a, .hero.is-light .tabs.is-boxed li.is-active a:hover, .hero.is-light .tabs.is-toggle li.is-active a, .hero.is-light .tabs.is-toggle li.is-active a:hover {\n  background-color: #363636;\n  border-color: #363636;\n  color: whitesmoke;\n}\n\n.hero.is-light.is-bold {\n  background-image: linear-gradient(141deg, #dfd8d9 0%, whitesmoke 71%, white 100%);\n}\n\n@media screen and (max-width: 768px) {\n  .hero.is-light.is-bold .navbar-menu {\n    background-image: linear-gradient(141deg, #dfd8d9 0%, whitesmoke 71%, white 100%);\n  }\n}\n\n.hero.is-dark {\n  background-color: #363636;\n  color: whitesmoke;\n}\n\n.hero.is-dark a:not(.button),\n.hero.is-dark strong {\n  color: inherit;\n}\n\n.hero.is-dark .title {\n  color: whitesmoke;\n}\n\n.hero.is-dark .subtitle {\n  color: rgba(245, 245, 245, 0.9);\n}\n\n.hero.is-dark .subtitle a:not(.button),\n.hero.is-dark .subtitle strong {\n  color: whitesmoke;\n}\n\n@media screen and (max-width: 1023px) {\n  .hero.is-dark .navbar-menu {\n    background-color: #363636;\n  }\n}\n\n.hero.is-dark .navbar-item,\n.hero.is-dark .navbar-link {\n  color: rgba(245, 245, 245, 0.7);\n}\n\n.hero.is-dark a.navbar-item:hover, .hero.is-dark a.navbar-item.is-active,\n.hero.is-dark .navbar-link:hover,\n.hero.is-dark .navbar-link.is-active {\n  background-color: #292929;\n  color: whitesmoke;\n}\n\n.hero.is-dark .tabs a {\n  color: whitesmoke;\n  opacity: 0.9;\n}\n\n.hero.is-dark .tabs a:hover {\n  opacity: 1;\n}\n\n.hero.is-dark .tabs li.is-active a {\n  opacity: 1;\n}\n\n.hero.is-dark .tabs.is-boxed a, .hero.is-dark .tabs.is-toggle a {\n  color: whitesmoke;\n}\n\n.hero.is-dark .tabs.is-boxed a:hover, .hero.is-dark .tabs.is-toggle a:hover {\n  background-color: rgba(10, 10, 10, 0.1);\n}\n\n.hero.is-dark .tabs.is-boxed li.is-active a, .hero.is-dark .tabs.is-boxed li.is-active a:hover, .hero.is-dark .tabs.is-toggle li.is-active a, .hero.is-dark .tabs.is-toggle li.is-active a:hover {\n  background-color: whitesmoke;\n  border-color: whitesmoke;\n  color: #363636;\n}\n\n.hero.is-dark.is-bold {\n  background-image: linear-gradient(141deg, #1f191a 0%, #363636 71%, #46403f 100%);\n}\n\n@media screen and (max-width: 768px) {\n  .hero.is-dark.is-bold .navbar-menu {\n    background-image: linear-gradient(141deg, #1f191a 0%, #363636 71%, #46403f 100%);\n  }\n}\n\n.hero.is-primary {\n  background-color: #00d1b2;\n  color: #fff;\n}\n\n.hero.is-primary a:not(.button),\n.hero.is-primary strong {\n  color: inherit;\n}\n\n.hero.is-primary .title {\n  color: #fff;\n}\n\n.hero.is-primary .subtitle {\n  color: rgba(255, 255, 255, 0.9);\n}\n\n.hero.is-primary .subtitle a:not(.button),\n.hero.is-primary .subtitle strong {\n  color: #fff;\n}\n\n@media screen and (max-width: 1023px) {\n  .hero.is-primary .navbar-menu {\n    background-color: #00d1b2;\n  }\n}\n\n.hero.is-primary .navbar-item,\n.hero.is-primary .navbar-link {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.hero.is-primary a.navbar-item:hover, .hero.is-primary a.navbar-item.is-active,\n.hero.is-primary .navbar-link:hover,\n.hero.is-primary .navbar-link.is-active {\n  background-color: #00b89c;\n  color: #fff;\n}\n\n.hero.is-primary .tabs a {\n  color: #fff;\n  opacity: 0.9;\n}\n\n.hero.is-primary .tabs a:hover {\n  opacity: 1;\n}\n\n.hero.is-primary .tabs li.is-active a {\n  opacity: 1;\n}\n\n.hero.is-primary .tabs.is-boxed a, .hero.is-primary .tabs.is-toggle a {\n  color: #fff;\n}\n\n.hero.is-primary .tabs.is-boxed a:hover, .hero.is-primary .tabs.is-toggle a:hover {\n  background-color: rgba(10, 10, 10, 0.1);\n}\n\n.hero.is-primary .tabs.is-boxed li.is-active a, .hero.is-primary .tabs.is-boxed li.is-active a:hover, .hero.is-primary .tabs.is-toggle li.is-active a, .hero.is-primary .tabs.is-toggle li.is-active a:hover {\n  background-color: #fff;\n  border-color: #fff;\n  color: #00d1b2;\n}\n\n.hero.is-primary.is-bold {\n  background-image: linear-gradient(141deg, #009e6c 0%, #00d1b2 71%, #00e7eb 100%);\n}\n\n@media screen and (max-width: 768px) {\n  .hero.is-primary.is-bold .navbar-menu {\n    background-image: linear-gradient(141deg, #009e6c 0%, #00d1b2 71%, #00e7eb 100%);\n  }\n}\n\n.hero.is-link {\n  background-color: #7a91c1;\n  color: #fff;\n}\n\n.hero.is-link a:not(.button),\n.hero.is-link strong {\n  color: inherit;\n}\n\n.hero.is-link .title {\n  color: #fff;\n}\n\n.hero.is-link .subtitle {\n  color: rgba(255, 255, 255, 0.9);\n}\n\n.hero.is-link .subtitle a:not(.button),\n.hero.is-link .subtitle strong {\n  color: #fff;\n}\n\n@media screen and (max-width: 1023px) {\n  .hero.is-link .navbar-menu {\n    background-color: #7a91c1;\n  }\n}\n\n.hero.is-link .navbar-item,\n.hero.is-link .navbar-link {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.hero.is-link a.navbar-item:hover, .hero.is-link a.navbar-item.is-active,\n.hero.is-link .navbar-link:hover,\n.hero.is-link .navbar-link.is-active {\n  background-color: #2366d1;\n  color: #fff;\n}\n\n.hero.is-link .tabs a {\n  color: #fff;\n  opacity: 0.9;\n}\n\n.hero.is-link .tabs a:hover {\n  opacity: 1;\n}\n\n.hero.is-link .tabs li.is-active a {\n  opacity: 1;\n}\n\n.hero.is-link .tabs.is-boxed a, .hero.is-link .tabs.is-toggle a {\n  color: #fff;\n}\n\n.hero.is-link .tabs.is-boxed a:hover, .hero.is-link .tabs.is-toggle a:hover {\n  background-color: rgba(10, 10, 10, 0.1);\n}\n\n.hero.is-link .tabs.is-boxed li.is-active a, .hero.is-link .tabs.is-boxed li.is-active a:hover, .hero.is-link .tabs.is-toggle li.is-active a, .hero.is-link .tabs.is-toggle li.is-active a:hover {\n  background-color: #fff;\n  border-color: #fff;\n  color: #7a91c1;\n}\n\n.hero.is-link.is-bold {\n  background-image: linear-gradient(141deg, #1577c6 0%, #7a91c1 71%, #4366e5 100%);\n}\n\n@media screen and (max-width: 768px) {\n  .hero.is-link.is-bold .navbar-menu {\n    background-image: linear-gradient(141deg, #1577c6 0%, #7a91c1 71%, #4366e5 100%);\n  }\n}\n\n.hero.is-info {\n  background-color: #209cee;\n  color: #fff;\n}\n\n.hero.is-info a:not(.button),\n.hero.is-info strong {\n  color: inherit;\n}\n\n.hero.is-info .title {\n  color: #fff;\n}\n\n.hero.is-info .subtitle {\n  color: rgba(255, 255, 255, 0.9);\n}\n\n.hero.is-info .subtitle a:not(.button),\n.hero.is-info .subtitle strong {\n  color: #fff;\n}\n\n@media screen and (max-width: 1023px) {\n  .hero.is-info .navbar-menu {\n    background-color: #209cee;\n  }\n}\n\n.hero.is-info .navbar-item,\n.hero.is-info .navbar-link {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.hero.is-info a.navbar-item:hover, .hero.is-info a.navbar-item.is-active,\n.hero.is-info .navbar-link:hover,\n.hero.is-info .navbar-link.is-active {\n  background-color: #118fe4;\n  color: #fff;\n}\n\n.hero.is-info .tabs a {\n  color: #fff;\n  opacity: 0.9;\n}\n\n.hero.is-info .tabs a:hover {\n  opacity: 1;\n}\n\n.hero.is-info .tabs li.is-active a {\n  opacity: 1;\n}\n\n.hero.is-info .tabs.is-boxed a, .hero.is-info .tabs.is-toggle a {\n  color: #fff;\n}\n\n.hero.is-info .tabs.is-boxed a:hover, .hero.is-info .tabs.is-toggle a:hover {\n  background-color: rgba(10, 10, 10, 0.1);\n}\n\n.hero.is-info .tabs.is-boxed li.is-active a, .hero.is-info .tabs.is-boxed li.is-active a:hover, .hero.is-info .tabs.is-toggle li.is-active a, .hero.is-info .tabs.is-toggle li.is-active a:hover {\n  background-color: #fff;\n  border-color: #fff;\n  color: #209cee;\n}\n\n.hero.is-info.is-bold {\n  background-image: linear-gradient(141deg, #04a6d7 0%, #209cee 71%, #3287f5 100%);\n}\n\n@media screen and (max-width: 768px) {\n  .hero.is-info.is-bold .navbar-menu {\n    background-image: linear-gradient(141deg, #04a6d7 0%, #209cee 71%, #3287f5 100%);\n  }\n}\n\n.hero.is-success {\n  background-color: #23d160;\n  color: #fff;\n}\n\n.hero.is-success a:not(.button),\n.hero.is-success strong {\n  color: inherit;\n}\n\n.hero.is-success .title {\n  color: #fff;\n}\n\n.hero.is-success .subtitle {\n  color: rgba(255, 255, 255, 0.9);\n}\n\n.hero.is-success .subtitle a:not(.button),\n.hero.is-success .subtitle strong {\n  color: #fff;\n}\n\n@media screen and (max-width: 1023px) {\n  .hero.is-success .navbar-menu {\n    background-color: #23d160;\n  }\n}\n\n.hero.is-success .navbar-item,\n.hero.is-success .navbar-link {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.hero.is-success a.navbar-item:hover, .hero.is-success a.navbar-item.is-active,\n.hero.is-success .navbar-link:hover,\n.hero.is-success .navbar-link.is-active {\n  background-color: #20bc56;\n  color: #fff;\n}\n\n.hero.is-success .tabs a {\n  color: #fff;\n  opacity: 0.9;\n}\n\n.hero.is-success .tabs a:hover {\n  opacity: 1;\n}\n\n.hero.is-success .tabs li.is-active a {\n  opacity: 1;\n}\n\n.hero.is-success .tabs.is-boxed a, .hero.is-success .tabs.is-toggle a {\n  color: #fff;\n}\n\n.hero.is-success .tabs.is-boxed a:hover, .hero.is-success .tabs.is-toggle a:hover {\n  background-color: rgba(10, 10, 10, 0.1);\n}\n\n.hero.is-success .tabs.is-boxed li.is-active a, .hero.is-success .tabs.is-boxed li.is-active a:hover, .hero.is-success .tabs.is-toggle li.is-active a, .hero.is-success .tabs.is-toggle li.is-active a:hover {\n  background-color: #fff;\n  border-color: #fff;\n  color: #23d160;\n}\n\n.hero.is-success.is-bold {\n  background-image: linear-gradient(141deg, #12af2f 0%, #23d160 71%, #2ce28a 100%);\n}\n\n@media screen and (max-width: 768px) {\n  .hero.is-success.is-bold .navbar-menu {\n    background-image: linear-gradient(141deg, #12af2f 0%, #23d160 71%, #2ce28a 100%);\n  }\n}\n\n.hero.is-warning {\n  background-color: #ffdd57;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.hero.is-warning a:not(.button),\n.hero.is-warning strong {\n  color: inherit;\n}\n\n.hero.is-warning .title {\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.hero.is-warning .subtitle {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.hero.is-warning .subtitle a:not(.button),\n.hero.is-warning .subtitle strong {\n  color: rgba(0, 0, 0, 0.7);\n}\n\n@media screen and (max-width: 1023px) {\n  .hero.is-warning .navbar-menu {\n    background-color: #ffdd57;\n  }\n}\n\n.hero.is-warning .navbar-item,\n.hero.is-warning .navbar-link {\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.hero.is-warning a.navbar-item:hover, .hero.is-warning a.navbar-item.is-active,\n.hero.is-warning .navbar-link:hover,\n.hero.is-warning .navbar-link.is-active {\n  background-color: #ffd83d;\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.hero.is-warning .tabs a {\n  color: rgba(0, 0, 0, 0.7);\n  opacity: 0.9;\n}\n\n.hero.is-warning .tabs a:hover {\n  opacity: 1;\n}\n\n.hero.is-warning .tabs li.is-active a {\n  opacity: 1;\n}\n\n.hero.is-warning .tabs.is-boxed a, .hero.is-warning .tabs.is-toggle a {\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.hero.is-warning .tabs.is-boxed a:hover, .hero.is-warning .tabs.is-toggle a:hover {\n  background-color: rgba(10, 10, 10, 0.1);\n}\n\n.hero.is-warning .tabs.is-boxed li.is-active a, .hero.is-warning .tabs.is-boxed li.is-active a:hover, .hero.is-warning .tabs.is-toggle li.is-active a, .hero.is-warning .tabs.is-toggle li.is-active a:hover {\n  background-color: rgba(0, 0, 0, 0.7);\n  border-color: rgba(0, 0, 0, 0.7);\n  color: #ffdd57;\n}\n\n.hero.is-warning.is-bold {\n  background-image: linear-gradient(141deg, #ffaf24 0%, #ffdd57 71%, #fffa70 100%);\n}\n\n@media screen and (max-width: 768px) {\n  .hero.is-warning.is-bold .navbar-menu {\n    background-image: linear-gradient(141deg, #ffaf24 0%, #ffdd57 71%, #fffa70 100%);\n  }\n}\n\n.hero.is-danger {\n  background-color: #ff3860;\n  color: #fff;\n}\n\n.hero.is-danger a:not(.button),\n.hero.is-danger strong {\n  color: inherit;\n}\n\n.hero.is-danger .title {\n  color: #fff;\n}\n\n.hero.is-danger .subtitle {\n  color: rgba(255, 255, 255, 0.9);\n}\n\n.hero.is-danger .subtitle a:not(.button),\n.hero.is-danger .subtitle strong {\n  color: #fff;\n}\n\n@media screen and (max-width: 1023px) {\n  .hero.is-danger .navbar-menu {\n    background-color: #ff3860;\n  }\n}\n\n.hero.is-danger .navbar-item,\n.hero.is-danger .navbar-link {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.hero.is-danger a.navbar-item:hover, .hero.is-danger a.navbar-item.is-active,\n.hero.is-danger .navbar-link:hover,\n.hero.is-danger .navbar-link.is-active {\n  background-color: #ff1f4b;\n  color: #fff;\n}\n\n.hero.is-danger .tabs a {\n  color: #fff;\n  opacity: 0.9;\n}\n\n.hero.is-danger .tabs a:hover {\n  opacity: 1;\n}\n\n.hero.is-danger .tabs li.is-active a {\n  opacity: 1;\n}\n\n.hero.is-danger .tabs.is-boxed a, .hero.is-danger .tabs.is-toggle a {\n  color: #fff;\n}\n\n.hero.is-danger .tabs.is-boxed a:hover, .hero.is-danger .tabs.is-toggle a:hover {\n  background-color: rgba(10, 10, 10, 0.1);\n}\n\n.hero.is-danger .tabs.is-boxed li.is-active a, .hero.is-danger .tabs.is-boxed li.is-active a:hover, .hero.is-danger .tabs.is-toggle li.is-active a, .hero.is-danger .tabs.is-toggle li.is-active a:hover {\n  background-color: #fff;\n  border-color: #fff;\n  color: #ff3860;\n}\n\n.hero.is-danger.is-bold {\n  background-image: linear-gradient(141deg, #ff0561 0%, #ff3860 71%, #ff5257 100%);\n}\n\n@media screen and (max-width: 768px) {\n  .hero.is-danger.is-bold .navbar-menu {\n    background-image: linear-gradient(141deg, #ff0561 0%, #ff3860 71%, #ff5257 100%);\n  }\n}\n\n.hero.is-small .hero-body {\n  padding-bottom: 1.5rem;\n  padding-top: 1.5rem;\n}\n\n@media screen and (min-width: 769px), print {\n  .hero.is-medium .hero-body {\n    padding-bottom: 9rem;\n    padding-top: 9rem;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .hero.is-large .hero-body {\n    padding-bottom: 18rem;\n    padding-top: 18rem;\n  }\n}\n\n.hero.is-halfheight .hero-body, .hero.is-fullheight .hero-body {\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.hero.is-halfheight .hero-body > .container, .hero.is-fullheight .hero-body > .container {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 1;\n      flex-shrink: 1;\n}\n\n.hero.is-halfheight {\n  min-height: 50vh;\n}\n\n.hero.is-fullheight {\n  min-height: 100vh;\n}\n\n.hero-video {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  overflow: hidden;\n}\n\n.hero-video video {\n  left: 50%;\n  min-height: 100%;\n  min-width: 100%;\n  position: absolute;\n  top: 50%;\n  -webkit-transform: translate3d(-50%, -50%, 0);\n          transform: translate3d(-50%, -50%, 0);\n}\n\n.hero-video.is-transparent {\n  opacity: 0.3;\n}\n\n@media screen and (max-width: 768px) {\n  .hero-video {\n    display: none;\n  }\n}\n\n.hero-buttons {\n  margin-top: 1.5rem;\n}\n\n@media screen and (max-width: 768px) {\n  .hero-buttons .button {\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n  }\n  .hero-buttons .button:not(:last-child) {\n    margin-bottom: 0.75rem;\n  }\n}\n\n@media screen and (min-width: 769px), print {\n  .hero-buttons {\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n    -webkit-box-pack: center;\n        -ms-flex-pack: center;\n            justify-content: center;\n  }\n  .hero-buttons .button:not(:last-child) {\n    margin-right: 1.5rem;\n  }\n}\n\n.hero-head,\n.hero-foot {\n  -webkit-box-flex: 0;\n      -ms-flex-positive: 0;\n          flex-grow: 0;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n}\n\n.hero-body {\n  -webkit-box-flex: 1;\n      -ms-flex-positive: 1;\n          flex-grow: 1;\n  -ms-flex-negative: 0;\n      flex-shrink: 0;\n  padding: 3rem 1.5rem;\n}\n\n.section {\n  padding: 1.5rem 1.5rem;\n}\n\n@media screen and (min-width: 1024px) {\n  .section.is-small {\n    padding: 1.5rem 1.5rem;\n  }\n  .section.is-medium {\n    padding: 9rem 1.5rem;\n  }\n  .section.is-large {\n    padding: 18rem 1.5rem;\n  }\n}\n\n.footer {\n  background-color: whitesmoke;\n  padding: 3rem 1.5rem 6rem;\n}\n/*# sourceMappingURL=bulma.css.map */\n"
  },
  {
    "path": "www/themes/mosquitto/assets/css/local.css",
    "content": ".title {\n\tcolor: #363636;\n\tfont-size: 2rem;\n\tfont-weight: 300;\n\tline-height: 1.125;\n}\n\nfinalfooter {\n\tdisplay: block;\n}\n\n.finalfooter {\n\tbackground-color: black;\n\tcolor: whitesmoke;\n\tpadding: 1rem 1.5rem 1rem;\n\tmargin-top: 1rem;\n}\n\ndiv.footerlink a {\n\tcolor: whitesmoke;\n\tcursor: pointer;\n\ttext-decoration: none;\n\t-webkit-transition: none 86ms ease-out;\n\ttransition: none 86ms ease-out;\n\tpadding-right: 1.5rem;\n}\n\ndiv.footerlink a:hover {\n\tcolor: gray;\n}\n\n.footer {\n\tbackground-color: whitesmoke;\n\tpadding: 2rem 1.5rem 1rem;\n}\n\n.column-justify {\n\ttext-align: justify;\n}\n\n.column-justify h1 {\n\ttext-align: center;\n}\n\nimg.center {\n\tdisplay: block;\n\tmargin-left: auto;\n\tmargin-right: auto;\n}\n\n/* Blog */\nh1.p-name,\n.authorpage h1,\n.tagpage h1 /* Tag page */{\n\tfont-size: 36px;\n\tmargin-top: 20px;\n\tmargin-bottom: 10px;\n}\n\n.e-content.entry-content h2 {\n\tfont-size: 150%;\n}\n\ndiv.e-content {\n\tmargin-top: 14px;\n}\n\ndiv.e-content.entry-content p,\ndiv.e-content.entry-content div p,\ndiv.e-content.entry-content div\n{\n\tmargin-bottom: 10px;\n}\n\ndiv.e-content.entry-content ul {\n\tpadding-left: 40px;\n\tmargin-bottom: 10px;\n}\ndiv.e-content.entry-content ul li {\n\tlist-style-type: disc;\n}\n\ndiv.e-content.entry-content div blockquote {\n\tbackground-color: whitesmoke;\n\tpadding: 1.5rem;\n\tmargin: 0.5rem;\n}\n\n.pager::before,\n.pager::after {\n\tclear: both;\n\tdisplay: table;\n\tcontent: \" \";\n}\n.pager .previous a {\n\tfloat: left;\n}\n.pager .next a {\n\tfloat: right;\n}\n.pager li {\n\tdisplay: inline;\n}\n.pager li a {\n\tdisplay: inline-block;\n\tpadding: 5px 14px;\n\tborder: 1px solid #ddd;\n\tborder-radius: 15px;\n}\n\n.dateline {\n\tmargin-top: -1rem;\n}\n\n/* Tag Page */\n.listdate {\n\tmargin-right: 3rem;\n}\n"
  },
  {
    "path": "www/themes/mosquitto/assets/css/man.css",
    "content": "/* DocBook Man Page */\n\n.refentry {\n}\n\n.refsynopsisdiv h3 {\n\tfont-size: 1.5rem;\n\tfont-weight: 350;\n\tline-height: 1.125;\n\tmargin-top: 1rem;\n\tmargin-bottom: 0.5rem;\n}\n\n.refnamediv h3 {\n\tfont-size: 1.5rem;\n\tfont-weight: 350;\n\tline-height: 1.125;\n}\n\n.refnamediv p {\n\n}\n\n.refsect1 {\n}\n\n.refsect1 h3 {\n\tmargin-top: 1rem;\n\tmargin-bottom: 0.5rem;\n\tfont-weight: 350;\n\tfont-size: 1.5rem;\n}\n\n.refsect1 p {\n\tmargin-top: 1rem;\n}\n\n.refsect2 {\n}\n\n.refsect2 h4 {\n\tfont-size: 1.25rem;\n\tline-height: 1.5;\n\tmargin-top: 1rem;\n\tmargin-bottom: 1rem;\n}\n\n.note h4 {\n\tfont-size: 1.25rem;\n\tline-height: 1.5;\n\tmargin-top: 1rem;\n\tmargin-bottom: 0;\n}\n\n.note {\n\tmargin-top: 1rem;\n}\n\n.funcsynopsis {\n}\n\n.funcprototype-table {\n}\n\n.funcdef {\n\tcolor: #d73a49;\n}\n\n.funcprototype-table tr td {\n\tfont-size: 12px;\n\tcolor: #d73a49;\n\tbackground-color: whitesmoke;\n\tfont-family: monospace;\n}\n\n.fsfunc {\n\tfont-size: 12px;\n\tcolor: #6f42c1;\n\tfont-weight: normal;\n}\n\n.pdparam {\n\tfont-size: 12px;\n\tcolor: black;\n}\n\n.email {\n\tcolor: black;\n\tbackground-color: inherit;\n}\n\n.command {\n\tcolor: black;\n}\n\n.uri,\n.literal {\n\tbackground-color: inherit;\n\tcolor: inherit;\n}\n\n.filename {\n\tcolor: inherit;\n}\n\n.option {\n\tfont-family: monospace;\n\tcolor: inherit;\n}\n\n.listitem {\n\tmargin: 1rem 0 1rem 2rem;\n}\n\ndl.variablelist dt {\n\tmargin-top: 1rem;\n}\n\ndl.variablelist dd {\n\tmargin-left: 1rem;\n}\n\n\n.term {\n\tbackground-color: whitesmoke;\n\tfont-size: 0.875rem;\n\tpadding: 0.25em 0.5em 0.25em 0;\n}\n\n.term .option,\ndd p .code,\n.cmdsynopsis,\n.cmdsynopsis p .command,\n.cmdsynopsis p .option,\n.replaceable code {\n\tfont-family: monospace;\n\tbackground-color: whitesmoke;\n\tcolor: black;\n\tfont-size: 0.875rem;\n}\n\n.cmdsynopsis {\n\tpadding-top: 0.25rem;\n\tpadding-bottom: 0.25rem;\n\ttext-indent: -2rem;\n\tpadding-left: 2rem;\n}\n\n.informaltable {\n\tpadding: 0.5rem;\n}\n\n.informaltable thead tr th,\n.informaltable tbody tr td{\n\tpadding: 0.5rem;\n}\n\n.itemizedlist .listitem p {\n\tdisplay: inline-block;\n\tpadding: 0.25rem;\n\tbackground-color: whitesmoke;\n\tfont-family: monospace;\n\tfont-size: 0.875rem;\n\tlist-style-type: none;\n\tmargin-top: 0;\n\tmargin-bottom: 0.5rem;\n}\n\n.itemizedlist .listitem,\nli.listitem {\n\tmargin-top: 0;\n\tmargin-bottom: 0;\n\tlist-style-type: none;\n}\n\ndiv.itemizedlist,\nul.itemizedlist {\n\tmargin-top: 0;\n\tmargin-bottom: 1rem;\n}\n"
  },
  {
    "path": "www/themes/mosquitto/engine",
    "content": "mako\n"
  },
  {
    "path": "www/themes/mosquitto/parent",
    "content": "base\n"
  },
  {
    "path": "www/themes/mosquitto/templates/base.tmpl",
    "content": "## -*- coding: utf-8 -*-\n<%namespace name=\"base\" file=\"base_helper.tmpl\" import=\"*\"/>\n<%namespace name=\"header\" file=\"base_header.tmpl\" import=\"*\"/>\n<%namespace name=\"footer\" file=\"base_footer.tmpl\" import=\"*\"/>\n${set_locale(lang)}\n${base.html_headstart()}\n<%block name=\"extra_head\">\n### Leave this block alone.\n</%block>\n${template_hooks['extra_head']()}\n</head>\n<body class=\"layout-documentation page-grid\">\n    ${header.html_header()}\n    <div class=\"container\">\n         <main id=\"content\">\n            <%block name=\"content\"></%block>\n         </main>\n    </div>\n    ${footer.html_footer()}\n    ${base.late_load_js()}\n    <%block name=\"extra_js\"></%block>\n    ${body_end}\n    ${template_hooks['body_end']()}\n</body>\n</html>\n"
  },
  {
    "path": "www/themes/mosquitto/templates/base_footer.tmpl",
    "content": "## -*- coding: utf-8 -*-\n<%namespace name=\"base\" file=\"base_helper.tmpl\" import=\"*\"/>\n\n<%def name=\"html_footer()\">\n  %if content_footer:\n  <footer id=\"footer\" class=\"finalfooter\">\n    <div class=\"container\">\n      <div class=\"content has-text-centered\">\n        <div class=\"footerlink is-centered\">\n          <a href=\"http://www.eclipse.org\">Eclipse Home</a>\n          <a href=\"http://marketplace.eclipse.org/\">Market Place</a>\n          <a href=\"http://www.planeteclipse.org/\">Eclipse Planet</a>\n          <a href=\"https://eclipse.org/security/\">Eclipse Security</a>\n          <a href=\"http://www.eclipse.org/legal/privacy.php\">Privacy Policy</a>\n          <a href=\"http://www.eclipse.org/legal/termsofuse.php\">Terms of Use</a>\n          <a href=\"http://www.eclipse.org/legal/copyright.php\">Copyright Agent</a>\n          <a href=\"http://www.eclipse.org/legal/\">Legal</a>\n        </div>\n      </div>\n    </div>\n  </footer>\n  %endif\n</%def>\n"
  },
  {
    "path": "www/themes/mosquitto/templates/base_header.tmpl",
    "content": "## -*- coding: utf-8 -*-\n<%namespace name=\"base\" file=\"base_helper.tmpl\" import=\"*\"/>\n\n<%def name=\"html_header()\">\n    <header id=\"header\">\n        ${html_translation_header()}\n        ${html_navigation_links()}\n        %if search_form:\n            <div class=\"searchform\" role=\"search\">\n                ${search_form}\n            </div>\n        %endif\n        ${html_site_title()}\n    </header>\n    ${template_hooks['page_header']()}\n</%def>\n\n<%def name=\"html_site_title()\">\n  % if (post and post.title):\n    <section class=\"hero\">\n      <div class=\"hero-body is-paddingless\">\n      <div class=\"container has-text-centered\">\n    % if title == blog_title:\n        <h1 class=\"title\">Eclipse Mosquitto™</h1>\n        <h2 class=\"subtitle\">An open source MQTT broker</h2>\n    % elif post and post.title:\n        <h1 class=\"title\" itemprop=\"headline name\">${post.title()|h}</h1>\n    % endif\n      </div>\n    </div>\n    <hr>\n  </section>\n  % endif\n</%def>\n\n<%def name=\"html_navigation_links()\">\n  <script>\ndocument.addEventListener('DOMContentLoaded', function () {\n\n  // Get all \"navbar-burger\" elements\n  var $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);\n\n  // Check if there are any navbar burgers\n  if ($navbarBurgers.length > 0) {\n\n    // Add a click event on each of them\n    $navbarBurgers.forEach(function ($el) {\n      $el.addEventListener('click', function () {\n\n        // Get the target from the \"data-target\" attribute\n        var target = $el.dataset.target;\n        var $target = document.getElementById(target);\n\n        // Toggle the class on both the \"navbar-burger\" and the \"navbar-menu\"\n        $el.classList.toggle('is-active');\n        $target.classList.toggle('is-active');\n\n      });\n    });\n  }\n\n});</script>\n  <div class=\"container\">\n    <nav class=\"navbar\" role=\"navigation\" aria-label=\"main navigation\">\n      <div class=\"navbar-brand\">\n        <a class=\"navbar-item\" href=\"https://mosquitto.org/\">\n          <img src=\"/images/mosquitto-text-side-28.png\" alt=\"Mosquitto: An Open Source MQTT Server\" width=\"139\" height=\"28\">\n        </a>\n        <a class=\"navbar-item\" href=\"https://eclipse.org/\">\n          <img src=\"/images/eclipse_logo_colour.png\" alt=\"The Eclipse Foundation\">\n        </a>\n        <button class=\"button navbar-burger\" data-target=\"navMenu\">\n          <span></span>\n          <span></span>\n          <span></span>\n        </button>\n      </div>\n      <div class=\"navbar-menu\" id=\"navMenu\">\n        <div class=\"navbar-end\">\n\n    %for url, text in navigation_links[lang]:\n        % if isinstance(url, tuple):\n      <div class=\"navbar-item has-dropdown is-hoverable\">\n        <a class=\"navbar-link\">${text}</a>\n        <div class=\"navbar-dropdown\">\n            %for suburl, text in url:\n                % if rel_link(permalink, suburl) == \"#\":\n                    <a class=\"navbar-item\" href=\"${permalink}\">${text}</a>\n                %else:\n                     <a class=\"navbar-item\" href=\"${suburl}\">${text}</a>\n                %endif\n            %endfor\n        </div>\n      </div>\n        % else:\n            % if rel_link(permalink, url) == \"#\":\n                <a class=\"navbar-item\" href=\"${permalink}\">${text}</a>\n            %else:\n                <a class=\"navbar-item\" href=\"${url}\">${text}</a>\n            %endif\n        % endif\n    %endfor\n    ${template_hooks['menu']()}\n    ${template_hooks['menu_alt']()}\n\n        </div>\n      </div>\n       </nav>\n    <hr style=\"margin-top: 0rem\">\n  </div>\n</%def>\n\n<%def name=\"html_translation_header()\">\n    %if len(translations) > 1:\n        <div id=\"toptranslations\">\n            <h2>${messages(\"Languages:\")}</h2>\n            ${base.html_translations()}\n        </div>\n    %endif\n</%def>\n"
  },
  {
    "path": "www/themes/mosquitto/templates/base_helper.tmpl",
    "content": "## -*- coding: utf-8 -*-\n\n<%def name=\"html_headstart()\">\n<!DOCTYPE html>\n<html \\\nprefix='\\\n%if use_open_graph or (twitter_card and twitter_card['use_twitter_cards']):\nog: http://ogp.me/ns# article: http://ogp.me/ns/article# \\\n%endif\n%if comment_system == 'facebook':\nfb: http://ogp.me/ns/fb#\n%endif\n' \\\n%if use_open_graph or (twitter_card and twitter_card['use_twitter_cards']):\nvocab=\"http://ogp.me/ns\" \\\n%endif\n% if is_rtl:\ndir=\"rtl\" \\\n% endif\n\\\nlang=\"${lang}\">\n<head>\n    <meta charset=\"utf-8\">\n\n    % if use_base_tag:\n    <base href=\"${abs_link(permalink)}\">\n    % endif\n    %if description:\n    <meta name=\"description\" content=\"${description|h}\">\n    %endif\n    <meta name=\"viewport\" content=\"width=device-width\">\n    %if title == blog_title:\n        <title>${blog_title|h}</title>\n    %else:\n        <title>${title|h} | ${blog_title|h}</title>\n    %endif\n\n    ${html_stylesheets()}\n    <meta name=\"theme-color\" content=\"${theme_color}\">\n    % if meta_generator_tag:\n    <meta name=\"generator\" content=\"Nikola (getnikola.com)\">\n    % endif\n    ${html_feedlinks()}\n    <link rel=\"canonical\" href=\"${abs_link(permalink)}\">\n\n    %if favicons:\n        %for name, file, size in favicons:\n            <link rel=\"${name}\" href=\"${file}\" sizes=\"${size}\"/>\n        %endfor\n    %endif\n\n    % if comment_system == 'facebook':\n        <meta property=\"fb:app_id\" content=\"${comment_system_id}\">\n    % endif\n\n    %if prevlink:\n        <link rel=\"prev\" href=\"${prevlink}\" type=\"text/html\">\n    %endif\n    %if nextlink:\n        <link rel=\"next\" href=\"${nextlink}\" type=\"text/html\">\n    %endif\n\n    %if use_cdn:\n        <!--[if lt IE 9]><script src=\"https://html5shim.googlecode.com/svn/trunk/html5.js\"></script><![endif]-->\n    %else:\n        <!--[if lt IE 9]><script src=\"${url_replacer(permalink, '/assets/js/html5.js', lang, url_type)}\"></script><![endif]-->\n    %endif\n\n    ${extra_head_data}\n</%def>\n\n<%def name=\"late_load_js()\">\n    ${social_buttons_code}\n</%def>\n\n<%def name=\"html_stylesheets()\">\n    %if use_bundles:\n        %if use_cdn:\n            <link href=\"/assets/css/all.css\" rel=\"stylesheet\" type=\"text/css\">\n        %else:\n            <link href=\"/assets/css/all-nocdn.css\" rel=\"stylesheet\" type=\"text/css\">\n        %endif\n    %else:\n        <link href=\"/assets/css/rst.css\" rel=\"stylesheet\" type=\"text/css\">\n        <link href=\"/assets/css/code.css\" rel=\"stylesheet\" type=\"text/css\">\n        <link href=\"/assets/css/bulma.css\" rel=\"stylesheet\" type=\"text/css\">\n        <link href=\"/assets/css/local.css\" rel=\"stylesheet\" type=\"text/css\">\n        <link href=\"/assets/css/man.css\" rel=\"stylesheet\" type=\"text/css\">\n        %if has_custom_css:\n            <link href=\"/assets/css/custom.css\" rel=\"stylesheet\" type=\"text/css\">\n        %endif\n    %endif\n    % if needs_ipython_css:\n        <link href=\"/assets/css/ipython.min.css\" rel=\"stylesheet\" type=\"text/css\">\n        <link href=\"/assets/css/nikola_ipython.css\" rel=\"stylesheet\" type=\"text/css\">\n    % endif\n</%def>\n\n<%def name=\"html_feedlinks()\">\n    %if rss_link:\n        ${rss_link}\n    %elif generate_rss:\n        %if len(translations) > 1:\n            %for language in sorted(translations):\n                <link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS (${language})\" href=\"${_link('rss', None, language)}\">\n            %endfor\n        %else:\n            <link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"${_link('rss', None)}\">\n        %endif\n    %endif\n    %if generate_atom:\n        %if len(translations) > 1:\n            %for language in sorted(translations):\n                <link rel=\"alternate\" type=\"application/atom+xml\" title=\"Atom (${language})\" href=\"${_link('index_atom', None, language)}\">\n            %endfor\n        %else:\n            <link rel=\"alternate\" type=\"application/atom+xml\" title=\"Atom\" href=\"${_link('index_atom', None)}\">\n        %endif\n    %endif\n</%def>\n\n<%def name=\"html_translations()\">\n    <ul class=\"translations\">\n    %for langname in sorted(translations):\n        %if langname != lang:\n            <li><a href=\"${abs_link(_link(\"root\", None, langname))}\" rel=\"alternate\" hreflang=\"${langname}\">${messages(\"LANGUAGE\", langname)}</a></li>\n        %endif\n    %endfor\n    </ul>\n</%def>\n"
  },
  {
    "path": "www/themes/mosquitto/templates/index.tmpl",
    "content": "## -*- coding: utf-8 -*-\n<%namespace name=\"helper\" file=\"index_helper.tmpl\"/>\n<%namespace name=\"math\" file=\"math_helper.tmpl\"/>\n<%namespace name=\"comments\" file=\"comments_helper.tmpl\"/>\n<%namespace name=\"pagination\" file=\"pagination_helper.tmpl\"/>\n<%inherit file=\"base.tmpl\"/>\n\n<%block name=\"extra_head\">\n    ${parent.extra_head()}\n    % if posts and (permalink == '/' or permalink == '/' + index_file):\n        <link rel=\"prefetch\" href=\"${posts[0].permalink()}\" type=\"text/html\">\n    % endif\n    ${math.math_styles_ifposts(posts)}\n</%block>\n\n<%block name=\"content\">\n<%block name=\"content_header\"></%block>\n% if 'main_index' in pagekind:\n    ${front_index_header}\n% endif\n% if page_links:\n    ${pagination.page_navigation(current_page, page_links, prevlink, nextlink, prev_next_links_reversed)}\n% endif\n<div class=\"postindex\">\n% for post in posts:\n    <article class=\"h-entry post-${post.meta('type')}\">\n    <header>\n        <h1 class=\"p-name entry-title\"><a href=\"${post.permalink()}\" class=\"u-url\">${post.title()|h}</a></h1>\n        <div class=\"metadata\">\n            ##<p class=\"byline author vcard\"><span class=\"byline-name fn\" itemprop=\"author\">\n            ##% if author_pages_generated:\n                ##<a href=\"${_link('author', post.author())}\">${post.author()|h}</a>\n            ##% else:\n                ##${post.author()|h}\n            ##% endif\n            ##</span></p>\n            <p class=\"dateline\"><a href=\"${post.permalink()}\" rel=\"bookmark\"><time class=\"published dt-published\" datetime=\"${post.formatted_date('webiso')}\" title=\"${post.formatted_date(date_format)|h}\">${post.formatted_date(date_format)|h}</time></a></p>\n            % if not post.meta('nocomments') and site_has_comments:\n                <p class=\"commentline\">${comments.comment_link(post.permalink(), post._base_path)}\n            % endif\n        </div>\n    </header>\n    %if index_teasers:\n    <div class=\"p-summary entry-summary\">\n    ${post.text(teaser_only=True)}\n    %else:\n    <div class=\"e-content entry-content\">\n    ${post.text(teaser_only=False)}\n    %endif\n    </div>\n\t<hr>\n    </article>\n% endfor\n</div>\n${helper.html_pager()}\n${comments.comment_link_script()}\n</%block>\n"
  },
  {
    "path": "www/themes/mosquitto/templates/post.tmpl",
    "content": "## -*- coding: utf-8 -*-\n<%namespace name=\"helper\" file=\"post_helper.tmpl\"/>\n<%namespace name=\"pheader\" file=\"post_header.tmpl\"/>\n<%namespace name=\"comments\" file=\"comments_helper.tmpl\"/>\n<%namespace name=\"math\" file=\"math_helper.tmpl\"/>\n<%inherit file=\"base.tmpl\"/>\n\n<%block name=\"extra_head\">\n    ${parent.extra_head()}\n    % if post.meta('keywords'):\n    <meta name=\"keywords\" content=\"${post.meta('keywords')|h}\">\n    % endif\n    ##<meta name=\"author\" content=\"${post.author()|h}\">\n    %if post.prev_post:\n        <link rel=\"prev\" href=\"${post.prev_post.permalink()}\" title=\"${post.prev_post.title()|h}\" type=\"text/html\">\n    %endif\n    %if post.next_post:\n        <link rel=\"next\" href=\"${post.next_post.permalink()}\" title=\"${post.next_post.title()|h}\" type=\"text/html\">\n    %endif\n    % if post.is_draft:\n        <meta name=\"robots\" content=\"noindex\">\n    % endif\n    ${helper.open_graph_metadata(post)}\n    ${helper.twitter_card_information(post)}\n    ${helper.meta_translations(post)}\n    ${math.math_styles_ifpost(post)}\n</%block>\n\n<%block name=\"content\">\n<article class=\"post-${post.meta('type')} h-entry hentry postpage\" itemscope=\"itemscope\" itemtype=\"http://schema.org/Article\">\n    ${pheader.html_post_header()}\n    <div class=\"content\">\n    ${post.text()}\n    </div>\n    <aside class=\"postpromonav\">\n    <nav>\n    ${helper.html_tags(post)}\n    ${helper.html_pager(post)}\n    </nav>\n    </aside>\n    % if not post.meta('nocomments') and site_has_comments:\n        <section class=\"comments hidden-print\">\n        <h2>${messages(\"Comments\")}</h2>\n        ${comments.comment_form(post.permalink(absolute=True), post.title(), post._base_path)}\n        </section>\n    % endif\n    ${math.math_scripts_ifpost(post)}\n</article>\n${comments.comment_link_script()}\n</%block>\n"
  },
  {
    "path": "www/themes/mosquitto/templates/post_header.tmpl",
    "content": "## -*- coding: utf-8 -*-\n<%namespace name=\"helper\" file=\"post_helper.tmpl\"/>\n<%namespace name=\"comments\" file=\"comments_helper.tmpl\"/>\n\n<%def name=\"html_title()\">\n%if title and not post.meta('hidetitle'):\n    <h1 class=\"p-name entry-title\" itemprop=\"headline name\"><a href=\"${post.permalink()}\" class=\"u-url\">${post.title()|h}</a></h1>\n%endif\n</%def>\n\n<%def name=\"html_translations(post)\">\n    % if len(post.translated_to) > 1:\n        <div class=\"metadata posttranslations translations\">\n            <h3 class=\"posttranslations-intro\">${messages(\"Also available in:\")}</h3>\n            % for langname in sorted(translations):\n                % if langname != lang and post.is_translation_available(langname):\n                <p><a href=\"${post.permalink(langname)}\" rel=\"alternate\" hreflang=\"${langname}\">${messages(\"LANGUAGE\", langname)}</a></p>\n                % endif\n            % endfor\n        </div>\n    % endif\n</%def>\n\n<%def name=\"html_sourcelink()\">\n    % if show_sourcelink:\n        <p class=\"sourceline\"><a href=\"${post.source_link()}\" class=\"sourcelink\">${messages(\"Source\")}</a></p>\n    % endif\n</%def>\n\n<%def name=\"html_post_header()\">\n    <header>\n        ##${html_title()}\n        <div class=\"metadata\">\n            ##<p class=\"byline author vcard\"><span class=\"byline-name fn\" itemprop=\"author\">\n                ##% if author_pages_generated:\n                    ##<a href=\"${_link('author', post.author())}\">${post.author()|h}</a>\n                ##% else:\n                    ##${post.author()|h}\n                ##% endif\n            ##</span></p>\n            <p class=\"dateline\">\n        <a href=\"${post.permalink()}\" rel=\"bookmark\"><time class=\"published dt-published\" datetime=\"${post.formatted_date('webiso')}\" itemprop=\"datePublished\" title=\"${post.formatted_date(date_format)|h}\">${post.formatted_date(date_format)|h}</time></a>\n        % if post.author() != blog_author:\n                  % if author_pages_generated:\n                      | <a href=\"${_link('author', post.author())}\">${post.author()|h}</a>\n                  % else:\n                      | ${post.author()|h}\n                  % endif\n        % endif\n      </p>\n            % if not post.meta('nocomments') and site_has_comments:\n                <p class=\"commentline\">${comments.comment_link(post.permalink(), post._base_path)}\n            % endif\n            ${html_sourcelink()}\n            % if post.meta('link'):\n                    <p class=\"linkline\"><a href=\"${post.meta('link')}\">${messages(\"Original site\")}</a></p>\n            % endif\n        </div>\n        ${html_translations(post)}\n    </header>\n</%def>\n"
  },
  {
    "path": "www/themes/mosquitto/templates/story.tmpl",
    "content": "## -*- coding: utf-8 -*-\n<%namespace name=\"helper\" file=\"post_helper.tmpl\"/>\n<%namespace name=\"pheader\" file=\"post_header.tmpl\"/>\n<%namespace name=\"comments\" file=\"comments_helper.tmpl\"/>\n<%namespace name=\"math\" file=\"math_helper.tmpl\"/>\n<%inherit file=\"post.tmpl\"/>\n\n<%block name=\"content\">\n<article class=\"post-${post.meta('type')} storypage\" itemscope=\"itemscope\" itemtype=\"http://schema.org/Article\">\n    <header>\n        ###${pheader.html_title()}\n        ${pheader.html_translations(post)}\n    </header>\n    <div class=\"content\">\n    ${post.text()}\n    </div>\n    %if site_has_comments and enable_comments and not post.meta('nocomments'):\n        <section class=\"comments\">\n        <h2>${messages(\"Comments\")}</h2>\n        ${comments.comment_form(post.permalink(absolute=True), post.title(), post.base_path)}\n        </section>\n    %endif\n    ${math.math_scripts_ifpost(post)}\n</article>\n</%block>\n"
  }
]